[[nbpython-commits]] [hg] main/contrib: (1) Package view for Python projects in the p...

  • From: Tor Norbye < >
  • To:
  • Subject: [[nbpython-commits]] [hg] main/contrib: (1) Package view for Python projects in the p...
  • Date: Thu, 21 Oct 2010 08:34:29 -0700

changeset 3d60af5cc330 in main/contrib
details: http://hg.netbeans.org/main/contrib?cmd=changeset;node=3d60af5cc330
description:
        (1) Package view for Python projects in the project explorer (2) 
Filter out .pyc and .pyo files, (3) Optional tree view selectable in the 
project context menu

diffstat:

 python.project/nbproject/project.xml                                         
              |    91 +-
 
python.project/src/org/netbeans/modules/python/project/PythonLogicalView.java 
             |    24 +-
 python.project/src/org/netbeans/modules/python/project/layer.xml             
              |     8 +
 
python.project/src/org/netbeans/modules/python/project/resources/packageBadge.gif
          |     0 
 
python.project/src/org/netbeans/modules/python/project/resources/packageEmpty.gif
          |     0 
 
python.project/src/org/netbeans/modules/python/project/resources/packagePrivate.gif
        |     0 
 
python.project/src/org/netbeans/modules/python/project/resources/packagePublic.gif
         |     0 
 python.project/src/org/netbeans/modules/python/project/ui/Bundle.properties  
              |    33 +-
 
python.project/src/org/netbeans/modules/python/project/ui/ChangePackageViewTypeAction.java
 |    91 +
 
python.project/src/org/netbeans/modules/python/project/ui/PackageDisplayUtils.java
         |   221 +
 
python.project/src/org/netbeans/modules/python/project/ui/PackageRootNode.java
             |   475 +++
 python.project/src/org/netbeans/modules/python/project/ui/PackageView.java   
              |   404 +++
 
python.project/src/org/netbeans/modules/python/project/ui/PackageViewChildren.java
         |  1291 ++++++++++
 
python.project/src/org/netbeans/modules/python/project/ui/PythonProjectSettings.java
       |   116 +
 
python.project/src/org/netbeans/modules/python/project/ui/SourceNodeFactory.java
           |    13 +-
 15 files changed, 2699 insertions(+), 68 deletions(-)

diffs (2956 lines):

diff --git a/python.project/nbproject/project.xml 
b/python.project/nbproject/project.xml
--- a/python.project/nbproject/project.xml
+++ b/python.project/nbproject/project.xml
@@ -6,47 +6,6 @@
             
<code-name-base>org.netbeans.modules.python.project</code-name-base>
             <module-dependencies>
                 <dependency>
-                    
<code-name-base>org.netbeans.modules.gototest</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <release-version>1</release-version>
-                        <specification-version>1.3</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
-                    
<code-name-base>org.netbeans.modules.python.core</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <specification-version>1.0</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
-                    
<code-name-base>org.netbeans.modules.python.debugger</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <specification-version>1.0</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
-                    
<code-name-base>org.netbeans.modules.gsf.codecoverage</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <specification-version>1.0</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
-                    
<code-name-base>org.netbeans.modules.python.editor</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <specification-version>1.0</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
                     <code-name-base>org.jdesktop.layout</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -65,6 +24,23 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    
<code-name-base>org.netbeans.modules.gototest</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.3</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    
<code-name-base>org.netbeans.modules.gsf.codecoverage</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.0</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     
<code-name-base>org.netbeans.modules.gsfpath.api</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -101,6 +77,30 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    
<code-name-base>org.netbeans.modules.python.core</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.0</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    
<code-name-base>org.netbeans.modules.python.debugger</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.0</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    
<code-name-base>org.netbeans.modules.python.editor</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.0</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     
<code-name-base>org.netbeans.modules.queries</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -173,6 +173,15 @@
                         <specification-version>6.24</specification-version>
                     </run-dependency>
                 </dependency>
+                <dependency>
+                    <code-name-base>org.openidex.util</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>3</release-version>
+                        <specification-version>3.16</specification-version>
+                    </run-dependency>
+                </dependency>
             </module-dependencies>
             <test-dependencies>
                 <test-type>
diff --git 
a/python.project/src/org/netbeans/modules/python/project/PythonLogicalView.java
 
b/python.project/src/org/netbeans/modules/python/project/PythonLogicalView.java
--- 
a/python.project/src/org/netbeans/modules/python/project/PythonLogicalView.java
+++ 
b/python.project/src/org/netbeans/modules/python/project/PythonLogicalView.java
@@ -15,6 +15,8 @@
 import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ProjectUtils;
 import org.netbeans.modules.gsf.codecoverage.api.CoverageActionFactory;
+import org.netbeans.modules.python.project.ui.ChangePackageViewTypeAction;
+import org.netbeans.modules.python.project.ui.PackageView;
 import org.netbeans.spi.project.ActionProvider;
 import org.netbeans.spi.project.ui.LogicalViewProvider;
 import org.netbeans.spi.project.ui.support.CommonProjectActions;
@@ -61,23 +63,15 @@
             if (!project.equals(owner)) {
                 return null; // Don't waste time if project does not own the 
fo
             }
-            
-            Node[] rootChildren = root.getChildren().getNodes(true);
-            for (int i = 0; i < rootChildren.length; i++) {
-                TreeRootNode.PathFinder pf2 = 
rootChildren[i].getLookup().lookup(TreeRootNode.PathFinder.class);
-                if (pf2 != null) {
-                    Node n =  pf2.findPath(rootChildren[i], target);
-                    if (n != null) {
-                        return n;
-                    }
-                }
-                FileObject childFO = 
rootChildren[i].getLookup().lookup(DataObject.class).getPrimaryFile();
-                if (targetFO.equals(childFO)) {
-                    return rootChildren[i];
+
+            for (Node n : root.getChildren().getNodes(true)) {
+                Node result = PackageView.findPath(n, target);
+                if (result != null) {
+                    return result;
                 }
             }
         }
-        
+
         return null;
     }
     
@@ -174,6 +168,8 @@
             actions.add(CommonProjectActions.copyProjectAction());
             actions.add(CommonProjectActions.deleteProjectAction());
             actions.add(null);
+            actions.add(new ChangePackageViewTypeAction());
+            actions.add(null);
             actions.add(SystemAction.get(FindAction.class));
             
             // honor 57874 contact
diff --git a/python.project/src/org/netbeans/modules/python/project/layer.xml 
b/python.project/src/org/netbeans/modules/python/project/layer.xml
--- a/python.project/src/org/netbeans/modules/python/project/layer.xml
+++ b/python.project/src/org/netbeans/modules/python/project/layer.xml
@@ -51,6 +51,14 @@
         </folder>
 
     </folder>
+
+    <folder name="ProjectsTabActions">
+        <file 
name="org-netbeans-modules-python-project-ui-ChangePackageViewTypeAction.instance">
+            <attr name="position" intvalue="1603"/>
+        </file>
+    </folder>
+
+
     <folder name="Templates">
         <folder name="Project">
             <folder name="Python">
diff --git 
a/python.project/src/org/netbeans/modules/python/project/resources/packageBadge.gif
 
b/python.project/src/org/netbeans/modules/python/project/resources/packageBadge.gif
new file mode 100644
index 
0000000000000000000000000000000000000000..da79fe92c48ba7202af865778b2754f2f731ef44
GIT binary patch
literal 314
zc%17D@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtmUzPnffIy#(?lOI#yL
zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=u@pN$vskoK&M*C7m>W}jQswz5X
z6!a9&e)x1WxUD?@zEK0Cva_@U!^S`Vf1f{YzU|@u87(4@zXoeduCdu9v4i!A<h#2T
z|LQ+KVv}!RNc#Er_xwY*-P<Ma+cjnEVhdqm?NP7<iGTerzkU^qaKw{ui=XqXDw@PI
zN2)d9-@Ccim5fXr8HeQE*xc0EEEqQJzdwf~t?3B^Q^(Wm@Ava;m$z#@t-RT>h=K9Q
zr|0MG*?4ucHyr5Ac7K>7@MH5w3uXrf!)L8sf71^zGcfTmyglc0a7*x#7@)rxJYD@<
J);T3K0RVfAeLnyI

diff --git 
a/python.project/src/org/netbeans/modules/python/project/resources/packageEmpty.gif
 
b/python.project/src/org/netbeans/modules/python/project/resources/packageEmpty.gif
new file mode 100644
index 
0000000000000000000000000000000000000000..1d02f79bc1d6619f52797206f34ea2f909b8f213
GIT binary patch
literal 440
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUzPnffIy#(?lOI#yL
zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+AuIMdU?7yhE&{2`t$$4J+mr<LPLau
zOJ?Ru!3zsqdwP0qotRWpQ^WJ=(<dH&dApP!UtS7#xh?p$+Tm*<10##sgX`<#mswd^
z&Fbsx)97-TwJv7op$Bi@@|y8TzPZ0YURWcYOTZz4`QN|4uNN&}eq2RGrRC~Og|jb;
z6uye<$CWU0C~ROnbLNbhdH%f=hQyo)H#R0m9CiG~z{HVrV1NDpBZW8Xett@2i;az4
zQ>0J}wDB{OvZ3O;Cr?<?-{0QOzlL9-A%RUmG&O#Io$JHqFN`b#o@{e$DuoU+G%zqp
zFIcmN=bN@(gE!w2puz)ecXkvuD{5(RtzK08{G6eLq-5gM=xsTY%nl3(_Z~QQtnD)E
zjwcQv%kvntwYB}23r;qIj1)GpJ9)|jWc?A2Q@slpF0|OJa^nAjf&;7|cQP1+OHQ6W
dmywBwf$3_a`yQ^pOMyYp;OXk;vd$@?2>{q+s?Goa

diff --git 
a/python.project/src/org/netbeans/modules/python/project/resources/packagePrivate.gif
 
b/python.project/src/org/netbeans/modules/python/project/resources/packagePrivate.gif
new file mode 100644
index 
0000000000000000000000000000000000000000..46088744f3d73d17c2194b8f5a535baff7a3427f
GIT binary patch
literal 699
zc$@*d0!00ZP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!O-V#SRCwBA
z{Qv(yLu6c10s{j>CJ>te@hKplzI@TV1q?(0fB<5Qh)tYgps$x566nvMtfa_r_uhSm
zHS0Gryt%iZ!BqJhgPfu&!=wAR89sjafTHm~!~d&{tc-C00mK4yKxRU0G(%ZcEyK^B
zzZlrr*%-Roni&d8E;Hn~`!hVhwS&RjJ(q!vOA^I^ONZubUcY+A6d-_@fMzkBIe!7D
z`6okhnje@3@&Et-#~{eh3swxG#YF|-SPY0&^&Of1{Qcts5I~G2wyOV?{@!Nb;9!ND
z{^Q4QhI`Da3_%4h42+Dp{eSBC@fQFA!~)WszWhAH-@ktue*gIcmH@e8MY=tMs)QTE
zk@I&LY>iZ~8gSzOKXHHnVgcFAba@5C#RqQ~TKC>%sJ5175dHlg#YHe6V9*3-f4ES`
z@aOkmMt}ffI{yl2>!(i)h5{@MYj3<|&=qE907WfG9Nqapf4qld5cu==9|J%DG5`3*
zV9Le9pvLv*GlQ&(0)yDc#|&F9JYsmx!OP(6pvLg%`8x)ANg;@aHz(j21Wum6%K#8S
zjDoBT-ln>uTW;Qd&A<qR!1!fQR~2Qjwvc7e(-I|^kN^UR<^6Al{c0*)3>Eota6v(S
zP6kmyE(T6uI2<~Ck-^SXAFJlRnQIvU0tlFp{(q~8S7b2IRl%KHS$VLE|M~Nm0U&@_
zV1dEN!j0Pi9C9Fi00G1VV*LI4pMe|z(gzSgjG!pX3zxxZf|H#N!@fgTa2f&-KwtwH
h85xZk$OHfZ1^@#e*`}zkhXDWp002ovPDHLkV1n8mH;4cL

diff --git 
a/python.project/src/org/netbeans/modules/python/project/resources/packagePublic.gif
 
b/python.project/src/org/netbeans/modules/python/project/resources/packagePublic.gif
new file mode 100644
index 
0000000000000000000000000000000000000000..7e91768bc0baa559280533b43b03575836b4cd55
GIT binary patch
literal 723
zc$@*#0xbQBP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!Wl2OqRCwBA
z{Qv(y12XUp4CMh*T|k@(6`BsjWt-Qp{=$F-00a;t$bi|+rbhpN|6jz&z^HxksSty)
zT?|8Fd@NYO{Dq4dE}vM%pegzSMdN>l|5q7V8RGx~hy}#?`kB#GQ`=Ni&n}T+V__#l
zk)16=aCkIWVRuI>!`k&58A{8hp%`%K(0tA7SI?LN1Q6JOKY#zYsOmd1iHZm?fLwI_
z)@_C#KYoA}g4iH2F`yhHBO}yy1_qG&%a=~L00a<=ot@o0VPV<eGaYMz!fP3LIawKI
zPoK*0<J&g|HV$sE=70bGF`U}n&j8gA2W-s$Z2$s@MMy{}a{BZcjDP?BW%%>w4_M;o
z&z}rsWn~PS@=Od<r%q=uP-0?Gm0-d!;Kcub;s61}!T|)29z9~XdGiXxx^<Hoyu4Bw
zn3&iZfWizBj_eE~9Jd)<ELFjp1q_<tnm=5qWBBv?FC#zzF+F+mgyGYtPYmMXG7P6q
zU1X4wk_S5*BrYS)%b+C72d2R;`|%!*Az}BA0U&^wfBpJp%F4>B2GS%gEeX`j1~ljt
z1LwcT4BmEf43A#CW002=f@pYi0**o8<oUY{00G1ZbgH+huGp3<cRs_Nr=!Bl;Akes
zV5lj~KnMT?5DPH6_UkJ$GBoD+!UYBSIT=I+xfnP(SQ!qTzQ|x_s*hE3-^{fP00G1T
zbo#f7ctr*ST@{?RFtYMs760?+F9SdTvA_a@k%b$#0XXD9`Tzom3B>sO_df$U0HhBf
zfWVn4FI)zv2~Ku84EqjU!D$FU0D%nvrg38iG66t<0RSj2;3<-Br|JLz002ovPDHLk
FV1iDwGoJte

diff --git 
a/python.project/src/org/netbeans/modules/python/project/ui/Bundle.properties 
b/python.project/src/org/netbeans/modules/python/project/ui/Bundle.properties
--- 
a/python.project/src/org/netbeans/modules/python/project/ui/Bundle.properties
+++ 
b/python.project/src/org/netbeans/modules/python/project/ui/Bundle.properties
@@ -24,4 +24,35 @@
 MainModuleChooser.jLabel1.text=&Main Modules:
 LBL_SelectMainModule=Select Main Module
 LBL_BrowseMainModules=Browse Main Modules
-TXT_PleaseWait=Please Wait...
\ No newline at end of file
+TXT_PleaseWait=Please Wait...
+
+# PackageView
+PackageView.find_packages_progress=Finding packages in {0}
+
+# PackageViewChildren
+TXT_PastePackage=Paste Package
+MSG_InvalidPackageName=Name is not a valid Java package.
+LBL_CompilePackage_Action=Compile Package
+PROP_name=Name
+HINT_name=Package Name
+
+#PackageRootNode
+PROP_rootpath=Source Root
+HINT_rootpath=Source Root
+
+# PackageDisplayUtils
+#LBL_DefaultPackage=<default package>
+LBL_DefaultPackage=<Top Level>
+# {0} - full package name
+LBL_package= PythonPackage ({0})
+# {0} - full package name
+LBL_public_package=Exported Python Source Package ({0})
+# {0} - full package name
+LBL_private_package=Private Python Source Package ({0})
+
+# ChangePackageViewTypeAction
+LBL_change_package_type=&View Python Packages as
+ChangePackageViewTypeAction_list=&List
+ChangePackageViewTypeAction_tree=&Tree
+
+
diff --git 
a/python.project/src/org/netbeans/modules/python/project/ui/ChangePackageViewTypeAction.java
 
b/python.project/src/org/netbeans/modules/python/project/ui/ChangePackageViewTypeAction.java
new file mode 100644
--- /dev/null
+++ 
b/python.project/src/org/netbeans/modules/python/project/ui/ChangePackageViewTypeAction.java
@@ -0,0 +1,91 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
+ *
+ * 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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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-2006 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.python.project.ui;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.AbstractAction;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JRadioButtonMenuItem;
+import org.openide.awt.Mnemonics;
+import org.openide.util.NbBundle;
+import org.openide.util.actions.Presenter;
+
+/**
+ * Popup menu in Projects tab permitting you to change the package view type.
+ *
+ * <p>
+ * <b>This is copied from the corresponding Java action in java.projects</b>
+ * </p>
+ *
+ * @author Jesse Glick
+ */
+public final class ChangePackageViewTypeAction extends AbstractAction 
implements Presenter.Popup {
+    
+    public ChangePackageViewTypeAction() {}
+
+    public void actionPerformed(ActionEvent e) {
+        assert false : e;
+    }
+
+    public JMenuItem getPopupPresenter() {
+        JMenu menu = new JMenu();
+        Mnemonics.setLocalizedText(menu, 
NbBundle.getMessage(ChangePackageViewTypeAction.class, 
"LBL_change_package_type"));
+        menu.add(createChoice(PythonProjectSettings.TYPE_PACKAGE_VIEW, 
NbBundle.getMessage(ChangePackageViewTypeAction.class, 
"ChangePackageViewTypeAction_list")));
+        menu.add(createChoice(PythonProjectSettings.TYPE_TREE, 
NbBundle.getMessage(ChangePackageViewTypeAction.class, 
"ChangePackageViewTypeAction_tree")));
+        return menu;
+    }
+    
+    private JMenuItem createChoice(final int type, String label) {
+        JRadioButtonMenuItem item = new JRadioButtonMenuItem();
+        Mnemonics.setLocalizedText(item, label);
+        item.setSelected(PythonProjectSettings.getPackageViewType() == type);
+        item.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                PythonProjectSettings.setPackageViewType(type);
+            }
+        });
+        return item;
+    }
+    
+}
diff --git 
a/python.project/src/org/netbeans/modules/python/project/ui/PackageDisplayUtils.java
 
b/python.project/src/org/netbeans/modules/python/project/ui/PackageDisplayUtils.java
new file mode 100644
--- /dev/null
+++ 
b/python.project/src/org/netbeans/modules/python/project/ui/PackageDisplayUtils.java
@@ -0,0 +1,221 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
+ *
+ * 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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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-2006 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.python.project.ui;
+
+import java.awt.Image;
+//import org.netbeans.api.java.queries.AccessibilityQuery;
+import org.netbeans.api.queries.VisibilityQuery;
+import org.openide.filesystems.FileObject;
+import org.openide.util.ImageUtilities;
+import org.openide.util.NbBundle;
+
+// XXX needs unit test
+
+/**
+ * Provides display name and icon utilities for
+ * {@link PackageViewChildren.PackageNode} and {@link 
PackageListView.PackageItem}.
+ *
+ * <p>
+ * <b>This is copied from the corresponding Java action in java.projects</b>
+ * </p>
+ *
+ *
+ * @author Jesse Glick
+ */
+public final class PackageDisplayUtils {
+
+    private PackageDisplayUtils() {}
+    
+    /** whether to turn on #42589 */
+    private static final boolean TRUNCATE_PACKAGE_NAMES =
+        
Boolean.getBoolean("org.netbeans.spi.java.project.support.ui.packageView.TRUNCATE_PACKAGE_NAMES");
 // NOI18N
+
+    private static final Image PACKAGE = 
ImageUtilities.loadImage("org/netbeans/modules/python/project/resources/package.gif");
 // NOI18N
+    private static final Image PACKAGE_EMPTY = 
ImageUtilities.loadImage("org/netbeans/modules/python/project/resources/packageEmpty.gif");
 // NOI18N
+    private static final Image PACKAGE_PRIVATE = 
ImageUtilities.loadImage("org/netbeans/modules/python/project/resources/packagePrivate.gif");
 // NOI18N
+    private static final Image PACKAGE_PUBLIC = 
ImageUtilities.loadImage("org/netbeans/modules/python/project/resources/packagePublic.gif");
 // NOI18N
+
+    /**
+     * Find the proper display label for a package.
+     * @param pkg the actual folder
+     * @param pkgname the dot-separated package name (<code>""</code> for 
default package)
+     * @return an appropriate display label for it
+     */
+    public static String getDisplayLabel(String pkgname) {
+        return computePackageName(pkgname, TRUNCATE_PACKAGE_NAMES);
+    }
+    
+    /**
+     * Find the proper tool tip for a package.
+     * May have more info than the display label.
+     * @param pkg the actual folder
+     * @param pkgname the dot-separated package name (<code>""</code> for 
default package)
+     * @return an appropriate display label for it
+     */
+    public static String getToolTip(FileObject pkg, String pkgname) {
+        String pkglabel = computePackageName(pkgname, false);
+//        Boolean b = AccessibilityQuery.isPubliclyAccessible(pkg);
+//        if (b != null) {
+//            if (b.booleanValue()) {
+//                return NbBundle.getMessage(PackageDisplayUtils.class, 
"LBL_public_package", pkglabel);
+//            } else {
+//                return NbBundle.getMessage(PackageDisplayUtils.class, 
"LBL_private_package", pkglabel);
+//            }
+//        } else {
+            return NbBundle.getMessage(PackageDisplayUtils.class, 
"LBL_package", pkglabel);
+//        }
+    }
+    
+    /**
+     * Get package name.
+     * Handles default package specially.
+     * @param truncate if true, show a truncated version to save display 
space
+     */
+    private static String computePackageName(String pkgname, boolean 
truncate) {
+        if (pkgname.length() == 0) {
+            return NbBundle.getMessage(PackageDisplayUtils.class, 
"LBL_DefaultPackage"); // NOI18N
+        } else {
+            if (truncate) {
+                // #42589: keep only first letter of first package 
component, up to three of others
+                return pkgname.replaceFirst("^([^.])[^.]+\\.", 
"$1.").replaceAll("([^.]{3})[^.]+\\.", "$1."); // NOI18N
+            } else {
+                return pkgname;
+            }
+        }
+    }
+
+     
+    
+    /**
+     * Find the proper display icon for a package.
+     * @param pkg the actual folder
+     * @param pkgname the dot-separated package name (<code>""</code> for 
default package)
+     * @return an appropriate display icon for it
+     */
+    public static Image getIcon(FileObject pkg, String pkgname) {
+        return getIcon( pkg, pkgname, isEmpty(pkg) );
+    }
+    
+    /** Performance optiomization if the the isEmpty status is alredy known.
+     * 
+     */
+    public static Image getIcon(FileObject pkg, String pkgname, boolean 
empty ) {
+        if ( empty ) {
+            return PACKAGE_EMPTY;
+        } else {
+//            Boolean b = pkg.isValid() ? 
AccessibilityQuery.isPubliclyAccessible(pkg) : null;
+//            if (b != null) {
+//                if (b.booleanValue()) {
+//                    return PACKAGE_PUBLIC;
+//                } else {
+//                    return PACKAGE_PRIVATE;
+//                }
+//            } else {
+                return PACKAGE;
+//            }
+        }
+    }
+    
+    
+    /**
+     * Check whether a package is empty (devoid of files except for 
subpackages).
+     */
+    public static boolean isEmpty( FileObject fo ) {    
+        return isEmpty (fo, true, false );
+    }
+
+    /**
+     * Check whether a package is empty (devoid of files except for 
subpackages).
+     * @param recurse specifies whether to check if subpackages are empty 
too.
+     * @param initIsEmpty If true, don't consider __init__.py presence
+     */
+    public static boolean isEmpty( FileObject fo, boolean recurse, boolean 
initIsEmpty ) {
+        FileObject[] kids = fo.getChildren();
+        for( int i = 0; i < kids.length; i++ ) {
+            final FileObject kid = kids[i];
+            // Package init files don't count unless they have contents (or 
are pyc files)
+            if (initIsEmpty && kid.getName().equals("__init__")) { // NOI18N
+                if ("pyc".equals(kid.getExt()) || "pyo".equals(kid.getExt()) 
|| kid.getSize() == 0) { // NOI18N
+                    continue;
+                }
+            }
+            // XXX consider using group.contains() here
+            if ( !kid.isFolder() && VisibilityQuery.getDefault().isVisible( 
kid) ) {
+                return false;
+            }  
+            else if (recurse && VisibilityQuery.getDefault().isVisible( kid) 
&& !isEmpty(kid)) {
+                    return false;
+            }
+        }
+        return true;
+    }
+    
+    /**
+     * Check whether a package should be displayed.
+     * It should be displayed if {@link VisibilityQuery} says it should be,
+     * and it is either completely empty, or contains files (as opposed to
+     * containing some subpackages but no files).
+     */
+    public static boolean isSignificant(FileObject pkg) throws 
IllegalArgumentException {
+        if (!pkg.isFolder()) {
+            throw new IllegalArgumentException("Not a folder"); // NOI18N
+        }
+        // XXX consider using group.contains() here
+        if (!VisibilityQuery.getDefault().isVisible(pkg)) {
+            return false;
+        }
+        FileObject[] kids = pkg.getChildren();
+        boolean subpackages = false;
+        for (int i = 0; i < kids.length; i++) {
+            if (!VisibilityQuery.getDefault().isVisible(kids[i])) {
+                continue;
+            }
+            if (kids[i].isData()) {
+                return true;
+            } else {
+                subpackages = true;
+            }
+        }
+        return !subpackages;
+    }
+    
+}
diff --git 
a/python.project/src/org/netbeans/modules/python/project/ui/PackageRootNode.java
 
b/python.project/src/org/netbeans/modules/python/project/ui/PackageRootNode.java
new file mode 100644
--- /dev/null
+++ 
b/python.project/src/org/netbeans/modules/python/project/ui/PackageRootNode.java
@@ -0,0 +1,475 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
+ *
+ * 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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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-2006 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.python.project.ui;
+
+import java.awt.Image;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import javax.swing.Action;
+import javax.swing.Icon;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.spi.project.ui.support.CommonProjectActions;
+import org.openide.ErrorManager;
+import org.openide.actions.FileSystemAction;
+import org.openide.actions.FindAction;
+import org.openide.actions.PasteAction;
+import org.openide.actions.ToolsAction;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileStateInvalidException;
+import org.openide.filesystems.FileStatusEvent;
+import org.openide.filesystems.FileStatusListener;
+import org.openide.filesystems.FileSystem;
+import org.openide.filesystems.FileUtil;
+import org.openide.loaders.DataFolder;
+import org.openide.loaders.DataObject;
+import org.openide.nodes.AbstractNode;
+import org.openide.nodes.Children;
+import org.openide.nodes.Node;
+import org.openide.nodes.Node.PropertySet;
+import org.openide.nodes.NodeNotFoundException;
+import org.openide.nodes.NodeOp;
+import org.openide.nodes.PropertySupport;
+import org.openide.nodes.Sheet;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.RequestProcessor;
+import org.openide.util.actions.SystemAction;
+import org.openide.util.datatransfer.ExTransferable;
+import org.openide.util.datatransfer.MultiTransferObject;
+import org.openide.util.datatransfer.PasteType;
+import org.openide.util.lookup.AbstractLookup;
+import org.openide.util.lookup.InstanceContent;
+import org.openide.util.lookup.Lookups;
+import org.openide.util.lookup.ProxyLookup;
+import org.openidex.search.SearchInfo;
+import org.openidex.search.SearchInfoFactory;
+
+/** Node displaying a packages in given SourceGroup
+ * 
+ * <p>
+ * <b>This is copied from the corresponding Java action in java.projects</b>
+ * </p>
+ *
+ * @author Petr Hrebejk
+ */
+final class PackageRootNode extends AbstractNode implements Runnable, 
FileStatusListener {
+
+    static Image PACKAGE_BADGE = 
ImageUtilities.loadImage("org/netbeans/modules/python/project/resources/packageBadge.gif");
 // NOI18N
+        
+    private static Action actions[]; 
+
+    private SourceGroup group;
+
+    private final FileObject file;
+    private final Set<FileObject> files;
+    private FileStatusListener fileSystemListener;
+    private RequestProcessor.Task task;
+    private volatile boolean iconChange;
+    private volatile boolean nameChange;
+    
+    PackageRootNode( SourceGroup group ) {
+        this( group, new InstanceContent() );
+    }
+    
+    private PackageRootNode( SourceGroup group, InstanceContent ic ) {
+        super( new PackageViewChildren(group),
+                new ProxyLookup(createLookup(group), new 
AbstractLookup(ic)));
+        
ic.add(alwaysSearchableSearchInfo(SearchInfoFactory.createSearchInfoBySubnodes(this)));
+        this.group = group;
+        file = group.getRootFolder();
+        files = Collections.singleton(file);
+        try {
+            FileSystem fs = file.getFileSystem();
+            fileSystemListener = FileUtil.weakFileStatusListener(this, fs);
+            fs.addFileStatusListener(fileSystemListener);
+        } catch (FileStateInvalidException e) {
+            ErrorManager err = ErrorManager.getDefault();
+            err.annotate(e, "Can not get " + file + " filesystem, 
ignoring...");  // NO18N
+            err.notify(ErrorManager.INFORMATIONAL, e);
+        }
+        setName( group.getName() );
+        setDisplayName( group.getDisplayName() );        
+        // 
setIconBase("org/netbeans/modules/java/j2seproject/ui/resources/packageRoot");
+    }
+
+    public @Override Image getIcon(int type) {
+        return computeIcon( false, type );
+    }
+        
+    public @Override Image getOpenedIcon(int type) {
+        return computeIcon( true, type );
+    }
+    
+    public @Override String getDisplayName() {
+        String s = super.getDisplayName ();
+
+        try {            
+            s = file.getFileSystem ().getStatus ().annotateName (s, files);
+        } catch (FileStateInvalidException e) {
+            ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
+        }
+
+        return s;
+    }
+
+    public @Override String getHtmlDisplayName() {
+         try {
+             FileSystem.Status stat = file.getFileSystem().getStatus();
+             if (stat instanceof FileSystem.HtmlStatus) {
+                 FileSystem.HtmlStatus hstat = (FileSystem.HtmlStatus) stat;
+
+                 String result = hstat.annotateNameHtml (
+                     super.getDisplayName(), files);
+
+                 //Make sure the super string was really modified
+                 if (!super.getDisplayName().equals(result)) {
+                     return result;
+                 }
+             }
+         } catch (FileStateInvalidException e) {
+             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
+         }
+         return super.getHtmlDisplayName();
+    }
+
+    public void run() {
+        if (iconChange) {
+            fireIconChange();
+            fireOpenedIconChange();
+            iconChange = false;
+        }
+        if (nameChange) {
+            fireDisplayNameChange(null, null);
+            nameChange = false;
+        }
+    }
+
+    public void annotationChanged(FileStatusEvent event) {
+        if (task == null) {
+            task = RequestProcessor.getDefault().create(this);
+        }
+
+        if ((iconChange == false && event.isIconChange())  || (nameChange == 
false && event.isNameChange())) {
+            if (event.hasChanged(file)) {
+                iconChange |= event.isIconChange();
+                nameChange |= event.isNameChange();
+            }
+        }
+
+        task.schedule(50);  // batch by 50 ms
+    }    
+    
+    public @Override Action[] getActions(boolean context) {
+        if ( actions == null ) {
+            actions = new Action[] {
+                CommonProjectActions.newFileAction(),
+                null,
+                SystemAction.get( FileSystemAction.class ),
+                null,
+                SystemAction.get( FindAction.class ),
+                null,
+                SystemAction.get( PasteAction.class ),
+                null,
+                SystemAction.get( ToolsAction.class ),
+            };
+        }
+        return actions;            
+    }
+
+    // Show reasonable properties of the DataFolder,
+    //it shows the sorting names as rw property, the name as ro property and 
the path to root as ro property
+    public @Override PropertySet[] getPropertySets() {            
+        PropertySet[] properties =  
getDataFolderNodeDelegate().getPropertySets();
+        for (int i=0; i< properties.length; i++) {
+            if (Sheet.PROPERTIES.equals(properties[i].getName())) {
+                //Replace the Sheet.PROPERTIES by the new one
+                //having the ro name property and ro path property
+                properties[i] = Sheet.createPropertiesSet();
+                ((Sheet.Set) properties[i]).put(new 
PropertySupport.ReadOnly<String>(DataObject.PROP_NAME, String.class,
+                        
NbBundle.getMessage(PackageRootNode.class,"PROP_name"), 
NbBundle.getMessage(PackageRootNode.class,"HINT_name")) {
+                    @Override
+                    public String getValue() {
+                        return PackageRootNode.this.getDisplayName();
+                    }
+                });
+                ((Sheet.Set) properties[i]).put(new 
PropertySupport.ReadOnly<String>("ROOT_PATH", String.class,    //NOI18N
+                        
NbBundle.getMessage(PackageRootNode.class,"PROP_rootpath"), 
NbBundle.getMessage(PackageRootNode.class,"HINT_rootpath")) {
+                    @Override
+                    public String getValue() {
+                        return 
FileUtil.getFileDisplayName(PackageRootNode.this.file);
+                    }
+                });
+            }
+        }
+        return properties;
+    }
+
+    // XXX Paste types - probably not very nice 
+    public @Override void createPasteTypes(Transferable t, List<PasteType> 
list) {
+        if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
+            try {
+                MultiTransferObject mto = (MultiTransferObject) 
t.getTransferData(ExTransferable.multiFlavor);
+                List<PackageViewChildren.PackageNode> l = new 
ArrayList<PackageViewChildren.PackageNode>();
+                boolean isPackageFlavor = false;
+                boolean hasTheSameRoot = false;
+                int op = -1;
+                for (int i=0; i < mto.getCount(); i++) {
+                    Transferable pt = mto.getTransferableAt(i);
+                    DataFlavor[] flavors = mto.getTransferDataFlavors(i);
+                    for (int j=0; j< flavors.length; j++) {
+                        if 
(PackageViewChildren.SUBTYPE.equals(flavors[j].getSubType ()) &&
+                                
PackageViewChildren.PRIMARY_TYPE.equals(flavors[j].getPrimaryType ())) {
+                            if (op == -1) {
+                                op = Integer.valueOf 
(flavors[j].getParameter (PackageViewChildren.MASK)).intValue ();
+                            }
+                            PackageViewChildren.PackageNode pkgNode = 
(PackageViewChildren.PackageNode) pt.getTransferData(flavors[j]);
+                            if ( 
!((PackageViewChildren)getChildren()).getRoot().equals( pkgNode.getRoot() ) ) 
{
+                                l.add(pkgNode);
+                            }
+                            else {
+                                hasTheSameRoot = true;
+                            }
+                            isPackageFlavor = true;
+                        }
+                    }
+                }
+                if (isPackageFlavor && !hasTheSameRoot) {
+                    list.add(new 
PackageViewChildren.PackagePasteType(this.group.getRootFolder(),
+                            l.toArray(new 
PackageViewChildren.PackageNode[l.size()]),
+                            op));
+                }
+                else if (!isPackageFlavor) {
+                    list.addAll( Arrays.asList( 
getDataFolderNodeDelegate().getPasteTypes( t ) ) );
+                }
+            } catch (UnsupportedFlavorException e) {
+                ErrorManager.getDefault().notify(e);
+            } catch (IOException e) {
+                ErrorManager.getDefault().notify(e);
+            }
+        }
+        else {
+            DataFlavor[] flavors = t.getTransferDataFlavors();
+            FileObject root = this.group.getRootFolder();
+            boolean isPackageFlavor = false;
+            if (root!= null  && root.canWrite()) {
+                for (DataFlavor flavor : flavors) {
+                    if (PackageViewChildren.SUBTYPE.equals(flavor.getSubType 
()) &&
+                            
PackageViewChildren.PRIMARY_TYPE.equals(flavor.getPrimaryType ())) {
+                        isPackageFlavor = true;
+                        try {
+                            int op = 
Integer.parseInt(flavor.getParameter(PackageViewChildren.MASK));
+                            PackageViewChildren.PackageNode pkgNode = 
(PackageViewChildren.PackageNode) t.getTransferData(flavor);
+                            if ( 
!((PackageViewChildren)getChildren()).getRoot().equals( pkgNode.getRoot() ) ) 
{
+                                list.add(new 
PackageViewChildren.PackagePasteType (root, new 
PackageViewChildren.PackageNode[] {pkgNode}, op));
+                            }
+                        } catch (IOException ioe) {
+                            ErrorManager.getDefault().notify(ioe);
+                        }
+                        catch (UnsupportedFlavorException ufe) {
+                            ErrorManager.getDefault().notify(ufe);
+                        }
+                    }
+                }
+            }
+            if (!isPackageFlavor) {
+                list.addAll( Arrays.asList( 
getDataFolderNodeDelegate().getPasteTypes( t ) ) );
+            }
+        }
+    }
+    
+    @Override
+    public PasteType getDropType(Transferable t, int action, int index) {
+        PasteType pasteType = super.getDropType(t, action, index);
+        //The pasteType can be:
+        // 1) PackagePasteType - the t.flavor is package flavor
+        // 2) null or DataPasteType - the t.flavor in not package flavor
+        if (pasteType instanceof PackageViewChildren.PackagePasteType) {
+            ((PackageViewChildren.PackagePasteType)pasteType).setOperation 
(action);
+        }
+        return pasteType;
+    }
+
+    // Private methods 
---------------------------------------------------------
+    
+    private Node getDataFolderNodeDelegate() {
+        DataFolder df = getLookup().lookup(DataFolder.class);
+        try {
+            if (df.isValid()) {
+                return df.getNodeDelegate();
+            } 
+        } catch (IllegalStateException e) {
+            //The data systems API is not thread save,
+            //the DataObject may become invalid after isValid call and before
+            //getNodeDelegate call, we have to catch the ISE. When the 
DataObject
+            //is valid - other cause rethrow it otherwise return leaf node.
+            //todo: The DataObject.getNodedelegate should throw specialized 
exception type.
+            if (df.isValid()) {
+                throw e;
+            }
+        }
+        return new AbstractNode(Children.LEAF);
+    }
+    
+    private Image computeIcon( boolean opened, int type ) {
+        Image image;
+        Icon icon = group.getIcon( opened );
+        
+        if ( icon == null ) {
+            image = opened ? getDataFolderNodeDelegate().getOpenedIcon( type 
) : 
+                             getDataFolderNodeDelegate().getIcon( type );
+            image = ImageUtilities.mergeImages(image, PACKAGE_BADGE, 7, 7);
+        }
+        else {
+            image = ImageUtilities.icon2Image(icon);
+        }
+        
+        return image;        
+    }
+    
+    private static Lookup createLookup( SourceGroup group ) {
+        // XXX Remove DataFolder when paste, find and refresh are 
reimplemented
+        FileObject rootFolder = group.getRootFolder();
+        DataFolder dataFolder = DataFolder.findFolder( rootFolder );        
+        return Lookups.fixed(dataFolder, new PathFinder(group));
+    }
+    
+    /** If contained in the lookup can perform the search for a node
+     */    
+    public static class PathFinder {
+        
+        private SourceGroup group;
+        
+        public PathFinder( SourceGroup group ) {
+            this.group = group;
+        }
+        
+        public Node findPath( Node root, Object object ) {
+            FileObject fo;
+            if (object instanceof FileObject) {
+                fo = (FileObject) object;
+            } else if (object instanceof DataObject) {
+                fo = ((DataObject) object).getPrimaryFile();
+            } else {
+                return null;
+            }
+            
+            FileObject groupRoot = group.getRootFolder();
+            if ( FileUtil.isParentOf( groupRoot, fo ) /* && group.contains( 
fo ) */ ) {
+                // The group contains the object
+
+                String relPath = FileUtil.getRelativePath( groupRoot, 
fo.isFolder() ? fo : fo.getParent() );
+
+                String[] path = new String[] { relPath.replace( '/', '.' ) };
+                try {
+                    Node packageNode = NodeOp.findPath( root, path );
+                    if (fo.isFolder()) {
+                        return packageNode;
+                    } else {
+                        for (Node child : 
packageNode.getChildren().getNodes(true)) {
+                           DataObject dobj = 
child.getLookup().lookup(DataObject.class);
+                           if (dobj != null && 
dobj.getPrimaryFile().getNameExt().equals(fo.getNameExt())) {
+                               return child;
+                           }
+                        }
+                    }
+                }
+                catch ( NodeNotFoundException e ) {
+                    // did not manage to find it after all... why?
+                    return null;
+                }
+            }   
+            else if ( groupRoot.equals( fo ) ) {
+                // First try to find default package
+                try {
+                    return NodeOp.findPath( root, new String[] { "" } ); // 
NOI18N
+                }
+                catch ( NodeNotFoundException e ) {
+                    // If it does not exists return this node
+                }                        
+                return root;
+            }
+
+            return null;
+        }
+        
+        public @Override String toString() {
+            return "PathFinder[" + group + "]"; // NOI18N
+        }
+                    
+    }
+    
+    /**
+     * Produce a {@link SearchInfo} variant that is always searchable, for 
speed.
+     * @see "#48685"
+     */
+    static SearchInfo alwaysSearchableSearchInfo(SearchInfo i) {
+        return new AlwaysSearchableSearchInfo(i);
+    }    
+    
+    private static final class AlwaysSearchableSearchInfo implements 
SearchInfo {
+        
+        private final SearchInfo delegate;
+        
+        public AlwaysSearchableSearchInfo(SearchInfo delegate) {
+            this.delegate = delegate;
+        }
+
+        public boolean canSearch() {
+            return true;
+        }
+
+        public Iterator<DataObject> objectsToSearch() {
+            return delegate.objectsToSearch();
+        }
+        
+    }
+    
+}
diff --git 
a/python.project/src/org/netbeans/modules/python/project/ui/PackageView.java 
b/python.project/src/org/netbeans/modules/python/project/ui/PackageView.java
new file mode 100644
--- /dev/null
+++ 
b/python.project/src/org/netbeans/modules/python/project/ui/PackageView.java
@@ -0,0 +1,404 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
+ *
+ * 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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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-2006 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.python.project.ui;
+
+import java.awt.Component;
+import java.awt.EventQueue;
+import java.awt.Image;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import javax.swing.ComboBoxModel;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+import javax.swing.plaf.UIResource;
+import org.netbeans.api.progress.ProgressHandle;
+import org.netbeans.api.progress.ProgressHandleFactory;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.api.queries.VisibilityQuery;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.nodes.AbstractNode;
+import org.openide.nodes.FilterNode;
+import org.openide.nodes.Node;
+import org.openide.util.NbBundle;
+import org.openide.util.Parameters;
+import org.openide.util.WeakListeners;
+
+/**
+ * Factory for package views.
+ * @see org.netbeans.spi.project.ui.LogicalViewProvider
+ * <p>
+ * <b>This is copied from the corresponding Java action in java.projects</b>
+ * </p>
+ *
+ * @author Jesse Glick
+ */
+public class PackageView {
+        
+    private PackageView() {}
+    
+    /**
+     * Create a node which will contain package-oriented view of a source 
group.
+     * <p>
+     * The precise structure of this node is <em>not</em> specified by the 
API
+     * and is subject to arbitrary change (perhaps at user option).
+     * Callers should not make assumptions about the nature of subnodes, the
+     * code or display names of certain nodes, and so on. You may use 
cookies/lookup
+     * to find if particular subnodes correspond to folders or files.
+     * </p>
+     * @param group a source group which should be represented
+     * @return node which will display packages in given group
+     */
+    public static Node createPackageView( SourceGroup group ) {
+        return new RootNode (group);                
+    }
+    
+    /**
+     * Finds the node representing given object, if any.
+     * The current implementation works only for {@link 
org.openide.filesystems.FileObject}s
+     * and {@link org.openide.loaders.DataObject}s.
+     * @param rootNode a node some descendant of which should contain the 
object
+     * @param object object to find
+     * @return a node representing the given object, or null if no such node 
was found
+     */
+    public static Node findPath(Node rootNode, Object object) {
+        
+        PackageRootNode.PathFinder pf = 
rootNode.getLookup().lookup(PackageRootNode.PathFinder.class);
+        
+        if ( pf != null ) {
+            return pf.findPath( rootNode, object );
+        } else {
+            TreeRootNode.PathFinder pf2 = 
rootNode.getLookup().lookup(TreeRootNode.PathFinder.class);
+            if (pf2 != null) {
+                return pf2.findPath(rootNode, object);
+            } else {
+                return null;
+            }
+        }
+    }
+    
+    /**
+     * Create a list or combo box model suitable for {@link 
javax.swing.JList} from a source group
+     * showing all Java packages in the source group.
+     * To display it you will also need {@link #listRenderer}.
+     * <p>No particular guarantees are made as to the nature of the model 
objects themselves,
+     * except that {@link Object#toString} will give the fully-qualified 
package name
+     * (or <code>""</code> for the default package), regardless of what the 
renderer
+     * actually displays.</p>
+     * @param group a Java-like source group
+     * @return a model of its packages
+     * @since org.netbeans.modules.java.project/1 1.3 
+     */
+    
+    public static ComboBoxModel createListView(SourceGroup group) {
+        Parameters.notNull("group", group); //NOI18N
+        SortedSet<PackageItem> data = new TreeSet<PackageItem>();
+        findNonExcludedPackages(null, data, group.getRootFolder(), group, 
false);
+        return new DefaultComboBoxModel(data.toArray(new 
PackageItem[data.size()]));
+    }
+    
+    /** Fills given collection with flattened packages under given folder
+     *@param target The collection to be filled
+     *@param fo The folder to be scanned
+     * @param group the group to scan
+     * @param createPackageItems if false the collection will be filled with 
file objects; if
+     *       true PackageItems will be created.
+     * @param showProgress whether to show a progress handle or not
+     */
+    static void findNonExcludedPackages(PackageViewChildren children, 
Collection<PackageItem> target, FileObject fo, SourceGroup group, boolean 
showProgress) {
+        if (showProgress) {
+            ProgressHandle progress = 
ProgressHandleFactory.createHandle(NbBundle.getMessage(PackageView.class, 
"PackageView.find_packages_progress", FileUtil.getFileDisplayName(fo)));
+            progress.start(1000);
+            findNonExcludedPackages(children, target, fo, group, progress, 
0, 1000);
+            progress.finish();
+        } else {
+            findNonExcludedPackages(children, target, fo, group, null, 0, 0);
+        }
+    }
+
+    private static void findNonExcludedPackages(PackageViewChildren 
children, Collection<PackageItem> target, FileObject fo, SourceGroup group, 
ProgressHandle progress, int start, int end) {
+        
+        assert fo.isFolder() : "Package view only accepts folders"; // NOI18N
+        
+        if (progress != null) {
+            String path = FileUtil.getRelativePath(children.getRoot(), fo);
+            assert path != null : fo + " in " + children.getRoot();
+            progress.progress(path.replace('/', '.'), start);
+        }
+        
+        if (!fo.isValid()) {
+            return;
+        }
+               
+        if ( !VisibilityQuery.getDefault().isVisible( fo ) ) {
+            return; // Don't show hidden packages
+        }
+        
+        boolean hasSubfolders = false;
+        boolean hasFiles = false;
+        List<FileObject> folders = new ArrayList<FileObject>();
+        for (FileObject kid : fo.getChildren()) {
+            // XXX could use PackageDisplayUtils.isSignificant here
+            if (kid.isValid() && VisibilityQuery.getDefault().isVisible(kid) 
&& group.contains(kid)) {
+                if (kid.isFolder()) {
+                    folders.add(kid);
+                    hasSubfolders = true;
+                } 
+                else {
+                    hasFiles = true;
+                }
+            }
+        }
+        if (hasFiles || !hasSubfolders) {
+            if (target != null) {
+                target.add( new PackageItem(group, fo, !hasFiles ) );
+            }
+            else {
+                if (fo.isValid()) {
+                    children.add(fo, !hasFiles, false);
+                }
+            }
+        }
+        if (!folders.isEmpty()) {
+            int diff = (end - start) / folders.size();
+            int c = 0;
+            for (FileObject kid : folders) {
+                // Do this after adding the parent, so we get a pre-order 
traversal.
+                // Also see PackageViewChildren.findChild: prefer to get 
root first.
+                findNonExcludedPackages(children, target, kid, group, 
progress, start + c * diff, start + (c + 1) * diff);
+                c++;
+            }
+        }
+    }
+         
+//    public static ComboBoxModel createListView(SourceGroup group) {
+//        DefaultListModel model = new DefaultListModel();
+//        SortedSet/*<PackageItem>*/ items = new TreeSet();
+//        FileObject root = group.getRootFolder();
+//        if (PackageDisplayUtils.isSignificant(root)) {
+//            items.add(new PackageItem(group, root));
+//        }
+//        Enumeration/*<FileObject>*/ files = root.getChildren(true);
+//        while (files.hasMoreElements()) {
+//            FileObject f = (FileObject) files.nextElement();
+//            if (f.isFolder() && PackageDisplayUtils.isSignificant(f)) {
+//                items.add(new PackageItem(group, f));
+//            }
+//        }
+//        return new DefaultComboBoxModel(items.toArray(new 
PackageItem[items.size()]));
+//    }
+    
+    
+    /**
+     * Create a renderer suited to rendering models created using {@link 
#createListView}.
+     * The exact nature of the display is not specified.
+     * Instances of String can also be rendered.
+     * @return a suitable package renderer
+     * @since org.netbeans.modules.java.project/1 1.3 
+     */
+    public static ListCellRenderer listRenderer() {
+        return new PackageListCellRenderer();
+    }
+    
+    /**
+     * FilterNode which listens on the PackageViewSettings and changes the 
view to 
+     * the package view or tree view
+     *
+     */
+    private static final class RootNode extends FilterNode implements 
PropertyChangeListener {
+        
+        private SourceGroup sourceGroup;
+        
+        private RootNode (SourceGroup group) {
+            super(getOriginalNode(group));
+            this.sourceGroup = group;
+            
PythonProjectSettings.addPropertyChangeListener(WeakListeners.propertyChange(this,
 PythonProjectSettings.class));
+            
group.addPropertyChangeListener(WeakListeners.propertyChange(this, group));
+        }
+
+        // XXX #98573: very crude, but what else to do? Want to call 
changeOriginal asynchronously.
+        // But this could randomly screw up tests - not just 
PackageViewTest, but maybe others too.
+        // (org.netbeans.modules.java.freeform.ui.ViewTest does not appear 
to be affected.)
+        private static boolean IN_UNIT_TEST = false;
+        static {
+            try {
+                Class.forName("junit.framework.TestCase");
+                IN_UNIT_TEST = true;
+            } catch (ClassNotFoundException e) {}
+        }
+        public void propertyChange (PropertyChangeEvent event) {
+            String prop = event.getPropertyName();
+            if (PythonProjectSettings.PROP_PACKAGE_VIEW_TYPE.equals(prop) || 
SourceGroup.PROP_CONTAINERSHIP.equals(prop)) {
+                if (IN_UNIT_TEST) {
+                    changeOriginal(getOriginalNode(sourceGroup), true);
+                } else {
+                    EventQueue.invokeLater(new Runnable() {
+                        public void run() {
+                            changeOriginal(getOriginalNode(sourceGroup), 
true);
+                        }
+                    });
+                }
+            }
+        }
+        
+        private static Node getOriginalNode(SourceGroup group) {
+            FileObject root = group.getRootFolder();
+            //Guard condition, if the project is (closed) and deleted but 
not yet gced
+            // and the view is switched, the source group is not valid.
+            if ( root == null || !root.isValid()) {
+                return new AbstractNode (Children.LEAF);
+            }
+            switch (PythonProjectSettings.getPackageViewType()) {
+                case PythonProjectSettings.TYPE_PACKAGE_VIEW:
+                    return new PackageRootNode(group);
+                case PythonProjectSettings.TYPE_TREE:
+                    return new TreeRootNode(group);
+                default:
+                    assert false : "Unknown PackageView Type"; //NOI18N
+                    return new PackageRootNode(group);
+            }
+        }        
+    }
+    
+    /**
+     * Model item representing one package.
+     */
+    static final class PackageItem implements Comparable<PackageItem> {
+        
+        private static Map<Image,Icon> image2icon = new 
IdentityHashMap<Image,Icon>();
+        
+        private final boolean empty;
+        private final FileObject pkg;
+        private final String pkgname;
+        private Icon icon;
+        
+        public PackageItem(SourceGroup group, FileObject pkg, boolean empty) 
{
+            this.pkg = pkg;
+            this.empty = empty;
+            String path = FileUtil.getRelativePath(group.getRootFolder(), 
pkg);
+            assert path != null : "No " + pkg + " in " + group;
+            pkgname = path.replace('/', '.');
+        }
+        
+        public String toString() {
+            return pkgname;
+        }
+        
+        public String getLabel() {
+            return PackageDisplayUtils.getDisplayLabel(pkgname);
+        }
+        
+        public Icon getIcon() {
+            if ( icon == null ) {
+                Image image = PackageDisplayUtils.getIcon(pkg, pkgname, 
empty);                
+                icon = image2icon.get(image);
+                if ( icon == null ) {            
+                    icon = new ImageIcon( image );
+                    image2icon.put( image, icon );
+                }
+            }
+            return icon;
+        }
+
+        public int compareTo(PackageItem p) {
+            return pkgname.compareTo(p.pkgname);
+        }
+        
+    }
+    
+    /**
+     * The renderer which just displays {@link PackageItem#getLabel} and 
{@link PackageItem#getIcon}.
+     */
+    private static final class PackageListCellRenderer extends JLabel 
implements ListCellRenderer, UIResource {
+        
+        public PackageListCellRenderer() {
+            setOpaque(true);
+        }
+
+        public Component getListCellRendererComponent(JList list, Object 
value, int index, boolean isSelected, boolean cellHasFocus) {
+            // #93658: GTK needs name to render cell renderer "natively"
+            setName("ComboBox.listRenderer"); // NOI18N
+            
+            if (value instanceof PackageItem) {
+                PackageItem pkgitem = (PackageItem) value;
+                setText(pkgitem.getLabel());
+                setIcon(pkgitem.getIcon());
+            } else {
+                // #49954: render a specially inserted package somehow.
+                String pkgitem = (String) value;
+                setText(pkgitem);
+                setIcon(null);
+            }
+            
+            if ( isSelected ) {
+                setBackground(list.getSelectionBackground());
+                setForeground(list.getSelectionForeground());             
+            }
+            else {
+                setBackground(list.getBackground());
+                setForeground(list.getForeground());
+            }
+            
+            return this;
+        }
+        
+        // #93658: GTK needs name to render cell renderer "natively"
+        public String getName() {
+            String name = super.getName();
+            return name == null ? "ComboBox.renderer" : name;  // NOI18N
+    }
+    
+    }
+    
+    
+}
diff --git 
a/python.project/src/org/netbeans/modules/python/project/ui/PackageViewChildren.java
 
b/python.project/src/org/netbeans/modules/python/project/ui/PackageViewChildren.java
new file mode 100644
--- /dev/null
+++ 
b/python.project/src/org/netbeans/modules/python/project/ui/PackageViewChildren.java
@@ -0,0 +1,1291 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
+ *
+ * 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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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-2006 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.python.project.ui;
+
+import java.awt.EventQueue;
+import java.awt.Image;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.dnd.DnDConstants;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import javax.swing.Action;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import org.netbeans.api.fileinfo.NonRecursiveFolder;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.api.queries.VisibilityQuery;
+import org.netbeans.spi.project.ActionProvider;
+import org.netbeans.spi.project.ui.support.FileSensitiveActions;
+import org.openide.DialogDisplayer;
+import org.openide.ErrorManager;
+import org.openide.NotifyDescriptor;
+import org.openide.actions.FileSystemAction;
+import org.openide.actions.PropertiesAction;
+import org.openide.filesystems.FileAttributeEvent;
+import org.openide.filesystems.FileChangeListener;
+import org.openide.filesystems.FileEvent;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileRenameEvent;
+import org.openide.filesystems.FileStateInvalidException;
+import org.openide.filesystems.FileSystem;
+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.Children;
+import org.openide.nodes.FilterNode;
+import org.openide.nodes.Node;
+import org.openide.nodes.PropertySupport;
+import org.openide.nodes.Sheet;
+import org.openide.util.ChangeSupport;
+import org.openide.util.Exceptions;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.RequestProcessor;
+import org.openide.util.WeakListeners;
+import org.openide.util.datatransfer.ExTransferable;
+import org.openide.util.datatransfer.MultiTransferObject;
+import org.openide.util.datatransfer.PasteType;
+import org.openide.util.lookup.Lookups;
+import org.openide.util.lookup.ProxyLookup;
+import org.openidex.search.FileObjectFilter;
+import org.openidex.search.SearchInfoFactory;
+
+/**
+ * Display of Java sources in a package structure rather than folder 
structure.
+ *
+ * <p>
+ * <b>This is copied from the corresponding Java action in java.projects</b>
+ * </p>
+ *
+ * @author Adam Sotona, Jesse Glick, Petr Hrebejk, Tomas Zezula
+ */
+final class PackageViewChildren extends Children.Keys<String> implements 
FileChangeListener, ChangeListener, Runnable {
+    
+    private static final String NODE_NOT_CREATED = "NNC"; // NOI18N
+    private static final String NODE_NOT_CREATED_EMPTY = "NNC_E"; //NOI18N
+    
+    private static final MessageFormat PACKAGE_FLAVOR = new 
MessageFormat("application/x-java-org-netbeans-modules-java-project-packagenodednd;
 
class=org.netbeans.spi.java.project.support.ui.PackageViewChildren$PackageNode;
 mask={0}"); //NOI18N
+        
+    static final String PRIMARY_TYPE = "application";   //NOI18N
+    static final String SUBTYPE = 
"x-java-org-netbeans-modules-java-project-packagenodednd";    //NOI18N
+    static final String MASK = "mask";  //NOI18N
+
+    private 
java.util.Map<String,Object/*NODE_NOT_CREATED|NODE_NOT_CREATED_EMPTY|PackageNode*/>
 names2nodes;
+    private final FileObject root;
+    private final SourceGroup group;
+    private FileChangeListener wfcl;    // Weak listener on the system 
filesystem
+    private ChangeListener wvqcl;       // Weak listener on the 
VisibilityQuery
+
+    /**
+     * Creates children based on a single source root.
+     * @param root the folder where sources start (must be a package root)
+     */    
+    public PackageViewChildren(SourceGroup group) {
+        
+        // Sem mas dat cache a bude to uplne nejrychlejsi na svete
+        
+        this.root = group.getRootFolder();
+        this.group = group;
+    }
+
+    FileObject getRoot() {
+        return root; // Used from PackageRootNode
+    }
+    
+    protected Node[] createNodes(String path) {
+        FileObject fo = root.getFileObject(path);
+        if ( fo != null && fo.isValid()) {
+            Object o = names2nodes.get(path);
+            PackageNode n;
+            DataFolder folder = DataFolder.findFolder(fo);
+            if (folder.isValid()) {
+                if ( o == NODE_NOT_CREATED ) {
+                    n = new PackageNode(root, folder, false);
+                } else { // NODE_NOT_CREATED_EMPTY, PackageNode
+                    n = new PackageNode(root, folder);
+                }
+                names2nodes.put(path, n);
+                return new Node[] {n};
+            }
+        }
+        return new Node[0];
+    }
+    
+    RequestProcessor.Task task = RequestProcessor.getDefault().create( this 
);
+        
+    protected void addNotify() {
+        // System.out.println("ADD NOTIFY" + root + " : " + this );
+        super.addNotify();
+        task.schedule( 0 );
+    }
+    
+    public Node[] getNodes( boolean optimal ) {
+        if ( optimal ) {
+            Node[] garbage = super.getNodes( false );        
+            task.waitFinished();
+        }
+        return super.getNodes( false );
+    }
+    
+    public Node findChild (String name) {
+        while (true) {
+            Node n = super.findChild(name);
+            if (n != null) {
+                // If already there, get it quickly.
+                return n;
+            }
+            // In case a project is made on a large existing source root,
+            // which happens to have a file in the root dir (so package node
+            // should exist), try to select the root package node soon; no 
need
+            // to wait for whole tree.
+            try {
+                if (task.waitFinished(5000)) {
+                    return super.findChild(name);
+                }
+                // refreshKeysAsync won't run since we are blocking EQ!
+                refreshKeys();
+            } catch (InterruptedException x) {
+                Exceptions.printStackTrace(x);
+            }
+        }
+    }
+    
+    public void run() {
+        computeKeys();
+        refreshKeys();
+        try { 
+            FileSystem fs = root.getFileSystem();
+            wfcl = FileUtil.weakFileChangeListener(this, fs);
+            fs.addFileChangeListener( wfcl );
+        }
+        catch ( FileStateInvalidException e ) {
+            ErrorManager.getDefault().notify( ErrorManager.INFORMATIONAL, e 
);
+        }
+        wvqcl = WeakListeners.change( this, VisibilityQuery.getDefault() );
+        VisibilityQuery.getDefault().addChangeListener( wvqcl );
+    }
+
+    protected void removeNotify() {
+        // System.out.println("REMOVE NOTIFY" + root + " : " + this );       
 
+        VisibilityQuery.getDefault().removeChangeListener( wvqcl );
+        try {
+            root.getFileSystem().removeFileChangeListener( wfcl );
+        }
+        catch ( FileStateInvalidException e ) {
+            ErrorManager.getDefault().notify( ErrorManager.INFORMATIONAL, e 
);
+        }
+        setKeys(new String[0]);
+        names2nodes.clear();
+        super.removeNotify();
+    }
+    
+    // Private methods 
---------------------------------------------------------
+        
+    private void refreshKeys() {
+        Set<String> keys;
+        synchronized (names2nodes) {
+            keys = new TreeSet<String>(names2nodes.keySet());
+        }
+        setKeys(keys);
+    }
+    
+    /* #70097: workaround of a javacore deadlock
+     * See related issue: #61027
+     */
+    private void refreshKeysAsync () {
+        EventQueue.invokeLater(new Runnable() {
+            public void run () {
+                refreshKeys();
+            }
+         });
+    }
+
+    private void computeKeys() {
+        // XXX this is not going to perform too well for a huge source 
root...
+        // However we have to go through the whole hierarchy in order to find
+        // all packages (Hrebejk)
+        names2nodes = Collections.synchronizedMap(new 
TreeMap<String,Object>());
+        findNonExcludedPackages( root );
+    }
+    
+    /**
+     * Collect all recursive subfolders, except those which have subfolders
+     * but no files.
+     */    
+    private void findNonExcludedPackages( FileObject fo ) {
+        PackageView.findNonExcludedPackages(this, null, fo, group, true);
+    }
+    
+    
+    /** Finds all empty parents of given package and deletes them
+     */
+    private void cleanEmptyKeys( FileObject fo ) {
+        FileObject parent = fo.getParent(); 
+        
+        // Special case for default package
+        if ( root.equals( parent ) ) {
+            PackageNode n = get( parent );
+            // the default package is considered empty if it only contains 
folders,
+            // regardless of the contents of these folders (empty or not)
+            if ( n != null && PackageDisplayUtils.isEmpty( root, false, 
false ) ) {
+                remove( root );
+            }
+            return;
+        }
+        
+        while ( FileUtil.isParentOf( root, parent ) ) {
+            PackageNode n = get( parent );
+            if ( n != null && n.isLeaf() ) {
+                // System.out.println("Cleaning " + parent);
+                remove( parent );
+            }
+            parent = parent.getParent();
+        }
+    }
+    
+    // Non private only to be able to have the findNonExcludedPackages impl
+    // in on place (PackageView) 
+    void add(FileObject fo, boolean empty, boolean refreshImmediately) {
+        String path = FileUtil.getRelativePath( root, fo );
+        assert path != null : "Adding wrong folder " + fo 
+"(valid="+fo.isValid()+")"+ "under root" + this.root + 
"(valid="+this.root.isValid()+")";
+        if ( get( fo ) == null ) { 
+            names2nodes.put( path, empty ? NODE_NOT_CREATED_EMPTY : 
NODE_NOT_CREATED );
+            if (refreshImmediately) {
+                refreshKeysAsync();
+            } else {
+                synchronized (this) {
+                    if (refreshLazilyTask == null) {
+                        refreshLazilyTask = 
RequestProcessor.getDefault().post(new Runnable() {
+                            public void run() {
+                                synchronized (PackageViewChildren.this) {
+                                    refreshLazilyTask = null;
+                                    refreshKeysAsync();
+                                }
+                            }
+                        }, 2500);
+                    }
+                }
+            }
+        }
+    }
+    private RequestProcessor.Task refreshLazilyTask;
+
+    private void remove( FileObject fo ) {
+        String path = FileUtil.getRelativePath( root, fo );        
+        assert path != null : "Removing wrong folder" + fo;
+        names2nodes.remove( path );
+    }
+
+    private void removeSubTree (FileObject fo) {
+        String path = FileUtil.getRelativePath( root, fo );
+        assert path != null : "Removing wrong folder" + fo;
+        synchronized (names2nodes) {
+            Set<String> keys = names2nodes.keySet();
+            keys.remove(path);
+            path = path + '/';  //NOI18N
+            Iterator<String> it = keys.iterator();
+            while (it.hasNext()) {
+                if (it.next().startsWith(path)) {
+                    it.remove();
+                }
+            }
+        }
+    }
+
+    private PackageNode get( FileObject fo ) {
+        String path = FileUtil.getRelativePath( root, fo );        
+        assert path != null : "Asking for wrong folder" + fo;
+        Object o = names2nodes.get( path );
+        return !isNodeCreated( o ) ? null : (PackageNode)o;
+    }
+    
+    private boolean contains( FileObject fo ) {
+        String path = FileUtil.getRelativePath( root, fo );        
+        assert path != null : "Asking for wrong folder" + fo;
+        Object o = names2nodes.get( path );
+        return o != null;
+    }
+    
+    private boolean exists( FileObject fo ) {
+        String path = FileUtil.getRelativePath( root, fo );
+        return names2nodes.get( path ) != null;
+    }
+    
+    private boolean isNodeCreated( Object o ) {
+        return o instanceof Node;
+    }
+    
+    private PackageNode updatePath( String oldPath, String newPath ) {
+        assert newPath != null;
+        Object o = names2nodes.get( oldPath );
+        if ( o == null ) {
+            return null;
+        }        
+        names2nodes.remove( oldPath );
+        names2nodes.put( newPath, o );
+        return !isNodeCreated( o ) ? null : (PackageNode)o;
+    }
+    
+    // Implementation of FileChangeListener 
------------------------------------
+    
+    public void fileAttributeChanged( FileAttributeEvent fe ) {}
+
+    public void fileChanged( FileEvent fe ) {} 
+
+    public void fileFolderCreated( FileEvent fe ) {
+        FileObject fo = fe.getFile();        
+        if ( FileUtil.isParentOf( root, fo ) && isVisible( root, fo ) ) {
+            cleanEmptyKeys( fo );                
+//            add( fo, false);
+            findNonExcludedPackages( fo );
+            refreshKeys();
+        }
+    }
+    
+    public void fileDataCreated( FileEvent fe ) {
+        FileObject fo = fe.getFile();
+        if ( FileUtil.isParentOf( root, fo ) && isVisible( root, fo ) ) {
+            FileObject parent = fo.getParent();
+            // XXX consider using group.contains() here
+            if ( !VisibilityQuery.getDefault().isVisible( parent ) ) {
+                return; // Adding file into ignored directory
+            }
+            PackageNode n = get( parent );
+            if ( n == null && !contains( parent ) ) {                
+                add(parent, false, true);
+                refreshKeys();
+            }
+            else if ( n != null ) {
+                n.updateChildren();
+            }
+        }
+    }
+
+    public void fileDeleted( FileEvent fe ) {
+        FileObject fo = fe.getFile();       
+        
+        // System.out.println("FILE DELETED " + FileUtil.getRelativePath( 
root, fo ) );
+        
+        if ( FileUtil.isParentOf( root, fo ) && isVisible( root, fo ) ) {
+            
+            // System.out.println("IS FOLDER? " + fo + " : " + fo.isFolder() 
);
+                                  /* Hack for MasterFS see #42464 */
+            if ( fo.isFolder() || get( fo ) != null ) {
+                // System.out.println("REMOVING FODER " + fo );              
  
+                removeSubTree( fo );
+                // Now add the parent if necessary 
+                FileObject parent = fo.getParent();
+                if ( ( FileUtil.isParentOf( root, parent ) || root.equals( 
parent ) ) && get( parent ) == null && parent.isValid() ) {
+                    // Candidate for adding
+                    if ( !toBeRemoved( parent ) ) {
+                        // System.out.println("ADDING PARENT " + parent );
+                        add(parent, true, true);
+                    }
+                }
+                refreshKeysAsync();
+            }
+            else {
+                FileObject parent = fo.getParent();
+                final PackageNode n = get( parent );
+                if ( n != null ) {
+                    //#61027: workaround to a deadlock when the package is 
being changed from non-leaf to leaf:
+                    boolean leaf = n.isLeaf();
+                    DataFolder df = n.getDataFolder();
+                    boolean empty = isEmpty(df);
+                    
+                    if (leaf != empty) {
+                        SwingUtilities.invokeLater(new Runnable() {
+                            public void run() {
+                                n.updateChildren();
+                            }
+                        });
+                    } else {
+                        n.updateChildren();
+                    }
+                }
+                // If the parent folder only contains folders remove it
+                if ( toBeRemoved( parent ) ) {
+                    remove( parent );
+                    refreshKeysAsync();
+                }
+                 
+            }
+        }
+        // else {
+        //    System.out.println("NOT A PARENT " + fo );
+        // }
+    }
+    
+    /** Returns true if the folder should be removed from the view
+     * i.e. it has some unignored children and the children are folders only
+     */
+    private boolean toBeRemoved( FileObject folder ) {
+        boolean ignoredOnly = true;
+        boolean foldersOnly = true;
+        for (FileObject kid : folder.getChildren()) {
+            // XXX consider using group.contains() here
+            if (VisibilityQuery.getDefault().isVisible(kid)) {
+                ignoredOnly = false;
+                if (!kid.isFolder()) {
+                    foldersOnly = false;
+                    break;
+                }
+            }                                  
+        }
+        if ( ignoredOnly ) {
+            return false; // It is either empty or it only contains ignored 
files
+                          // thus is leaf and it means package
+        }
+        else {
+            return foldersOnly;
+        }
+    }
+    
+    
+    public void fileRenamed( FileRenameEvent fe ) {
+        FileObject fo = fe.getFile();        
+        if ( FileUtil.isParentOf( root, fo ) && fo.isFolder() ) {
+            String rp = FileUtil.getRelativePath( root, fo.getParent() );
+            String oldPath = rp + ( rp.length() == 0 ? "" : "/" ) + 
fe.getName() + fe.getExt(); // NOI18N
+
+            // XXX consider using group.contains() here
+            boolean visible = VisibilityQuery.getDefault().isVisible( fo );
+            boolean doUpdate = false;
+            
+            // Find all entries which have to be updated
+            List<String> needsUpdate = new ArrayList<String>();
+            synchronized (names2nodes) {
+                for (Iterator<String> it = names2nodes.keySet().iterator(); 
it.hasNext(); ) {
+                    String p = it.next();
+                    if ( p.startsWith( oldPath ) ) {
+                        if ( visible ) {
+                            needsUpdate.add( p );
+                        } else {
+                            it.remove();
+                            doUpdate = true;
+                        }
+                    }
+                }
+            }   
+                        
+            // If the node does not exists then there might have been update
+            // from ignored to non ignored
+            if ( get( fo ) == null && visible ) {
+                cleanEmptyKeys( fo );                
+                findNonExcludedPackages( fo );
+                doUpdate = true;  // force refresh
+            }
+            
+            int oldPathLen = oldPath.length();
+            String newPath = FileUtil.getRelativePath( root, fo );
+            for (String p : needsUpdate) {
+                StringBuilder np = new StringBuilder(p);
+                np.replace( 0, oldPathLen, newPath );                    
+                PackageNode n = updatePath( p, np.toString() ); // Replace 
entries in cache
+                if ( n != null ) {
+                    n.updateDisplayName(); // Update nodes
+                }
+            }
+            
+            if ( needsUpdate.size() > 1 || doUpdate ) {
+                // Sorting might change
+                refreshKeys();
+            }
+        }
+        /*
+        else if ( FileUtil.isParentOf( root, fo ) && fo.isFolder() ) {
+            FileObject parent = fo.getParent();
+            PackageNode n = get( parent );
+            if ( n != null && VisibilityQuery.getDefault().isVisible( parent 
) ) {
+                n.updateChildren();
+            }
+            
+        }
+        */
+        
+    }
+    
+    /** Test whether file and all it's parent up to parent paremeter
+     * are visible
+     */    
+    private boolean isVisible( FileObject parent, FileObject file ) {
+        
+        do {    
+            // XXX consider using group.contains() here
+            if ( !VisibilityQuery.getDefault().isVisible( file ) )  {
+                return false;
+            }
+            file = file.getParent();
+        }
+        while ( file != null && file != parent );    
+                
+        return true;        
+    }
+    
+
+    // Implementation of ChangeListener ------------------------------------
+        
+    public void stateChanged( ChangeEvent e ) {
+        computeKeys();
+        refreshKeys();
+    }
+    
+
+    /*
+    private void debugKeySet() {
+        for( Iterator it = names2nodes.keySet().iterator(); it.hasNext(); ) {
+            String k = (String)it.next();
+            System.out.println( "    " + k + " -> " +  names2nodes.get( k ) 
);
+        }
+    }
+     */
+    
+    private final DataFilter NO_FOLDERS_FILTER = new NoFoldersDataFilter();
+        
+    private static boolean isEmpty(DataFolder dataFolder) {
+        if ( dataFolder == null ) {
+            return true;
+        }
+        return PackageDisplayUtils.isEmpty( dataFolder.getPrimaryFile() );
+    }
+    
+    final class PackageNode extends FilterNode {
+        
+        private Action actions[];
+        
+        private final FileObject root;
+        private DataFolder dataFolder;
+        private boolean isDefaultPackage;
+        
+        public PackageNode( FileObject root, DataFolder dataFolder ) {
+            this( root, dataFolder, isEmpty( dataFolder ) );
+        }
+        
+        public PackageNode( FileObject root, DataFolder dataFolder, boolean 
empty ) {    
+            super( dataFolder.getNodeDelegate(), 
+                   empty ? Children.LEAF : dataFolder.createNodeChildren( 
NO_FOLDERS_FILTER ),
+                   new ProxyLookup(
+                        Lookups.singleton(new NoFoldersContainer 
(dataFolder)),
+                        dataFolder.getNodeDelegate().getLookup(),
+                        
Lookups.singleton(PackageRootNode.alwaysSearchableSearchInfo(SearchInfoFactory.createSearchInfo(
+                                                  
dataFolder.getPrimaryFile(),
+                                                  false,      //not recursive
+                                                  new FileObjectFilter[] {
+                                                          
SearchInfoFactory.VISIBILITY_FILTER})))));
+            this.root = root;
+            this.dataFolder = dataFolder;
+            this.isDefaultPackage = root.equals( dataFolder.getPrimaryFile() 
);
+        }
+    
+        FileObject getRoot() {
+            return root; // Used from PackageRootNode
+        }
+    
+        
+        public String getName() {
+            String relativePath = FileUtil.getRelativePath(root, 
dataFolder.getPrimaryFile());
+            return relativePath == null ?  null : relativePath.replace('/', 
'.'); // NOI18N
+        }
+        
+        public Action[] getActions( boolean context ) {
+            
+            if ( !context ) {
+                if ( actions == null ) {                
+                    // Copy actions and leave out the PropertiesAction and 
FileSystemAction.                
+                    Action superActions[] = super.getActions( context );     
       
+                    List<Action> actionList = new 
ArrayList<Action>(superActions.length);
+                    
+                    for( int i = 0; i < superActions.length; i++ ) {
+
+                        if ( (i <= superActions.length - 2) && superActions[ 
i ] == null && superActions[i + 1] instanceof PropertiesAction ) {
+                            i ++;
+                            continue;
+                        }
+                        else if ( superActions[i] instanceof 
PropertiesAction ) {
+                            continue;
+                        }
+                        else if ( superActions[i] instanceof 
FileSystemAction ) {
+                            actionList.add (null); // insert separator and 
new action
+                            actionList.add 
(FileSensitiveActions.fileCommandAction(ActionProvider.COMMAND_COMPILE_SINGLE,
 
+                                NbBundle.getMessage( 
PackageViewChildren.class, "LBL_CompilePackage_Action" ), // NOI18N
+                                null ));                            
+                        }
+                        
+                        actionList.add( superActions[i] );                   
                               
+                    }
+
+                    actions = new Action[ actionList.size() ];
+                    actionList.toArray( actions );
+                }
+                return actions;
+            }
+            else {
+                return super.getActions( context );
+            }
+        }
+        
+        public boolean canRename() {
+            if ( isDefaultPackage ) {
+                return false;
+            }
+            else {         
+                return true;
+            }
+        }
+
+        public boolean canCut () {
+            return !isDefaultPackage;    
+        }
+
+        /**
+         * Copy handling
+         */
+        public Transferable clipboardCopy () throws IOException {
+            try {
+                return new PackageTransferable (this, 
DnDConstants.ACTION_COPY);
+            } catch (ClassNotFoundException e) {
+                throw new AssertionError(e);
+            }
+        }
+        
+        public Transferable clipboardCut () throws IOException {
+            try {
+                return new PackageTransferable (this, 
DnDConstants.ACTION_MOVE);
+            } catch (ClassNotFoundException e) {
+                throw new AssertionError(e);
+            }
+        }
+        
+        public /*@Override*/ Transferable drag () throws IOException {
+            try {
+                return new PackageTransferable (this, 
DnDConstants.ACTION_NONE);
+            } catch (ClassNotFoundException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        public PasteType[] getPasteTypes(Transferable t) {
+            if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
+                try {
+                    MultiTransferObject mto = (MultiTransferObject) 
t.getTransferData (ExTransferable.multiFlavor);
+                    boolean hasPackageFlavor = false;
+                    for (int i=0; i < mto.getCount(); i++) {
+                        DataFlavor[] flavors = mto.getTransferDataFlavors(i);
+                        if (isPackageFlavor(flavors)) {
+                            hasPackageFlavor = true;
+                        }
+                    }
+                    return hasPackageFlavor ? new PasteType[0] : 
super.getPasteTypes (t);
+                } catch (UnsupportedFlavorException e) {
+                    ErrorManager.getDefault().notify(e);
+                    return new PasteType[0];
+                } catch (IOException e) {
+                    ErrorManager.getDefault().notify(e);
+                    return new PasteType[0];
+                }
+            }
+            else {
+                DataFlavor[] flavors = t.getTransferDataFlavors();
+                if (isPackageFlavor(flavors)) {
+                    return new PasteType[0];
+                }
+                else {
+                    return super.getPasteTypes(t);
+                }
+            }
+        }
+        
+        public /*@Override*/ PasteType getDropType (Transferable t, int 
action, int index) {
+            if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
+                try {
+                    MultiTransferObject mto = (MultiTransferObject) 
t.getTransferData (ExTransferable.multiFlavor);
+                    boolean hasPackageFlavor = false;
+                    for (int i=0; i < mto.getCount(); i++) {
+                        DataFlavor[] flavors = mto.getTransferDataFlavors(i);
+                        if (isPackageFlavor(flavors)) {
+                            hasPackageFlavor = true;
+                        }
+                    }
+                    return hasPackageFlavor ? null : super.getDropType (t, 
action, index);
+                } catch (UnsupportedFlavorException e) {
+                    ErrorManager.getDefault().notify(e);
+                    return null;
+                } catch (IOException e) {
+                    ErrorManager.getDefault().notify(e);
+                    return null;
+                }
+            }
+            else {
+                DataFlavor[] flavors = t.getTransferDataFlavors();
+                if (isPackageFlavor(flavors)) {
+                    return null;
+                }
+                else {
+                    return super.getDropType (t, action, index);
+                }
+            }
+        }
+
+
+        private boolean isPackageFlavor (DataFlavor[] flavors) {
+            for (int i=0; i<flavors.length; i++) {
+                if (SUBTYPE.equals(flavors[i].getSubType ()) && 
PRIMARY_TYPE.equals(flavors[i].getPrimaryType ())) {
+                    //Disable pasting into package, only paste into root is 
allowed
+                    return true;
+                }
+            }
+            return false;
+        }
+
+// TOR
+//        private synchronized PackageRenameHandler getRenameHandler() {
+//            Collection<? extends PackageRenameHandler> handlers = 
Lookup.getDefault().lookupAll(PackageRenameHandler.class);
+//            if (handlers.size()==0)
+//                return null;
+//            if (handlers.size()>1)
+//                ErrorManager.getDefault().log(ErrorManager.WARNING, 
"Multiple instances of PackageRenameHandler found in Lookup; only using first 
one: " + handlers); //NOI18N
+//            return handlers.iterator().next();
+//        }
+        
+        public void setName(String name) {
+//            PackageRenameHandler handler = getRenameHandler();
+//            if (handler!=null) {
+//                handler.handleRename(this, name);
+//                return;
+//            }
+            
+            if (isDefaultPackage) {
+                return;
+            }
+            String oldName = getName();
+            if (oldName.equals(name)) {
+                return;
+            }
+            if (!isValidPackageName (name)) {
+                DialogDisplayer.getDefault().notify(new 
NotifyDescriptor.Message (
+                        
NbBundle.getMessage(PackageViewChildren.class,"MSG_InvalidPackageName"), 
NotifyDescriptor.INFORMATION_MESSAGE));
+                return;
+            }
+            name = name.replace('.','/')+'/';           //NOI18N
+            oldName = oldName.replace('.','/')+'/';     //NOI18N
+            int i;
+            for (i=0; i<oldName.length() && i< name.length(); i++) {
+                if (oldName.charAt(i) != name.charAt(i)) {
+                    break;
+                }
+            }
+            i--;
+            int index = oldName.lastIndexOf('/',i);     //NOI18N
+            String commonPrefix = index == -1 ? null : 
oldName.substring(0,index);
+            String toCreate = (index+1 == name.length()) ? "" : 
name.substring(index+1);    //NOI18N
+            try {
+                FileObject commonFolder = commonPrefix == null ? this.root : 
this.root.getFileObject(commonPrefix);
+                FileObject destination = commonFolder;
+                StringTokenizer dtk = new StringTokenizer(toCreate,"/");    
//NOI18N
+                while (dtk.hasMoreTokens()) {
+                    String pathElement = dtk.nextToken();
+                    FileObject tmp = destination.getFileObject(pathElement);
+                    if (tmp == null) {
+                        tmp = destination.createFolder (pathElement);
+                    }
+                    destination = tmp;
+                }
+                FileObject source = this.dataFolder.getPrimaryFile();        
        
+                DataFolder sourceFolder = DataFolder.findFolder (source);
+                DataFolder destinationFolder = DataFolder.findFolder 
(destination);
+                DataObject[] children = sourceFolder.getChildren();
+                for (int j=0; j<children.length; j++) {
+                    if (children[j].getPrimaryFile().isData()) {
+                        children[j].move(destinationFolder);
+                    }
+                }
+                while (!commonFolder.equals(source)) {
+                    if (source.getChildren().length==0) {
+                        FileObject tmp = source;
+                        source = source.getParent();
+                        tmp.delete();
+                    }
+                    else {
+                        break;
+                    }
+                }
+            } catch (IOException ioe) {
+                ErrorManager.getDefault().notify (ioe);
+            }
+        }
+        
+        
+        
+        public boolean canDestroy() {
+            if ( isDefaultPackage ) {
+                return false;
+            }
+            else {
+                return true;
+            }
+        }
+        
+        public void destroy() throws IOException {
+            FileObject parent = dataFolder.getPrimaryFile().getParent();
+            // First; delete all files except packages
+            DataObject ch[] = dataFolder.getChildren();
+            boolean empty = true;
+            for( int i = 0; ch != null && i < ch.length; i++ ) {
+                if ( !ch[i].getPrimaryFile().isFolder() ) {
+                    ch[i].delete();
+                }
+                else {
+                    empty = false;
+                }
+            }
+            
+            // If empty delete itself
+            if ( empty ) {
+                super.destroy();
+            }
+            
+            
+            // Second; delete empty super packages
+            while( !parent.equals( root ) && parent.getChildren().length == 
0  ) {
+                FileObject newParent = parent.getParent();
+                parent.delete();
+                parent = newParent;
+            }
+        }
+        
+        /**
+         * Initially overridden to support CVS status labels in package 
nodes.
+         *  
+         * @return annotated display name
+         */ 
+        public String getHtmlDisplayName() {
+            String name = getDisplayName();
+            try {
+                FileObject fo = dataFolder.getPrimaryFile();
+                Set<FileObject> set = new NonRecursiveFolderSet(fo);
+                FileSystem.Status status = fo.getFileSystem().getStatus();
+                if (status instanceof FileSystem.HtmlStatus) {
+                    name = ((FileSystem.HtmlStatus) 
status).annotateNameHtml(name, set);
+                } else {
+                    // #89138: return null if the name starts with '<' and 
status is not HtmlStatus
+                    if (name.startsWith("<")) {
+                        name = null;
+                    } else {
+                        name = status.annotateName(name, set);
+                    }
+                }
+            } catch (FileStateInvalidException e) {
+                // no fs, do nothing
+            }
+            return name;
+        }
+        
+        public String getDisplayName() {
+            FileObject folder = dataFolder.getPrimaryFile();
+            String path = FileUtil.getRelativePath(root, folder);
+            if (path == null) {
+                // ???
+                return "";
+            }
+            return PackageDisplayUtils.getDisplayLabel( path.replace('/', 
'.'));
+        }
+        
+        public String getShortDescription() {
+            FileObject folder = dataFolder.getPrimaryFile();
+            String path = FileUtil.getRelativePath(root, folder);
+            if (path == null) {
+                // ???
+                return "";
+            }
+            return PackageDisplayUtils.getToolTip(folder, path.replace('/', 
'.'));
+        }
+
+        public Image getIcon (int type) {
+            Image img = getMyIcon (type);
+
+            try {
+                FileObject fo = dataFolder.getPrimaryFile();
+                Set<FileObject> set = new NonRecursiveFolderSet(fo);
+                img = fo.getFileSystem ().getStatus ().annotateIcon (img, 
type, set);
+            } catch (FileStateInvalidException e) {
+                // no fs, do nothing
+            }
+
+            return img;
+        }
+
+        public Image getOpenedIcon (int type) {
+            Image img = getMyOpenedIcon(type);
+
+            try {
+                FileObject fo = dataFolder.getPrimaryFile();
+                Set<FileObject> set = new NonRecursiveFolderSet(fo);
+                img = fo.getFileSystem ().getStatus ().annotateIcon (img, 
type, set);
+            } catch (FileStateInvalidException e) {
+                // no fs, do nothing
+            }
+
+            return img;
+        }
+        
+        
+        private Image getMyIcon(int type) {
+            FileObject folder = dataFolder.getPrimaryFile();
+            String path = FileUtil.getRelativePath(root, folder);
+            if (path == null) {
+                // ??? - #103711: null cannot be returned because the icon 
+                // must be annotated; general package icon is returned 
instead
+                return 
ImageUtilities.loadImage("org/netbeans/modules/python/project/resources/package.gif");
 // NOI18N
+            }
+            //return PackageDisplayUtils.getIcon(folder, path.replace('/', 
'.'), isLeaf() );
+            return PackageDisplayUtils.getIcon(folder, path.replace('/', 
'.'),
+                    PackageDisplayUtils.isEmpty(dataFolder.getPrimaryFile(), 
false, true));
+        }
+        
+        private Image getMyOpenedIcon(int type) {
+            return getMyIcon(type);
+        }
+        
+        public void update() {
+            fireIconChange();
+            fireOpenedIconChange();            
+        }
+        
+        public void updateDisplayName() {
+            fireNameChange(null, null);
+            fireDisplayNameChange(null, null);
+            fireShortDescriptionChange(null, null);
+        }
+        
+        public void updateChildren() {            
+            boolean leaf = isLeaf();
+            DataFolder df = getDataFolder();
+            boolean empty = isEmpty( df ); 
+            if ( leaf != empty ) {
+                setChildren( empty ? Children.LEAF: df.createNodeChildren( 
NO_FOLDERS_FILTER ) );                
+                update();
+            }
+        }
+        
+        @Override
+        public Node.PropertySet[] getPropertySets () {
+            Node.PropertySet[] properties = super.getPropertySets ();
+            for (int i=0; i< properties.length; i++) {
+                if (Sheet.PROPERTIES.equals(properties[i].getName())) {
+                    //Replace the Sheet.PROPERTIES by the new one
+                    //having only the name property which does refactoring
+                    properties[i] = Sheet.createPropertiesSet();
+                    ((Sheet.Set) properties[i]).put(new 
PropertySupport.ReadWrite<String>(DataObject.PROP_NAME, String.class,
+                            
NbBundle.getMessage(PackageViewChildren.class,"PROP_name"), 
NbBundle.getMessage(PackageViewChildren.class,"HINT_name")) {
+                        @Override
+                        public String getValue() {
+                            return 
PackageViewChildren.PackageNode.this.getName();
+                        }
+                        @Override
+                        public void setValue(String n) throws 
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+                            if (!canRename()) {
+                                throw new IllegalAccessException();
+                            }
+                            PackageViewChildren.PackageNode.this.setName(n);
+                        }
+                        @Override
+                        public boolean canWrite() {
+                            return 
PackageViewChildren.PackageNode.this.canRename();
+                        }
+                    });
+                }
+            }
+            return properties;
+        }
+        
+        private DataFolder getDataFolder() {
+            return getCookie(DataFolder.class);
+        }
+        
+        private boolean isValidPackageName(String name) {
+            if (name.length() == 0) {
+                //Fast check of default pkg
+                return true;
+            }
+            StringTokenizer tk = new StringTokenizer(name,".",true); //NOI18N
+            boolean delimExpected = false;
+            while (tk.hasMoreTokens()) {
+                String namePart = tk.nextToken();
+                if (!delimExpected) {
+                    if (namePart.equals(".")) { //NOI18N
+                        return false;
+                    }
+                    for (int i=0; i< namePart.length(); i++) {
+                        char c = namePart.charAt(i);
+                        if (i == 0) {
+                            if (!Character.isJavaIdentifierStart (c)) {
+                                return false;
+                            }
+                        }
+                        else {
+                            if (!Character.isJavaIdentifierPart(c)) {
+                                return false;
+                            }
+                        }
+                    }
+                }
+                else {
+                    if (!namePart.equals(".")) { //NOI18N
+                        return false;
+                    }
+                }
+                delimExpected = !delimExpected;
+            }
+            return delimExpected;
+        }
+    }
+    
+    private static final class NoFoldersContainer 
+    implements DataObject.Container, PropertyChangeListener,
+               NonRecursiveFolder {
+        private DataFolder folder;
+        private PropertyChangeSupport prop = new PropertyChangeSupport 
(this);
+        
+        public NoFoldersContainer (DataFolder folder) {
+            this.folder = folder;
+        }
+        
+        public FileObject getFolder() {
+            return folder.getPrimaryFile();
+        }
+        
+        public DataObject[] getChildren () {
+            DataObject[] arr = folder.getChildren ();
+            List<DataObject> list = new ArrayList<DataObject>(arr.length);
+            for (int i = 0; i < arr.length; i++) {
+                if (arr[i] instanceof DataFolder) continue;
+                
+                list.add (arr[i]);
+            }
+            return list.size() == arr.length ? arr : list.toArray(new 
DataObject[0]);
+        }
+
+        public void addPropertyChangeListener(PropertyChangeListener l) {
+            prop.addPropertyChangeListener (l);
+        }
+
+        public void removePropertyChangeListener(PropertyChangeListener l) {
+            prop.removePropertyChangeListener (l);
+        }
+
+        public void propertyChange(PropertyChangeEvent evt) {
+            if (DataObject.Container.PROP_CHILDREN.equals 
(evt.getPropertyName ())) {
+                prop.firePropertyChange (PROP_CHILDREN, null, null);
+            }
+        }
+    }
+    
+    private final class NoFoldersDataFilter implements ChangeListener, 
ChangeableDataFilter, DataFilter.FileBased {
+        
+        private final ChangeSupport cs = new ChangeSupport(this);
+        
+        public NoFoldersDataFilter() {
+            
VisibilityQuery.getDefault().addChangeListener(WeakListeners.change(this, 
VisibilityQuery.getDefault()));
+        }
+                
+        public boolean acceptDataObject(DataObject obj) {  
+            // Filter out .pyc or .pyo files!
+            String ext = obj.getPrimaryFile().getExt();
+            if ("pyc".equals(ext) || "pyo".equals(ext)) { // NOI18N
+                return false;
+            }
+            return acceptFileObject(obj.getPrimaryFile());
+        }
+        
+        public void stateChanged( ChangeEvent e) {            
+            cs.fireChange();
+        }        
+    
+        public void addChangeListener( ChangeListener listener ) {
+            cs.addChangeListener(listener);
+        }        
+                        
+        public void removeChangeListener( ChangeListener listener ) {
+            cs.removeChangeListener(listener);
+        }
+
+        public boolean acceptFileObject(FileObject fo) {
+            return  fo.isValid() && 
VisibilityQuery.getDefault().isVisible(fo) && fo.isData() && 
group.contains(fo);
+        }
+        
+    }
+
+    static class PackageTransferable extends ExTransferable.Single {
+
+        private PackageNode node;
+
+        public PackageTransferable (PackageNode node, int operation) throws 
ClassNotFoundException {
+            super(new DataFlavor(PACKAGE_FLAVOR.format(new Object[] {new 
Integer(operation)}), null, PackageNode.class.getClassLoader()));
+            this.node = node;
+        }
+
+        protected Object getData() throws IOException, 
UnsupportedFlavorException {
+            return this.node;
+        }
+    }
+
+
+    static class PackagePasteType extends PasteType {
+        
+        private int op;
+        private PackageNode[] nodes;
+        private FileObject srcRoot;
+
+        public PackagePasteType (FileObject srcRoot, PackageNode[] node, int 
op) {
+            assert op == DnDConstants.ACTION_COPY || op == 
DnDConstants.ACTION_MOVE  || op == DnDConstants.ACTION_NONE : "Invalid DnD 
operation";  //NOI18N
+            this.nodes = node;
+            this.op = op;
+            this.srcRoot = srcRoot;
+        }
+        
+        public void setOperation (int op) {
+            this.op = op;
+        }
+
+        public Transferable paste() throws IOException {
+            assert this.op != DnDConstants.ACTION_NONE;
+            for (int ni=0; ni< nodes.length; ni++) {
+                FileObject fo = srcRoot;
+                if (!nodes[ni].isDefaultPackage) {
+                    String pkgName = nodes[ni].getName();
+                    StringTokenizer tk = new StringTokenizer(pkgName,".");  
//NOI18N
+                    while (tk.hasMoreTokens()) {
+                        String name = tk.nextToken();
+                        FileObject tmp = fo.getFileObject(name,null);
+                        if (tmp == null) {
+                            tmp = fo.createFolder(name);
+                        }
+                        fo = tmp;
+                    }
+                }
+                DataFolder dest = DataFolder.findFolder(fo);
+                DataObject[] children = nodes[ni].dataFolder.getChildren();
+                boolean cantDelete = false;
+                for (int i=0; i< children.length; i++) {
+                    if (children[i].getPrimaryFile().isData() 
+                    && VisibilityQuery.getDefault().isVisible 
(children[i].getPrimaryFile())) {
+                        //Copy only the package level
+                        if (this.op == DnDConstants.ACTION_MOVE) {
+                            children[i].move(dest);
+                        }
+                        else {
+                            children[i].copy (dest);
+                        }                                                
+                    }
+                    else {
+                        cantDelete = true;
+                    }
+                }
+                if (this.op == DnDConstants.ACTION_MOVE && !cantDelete) {
+                    try {
+                        FileObject tmpFo = 
nodes[ni].dataFolder.getPrimaryFile();
+                        FileObject originalRoot = nodes[ni].root;
+                        assert tmpFo != null && originalRoot != null;
+                        while (!tmpFo.equals(originalRoot)) {
+                            if (tmpFo.getChildren().length == 0) {
+                                FileObject tmpFoParent = tmpFo.getParent();
+                                tmpFo.delete ();
+                                tmpFo = tmpFoParent;
+                            }
+                            else {
+                                break;
+                            }
+                        }
+                    } catch (IOException ioe) {
+                        //Not important
+                    }
+                }
+            }
+            return ExTransferable.EMPTY;
+        }
+
+        public String getName() {
+            return 
NbBundle.getMessage(PackageViewChildren.class,"TXT_PastePackage");
+        }
+    }
+
+    /**
+     * FileObject set that represents package. It means
+     * that it's content must not be processed recursively.
+     */
+    private static class NonRecursiveFolderSet extends HashSet<FileObject> 
implements NonRecursiveFolder {
+        
+        private final FileObject folder;
+        
+        /**
+         * Creates set with one element, the folder.
+         */
+        public NonRecursiveFolderSet(FileObject folder) {
+            this.folder = folder;
+            add(folder);
+        }
+        
+        public FileObject getFolder() {
+            return folder;
+        }        
+    }
+}
diff --git 
a/python.project/src/org/netbeans/modules/python/project/ui/PythonProjectSettings.java
 
b/python.project/src/org/netbeans/modules/python/project/ui/PythonProjectSettings.java
new file mode 100644
--- /dev/null
+++ 
b/python.project/src/org/netbeans/modules/python/project/ui/PythonProjectSettings.java
@@ -0,0 +1,116 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
+ *
+ * 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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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-2006 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.python.project.ui;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.prefs.Preferences;
+import org.openide.util.NbPreferences;
+
+/**
+ * Preferences for the module.
+ * <p>
+ * <b>This is copied from the corresponding Java action in java.projects 
(JavaProjectSettings)</b>
+ * </p>
+ *
+ * @author Tomas Zezula, Jesse Glick
+ */
+public class PythonProjectSettings {
+
+    private PythonProjectSettings() {}
+
+    private static final PropertyChangeSupport pcs = new 
PropertyChangeSupport(PythonProjectSettings.class);
+
+    /**
+     * The package view should be displayed as a list of packages.
+     */
+    public static final int TYPE_PACKAGE_VIEW = 0;
+
+    /**
+     * The package view should be displayed as a tree of folders.
+     */
+    public static final int TYPE_TREE = 1;
+
+    public static final String PROP_PACKAGE_VIEW_TYPE = "packageViewType"; 
//NOI18N
+//    private static final String PROP_SHOW_AGAIN_BROKEN_REF_ALERT = 
"showAgainBrokenRefAlert"; //NOI18N
+
+    private static Preferences prefs() {
+        return NbPreferences.forModule(PythonProjectSettings.class);
+    }
+
+    /**
+     * Returns how the package view should be displayed.
+     * @return {@link #TYPE_PACKAGE_VIEW} or {@link #TYPE_TREE}
+     */
+    public static int getPackageViewType() {
+        return prefs().getInt(PROP_PACKAGE_VIEW_TYPE, TYPE_PACKAGE_VIEW);
+    }
+
+    /**
+     * Sets how the package view should be displayed.
+     * @param type either {@link #TYPE_PACKAGE_VIEW} or {@link #TYPE_TREE}
+     */
+    public static void setPackageViewType(int type) {
+        int currentType = getPackageViewType();
+        if (currentType != type) {
+            prefs().putInt(PROP_PACKAGE_VIEW_TYPE, type);
+            pcs.firePropertyChange(PROP_PACKAGE_VIEW_TYPE, currentType, 
type);
+        }
+    }
+
+//    public static boolean isShowAgainBrokenRefAlert() {
+//        return prefs().getBoolean(PROP_SHOW_AGAIN_BROKEN_REF_ALERT, true);
+//    }
+//
+//    public static void setShowAgainBrokenRefAlert(boolean again) {
+//        prefs().putBoolean(PROP_SHOW_AGAIN_BROKEN_REF_ALERT, again);
+//    }
+
+    public static void addPropertyChangeListener(PropertyChangeListener l) {
+        pcs.addPropertyChangeListener(l);
+    }
+
+    public static void removePropertyChangeListener(PropertyChangeListener 
l) {
+        pcs.removePropertyChangeListener(l);
+    }
+
+}
diff --git 
a/python.project/src/org/netbeans/modules/python/project/ui/SourceNodeFactory.java
 
b/python.project/src/org/netbeans/modules/python/project/ui/SourceNodeFactory.java
--- 
a/python.project/src/org/netbeans/modules/python/project/ui/SourceNodeFactory.java
+++ 
b/python.project/src/org/netbeans/modules/python/project/ui/SourceNodeFactory.java
@@ -192,22 +192,11 @@
         Action[] actions;
         
         public PackageViewFilterNode(SourceGroup sourceGroup, Project 
project) {
-            super(getOriginalNode(sourceGroup));
+            super(PackageView.createPackageView(sourceGroup));
             this.project = project;
             this.nodeName = "Sources";  //NOI18N
         }
         
-        private static Node getOriginalNode(final SourceGroup group) {
-            if (group == null) {
-                return new AbstractNode(Children.LEAF);
-            }
-            FileObject root = group.getRootFolder();
-            if (root == null || !root.isValid()) {
-                return new AbstractNode(Children.LEAF);
-            }
-            return new TreeRootNode(group);
-        }
-        
         public Action[] getActions(boolean context) {
             if (!context) {
                 if (actions == null) {

[[nbpython-commits]] [hg] main/contrib: (1) Package view for Python projects in the p...

Tor Norbye 10/21/2010

Project Features

About this Project

www was started in November 2009, is owned by jpirek, and has 21 members.
By use of this website, you agree to the NetBeans Policies and Terms of Use (revision 20140418.2d69abc). © 2013, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo
 
 
Close
loading
Please Confirm
Close