2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
41
package org.netbeans.modules.groovy.grailsproject.ui;
43
import java.awt.Image;
44
import java.beans.PropertyChangeEvent;
45
import java.beans.PropertyChangeListener;
46
import java.util.ArrayList;
47
import java.util.Collections;
48
import java.util.List;
49
import java.util.StringTokenizer;
50
import javax.swing.Icon;
51
import javax.swing.event.ChangeEvent;
52
import javax.swing.event.ChangeListener;
53
import javax.swing.event.EventListenerList;
54
import org.netbeans.api.project.SourceGroup;
55
import org.netbeans.api.queries.VisibilityQuery;
56
import org.openide.DialogDisplayer;
57
import org.openide.NotifyDescriptor;
58
import org.openide.filesystems.FileObject;
59
import org.openide.filesystems.FileUtil;
60
import org.openide.loaders.ChangeableDataFilter;
61
import org.openide.loaders.DataFolder;
62
import org.openide.loaders.DataObject;
63
import org.openide.nodes.FilterNode;
64
import org.openide.nodes.Node;
65
import org.openide.nodes.NodeNotFoundException;
66
import org.openide.nodes.NodeOp;
67
import org.openide.util.NbBundle;
68
import org.openide.util.Utilities;
69
import org.openide.util.WeakListeners;
70
import org.openide.util.lookup.Lookups;
71
import org.openide.util.lookup.ProxyLookup;
72
import javax.swing.Action;
73
import org.netbeans.modules.groovy.grailsproject.actions.*;
74
import org.netbeans.modules.groovy.grailsproject.SourceCategory;
76
import java.util.logging.Logger;
80
* @author Martin Adamek
82
public final class TreeRootNode extends FilterNode implements PropertyChangeListener {
84
private static Image PACKAGE_BADGE = Utilities.loadImage("org/netbeans/modules/groovy/grailsproject/resources/packageBadge.gif"); // NOI18N
85
private final SourceGroup g;
86
private SourceCategory category = SourceCategory.NONE;
87
private final Logger LOG = Logger.getLogger(TreeRootNode.class.getName());
89
public TreeRootNode(SourceGroup g) {
90
this(DataFolder.findFolder(g.getRootFolder()), g);
92
int lastSlash = g.getName().lastIndexOf(File.separator);
93
String dirName = g.getName().substring(lastSlash + 1);
96
if (dirName.startsWith("conf")) {
97
category = SourceCategory.CONFIGURATION;
98
} else if (dirName.startsWith("controllers")) {
99
category = SourceCategory.CONTROLLERS;
100
} else if (dirName.startsWith("domain")) {
101
category = SourceCategory.DOMAIN;
102
} else if (dirName.startsWith("i18n")) {
103
category = SourceCategory.MESSAGES;
104
} else if (dirName.startsWith("services")) {
105
category = SourceCategory.SERVICES;
106
} else if (dirName.startsWith("taglib")) {
107
category = SourceCategory.TAGLIB;
108
} else if (dirName.startsWith("util")) {
109
category = SourceCategory.UTIL;
110
} else if (dirName.startsWith("views")) {
111
category = SourceCategory.VIEWS;
115
private TreeRootNode(DataFolder folder, SourceGroup g) {
116
this(new FilterNode(folder.getNodeDelegate(), folder.createNodeChildren(new VisibilityQueryDataFilter(g))), g);
119
private TreeRootNode(Node originalNode, SourceGroup g) {
120
super(originalNode, new PackageFilterChildren(originalNode),
122
originalNode.getLookup(),
123
Lookups.singleton(new PathFinder(g)) // no need for explicit search info
126
g.addPropertyChangeListener(WeakListeners.propertyChange(this, g));
129
/* Here we can customize the Actions on the different source-directories.
130
Dispatching based on the Nodes name (better type) needs to be done here.
132
public Action[] getActions(boolean context) {
133
List<Action> result = new ArrayList<Action>();
137
result.add(new NewConfigurationAction());
140
result.add(new NewControllerAction());
143
result.add(new NewDomainAction());
146
result.add(new NewMessageAction());
149
result.add(new NewServiceAction());
152
result.add(new NewTaglibAction());
154
case UTIL: // result.add(new NewU());
157
result.add(new NewViewAction());
161
return result.toArray(new Action[result.size()]);
164
// public NewType[] getNewTypes() {
165
// return new NewType[] { new NewType() {
166
// public String getName() {
167
// return "create somethin new...";
169
// public HelpCtx getHelpCtx() {
170
// return new HelpCtx("help me");
172
// public void create() {
177
/** Copied from PackageRootNode with modifications. */
178
private Image computeIcon(boolean opened, int type) {
179
Icon icon = g.getIcon(opened);
181
Image image = opened ? super.getOpenedIcon(type) : super.getIcon(type);
182
return Utilities.mergeImages(image, PACKAGE_BADGE, 7, 7);
184
return Utilities.icon2Image(icon);
188
public Image getIcon(int type) {
189
return computeIcon(false, type);
192
public Image getOpenedIcon(int type) {
193
return computeIcon(true, type);
196
public String getName() {
200
public String getDisplayName() {
201
return g.getDisplayName();
204
public boolean canRename() {
208
public boolean canDestroy() {
212
public boolean canCut() {
216
public void propertyChange(PropertyChangeEvent ev) {
217
// XXX handle SourceGroup.rootFolder change too
218
fireNameChange(null, null);
219
fireDisplayNameChange(null, null);
221
fireOpenedIconChange();
224
public static Node findPath(Node rootNode, Object object) {
226
TreeRootNode.PathFinder pf = rootNode.getLookup().lookup(TreeRootNode.PathFinder.class);
229
return pf.findPath(rootNode, object);
231
TreeRootNode.PathFinder pf2 = rootNode.getLookup().lookup(TreeRootNode.PathFinder.class);
233
return pf2.findPath(rootNode, object);
240
/** Copied from PhysicalView and PackageRootNode. */
241
public static final class PathFinder {
243
private final SourceGroup g;
245
PathFinder(SourceGroup g) {
249
public Node findPath(Node rootNode, Object o) {
251
if (o instanceof FileObject) {
253
} else if (o instanceof DataObject) {
254
fo = ((DataObject) o).getPrimaryFile();
258
FileObject groupRoot = g.getRootFolder();
259
if (FileUtil.isParentOf(groupRoot, fo) /* && group.contains(fo) */) {
260
FileObject folder = fo.isFolder() ? fo : fo.getParent();
261
String relPath = FileUtil.getRelativePath(groupRoot, folder);
262
List<String> path = new ArrayList<String>();
263
StringTokenizer strtok = new StringTokenizer(relPath, File.separator); // NOI18N
264
while (strtok.hasMoreTokens()) {
265
String token = strtok.nextToken();
269
Node folderNode = folder.equals(groupRoot) ? rootNode : NodeOp.findPath(rootNode, Collections.enumeration(path));
273
Node[] childs = folderNode.getChildren().getNodes(true);
274
for (int i = 0; i < childs.length; i++) {
275
DataObject dobj = childs[i].getLookup().lookup(DataObject.class);
276
if (dobj != null && dobj.getPrimaryFile().getNameExt().equals(fo.getNameExt())) {
281
} catch (NodeNotFoundException e) {
284
} else if (groupRoot.equals(fo)) {
291
private static final class VisibilityQueryDataFilter implements ChangeListener, PropertyChangeListener, ChangeableDataFilter {
293
private static final long serialVersionUID = 1L; // in case a DataFolder.ClonedFilterHandle saves me
294
private final EventListenerList ell = new EventListenerList();
295
private final SourceGroup g;
297
public VisibilityQueryDataFilter(SourceGroup g) {
299
VisibilityQuery.getDefault().addChangeListener(WeakListeners.change(this, VisibilityQuery.getDefault()));
300
g.addPropertyChangeListener(WeakListeners.propertyChange(this, g));
303
public boolean acceptDataObject(DataObject obj) {
304
FileObject fo = obj.getPrimaryFile();
305
return g.contains(fo) && VisibilityQuery.getDefault().isVisible(fo);
308
public void stateChanged(ChangeEvent e) {
312
public void propertyChange(PropertyChangeEvent e) {
313
if (SourceGroup.PROP_CONTAINERSHIP.equals(e.getPropertyName())) {
318
private void fireChange() {
319
Object[] listeners = ell.getListenerList();
320
ChangeEvent event = null;
321
for (int i = listeners.length - 2; i >= 0; i -= 2) {
322
if (listeners[i] == ChangeListener.class) {
324
event = new ChangeEvent(this);
326
((ChangeListener) listeners[i + 1]).stateChanged(event);
331
public void addChangeListener(ChangeListener listener) {
332
ell.add(ChangeListener.class, listener);
335
public void removeChangeListener(ChangeListener listener) {
336
ell.remove(ChangeListener.class, listener);
340
private static final class PackageFilterChildren extends FilterNode.Children {
342
public PackageFilterChildren(final Node originalNode) {
347
protected Node copyNode(final Node originalNode) {
348
DataObject dobj = originalNode.getLookup().lookup(DataObject.class);
349
return (dobj instanceof DataFolder) ? new PackageFilterNode(originalNode) : super.copyNode(originalNode);
353
private static final class PackageFilterNode extends FilterNode {
355
public PackageFilterNode(final Node origNode) {
356
super(origNode, new PackageFilterChildren(origNode));
360
public void setName(final String name) {
361
if (Utilities.isJavaIdentifier(name)) {
364
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
365
NbBundle.getMessage(TreeRootNode.class, "MSG_InvalidPackageName"), NotifyDescriptor.INFORMATION_MESSAGE));