/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. * * Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common * Development and Distribution License("CDDL") (collectively, the * "License"). You may not use this file except in compliance with the * License. You can obtain a copy of the License at * http://www.netbeans.org/cddl-gplv2.html * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the * specific language governing permissions and limitations under the * License. When distributing the software, include this License Header * Notice in each file and include the License file at * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the * License Header, with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * Contributor(s): * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun * Microsystems, Inc. All Rights Reserved. * * If you wish your version of this file to be governed by only the CDDL * or only the GPL Version 2, indicate your decision by adding * "[Contributor] elects to include this software in this distribution * under the [CDDL or GPL Version 2] license." If you do not indicate a * single choice of license, a recipient has the option to distribute * your version of this file under either the CDDL, the GPL Version 2 or * to extend the choice of license to its licensees as provided above. * However, if you add GPL Version 2 code and therefore, elected the GPL * Version 2 license, then the option applies only if the new code is * made subject to such option by the copyright holder. */ package org.netbeans.modules.ruby.railsprojects.ui; import java.awt.Image; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import javax.swing.Action; import javax.swing.Icon; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.EventListenerList; import org.netbeans.modules.ruby.railsprojects.GenerateAction; import org.netbeans.modules.ruby.railsprojects.Generator; import org.netbeans.api.project.SourceGroup; import org.netbeans.api.queries.VisibilityQuery; import org.netbeans.spi.project.ui.support.CommonProjectActions; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.loaders.ChangeableDataFilter; import org.openide.loaders.DataFilter; import org.openide.loaders.DataFolder; import org.openide.loaders.DataObject; import org.openide.nodes.FilterNode; import org.openide.nodes.Node; import org.openide.nodes.NodeNotFoundException; import org.openide.nodes.NodeOp; import org.openide.util.ImageUtilities; import org.openide.util.WeakListeners; import org.openide.util.actions.SystemAction; import org.openide.util.lookup.Lookups; import org.openide.util.lookup.ProxyLookup; // XXX need unit test /** * (Copied from Java Project Source (org.netbeans.modules.gsfpath.spi.project.support.ui) * Displays a package root in a tree. * @see "#42151" * @author Jesse Glick */ public final class TreeRootNode extends FilterNode implements PropertyChangeListener { private static final DataFilter VISIBILITY_QUERY_FILTER = new VisibilityQueryDataFilter(); private final SourceGroup g; private final Generator generator; public TreeRootNode(SourceGroup g, Generator generator) { this(DataFolder.findFolder(g.getRootFolder()), g, generator); } private TreeRootNode(DataFolder folder, SourceGroup g, Generator generator) { this (new FilterNode (folder.getNodeDelegate(), folder.createNodeChildren(VISIBILITY_QUERY_FILTER)), g, generator); } private TreeRootNode (Node originalNode, SourceGroup g, Generator generator) { super(originalNode, new PackageFilterChildren(originalNode), new ProxyLookup( originalNode.getLookup(), Lookups.singleton(generator), Lookups.singleton(new PathFinder(g)) // no need for explicit search info )); this.g = g; this.generator = generator; g.addPropertyChangeListener(WeakListeners.propertyChange(this, g)); } private final static Image PACKAGE_BADGE = ImageUtilities.loadImage("org/netbeans/modules/ruby/railsprojects/ui/packageBadge.gif"); // NOI18N /** Copied from PackageRootNode with modifications. */ private Image computeIcon(boolean opened, int type) { Icon icon = g.getIcon(opened); if (icon == null) { Image image = opened ? super.getOpenedIcon(type) : super.getIcon(type); return ImageUtilities.mergeImages(image, /*PackageRootNode.*/PACKAGE_BADGE, 7, 7); } else { return ImageUtilities.icon2Image(icon); } } @Override public Image getIcon(int type) { return computeIcon(false, type); } @Override public Image getOpenedIcon(int type) { return computeIcon(true, type); } @Override public String getName() { return g.getName(); } @Override public String getDisplayName() { return g.getDisplayName(); } @Override public boolean canRename() { return false; } @Override public boolean canDestroy() { return false; } @Override public boolean canCut() { return false; } public void propertyChange(PropertyChangeEvent ev) { // XXX handle SourceGroup.rootFolder change too fireNameChange(null, null); fireDisplayNameChange(null, null); fireIconChange(); fireOpenedIconChange(); } @Override public Action[] getActions(boolean context) { return initActions(context); } // From ActionFilterNode private Action[] actionCache; private Action[] initActions (boolean context) { if (actionCache == null) { Action[] existing = super.getActions(context); Action[] additional; if (generator == Generator.NONE) { additional = new Action[] { CommonProjectActions.newFileAction(), null }; // null: separator } else { additional = new Action[] { SystemAction.get(GenerateAction.class), null, CommonProjectActions.newFileAction(), null }; // null: separator } if (existing != null && existing.length > 0) { actionCache = new Action[existing.length+additional.length]; System.arraycopy(additional, 0, actionCache, 0, additional.length); System.arraycopy(existing, 0, actionCache, additional.length, existing.length); } else { actionCache = additional; } } return actionCache; } /** Copied from PhysicalView and PackageRootNode. */ public static final class PathFinder { private final SourceGroup g; PathFinder(SourceGroup g) { this.g = g; } public Node findPath(Node rootNode, Object o) { FileObject fo; if (o instanceof FileObject) { fo = (FileObject) o; } else if (o instanceof DataObject) { fo = ((DataObject) o).getPrimaryFile(); } else { return null; } FileObject groupRoot = g.getRootFolder(); if (FileUtil.isParentOf(groupRoot, fo) /* && group.contains(fo) */) { FileObject folder = fo.isFolder() ? fo : fo.getParent(); String relPath = FileUtil.getRelativePath(groupRoot, folder); List<String> path = new ArrayList<String>(); StringTokenizer strtok = new StringTokenizer(relPath, "/"); // NOI18N while (strtok.hasMoreTokens()) { String token = strtok.nextToken(); path.add(token); } try { Node folderNode = folder.equals(groupRoot) ? rootNode : NodeOp.findPath(rootNode, Collections.enumeration(path)); if (fo.isFolder()) { return folderNode; } else { Node[] childs = folderNode.getChildren().getNodes(true); for (int i = 0; i < childs.length; i++) { DataObject dobj = childs[i].getLookup().lookup(DataObject.class); if (dobj != null && dobj.getPrimaryFile().getNameExt().equals(fo.getNameExt())) { return childs[i]; } } } } catch (NodeNotFoundException e) { e.printStackTrace(); } } else if (groupRoot.equals(fo)) { return rootNode; } return null; } } /** Copied from PhysicalView. */ private static final class VisibilityQueryDataFilter implements ChangeListener, ChangeableDataFilter { private static final long serialVersionUID = 1L; // in case a DataFolder.ClonedFilterHandle saves me private final EventListenerList ell = new EventListenerList(); public VisibilityQueryDataFilter() { VisibilityQuery.getDefault().addChangeListener(this); } public boolean acceptDataObject(DataObject obj) { FileObject fo = obj.getPrimaryFile(); return VisibilityQuery.getDefault().isVisible(fo); } public void stateChanged(ChangeEvent e) { Object[] listeners = ell.getListenerList(); ChangeEvent event = null; for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChangeListener.class) { if (event == null) { event = new ChangeEvent(this); } ((ChangeListener) listeners[i+1]).stateChanged(event); } } } public void addChangeListener(ChangeListener listener) { ell.add(ChangeListener.class, listener); } public void removeChangeListener(ChangeListener listener) { ell.remove(ChangeListener.class, listener); } } private static final class PackageFilterChildren extends FilterNode.Children { public PackageFilterChildren (final Node originalNode) { super (originalNode); } @Override protected Node copyNode(final Node originalNode) { DataObject dobj = originalNode.getLookup().lookup(DataObject.class); return (dobj instanceof DataFolder) ? new FilterNode(originalNode, new PackageFilterChildren(originalNode)) : super.copyNode(originalNode); } } }