Python Project2 - based on setuptools python
authorJulio C. Rocha <juniel_katarn@netbeans.org>
Tue, 24 Feb 2015 01:58:36 -0800
branchpython
changeset 182213005fa07a159
parent 18219 806797e65aba
child 18222 a8bb07049012
Python Project2 - based on setuptools
Contributed by Ralph Benjamin from:
https://github.com/GeertjanWielenga/Python4NetBeans/commit/13300c50162a2f0d89786251daed86bb5eac6d4b
python.core/release/platform_info.py
python.core/src/org/netbeans/modules/python/api/PythonExecution.java
python.core/src/org/netbeans/modules/python/api/PythonPlatform.java
python.core/src/org/netbeans/modules/python/api/PythonPlatformManager.java
python.debugger/src/org/netbeans/modules/python/debugger/Debuggee.java
python.debugger/src/org/netbeans/modules/python/debugger/DebuggerPythonLogger.java
python.debugger/src/org/netbeans/modules/python/debugger/PythonDebugger.java
python.debugger/src/org/netbeans/modules/python/debugger/PythonSourceDebuggee.java
python.debugger/src/org/netbeans/modules/python/debugger/actions/JpyDbgView.java
python.debugger/src/org/netbeans/modules/python/debugger/actions/PluginEventListener.java
python.debugger/src/org/netbeans/modules/python/debugger/backend/PluginEventListener.java
python.debugger/src/org/netbeans/modules/python/debugger/gui/PythonDebugContainer.java
python.debugger/src/org/netbeans/modules/python/debugger/resources/python/nbpythondebug/jpydaemon.py
python.debugger/src/org/netbeans/modules/python/debugger/spi/PythonDebuggerTargetExecutor.java
python.debugger/src/org/netbeans/modules/python/debugger/spi/PythonSourceDebuggee.java
python.debugger/src/org/netbeans/modules/python/debugger/spi/TargetExecutor.java
python.editor/manifest.mf
python.editor/nbproject/project.properties
python.editor/nbproject/project.xml
python.editor/src/org/netbeans/modules/python/editor/PythonParser.java
python.editor/src/org/netbeans/modules/python/editor/file/PyDataObject.java
python.editor/src/org/netbeans/modules/python/editor/file/PythonShebangSourceLevelQuery.java
python.editor/src/org/netbeans/modules/python/editor/file/RunSingleCommand.java
python.editor/src/org/netbeans/modules/python/editor/layer.xml
python.editor/src/org/netbeans/modules/python/editor/templates/executable_module.py.ftl
python.editor/src/org/netbeans/modules/python/editor/templates/init.py.ftl
python.editor/src/org/netbeans/modules/python/editor/templates/module.py.ftl
python.editor/src/org/netbeans/modules/python/editor/templates/setup.py.ftl
python.kit/nbproject/project.xml
python.project/src/org/netbeans/modules/python/project/queries/PythonProjectSourceLevelQuery.java
python.project/src/org/netbeans/modules/python/project/queries/PythonShebangSourceLevelQuery.java
python.project/src/org/netbeans/modules/python/project/templates/EmptyPythonProjectDescription.html
python.project/src/org/netbeans/modules/python/project/templates/ExistingPythonProjectDescription.html
python.project2/build.xml
python.project2/manifest.mf
python.project2/nbproject/project.properties
python.project2/nbproject/project.xml
python.project2/src/org/netbeans/modules/python/project2/Bundle.properties
python.project2/src/org/netbeans/modules/python/project2/Python2LogicalView.java
python.project2/src/org/netbeans/modules/python/project2/PythonActionProvider.java
python.project2/src/org/netbeans/modules/python/project2/PythonAuxilaryConfig.java
python.project2/src/org/netbeans/modules/python/project2/PythonProject2.java
python.project2/src/org/netbeans/modules/python/project2/PythonProjectFactory.java
python.project2/src/org/netbeans/modules/python/project2/PythonProjectSourceLevelQuery.java
python.project2/src/org/netbeans/modules/python/project2/PythonSources.java
python.project2/src/org/netbeans/modules/python/project2/classpath/BootClassPathImplementation.java
python.project2/src/org/netbeans/modules/python/project2/classpath/ClassPathProviderImpl.java
python.project2/src/org/netbeans/modules/python/project2/classpath/CompilePathImplementation.java
python.project2/src/org/netbeans/modules/python/project2/classpath/SourcePathImplementation.java
python.project2/src/org/netbeans/modules/python/project2/layer.xml
python.project2/src/org/netbeans/modules/python/project2/resources/brokenProjectBadge.gif
python.project2/src/org/netbeans/modules/python/project2/resources/package.gif
python.project2/src/org/netbeans/modules/python/project2/resources/packageBadge.gif
python.project2/src/org/netbeans/modules/python/project2/resources/packageEmpty.gif
python.project2/src/org/netbeans/modules/python/project2/resources/packagePrivate.gif
python.project2/src/org/netbeans/modules/python/project2/resources/packagePublic.gif
python.project2/src/org/netbeans/modules/python/project2/resources/py_25_16.png
python.project2/src/org/netbeans/modules/python/project2/resources/sourceBadge.gif
python.project2/src/org/netbeans/modules/python/project2/templates/Bundle.properties
python.project2/src/org/netbeans/modules/python/project2/templates/EmptyPythonProjectDescription.html
python.project2/src/org/netbeans/modules/python/project2/templates/EmptyPythonProjectPanelVisual.form
python.project2/src/org/netbeans/modules/python/project2/templates/EmptyPythonProjectPanelVisual.java
python.project2/src/org/netbeans/modules/python/project2/templates/NewPythonProjectWizardIterator.java
python.project2/src/org/netbeans/modules/python/project2/templates/PanelConfigureProject.java
python.project2/src/org/netbeans/modules/python/project2/templates/PanelConfigureProjectVisual.form
python.project2/src/org/netbeans/modules/python/project2/templates/PanelConfigureProjectVisual.java
python.project2/src/org/netbeans/modules/python/project2/templates/PanelOptionsVisual.form
python.project2/src/org/netbeans/modules/python/project2/templates/PanelOptionsVisual.java
python.project2/src/org/netbeans/modules/python/project2/templates/SettingsPanel.java
python.project2/src/org/netbeans/modules/python/project2/ui/Bundle.properties
python.project2/src/org/netbeans/modules/python/project2/ui/ChangePackageViewTypeAction.java
python.project2/src/org/netbeans/modules/python/project2/ui/MainModuleChooser.form
python.project2/src/org/netbeans/modules/python/project2/ui/MainModuleChooser.java
python.project2/src/org/netbeans/modules/python/project2/ui/PackageDisplayUtils.java
python.project2/src/org/netbeans/modules/python/project2/ui/PackageRootNode.java
python.project2/src/org/netbeans/modules/python/project2/ui/PackageView.java
python.project2/src/org/netbeans/modules/python/project2/ui/PackageViewChildren.java
python.project2/src/org/netbeans/modules/python/project2/ui/PythonProjectSettings.java
python.project2/src/org/netbeans/modules/python/project2/ui/SourceNodeFactory.java
python.project2/src/org/netbeans/modules/python/project2/ui/TreeRootNode.java
python.project2/src/org/netbeans/modules/python/project2/ui/Utils.java
python.project2/src/org/netbeans/modules/python/project2/ui/actions/Command.java
python.project2/src/org/netbeans/modules/python/project2/ui/actions/RunCommand.java
python.project2/src/org/netbeans/modules/python/project2/ui/actions/RunSingleCommand.java
python.project2/src/org/netbeans/modules/python/project2/ui/customizer/Bundle.properties
python.project2/src/org/netbeans/modules/python/project2/ui/customizer/CompositePanelProviderImpl.java
python.project2/src/org/netbeans/modules/python/project2/ui/customizer/CustomizerPythonPath.form
python.project2/src/org/netbeans/modules/python/project2/ui/customizer/CustomizerPythonPath.java
python.project2/src/org/netbeans/modules/python/project2/ui/customizer/CustomizerRun.form
python.project2/src/org/netbeans/modules/python/project2/ui/customizer/CustomizerRun.java
python.project2/src/org/netbeans/modules/python/project2/ui/customizer/PythonCustomizerProvider.java
python.source/nbproject/project.xml
python.source/src/org/netbeans/modules/python/source/PythonProjectSourceLevelQuery.java
     1.1 --- a/python.core/release/platform_info.py	Wed Feb 04 01:21:44 2015 -0800
     1.2 +++ b/python.core/release/platform_info.py	Tue Feb 24 01:58:36 2015 -0800
     1.3 @@ -4,20 +4,25 @@
     1.4  import os
     1.5  
     1.6  command = sys.executable
     1.7 -version = sys.version.split()[0]
     1.8 +major = sys.version_info[0]
     1.9 +minor = sys.version_info[1]
    1.10 +micro = sys.version_info[2]
    1.11 +sourceLevel = str(major) + '.' + str(minor)
    1.12 +version = sourceLevel + '.' + str(micro)
    1.13  isJava = sys.platform.count("java")
    1.14  if isJava :
    1.15      print("platform.name="+ "Jython " + version)
    1.16  else:
    1.17      print("platform.name="+ "Python " + version)
    1.18 -if command != None :    
    1.19 +print("platform.sourcelevel=" + sourceLevel)
    1.20 +if command != None :
    1.21      print("python.command="+ command.replace("\\", "\\\\"))
    1.22  path = ""
    1.23  for pathItem in sys.path:
    1.24      path += pathItem + os.pathsep
    1.25  print("python.path="+path.replace("\\", "\\\\"))
    1.26  
    1.27 -if isJava  :  
    1.28 +if isJava  :
    1.29      from java.lang import System
    1.30      classpath = System.getProperty('java.class.path')
    1.31      print(classpath)
     2.1 --- a/python.core/src/org/netbeans/modules/python/api/PythonExecution.java	Wed Feb 04 01:21:44 2015 -0800
     2.2 +++ b/python.core/src/org/netbeans/modules/python/api/PythonExecution.java	Tue Feb 24 01:58:36 2015 -0800
     2.3 @@ -28,7 +28,7 @@
     2.4   * @author Allan Davis
     2.5   * @author Jean-Yves
     2.6   */
     2.7 -public class PythonExecution {
     2.8 +public final class PythonExecution {
     2.9      // execution commands
    2.10      private String command;
    2.11      private String workingDirectory;
     3.1 --- a/python.core/src/org/netbeans/modules/python/api/PythonPlatform.java	Wed Feb 04 01:21:44 2015 -0800
     3.2 +++ b/python.core/src/org/netbeans/modules/python/api/PythonPlatform.java	Tue Feb 24 01:58:36 2015 -0800
     3.3 @@ -32,6 +32,7 @@
     3.4      private String interpreterConsoleComand;
     3.5      private String interpreterArgs;
     3.6      private String homeUrl;
     3.7 +    private String sourceLevel;
     3.8      private boolean dirty;
     3.9      // When adding properties, be sure to update the persistence code in PythonPlatformManager
    3.10  
    3.11 @@ -72,6 +73,15 @@
    3.12          this.interpreterConsoleComand = interpreterConsoleComand;
    3.13      }
    3.14  
    3.15 +    public String getSourceLevel() {
    3.16 +        return sourceLevel;
    3.17 +    }
    3.18 +
    3.19 +    public void setSourceLevel(String sourceLevel) {
    3.20 +        checkDirty(this.sourceLevel, sourceLevel);
    3.21 +        this.sourceLevel = sourceLevel;
    3.22 +    }
    3.23 +
    3.24      public List<String> getJavaPath() {
    3.25          return javaPath;
    3.26      }
     4.1 --- a/python.core/src/org/netbeans/modules/python/api/PythonPlatformManager.java	Wed Feb 04 01:21:44 2015 -0800
     4.2 +++ b/python.core/src/org/netbeans/modules/python/api/PythonPlatformManager.java	Tue Feb 24 01:58:36 2015 -0800
     4.3 @@ -47,6 +47,7 @@
     4.4      private static final String PLATFORM_INTEPRETER = ".interpreter"; // NOI18N
     4.5      private static final String JAVA_LIB_DIR = "javalib"; // NOI18N
     4.6      private static final String PLATFORM_NAME = "name"; // NOI18N
     4.7 +    private static final String SOURCE_LEVEL = "sourcelevel"; // NOI18N
     4.8      private static final String INTERPRETER_ARGS = "args"; // NOI18N
     4.9      private static final String CONSOLE_PATH = "console"; // NOI18N
    4.10      private static final String PYTHON_LIB_DIR = "pythonlib"; // NOI18N
    4.11 @@ -101,7 +102,7 @@
    4.12          } catch (IOException ex) {
    4.13              Exceptions.printStackTrace(ex);
    4.14          }
    4.15 -        
    4.16 +
    4.17          return platform;
    4.18      }
    4.19  
    4.20 @@ -139,6 +140,7 @@
    4.21                  String libDir = p.get(PLATFORM_PREFIX + idDot + PYTHON_LIB_DIR);
    4.22                  String javaPath = p.get(PLATFORM_PREFIX + idDot + JAVA_LIB_DIR);
    4.23                  String name = p.get(PLATFORM_PREFIX + idDot + PLATFORM_NAME);
    4.24 +                String sourceLevel = p.get(PLATFORM_PREFIX + idDot + SOURCE_LEVEL);
    4.25                  String interpreterArgs = p.get(PLATFORM_PREFIX + idDot + INTERPRETER_ARGS);
    4.26                  String interpreterConsolePath = p.get(PLATFORM_PREFIX + idDot + CONSOLE_PATH);
    4.27  
    4.28 @@ -155,6 +157,9 @@
    4.29                  if (name != null && name.length() > 0) {
    4.30                      platform.setName(name);
    4.31                  }
    4.32 +                if (sourceLevel != null && !sourceLevel.isEmpty()) {
    4.33 +                    platform.setSourceLevel(sourceLevel);
    4.34 +                }
    4.35                  if (libDir != null && libDir.length() > 0) {
    4.36                      platform.setPythonPath(libDir.split(File.pathSeparator));
    4.37                  }
    4.38 @@ -180,7 +185,7 @@
    4.39                  defaultPlatform = deflt.getId();
    4.40                  platforms.put(defaultPlatform, deflt);
    4.41              }
    4.42 -            
    4.43 +
    4.44              if (Util.isFirstPlatformTouch()) {
    4.45                  RequestProcessor.getDefault().post(new Runnable() {
    4.46                      public void run() {
    4.47 @@ -188,7 +193,7 @@
    4.48                              autoDetect();
    4.49                          }
    4.50                      }
    4.51 -                });            
    4.52 +                });
    4.53              }
    4.54          }
    4.55      }
    4.56 @@ -216,7 +221,7 @@
    4.57                      EditableProperties props = PropertyUtils.getGlobalProperties();
    4.58                      clearProperties(platform, props);
    4.59                      putPlatformProperties(platform, props);
    4.60 -                    PropertyUtils.putGlobalProperties(props);                    
    4.61 +                    PropertyUtils.putGlobalProperties(props);
    4.62                      return null;
    4.63                  }
    4.64              });
    4.65 @@ -236,6 +241,7 @@
    4.66          props.remove(PLATFORM_PREFIX + idDot + PYTHON_LIB_DIR);
    4.67          props.remove(PLATFORM_PREFIX + idDot + JAVA_LIB_DIR);
    4.68          props.remove(PLATFORM_PREFIX + idDot + PLATFORM_NAME);
    4.69 +        props.remove(PLATFORM_PREFIX + idDot + SOURCE_LEVEL);
    4.70          props.remove(PLATFORM_PREFIX + idDot + INTERPRETER_ARGS);
    4.71          props.remove(PLATFORM_PREFIX + idDot + CONSOLE_PATH);
    4.72      }
    4.73 @@ -264,6 +270,9 @@
    4.74          if (platform.getName() != null) {
    4.75              props.setProperty(PLATFORM_PREFIX + idDot + PLATFORM_NAME, platform.getName());
    4.76          }
    4.77 +        if (platform.getSourceLevel() != null) {
    4.78 +            props.setProperty(PLATFORM_PREFIX + idDot + SOURCE_LEVEL, platform.getSourceLevel());
    4.79 +        }
    4.80          if (platform.getInterpreterArgs() != null) {
    4.81              props.setProperty(PLATFORM_PREFIX + idDot + INTERPRETER_ARGS, platform.getInterpreterArgs());
    4.82          }
    4.83 @@ -340,16 +349,21 @@
    4.84  
    4.85          firePlatformsChanged();
    4.86      }
    4.87 -    
    4.88 +
    4.89      public PythonPlatform findPlatformProperties(String cmd, String id) throws PythonException{
    4.90          PythonPlatform platform = null;
    4.91          try{
    4.92              PythonExecution pye = new PythonExecution();
    4.93 -            pye.setCommand(cmd);
    4.94 +            int split = cmd.indexOf(" ");
    4.95 +            pye.setCommand(split > 0 ? cmd.substring(0, split) : cmd);
    4.96              pye.setDisplayName("Python Properties");
    4.97              File info = InstalledFileLocator.getDefault().locate(
    4.98                   "platform_info.py", "org.netbeans.modules.python.core", false);
    4.99              pye.setScript(info.getAbsolutePath());
   4.100 +            if(split > 0) { 
   4.101 +                String cmdArgs = cmd.substring(split).trim();
   4.102 +                pye.setCommandArgs(cmdArgs);
   4.103 +            }
   4.104              pye.setShowControls(false);
   4.105              pye.setShowInput(false);
   4.106              pye.setShowWindow(false);
   4.107 @@ -379,6 +393,7 @@
   4.108                  platform.setInterpreterConsoleComand(command);
   4.109                  // @@@Jean-Yves end of fix
   4.110                  platform.setName(name);
   4.111 +                platform.setSourceLevel(prop.getProperty("platform.sourcelevel"));
   4.112                  String pathString = prop.getProperty("python.path");
   4.113                  if(pathString != null)
   4.114                      platform.setPythonPath(pathString.split(File.pathSeparator));
   4.115 @@ -490,7 +505,7 @@
   4.116          }
   4.117  
   4.118      }
   4.119 -    
   4.120 +
   4.121      private ArrayList<String> discoverJythonClasspath(String command){
   4.122          ArrayList<String> temp = new ArrayList<String>();
   4.123          //@@@jean-yves in some case bin is not there(jython 2.2.1 installer)
     5.1 --- a/python.debugger/src/org/netbeans/modules/python/debugger/Debuggee.java	Wed Feb 04 01:21:44 2015 -0800
     5.2 +++ b/python.debugger/src/org/netbeans/modules/python/debugger/Debuggee.java	Tue Feb 24 01:58:36 2015 -0800
     5.3 @@ -46,7 +46,6 @@
     5.4  import org.netbeans.modules.python.api.PythonPlatform;
     5.5  import org.netbeans.modules.python.debugger.actions.JpyDbgView;
     5.6  import org.netbeans.modules.python.debugger.spi.PythonSession;
     5.7 -import org.netbeans.modules.python.debugger.spi.PythonSourceDebuggee;
     5.8  import org.openide.filesystems.FileObject;
     5.9  import org.openide.filesystems.FileUtil;
    5.10  import org.openide.loaders.DataObject;
     6.1 --- a/python.debugger/src/org/netbeans/modules/python/debugger/DebuggerPythonLogger.java	Wed Feb 04 01:21:44 2015 -0800
     6.2 +++ b/python.debugger/src/org/netbeans/modules/python/debugger/DebuggerPythonLogger.java	Tue Feb 24 01:58:36 2015 -0800
     6.3 @@ -62,7 +62,6 @@
     6.4  import java.util.Set;
     6.5  import org.netbeans.modules.python.debugger.spi.PythonEvent;
     6.6  import org.netbeans.modules.python.debugger.spi.PythonSession;
     6.7 -import org.netbeans.modules.python.debugger.spi.PythonSourceDebuggee;
     6.8  
     6.9  /**
    6.10   * Debugger's tasking entry point class
     7.1 --- a/python.debugger/src/org/netbeans/modules/python/debugger/PythonDebugger.java	Wed Feb 04 01:21:44 2015 -0800
     7.2 +++ b/python.debugger/src/org/netbeans/modules/python/debugger/PythonDebugger.java	Tue Feb 24 01:58:36 2015 -0800
     7.3 @@ -56,7 +56,6 @@
     7.4  import org.netbeans.modules.python.debugger.backend.PluginEvent;
     7.5  import org.netbeans.modules.python.debugger.spi.PythonEvent;
     7.6  import org.netbeans.modules.python.debugger.spi.PythonSession;
     7.7 -import org.netbeans.modules.python.debugger.spi.PythonSourceDebuggee;
     7.8  import org.netbeans.modules.python.debugger.spi.SessionsModel;
     7.9  import org.netbeans.modules.python.debugger.gui.PythonDebugContainer;
    7.10  import org.netbeans.modules.python.debugger.gui.PythonVariableTreeDataNode;
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/python.debugger/src/org/netbeans/modules/python/debugger/PythonSourceDebuggee.java	Tue Feb 24 01:58:36 2015 -0800
     8.3 @@ -0,0 +1,81 @@
     8.4 +/*
     8.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     8.6 + *
     8.7 + * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
     8.8 + *
     8.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    8.10 + * Other names may be trademarks of their respective owners.
    8.11 + *
    8.12 + * The contents of this file are subject to the terms of either the GNU
    8.13 + * General Public License Version 2 only ("GPL") or the Common
    8.14 + * Development and Distribution License("CDDL") (collectively, the
    8.15 + * "License"). You may not use this file except in compliance with the
    8.16 + * License. You can obtain a copy of the License at
    8.17 + * http://www.netbeans.org/cddl-gplv2.html
    8.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    8.19 + * specific language governing permissions and limitations under the
    8.20 + * License.  When distributing the software, include this License Header
    8.21 + * Notice in each file and include the License file at
    8.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    8.23 + * particular file as subject to the "Classpath" exception as provided
    8.24 + * by Oracle in the GPL Version 2 section of the License file that
    8.25 + * accompanied this code. If applicable, add the following below the
    8.26 + * License Header, with the fields enclosed by brackets [] replaced by
    8.27 + * your own identifying information:
    8.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    8.29 + *
    8.30 + * If you wish your version of this file to be governed by only the CDDL
    8.31 + * or only the GPL Version 2, indicate your decision by adding
    8.32 + * "[Contributor] elects to include this software in this distribution
    8.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    8.34 + * single choice of license, a recipient has the option to distribute
    8.35 + * your version of this file under either the CDDL, the GPL Version 2 or
    8.36 + * to extend the choice of license to its licensees as provided above.
    8.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    8.38 + * Version 2 license, then the option applies only if the new code is
    8.39 + * made subject to such option by the copyright holder.
    8.40 + *
    8.41 + * Contributor(s):
    8.42 + *
    8.43 + * Portions Copyrighted 2008 Sun Microsystems, Inc.
    8.44 + */
    8.45 +
    8.46 +package org.netbeans.modules.python.debugger;
    8.47 +
    8.48 +import java.io.File ;
    8.49 +import org.netbeans.modules.python.debugger.actions.JpyDbgView;
    8.50 +import org.netbeans.modules.python.debugger.spi.PythonSession;
    8.51 +import org.openide.filesystems.FileObject;
    8.52 +
    8.53 +/**
    8.54 + * Node specialization for Python Sources debugging context
    8.55 + * @author jean-yves Mengant
    8.56 + */
    8.57 +public interface PythonSourceDebuggee
    8.58 +{
    8.59 +  /** Get the disk file for the python script.
    8.60 +  * @return the disk file, or null if none (but must be a file object)
    8.61 +  */
    8.62 +  File getFile ();
    8.63 +  /** Get the file object for the build script.
    8.64 +   * @return the file object, or null if none (but must be a disk file)
    8.65 +   */
    8.66 +  FileObject getFileObject ();
    8.67 +  
    8.68 +  /**
    8.69 +   bind a debug view object
    8.70 +  */
    8.71 +  public void setDebugView( JpyDbgView view ) ; 
    8.72 +  public JpyDbgView getDebugView() ; 
    8.73 +  
    8.74 +  /**
    8.75 +    execute current python shell action
    8.76 +  */ 
    8.77 +  //public void executePython() 
    8.78 +  //throws PythonDebugException ;
    8.79 +  
    8.80 +  /** set current python session */
    8.81 +  public void setSession( PythonSession pythonSession ) ; 
    8.82 +  public PythonSession getSession() ; 
    8.83 +  
    8.84 +}
     9.1 --- a/python.debugger/src/org/netbeans/modules/python/debugger/actions/JpyDbgView.java	Wed Feb 04 01:21:44 2015 -0800
     9.2 +++ b/python.debugger/src/org/netbeans/modules/python/debugger/actions/JpyDbgView.java	Tue Feb 24 01:58:36 2015 -0800
     9.3 @@ -47,7 +47,6 @@
     9.4  import org.netbeans.modules.python.debugger.PythonDebugger;
     9.5  import org.netbeans.modules.python.debugger.Utils;
     9.6  import org.netbeans.modules.python.debugger.backend.PluginEvent;
     9.7 -import org.netbeans.modules.python.debugger.backend.PluginEventListener;
     9.8  import org.netbeans.modules.python.debugger.breakpoints.PythonBreakpoint;
     9.9  import org.netbeans.modules.python.debugger.config.NetBeansFrontend;
    9.10  import org.netbeans.modules.python.debugger.spi.PythonEvent;
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/python.debugger/src/org/netbeans/modules/python/debugger/actions/PluginEventListener.java	Tue Feb 24 01:58:36 2015 -0800
    10.3 @@ -0,0 +1,64 @@
    10.4 +/*
    10.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    10.6 + *
    10.7 + * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
    10.8 + *
    10.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   10.10 + * Other names may be trademarks of their respective owners.
   10.11 + *
   10.12 + * The contents of this file are subject to the terms of either the GNU
   10.13 + * General Public License Version 2 only ("GPL") or the Common
   10.14 + * Development and Distribution License("CDDL") (collectively, the
   10.15 + * "License"). You may not use this file except in compliance with the
   10.16 + * License. You can obtain a copy of the License at
   10.17 + * http://www.netbeans.org/cddl-gplv2.html
   10.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   10.19 + * specific language governing permissions and limitations under the
   10.20 + * License.  When distributing the software, include this License Header
   10.21 + * Notice in each file and include the License file at
   10.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   10.23 + * particular file as subject to the "Classpath" exception as provided
   10.24 + * by Oracle in the GPL Version 2 section of the License file that
   10.25 + * accompanied this code. If applicable, add the following below the
   10.26 + * License Header, with the fields enclosed by brackets [] replaced by
   10.27 + * your own identifying information:
   10.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   10.29 + *
   10.30 + * If you wish your version of this file to be governed by only the CDDL
   10.31 + * or only the GPL Version 2, indicate your decision by adding
   10.32 + * "[Contributor] elects to include this software in this distribution
   10.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   10.34 + * single choice of license, a recipient has the option to distribute
   10.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   10.36 + * to extend the choice of license to its licensees as provided above.
   10.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   10.38 + * Version 2 license, then the option applies only if the new code is
   10.39 + * made subject to such option by the copyright holder.
   10.40 + *
   10.41 + * Contributor(s):
   10.42 + *
   10.43 + * Portions Copyrighted 2008 Sun Microsystems, Inc.
   10.44 + */
   10.45 +package org.netbeans.modules.python.debugger.actions;
   10.46 +
   10.47 +import org.netbeans.modules.python.debugger.backend.PluginEvent;
   10.48 +import org.netbeans.modules.python.debugger.backend.PythonDebugException;
   10.49 +
   10.50 +/**
   10.51 + *
   10.52 + * used by pluggin implementors interfaces to get populated
   10.53 + * With source debugging level instance 
   10.54 + *   
   10.55 + * @author jean-yves Mengant
   10.56 + */
   10.57 +public interface PluginEventListener {
   10.58 +
   10.59 +  /**
   10.60 +   * populate debugging event to the UI plugin interface for
   10.61 +   * sources synchronization process
   10.62 +   * @param e
   10.63 +   * @throws PythonDebugException
   10.64 +   */
   10.65 +  public void newDebuggingEvent(PluginEvent e)
   10.66 +          throws PythonDebugException;
   10.67 +}
    11.1 --- a/python.debugger/src/org/netbeans/modules/python/debugger/backend/PluginEventListener.java	Wed Feb 04 01:21:44 2015 -0800
    11.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.3 @@ -1,61 +0,0 @@
    11.4 -/*
    11.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    11.6 - *
    11.7 - * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
    11.8 - *
    11.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   11.10 - * Other names may be trademarks of their respective owners.
   11.11 - *
   11.12 - * The contents of this file are subject to the terms of either the GNU
   11.13 - * General Public License Version 2 only ("GPL") or the Common
   11.14 - * Development and Distribution License("CDDL") (collectively, the
   11.15 - * "License"). You may not use this file except in compliance with the
   11.16 - * License. You can obtain a copy of the License at
   11.17 - * http://www.netbeans.org/cddl-gplv2.html
   11.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   11.19 - * specific language governing permissions and limitations under the
   11.20 - * License.  When distributing the software, include this License Header
   11.21 - * Notice in each file and include the License file at
   11.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   11.23 - * particular file as subject to the "Classpath" exception as provided
   11.24 - * by Oracle in the GPL Version 2 section of the License file that
   11.25 - * accompanied this code. If applicable, add the following below the
   11.26 - * License Header, with the fields enclosed by brackets [] replaced by
   11.27 - * your own identifying information:
   11.28 - * "Portions Copyrighted [year] [name of copyright owner]"
   11.29 - *
   11.30 - * If you wish your version of this file to be governed by only the CDDL
   11.31 - * or only the GPL Version 2, indicate your decision by adding
   11.32 - * "[Contributor] elects to include this software in this distribution
   11.33 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
   11.34 - * single choice of license, a recipient has the option to distribute
   11.35 - * your version of this file under either the CDDL, the GPL Version 2 or
   11.36 - * to extend the choice of license to its licensees as provided above.
   11.37 - * However, if you add GPL Version 2 code and therefore, elected the GPL
   11.38 - * Version 2 license, then the option applies only if the new code is
   11.39 - * made subject to such option by the copyright holder.
   11.40 - *
   11.41 - * Contributor(s):
   11.42 - *
   11.43 - * Portions Copyrighted 2008 Sun Microsystems, Inc.
   11.44 - */
   11.45 -package org.netbeans.modules.python.debugger.backend;
   11.46 -
   11.47 -/**
   11.48 - *
   11.49 - * used by pluggin implementors interfaces to get populated
   11.50 - * With source debugging level instance 
   11.51 - *   
   11.52 - * @author jean-yves Mengant
   11.53 - */
   11.54 -public interface PluginEventListener {
   11.55 -
   11.56 -  /**
   11.57 -   * populate debugging event to the UI plugin interface for
   11.58 -   * sources synchronization process
   11.59 -   * @param e
   11.60 -   * @throws PythonDebugException
   11.61 -   */
   11.62 -  public void newDebuggingEvent(PluginEvent e)
   11.63 -          throws PythonDebugException;
   11.64 -}
    12.1 --- a/python.debugger/src/org/netbeans/modules/python/debugger/gui/PythonDebugContainer.java	Wed Feb 04 01:21:44 2015 -0800
    12.2 +++ b/python.debugger/src/org/netbeans/modules/python/debugger/gui/PythonDebugContainer.java	Tue Feb 24 01:58:36 2015 -0800
    12.3 @@ -71,7 +71,7 @@
    12.4  import org.netbeans.modules.python.debugger.Utils;
    12.5  import org.netbeans.modules.python.debugger.backend.DebuggerContextChangeListener;
    12.6  import org.netbeans.modules.python.debugger.backend.PluginEvent;
    12.7 -import org.netbeans.modules.python.debugger.backend.PluginEventListener;
    12.8 +import org.netbeans.modules.python.debugger.actions.PluginEventListener;
    12.9  import org.netbeans.modules.python.debugger.backend.PythonDebugClient;
   12.10  import org.netbeans.modules.python.debugger.backend.PythonDebugEvent;
   12.11  import org.netbeans.modules.python.debugger.backend.PythonDebugEventListener;
    13.1 --- a/python.debugger/src/org/netbeans/modules/python/debugger/resources/python/nbpythondebug/jpydaemon.py	Wed Feb 04 01:21:44 2015 -0800
    13.2 +++ b/python.debugger/src/org/netbeans/modules/python/debugger/resources/python/nbpythondebug/jpydaemon.py	Tue Feb 24 01:58:36 2015 -0800
    13.3 @@ -1483,9 +1483,8 @@
    13.4              self.cmd = FREEZE
    13.5          elif ( string.upper(verb) == "BP-"):
    13.6              self.cmd = CLEAR_BP
    13.7 -            file , optarg = _utils.nextArg(arg)
    13.8 -            line , optarg = _utils.nextArg(optarg)
    13.9 -            self.clear_break( file, int(line) )
   13.10 +            arg , optarg = _utils.nextArg(arg) # split BP arguments
   13.11 +            self.clear_break( arg , int(optarg) )
   13.12              self.cmd = FREEZE
   13.13          elif ( string.upper(verb) == "KILL"):
   13.14              self.cmd = QUIT
    14.1 --- a/python.debugger/src/org/netbeans/modules/python/debugger/spi/PythonDebuggerTargetExecutor.java	Wed Feb 04 01:21:44 2015 -0800
    14.2 +++ b/python.debugger/src/org/netbeans/modules/python/debugger/spi/PythonDebuggerTargetExecutor.java	Tue Feb 24 01:58:36 2015 -0800
    14.3 @@ -41,6 +41,7 @@
    14.4   */
    14.5  package org.netbeans.modules.python.debugger.spi;
    14.6  
    14.7 +import org.netbeans.modules.python.debugger.PythonSourceDebuggee;
    14.8  import org.openide.execution.ExecutorTask;
    14.9  import java.io.IOException;
   14.10  import java.io.OutputStream;
    15.1 --- a/python.debugger/src/org/netbeans/modules/python/debugger/spi/PythonSourceDebuggee.java	Wed Feb 04 01:21:44 2015 -0800
    15.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.3 @@ -1,80 +0,0 @@
    15.4 -/*
    15.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    15.6 - *
    15.7 - * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
    15.8 - *
    15.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   15.10 - * Other names may be trademarks of their respective owners.
   15.11 - *
   15.12 - * The contents of this file are subject to the terms of either the GNU
   15.13 - * General Public License Version 2 only ("GPL") or the Common
   15.14 - * Development and Distribution License("CDDL") (collectively, the
   15.15 - * "License"). You may not use this file except in compliance with the
   15.16 - * License. You can obtain a copy of the License at
   15.17 - * http://www.netbeans.org/cddl-gplv2.html
   15.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   15.19 - * specific language governing permissions and limitations under the
   15.20 - * License.  When distributing the software, include this License Header
   15.21 - * Notice in each file and include the License file at
   15.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   15.23 - * particular file as subject to the "Classpath" exception as provided
   15.24 - * by Oracle in the GPL Version 2 section of the License file that
   15.25 - * accompanied this code. If applicable, add the following below the
   15.26 - * License Header, with the fields enclosed by brackets [] replaced by
   15.27 - * your own identifying information:
   15.28 - * "Portions Copyrighted [year] [name of copyright owner]"
   15.29 - *
   15.30 - * If you wish your version of this file to be governed by only the CDDL
   15.31 - * or only the GPL Version 2, indicate your decision by adding
   15.32 - * "[Contributor] elects to include this software in this distribution
   15.33 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
   15.34 - * single choice of license, a recipient has the option to distribute
   15.35 - * your version of this file under either the CDDL, the GPL Version 2 or
   15.36 - * to extend the choice of license to its licensees as provided above.
   15.37 - * However, if you add GPL Version 2 code and therefore, elected the GPL
   15.38 - * Version 2 license, then the option applies only if the new code is
   15.39 - * made subject to such option by the copyright holder.
   15.40 - *
   15.41 - * Contributor(s):
   15.42 - *
   15.43 - * Portions Copyrighted 2008 Sun Microsystems, Inc.
   15.44 - */
   15.45 -
   15.46 -package org.netbeans.modules.python.debugger.spi;
   15.47 -
   15.48 -import java.io.File ;
   15.49 -import org.netbeans.modules.python.debugger.actions.JpyDbgView;
   15.50 -import org.openide.filesystems.FileObject;
   15.51 -
   15.52 -/**
   15.53 - * Node specialization for Python Sources debugging context
   15.54 - * @author jean-yves Mengant
   15.55 - */
   15.56 -public interface PythonSourceDebuggee
   15.57 -{
   15.58 -  /** Get the disk file for the python script.
   15.59 -  * @return the disk file, or null if none (but must be a file object)
   15.60 -  */
   15.61 -  File getFile ();
   15.62 -  /** Get the file object for the build script.
   15.63 -   * @return the file object, or null if none (but must be a disk file)
   15.64 -   */
   15.65 -  FileObject getFileObject ();
   15.66 -  
   15.67 -  /**
   15.68 -   bind a debug view object
   15.69 -  */
   15.70 -  public void setDebugView( JpyDbgView view ) ; 
   15.71 -  public JpyDbgView getDebugView() ; 
   15.72 -  
   15.73 -  /**
   15.74 -    execute current python shell action
   15.75 -  */ 
   15.76 -  //public void executePython() 
   15.77 -  //throws PythonDebugException ;
   15.78 -  
   15.79 -  /** set current python session */
   15.80 -  public void setSession( PythonSession pythonSession ) ; 
   15.81 -  public PythonSession getSession() ; 
   15.82 -  
   15.83 -}
    16.1 --- a/python.debugger/src/org/netbeans/modules/python/debugger/spi/TargetExecutor.java	Wed Feb 04 01:21:44 2015 -0800
    16.2 +++ b/python.debugger/src/org/netbeans/modules/python/debugger/spi/TargetExecutor.java	Tue Feb 24 01:58:36 2015 -0800
    16.3 @@ -41,6 +41,7 @@
    16.4   */
    16.5  package org.netbeans.modules.python.debugger.spi;
    16.6  
    16.7 +import org.netbeans.modules.python.debugger.PythonSourceDebuggee;
    16.8  import org.openide.windows.InputOutput;
    16.9  import org.openide.execution.ExecutorTask;
   16.10  import org.openide.windows.IOProvider;
    17.1 --- a/python.editor/manifest.mf	Wed Feb 04 01:21:44 2015 -0800
    17.2 +++ b/python.editor/manifest.mf	Tue Feb 24 01:58:36 2015 -0800
    17.3 @@ -3,4 +3,5 @@
    17.4  OpenIDE-Module-Layer: org/netbeans/modules/python/editor/layer.xml
    17.5  OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/python/editor/Bundle.properties
    17.6  AutoUpdate-Show-In-Client: false
    17.7 +OpenIDE-Module-Specification-Version: 1.0
    17.8  
    18.1 --- a/python.editor/nbproject/project.properties	Wed Feb 04 01:21:44 2015 -0800
    18.2 +++ b/python.editor/nbproject/project.properties	Tue Feb 24 01:58:36 2015 -0800
    18.3 @@ -15,7 +15,7 @@
    18.4  auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaces-per-tab=4
    18.5  auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.tab-size=4
    18.6  auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-limit-width=100
    18.7 -javac.source=1.6
    18.8 +javac.source=1.7
    18.9  javac.compilerargs=-Xlint -Xlint:-serial
   18.10  nbm.needs.restart=true
   18.11  spec.version.base=1.8.1
    19.1 --- a/python.editor/nbproject/project.xml	Wed Feb 04 01:21:44 2015 -0800
    19.2 +++ b/python.editor/nbproject/project.xml	Tue Feb 24 01:58:36 2015 -0800
    19.3 @@ -15,6 +15,30 @@
    19.4                      </run-dependency>
    19.5                  </dependency>
    19.6                  <dependency>
    19.7 +                    <code-name-base>org.eclipse.mylyn.wikitext.confluence.core</code-name-base>
    19.8 +                    <build-prerequisite/>
    19.9 +                    <compile-dependency/>
   19.10 +                    <run-dependency>
   19.11 +                        <specification-version>1.9.0</specification-version>
   19.12 +                    </run-dependency>
   19.13 +                </dependency>
   19.14 +                <dependency>
   19.15 +                    <code-name-base>org.eclipse.mylyn.wikitext.core</code-name-base>
   19.16 +                    <build-prerequisite/>
   19.17 +                    <compile-dependency/>
   19.18 +                    <run-dependency>
   19.19 +                        <specification-version>1.9.0</specification-version>
   19.20 +                    </run-dependency>
   19.21 +                </dependency>
   19.22 +                <dependency>
   19.23 +                    <code-name-base>org.eclipse.mylyn.wikitext.textile.core</code-name-base>
   19.24 +                    <build-prerequisite/>
   19.25 +                    <compile-dependency/>
   19.26 +                    <run-dependency>
   19.27 +                        <specification-version>1.9.0</specification-version>
   19.28 +                    </run-dependency>
   19.29 +                </dependency>
   19.30 +                <dependency>
   19.31                      <code-name-base>org.netbeans.api.annotations.common</code-name-base>
   19.32                      <build-prerequisite/>
   19.33                      <compile-dependency/>
   19.34 @@ -33,6 +57,15 @@
   19.35                      </run-dependency>
   19.36                  </dependency>
   19.37                  <dependency>
   19.38 +                    <code-name-base>org.netbeans.api.progress</code-name-base>
   19.39 +                    <build-prerequisite/>
   19.40 +                    <compile-dependency/>
   19.41 +                    <run-dependency>
   19.42 +                        <release-version>1</release-version>
   19.43 +                        <specification-version>1.38.1</specification-version>
   19.44 +                    </run-dependency>
   19.45 +                </dependency>
   19.46 +                <dependency>
   19.47                      <code-name-base>org.netbeans.core.multiview</code-name-base>
   19.48                      <build-prerequisite/>
   19.49                      <compile-dependency/>
   19.50 @@ -42,6 +75,15 @@
   19.51                      </run-dependency>
   19.52                  </dependency>
   19.53                  <dependency>
   19.54 +                    <code-name-base>org.netbeans.libs.lucene</code-name-base>
   19.55 +                    <build-prerequisite/>
   19.56 +                    <compile-dependency/>
   19.57 +                    <run-dependency>
   19.58 +                        <release-version>3</release-version>
   19.59 +                        <specification-version>3.16.1</specification-version>
   19.60 +                    </run-dependency>
   19.61 +                </dependency>
   19.62 +                <dependency>
   19.63                      <code-name-base>org.netbeans.modules.csl.api</code-name-base>
   19.64                      <build-prerequisite/>
   19.65                      <compile-dependency/>
   19.66 @@ -51,6 +93,15 @@
   19.67                      </run-dependency>
   19.68                  </dependency>
   19.69                  <dependency>
   19.70 +                    <code-name-base>org.netbeans.modules.editor</code-name-base>
   19.71 +                    <build-prerequisite/>
   19.72 +                    <compile-dependency/>
   19.73 +                    <run-dependency>
   19.74 +                        <release-version>3</release-version>
   19.75 +                        <specification-version>1.79.1.5.22.43</specification-version>
   19.76 +                    </run-dependency>
   19.77 +                </dependency>
   19.78 +                <dependency>
   19.79                      <code-name-base>org.netbeans.modules.editor.bracesmatching</code-name-base>
   19.80                      <build-prerequisite/>
   19.81                      <compile-dependency/>
   19.82 @@ -127,7 +178,15 @@
   19.83                      <compile-dependency/>
   19.84                      <run-dependency>
   19.85                          <release-version>1</release-version>
   19.86 -                        <specification-version>1.21</specification-version>
   19.87 +                        <specification-version>1.53.1</specification-version>
   19.88 +                    </run-dependency>
   19.89 +                </dependency>
   19.90 +                <dependency>
   19.91 +                    <code-name-base>org.netbeans.modules.editor.tools.storage</code-name-base>
   19.92 +                    <build-prerequisite/>
   19.93 +                    <compile-dependency/>
   19.94 +                    <run-dependency>
   19.95 +                        <specification-version>1.4.1</specification-version>
   19.96                      </run-dependency>
   19.97                  </dependency>
   19.98                  <dependency>
   19.99 @@ -351,10 +410,11 @@
  19.100              </module-dependencies>
  19.101              <friend-packages>
  19.102                  <friend>org.netbeans.modules.python.project</friend>
  19.103 +                <friend>org.netbeans.modules.python.project2</friend>
  19.104                  <friend>org.netbeans.modules.python.testrunner</friend>
  19.105                  <package>org.netbeans.modules.python.editor</package>
  19.106 +                <package>org.netbeans.modules.python.editor.codecoverage</package>
  19.107                  <package>org.netbeans.modules.python.editor.lexer</package>
  19.108 -                <package>org.netbeans.modules.python.editor.codecoverage</package>
  19.109              </friend-packages>
  19.110          </data>
  19.111      </configuration>
    20.1 --- a/python.editor/src/org/netbeans/modules/python/editor/PythonParser.java	Wed Feb 04 01:21:44 2015 -0800
    20.2 +++ b/python.editor/src/org/netbeans/modules/python/editor/PythonParser.java	Tue Feb 24 01:58:36 2015 -0800
    20.3 @@ -344,7 +344,7 @@
    20.4          } catch (Throwable t) {
    20.5              runtimeException = t;
    20.6              StackTraceElement[] stackTrace = t.getStackTrace();
    20.7 -            if (stackTrace != null && stackTrace.length > 0 && stackTrace[0].getClassName().equals("org.python.antlr.runtime.tree.RewriteRuleElementStream")) {
    20.8 +            if (stackTrace != null && stackTrace.length > 0 && stackTrace[0].getClassName().startsWith("org.python.antlr")) {//.runtime.tree.RewriteRuleElementStream")) {
    20.9                  // This is issue 150921
   20.10                  // Don't bug user about it -- we already know
   20.11                  Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Encountered issue #150921", t);
    21.1 --- a/python.editor/src/org/netbeans/modules/python/editor/file/PyDataObject.java	Wed Feb 04 01:21:44 2015 -0800
    21.2 +++ b/python.editor/src/org/netbeans/modules/python/editor/file/PyDataObject.java	Tue Feb 24 01:58:36 2015 -0800
    21.3 @@ -51,6 +51,16 @@
    21.4      ),
    21.5      @ActionReference(
    21.6              path = "Loaders/text/x-python/Actions",
    21.7 +            id = @ActionID(category = "Project", id = "org.netbeans.modules.project.ui.RunSingle"),
    21.8 +            position = 550
    21.9 +    ),
   21.10 +    @ActionReference(
   21.11 +            path = "Loaders/text/x.python/Actions",
   21.12 +            id = @ActionID(category = "Project", id = "org.netbeans.modules.project.ui.TestSingle"),
   21.13 +            position = 570
   21.14 +    ),
   21.15 +    @ActionReference(
   21.16 +            path = "Loaders/text/x-python/Actions",
   21.17              id = @ActionID(category = "Edit", id = "org.openide.actions.DeleteAction"),
   21.18              position = 600
   21.19      ),
   21.20 @@ -68,6 +78,17 @@
   21.21      ),
   21.22      @ActionReference(
   21.23              path = "Loaders/text/x-python/Actions",
   21.24 +            id = @ActionID(category = "Refactoring", id = "org.netbeans.modules.refactoring.api.ui.WhereUsedAction"),
   21.25 +            position = 1050
   21.26 +    ),
   21.27 +    @ActionReference(
   21.28 +            path = "Loaders/text/x-python/Actions",
   21.29 +            id = @ActionID(category = "Refactoring", id = "RefactoringAll"),
   21.30 +            position = 1090,
   21.31 +            separatorAfter = 1095
   21.32 +    ),
   21.33 +    @ActionReference(
   21.34 +            path = "Loaders/text/x-python/Actions",
   21.35              id = @ActionID(category = "System", id = "org.openide.actions.FileSystemAction"),
   21.36              position = 1100,
   21.37              separatorAfter = 1200
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/python.editor/src/org/netbeans/modules/python/editor/file/PythonShebangSourceLevelQuery.java	Tue Feb 24 01:58:36 2015 -0800
    22.3 @@ -0,0 +1,125 @@
    22.4 +/*
    22.5 + * To change this license header, choose License Headers in Project Properties.
    22.6 + * To change this template file, choose Tools | Templates
    22.7 + * and open the template in the editor.
    22.8 + */
    22.9 +package org.netbeans.modules.python.editor.file;
   22.10 +
   22.11 +import org.netbeans.modules.python.source.queries.SourceLevelQueryImplementation;
   22.12 +import java.io.FileNotFoundException;
   22.13 +import java.io.IOException;
   22.14 +import java.util.Scanner;
   22.15 +import javax.swing.event.ChangeListener;
   22.16 +import org.openide.filesystems.FileAttributeEvent;
   22.17 +import org.openide.filesystems.FileChangeListener;
   22.18 +import org.openide.filesystems.FileEvent;
   22.19 +import org.openide.filesystems.FileObject;
   22.20 +import org.openide.filesystems.FileRenameEvent;
   22.21 +import org.openide.util.ChangeSupport;
   22.22 +import org.openide.util.Exceptions;
   22.23 +import org.openide.util.lookup.ServiceProvider;
   22.24 +
   22.25 +/**
   22.26 + *
   22.27 + * @author Ralph Benjamin Ruijs <ralphbenjamin@netbeans.org>
   22.28 + */
   22.29 +@ServiceProvider(service = SourceLevelQueryImplementation.class)
   22.30 +public class PythonShebangSourceLevelQuery implements SourceLevelQueryImplementation {
   22.31 +
   22.32 +    @Override
   22.33 +    public Result getSourceLevel(FileObject pythonFile) {
   22.34 +        if(!pythonFile.isFolder()) {
   22.35 +            return new ResultImpl(pythonFile);
   22.36 +        }
   22.37 +        return null;
   22.38 +    }
   22.39 +
   22.40 +    private final static class ResultImpl implements Result, FileChangeListener {
   22.41 +        private final ChangeSupport cs = new ChangeSupport(this);
   22.42 +
   22.43 +        private final FileObject pythonFile;
   22.44 +        private String sourceLevel = "";
   22.45 +
   22.46 +        @SuppressWarnings("LeakingThisInConstructor")
   22.47 +        private ResultImpl(FileObject pythonFile) {
   22.48 +            this.pythonFile = pythonFile;
   22.49 +            this.pythonFile.addFileChangeListener(this);
   22.50 +            this.fileChanged(null);
   22.51 +        }
   22.52 +
   22.53 +        @Override
   22.54 +        public void addChangeListener(ChangeListener listener) {
   22.55 +            this.cs.addChangeListener(listener);
   22.56 +        }
   22.57 +
   22.58 +        @Override
   22.59 +        public void fileAttributeChanged(FileAttributeEvent fe) {
   22.60 +        }
   22.61 +
   22.62 +        @Override
   22.63 +        public void fileChanged(FileEvent fe) {
   22.64 +            if (pythonFile.isValid()) {
   22.65 +                String shebang = null;
   22.66 +                try (Scanner sc = new Scanner(pythonFile.getInputStream())) {
   22.67 +                    if (sc.hasNextLine()) {
   22.68 +                        shebang = sc.nextLine();
   22.69 +                    }
   22.70 +                } catch (FileNotFoundException ex) {
   22.71 +                    Exceptions.printStackTrace(ex);
   22.72 +                }
   22.73 +                processShebang(shebang);
   22.74 +            }
   22.75 +        }
   22.76 +
   22.77 +        @Override
   22.78 +        public void fileDataCreated(FileEvent fe) {
   22.79 +        }
   22.80 +
   22.81 +        @Override
   22.82 +        public void fileDeleted(FileEvent fe) {
   22.83 +        }
   22.84 +
   22.85 +        @Override
   22.86 +        public void fileFolderCreated(FileEvent fe) {
   22.87 +        }
   22.88 +
   22.89 +        @Override
   22.90 +        public void fileRenamed(FileRenameEvent fe) {
   22.91 +        }
   22.92 +
   22.93 +        @Override
   22.94 +        public String getSourceLevel() {
   22.95 +            return this.sourceLevel;
   22.96 +        }
   22.97 +
   22.98 +        @Override
   22.99 +        public void removeChangeListener(ChangeListener listener) {
  22.100 +            this.cs.removeChangeListener(listener);
  22.101 +        }
  22.102 +
  22.103 +        private void setSourceLevel(String sourceLevel) {
  22.104 +            this.sourceLevel = sourceLevel;
  22.105 +            cs.fireChange();
  22.106 +        }
  22.107 +
  22.108 +        private void processShebang(String shebang) {
  22.109 +            if (shebang != null && shebang.startsWith("#!")) {
  22.110 +                try {
  22.111 +                    Process proc = Runtime.getRuntime().exec(shebang.substring(2) + " --version");
  22.112 +                    String version = null;
  22.113 +                    try(Scanner sc = new Scanner(proc.getInputStream())) {
  22.114 +                        if(sc.hasNextLine()) {
  22.115 +                            version = sc.nextLine();
  22.116 +                        }
  22.117 +                    }
  22.118 +                    proc.destroy();
  22.119 +                    if(version != null && !version.isEmpty() && !version.equals(this.sourceLevel)) {
  22.120 +                        setSourceLevel(version);
  22.121 +                    }
  22.122 +                } catch(IOException ex) {
  22.123 +                    Exceptions.printStackTrace(ex);
  22.124 +                }
  22.125 +            }
  22.126 +        }
  22.127 +    }
  22.128 +}
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/python.editor/src/org/netbeans/modules/python/editor/file/RunSingleCommand.java	Tue Feb 24 01:58:36 2015 -0800
    23.3 @@ -0,0 +1,117 @@
    23.4 +package org.netbeans.modules.python.editor.file;
    23.5 +
    23.6 +import java.io.FileNotFoundException;
    23.7 +import java.util.Scanner;
    23.8 +import java.util.logging.Level;
    23.9 +import java.util.logging.Logger;
   23.10 +import javax.swing.JOptionPane;
   23.11 +import org.netbeans.modules.python.api.PythonException;
   23.12 +import org.netbeans.modules.python.api.PythonExecution;
   23.13 +import org.netbeans.modules.python.api.PythonMIMEResolver;
   23.14 +import org.netbeans.modules.python.api.PythonOptions;
   23.15 +import org.netbeans.modules.python.api.PythonPlatform;
   23.16 +import org.netbeans.modules.python.api.PythonPlatformManager;
   23.17 +import org.netbeans.spi.project.ActionProvider;
   23.18 +import org.openide.filesystems.FileObject;
   23.19 +import org.openide.filesystems.FileUtil;
   23.20 +import org.openide.loaders.DataObject;
   23.21 +import org.openide.nodes.Node;
   23.22 +import org.openide.util.Exceptions;
   23.23 +import org.openide.util.Lookup;
   23.24 +import org.openide.util.lookup.ServiceProvider;
   23.25 +import org.openide.windows.TopComponent;
   23.26 +
   23.27 +/**
   23.28 + *
   23.29 + * @author Ralph Benjamin Ruijs
   23.30 + */
   23.31 +@ServiceProvider(service = ActionProvider.class)
   23.32 +public class RunSingleCommand implements ActionProvider {
   23.33 +    private static final Logger LOG = Logger.getLogger(RunSingleCommand.class.getName());
   23.34 +
   23.35 +    PythonPlatformManager manager = PythonPlatformManager.getInstance();
   23.36 +
   23.37 +    public RunSingleCommand() {
   23.38 +    }
   23.39 +
   23.40 +    private Node[] getSelectedNodes() {
   23.41 +        return TopComponent.getRegistry().getCurrentNodes();
   23.42 +    }
   23.43 +    
   23.44 +    @Override
   23.45 +    public void invokeAction(String command, Lookup context) throws IllegalArgumentException {
   23.46 +        Node[] activatedNodes = getSelectedNodes();
   23.47 +        DataObject gdo = activatedNodes[0].getLookup().lookup(DataObject.class);
   23.48 +        FileObject file = gdo.getPrimaryFile();
   23.49 +        if (file.getMIMEType().equals(PythonMIMEResolver.PYTHON_MIME_TYPE)) {
   23.50 +            String path = FileUtil.toFile(file.getParent()).getAbsolutePath();
   23.51 +            String script = FileUtil.toFile(file).getAbsolutePath();
   23.52 +            String shebang = null;
   23.53 +            try(Scanner sc = new Scanner(file.getInputStream())) {
   23.54 +                if(sc.hasNextLine()) {
   23.55 +                    shebang = sc.nextLine();
   23.56 +                }
   23.57 +            } catch (FileNotFoundException ex) {
   23.58 +                Exceptions.printStackTrace(ex);
   23.59 +            }
   23.60 +
   23.61 +            PythonExecution pyexec = new PythonExecution();
   23.62 +            pyexec.setDisplayName(gdo.getName());
   23.63 +            pyexec.setWorkingDirectory(path);
   23.64 +            if (PythonOptions.getInstance().getPromptForArgs()) {
   23.65 +                String args = JOptionPane.showInputDialog("Enter the args for this script.", "");
   23.66 +                pyexec.setScriptArgs(args);
   23.67 +
   23.68 +            }
   23.69 +            PythonPlatform platform = null;
   23.70 +            if (shebang != null && shebang.startsWith("#!")) {
   23.71 +                try {
   23.72 +                    platform = manager.findPlatformProperties(shebang.substring(2), null);
   23.73 +                } catch (PythonException ex) {
   23.74 +                    LOG.log(Level.WARNING, "Unable to get platform from shebang: " + shebang, ex);
   23.75 +                }
   23.76 +            }
   23.77 +            if(platform == null) {
   23.78 +                platform = manager.getPlatform(manager.getDefaultPlatform());
   23.79 +                if (platform == null) {
   23.80 +                    return; // invalid platform user has been warn in check so safe to return
   23.81 +                }
   23.82 +            }
   23.83 +            pyexec.setCommand(platform.getInterpreterCommand());
   23.84 +            pyexec.setScript(script);
   23.85 +            pyexec.setCommandArgs(platform.getInterpreterArgs());
   23.86 +            pyexec.setPath(PythonPlatform.buildPath(platform.getPythonPath()));
   23.87 +//            pyexec.setJavaPath(PythonPlatform.buildPath(super.buildJavaPath(platform, pyProject)));
   23.88 +            pyexec.setShowControls(true);
   23.89 +            pyexec.setShowInput(true);
   23.90 +            pyexec.setShowWindow(true);
   23.91 +            pyexec.addStandardRecognizers();
   23.92 +
   23.93 +//            PythonCoverageProvider coverageProvider = PythonCoverageProvider.get(pyProject);
   23.94 +//            if (coverageProvider != null && coverageProvider.isEnabled()) {
   23.95 +//                pyexec = coverageProvider.wrapWithCoverage(pyexec);
   23.96 +//            }
   23.97 +
   23.98 +            pyexec.run();
   23.99 +        }
  23.100 +    }
  23.101 +
  23.102 +    @Override
  23.103 +    public boolean isActionEnabled(String command, Lookup context) throws IllegalArgumentException {
  23.104 +        boolean results = false; //super.enable(activatedNodes);
  23.105 +        Node[] activatedNodes = getSelectedNodes();
  23.106 +        if (activatedNodes != null && activatedNodes.length > 0) {
  23.107 +            DataObject gdo = activatedNodes[0].getLookup().lookup(DataObject.class);
  23.108 +            if (gdo != null && gdo.getPrimaryFile() != null) {
  23.109 +                results = gdo.getPrimaryFile().getMIMEType().equals(
  23.110 +                        PythonMIMEResolver.PYTHON_MIME_TYPE);
  23.111 +            }
  23.112 +        }
  23.113 +        return results;
  23.114 +    }
  23.115 +
  23.116 +    @Override
  23.117 +    public String[] getSupportedActions() {
  23.118 +        return new String[] {ActionProvider.COMMAND_RUN_SINGLE};
  23.119 +    }
  23.120 +}
    24.1 --- a/python.editor/src/org/netbeans/modules/python/editor/layer.xml	Wed Feb 04 01:21:44 2015 -0800
    24.2 +++ b/python.editor/src/org/netbeans/modules/python/editor/layer.xml	Tue Feb 24 01:58:36 2015 -0800
    24.3 @@ -195,74 +195,6 @@
    24.4              <folder name="x-python">
    24.5                  <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/netbeans/modules/python/editor/resources/pyNode25.png"/>
    24.6                  <attr name="iconBase" stringvalue="org/netbeans/modules/python/editor/resources/pyNode25.png"/>
    24.7 -                <folder name="Actions">
    24.8 -                   
    24.9 -                    <file name="RefactoringAll.shadow">
   24.10 -                        <attr name="originalFile" stringvalue="Actions/Refactoring/RefactoringAll.instance"/>
   24.11 -                        <attr name="position" intvalue="1800"/>
   24.12 -                    </file>
   24.13 -                    <file name="RefactoringWhereUsed.shadow">
   24.14 -                        <attr name="originalFile" stringvalue="Actions/Refactoring/org-netbeans-modules-refactoring-api-ui-WhereUsedAction.instance"/>
   24.15 -                        <attr name="position" intvalue="1700"/>
   24.16 -                    </file>
   24.17 -                    <file name="org-openide-actions-CopyAction.shadow">
   24.18 -                        <attr name="originalFile" stringvalue="Actions/Edit/org-openide-actions-CopyAction.instance"/>
   24.19 -                        <attr name="position" intvalue="500"/>
   24.20 -                    </file>
   24.21 -                    <file name="org-openide-actions-CutAction.shadow">
   24.22 -                        <attr name="originalFile" stringvalue="Actions/Edit/org-openide-actions-CutAction.instance"/>
   24.23 -                        <attr name="position" intvalue="400"/>
   24.24 -                    </file>
   24.25 -                    <file name="org-openide-actions-DeleteAction.shadow">
   24.26 -                        <attr name="originalFile" stringvalue="Actions/Edit/org-openide-actions-DeleteAction.instance"/>
   24.27 -                        <attr name="position" intvalue="700"/>
   24.28 -                    </file>
   24.29 -                    <file name="org-openide-actions-FileSystemAction.shadow">
   24.30 -                        <attr name="originalFile" stringvalue="Actions/System/org-openide-actions-FileSystemAction.instance"/>
   24.31 -                        <attr name="position" intvalue="200"/>
   24.32 -                    </file>
   24.33 -                    <file name="org-openide-actions-OpenAction.shadow">
   24.34 -                        <attr name="originalFile" stringvalue="Actions/System/org-openide-actions-OpenAction.instance"/>
   24.35 -                        <attr name="position" intvalue="100"/>
   24.36 -                    </file>
   24.37 -                    <file name="org-openide-actions-PropertiesAction.shadow">
   24.38 -                        <attr name="originalFile" stringvalue="Actions/System/org-openide-actions-PropertiesAction.instance"/>
   24.39 -                        <attr name="position" intvalue="1300"/>
   24.40 -                    </file>
   24.41 -                    <file name="org-openide-actions-RenameAction.shadow">
   24.42 -                        <attr name="originalFile" stringvalue="Actions/System/org-openide-actions-RenameAction.instance"/>
   24.43 -                        <attr name="position" intvalue="800"/>
   24.44 -                    </file>
   24.45 -                    <file name="org-openide-actions-SaveAsTemplateAction.shadow">
   24.46 -                        <attr name="originalFile" stringvalue="Actions/System/org-openide-actions-SaveAsTemplateAction.instance"/>
   24.47 -                        <attr name="position" intvalue="1000"/>
   24.48 -                    </file>
   24.49 -                    <file name="org-openide-actions-ToolsAction.shadow">
   24.50 -                        <attr name="originalFile" stringvalue="Actions/System/org-openide-actions-ToolsAction.instance"/>
   24.51 -                        <attr name="position" intvalue="1200"/>
   24.52 -                    </file>
   24.53 -                    <file name="sep-1.instance">
   24.54 -                        <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
   24.55 -                        <attr name="position" intvalue="300"/>
   24.56 -                    </file>
   24.57 -                    <file name="sep-2.instance">
   24.58 -                        <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
   24.59 -                        <attr name="position" intvalue="600"/>
   24.60 -                    </file>
   24.61 -                    <file name="sep-3.instance">
   24.62 -                        <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
   24.63 -                        <attr name="position" intvalue="900"/>
   24.64 -                    </file>
   24.65 -                    <file name="sep-4.instance">
   24.66 -                        <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
   24.67 -                        <attr name="position" intvalue="1100"/>
   24.68 -                    </file>
   24.69 -
   24.70 -                    <file name="RefactoringSeparator-1.instance">
   24.71 -                        <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
   24.72 -                        <attr name="position" intvalue="1900"/>
   24.73 -                    </file>
   24.74 -                </folder>
   24.75              </folder>
   24.76              <folder name="x-python-compiled">
   24.77                  <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/netbeans/modules/python/editor/resources/pyc_16.png"/>
    25.1 --- a/python.editor/src/org/netbeans/modules/python/editor/templates/executable_module.py.ftl	Wed Feb 04 01:21:44 2015 -0800
    25.2 +++ b/python.editor/src/org/netbeans/modules/python/editor/templates/executable_module.py.ftl	Tue Feb 24 01:58:36 2015 -0800
    25.3 @@ -1,4 +1,8 @@
    25.4 -#! /usr/bin/python
    25.5 +<#if python3style?? && python3style>
    25.6 +#!/usr/bin/env python3
    25.7 +<#else>
    25.8 +#!/usr/bin/env python2
    25.9 +</#if>
   25.10  
   25.11  <#-- This is a FreeMarker template -->
   25.12  <#-- You can change the contents of the license inserted into
   25.13 @@ -7,8 +11,9 @@
   25.14  <#assign licensePrefix = "# ">
   25.15  <#include "../Licenses/license-${project.license}.txt">
   25.16  
   25.17 -__author__="${user}"
   25.18 -__date__ ="$${date} ${time}$"
   25.19 -
   25.20  if __name__ == "__main__":
   25.21 -    print "Hello World";
   25.22 +<#if python3style?? && python3style>
   25.23 +    print("Hello World")
   25.24 +<#else>
   25.25 +    print "Hello World"
   25.26 +</#if>
   25.27 \ No newline at end of file
    26.1 --- a/python.editor/src/org/netbeans/modules/python/editor/templates/init.py.ftl	Wed Feb 04 01:21:44 2015 -0800
    26.2 +++ b/python.editor/src/org/netbeans/modules/python/editor/templates/init.py.ftl	Tue Feb 24 01:58:36 2015 -0800
    26.3 @@ -1,2 +0,0 @@
    26.4 -__author__="${user}"
    26.5 -__date__ ="$${date} ${time}$"
    26.6 \ No newline at end of file
    27.1 --- a/python.editor/src/org/netbeans/modules/python/editor/templates/module.py.ftl	Wed Feb 04 01:21:44 2015 -0800
    27.2 +++ b/python.editor/src/org/netbeans/modules/python/editor/templates/module.py.ftl	Tue Feb 24 01:58:36 2015 -0800
    27.3 @@ -5,9 +5,6 @@
    27.4  <#assign licensePrefix = "# ">
    27.5  <#include "../Licenses/license-${project.license}.txt">
    27.6  
    27.7 -__author__="${user}"
    27.8 -__date__ ="$${date} ${time}$"
    27.9 -
   27.10  if __name__ == "__main__":
   27.11  <#if python3style?? && python3style>
   27.12      print("Hello World")
    28.1 --- a/python.editor/src/org/netbeans/modules/python/editor/templates/setup.py.ftl	Wed Feb 04 01:21:44 2015 -0800
    28.2 +++ b/python.editor/src/org/netbeans/modules/python/editor/templates/setup.py.ftl	Tue Feb 24 01:58:36 2015 -0800
    28.3 @@ -1,6 +1,3 @@
    28.4 -__author__="${user}"
    28.5 -__date__ ="$${date} ${time}$"
    28.6 -
    28.7  from setuptools import setup,find_packages
    28.8  
    28.9  setup (
   28.10 @@ -16,7 +13,7 @@
   28.11    author = '${user}',
   28.12    author_email = '',
   28.13  
   28.14 -  summary = 'Just another Python package for the cheese shop',
   28.15 +  #summary = 'Just another Python package for the cheese shop',
   28.16    url = '',
   28.17    license = '',
   28.18    long_description= 'Long description of the package',
    29.1 --- a/python.kit/nbproject/project.xml	Wed Feb 04 01:21:44 2015 -0800
    29.2 +++ b/python.kit/nbproject/project.xml	Tue Feb 24 01:58:36 2015 -0800
    29.3 @@ -46,7 +46,7 @@
    29.4                  <dependency>
    29.5                      <code-name-base>org.netbeans.modules.python.editor</code-name-base>
    29.6                      <run-dependency>
    29.7 -                        <specification-version>1.6</specification-version>
    29.8 +                        <specification-version>1.0</specification-version>
    29.9                      </run-dependency>
   29.10                  </dependency>
   29.11                  <dependency>
   29.12 @@ -79,6 +79,12 @@
   29.13                          <specification-version>0.1</specification-version>
   29.14                      </run-dependency>
   29.15                  </dependency>
   29.16 +                <dependency>
   29.17 +                    <code-name-base>org.netbeans.modules.python.project2</code-name-base>
   29.18 +                    <run-dependency>
   29.19 +                        <specification-version>1.0</specification-version>
   29.20 +                    </run-dependency>
   29.21 +                </dependency>
   29.22              </module-dependencies>
   29.23              <public-packages/>
   29.24          </data>
    30.1 --- a/python.project/src/org/netbeans/modules/python/project/queries/PythonProjectSourceLevelQuery.java	Wed Feb 04 01:21:44 2015 -0800
    30.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.3 @@ -1,29 +0,0 @@
    30.4 -/*
    30.5 - * To change this license header, choose License Headers in Project Properties.
    30.6 - * To change this template file, choose Tools | Templates
    30.7 - * and open the template in the editor.
    30.8 - */
    30.9 -package org.netbeans.modules.python.project.queries;
   30.10 -
   30.11 -import org.netbeans.modules.python.source.queries.SourceLevelQueryImplementation;
   30.12 -import org.netbeans.api.project.FileOwnerQuery;
   30.13 -import org.netbeans.api.project.Project;
   30.14 -import org.openide.filesystems.FileObject;
   30.15 -import org.openide.util.lookup.ServiceProvider;
   30.16 -
   30.17 -@ServiceProvider(service = SourceLevelQueryImplementation.class)
   30.18 -public class PythonProjectSourceLevelQuery implements SourceLevelQueryImplementation {
   30.19 -
   30.20 -    @Override
   30.21 -    public Result getSourceLevel(FileObject pythonFile) {
   30.22 -        final Project project = FileOwnerQuery.getOwner(pythonFile);
   30.23 -        if (project != null) {
   30.24 -            SourceLevelQueryImplementation impl = project.getLookup().lookup(SourceLevelQueryImplementation.class);
   30.25 -            if (impl != null) {
   30.26 -                return impl.getSourceLevel(pythonFile);
   30.27 -            }
   30.28 -        }
   30.29 -        return null;
   30.30 -    }
   30.31 -    
   30.32 -}
    31.1 --- a/python.project/src/org/netbeans/modules/python/project/queries/PythonShebangSourceLevelQuery.java	Wed Feb 04 01:21:44 2015 -0800
    31.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.3 @@ -1,125 +0,0 @@
    31.4 -/*
    31.5 - * To change this license header, choose License Headers in Project Properties.
    31.6 - * To change this template file, choose Tools | Templates
    31.7 - * and open the template in the editor.
    31.8 - */
    31.9 -package org.netbeans.modules.python.project.queries;
   31.10 -
   31.11 -import org.netbeans.modules.python.source.queries.SourceLevelQueryImplementation;
   31.12 -import java.io.FileNotFoundException;
   31.13 -import java.io.IOException;
   31.14 -import java.util.HashSet;
   31.15 -import java.util.Scanner;
   31.16 -import java.util.Set;
   31.17 -import javax.swing.event.ChangeEvent;
   31.18 -import javax.swing.event.ChangeListener;
   31.19 -import org.openide.filesystems.FileAttributeEvent;
   31.20 -import org.openide.filesystems.FileChangeListener;
   31.21 -import org.openide.filesystems.FileEvent;
   31.22 -import org.openide.filesystems.FileObject;
   31.23 -import org.openide.filesystems.FileRenameEvent;
   31.24 -import org.openide.util.ChangeSupport;
   31.25 -import org.openide.util.Exceptions;
   31.26 -import org.openide.util.lookup.ServiceProvider;
   31.27 -
   31.28 -/**
   31.29 - *
   31.30 - * @author Ralph Benjamin Ruijs <ralphbenjamin@netbeans.org>
   31.31 - */
   31.32 -@ServiceProvider(service = SourceLevelQueryImplementation.class)
   31.33 -public class PythonShebangSourceLevelQuery implements SourceLevelQueryImplementation {
   31.34 -
   31.35 -    @Override
   31.36 -    public Result getSourceLevel(FileObject pythonFile) {
   31.37 -        return new ResultImpl(pythonFile);
   31.38 -    }
   31.39 -
   31.40 -    private final static class ResultImpl implements Result, FileChangeListener {
   31.41 -        private final ChangeSupport cs = new ChangeSupport(this);
   31.42 -
   31.43 -        private final FileObject pythonFile;
   31.44 -        private String sourceLevel = "";
   31.45 -
   31.46 -        @SuppressWarnings("LeakingThisInConstructor")
   31.47 -        private ResultImpl(FileObject pythonFile) {
   31.48 -            this.pythonFile = pythonFile;
   31.49 -            this.pythonFile.addFileChangeListener(this);
   31.50 -            this.fileChanged(null);
   31.51 -        }
   31.52 -
   31.53 -        @Override
   31.54 -        public void addChangeListener(ChangeListener listener) {
   31.55 -            this.cs.addChangeListener(listener);
   31.56 -        }
   31.57 -
   31.58 -        @Override
   31.59 -        public void fileAttributeChanged(FileAttributeEvent fe) {
   31.60 -        }
   31.61 -
   31.62 -        @Override
   31.63 -        public void fileChanged(FileEvent fe) {
   31.64 -            if (pythonFile.isValid()) {
   31.65 -                String shebang = null;
   31.66 -                try (Scanner sc = new Scanner(pythonFile.getInputStream())) {
   31.67 -                    if (sc.hasNextLine()) {
   31.68 -                        shebang = sc.nextLine();
   31.69 -                    }
   31.70 -                } catch (FileNotFoundException ex) {
   31.71 -                    Exceptions.printStackTrace(ex);
   31.72 -                }
   31.73 -                processShebang(shebang);
   31.74 -            }
   31.75 -        }
   31.76 -
   31.77 -        @Override
   31.78 -        public void fileDataCreated(FileEvent fe) {
   31.79 -        }
   31.80 -
   31.81 -        @Override
   31.82 -        public void fileDeleted(FileEvent fe) {
   31.83 -        }
   31.84 -
   31.85 -        @Override
   31.86 -        public void fileFolderCreated(FileEvent fe) {
   31.87 -        }
   31.88 -
   31.89 -        @Override
   31.90 -        public void fileRenamed(FileRenameEvent fe) {
   31.91 -        }
   31.92 -
   31.93 -        @Override
   31.94 -        public String getSourceLevel() {
   31.95 -            return this.sourceLevel;
   31.96 -        }
   31.97 -
   31.98 -        @Override
   31.99 -        public void removeChangeListener(ChangeListener listener) {
  31.100 -            this.cs.removeChangeListener(listener);
  31.101 -        }
  31.102 -
  31.103 -        private void setSourceLevel(String sourceLevel) {
  31.104 -            this.sourceLevel = sourceLevel;
  31.105 -            cs.fireChange();
  31.106 -        }
  31.107 -
  31.108 -        private void processShebang(String shebang) {
  31.109 -            if (shebang != null && shebang.startsWith("#!")) {
  31.110 -                try {
  31.111 -                    Process proc = Runtime.getRuntime().exec(shebang.substring(2) + " --version");
  31.112 -                    String version = null;
  31.113 -                    try(Scanner sc = new Scanner(proc.getInputStream())) {
  31.114 -                        if(sc.hasNextLine()) {
  31.115 -                            version = sc.nextLine();
  31.116 -                        }
  31.117 -                    }
  31.118 -                    proc.destroy();
  31.119 -                    if(version != null && !version.isEmpty() && !version.equals(this.sourceLevel)) {
  31.120 -                        setSourceLevel(version);
  31.121 -                    }
  31.122 -                } catch(IOException ex) {
  31.123 -                    Exceptions.printStackTrace(ex);
  31.124 -                }
  31.125 -            }
  31.126 -        }
  31.127 -    }
  31.128 -}
    32.1 --- a/python.project/src/org/netbeans/modules/python/project/templates/EmptyPythonProjectDescription.html	Wed Feb 04 01:21:44 2015 -0800
    32.2 +++ b/python.project/src/org/netbeans/modules/python/project/templates/EmptyPythonProjectDescription.html	Tue Feb 24 01:58:36 2015 -0800
    32.3 @@ -7,6 +7,6 @@
    32.4          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    32.5      </head>
    32.6      <body>
    32.7 -        New Empty Python Project
    32.8 +        New Empty Python Project using Ant
    32.9      </body>
   32.10  </html>
    33.1 --- a/python.project/src/org/netbeans/modules/python/project/templates/ExistingPythonProjectDescription.html	Wed Feb 04 01:21:44 2015 -0800
    33.2 +++ b/python.project/src/org/netbeans/modules/python/project/templates/ExistingPythonProjectDescription.html	Tue Feb 24 01:58:36 2015 -0800
    33.3 @@ -9,6 +9,6 @@
    33.4          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    33.5      </head>
    33.6      <body>
    33.7 -        New Python Project with Existing Sources
    33.8 +        New Python Project with Existing Sources using Ant
    33.9      </body>
   33.10  </html>
    34.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.2 +++ b/python.project2/build.xml	Tue Feb 24 01:58:36 2015 -0800
    34.3 @@ -0,0 +1,8 @@
    34.4 +<?xml version="1.0" encoding="UTF-8"?>
    34.5 +<!-- You may freely edit this file. See harness/README in the NetBeans platform -->
    34.6 +<!-- for some information on what you could do (e.g. targets to override). -->
    34.7 +<!-- If you delete this file and reopen the project it will be recreated. -->
    34.8 +<project name="contrib/python.project" default="netbeans" basedir=".">
    34.9 +    <description>Builds, tests, and runs the project org.netbeans.modules.python.project.</description>
   34.10 +    <import file="../../nbbuild/templates/projectized.xml"/>
   34.11 +</project>
    35.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.2 +++ b/python.project2/manifest.mf	Tue Feb 24 01:58:36 2015 -0800
    35.3 @@ -0,0 +1,7 @@
    35.4 +Manifest-Version: 1.0
    35.5 +AutoUpdate-Show-In-Client: true
    35.6 +OpenIDE-Module: org.netbeans.modules.python.project2
    35.7 +OpenIDE-Module-Layer: org/netbeans/modules/python/project2/layer.xml
    35.8 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/python/project2/Bundle.properties
    35.9 +OpenIDE-Module-Specification-Version: 1.0
   35.10 +
    36.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.2 +++ b/python.project2/nbproject/project.properties	Tue Feb 24 01:58:36 2015 -0800
    36.3 @@ -0,0 +1,15 @@
    36.4 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=true
    36.5 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=4
    36.6 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=4
    36.7 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=4
    36.8 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
    36.9 +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project
   36.10 +auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineArrayInit=true
   36.11 +auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.blankLinesAfterClassHeader=0
   36.12 +auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.indentCasesFromSwitch=false
   36.13 +auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceAfterTypeCast=false
   36.14 +javac.source=1.7
   36.15 +javac.compilerargs=-Xlint -Xlint:-serial
   36.16 +
   36.17 +test-unit-sys-prop.xtest.python.home=${netbeans.dest.dir}/python/jython-2.5.1
   36.18 +test-unit-sys-prop.xtest.platform_info.py=${netbeans.dest.dir}/python/platform_info.py
    37.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    37.2 +++ b/python.project2/nbproject/project.xml	Tue Feb 24 01:58:36 2015 -0800
    37.3 @@ -0,0 +1,162 @@
    37.4 +<?xml version="1.0" encoding="UTF-8"?>
    37.5 +<project xmlns="http://www.netbeans.org/ns/project/1">
    37.6 +    <type>org.netbeans.modules.apisupport.project</type>
    37.7 +    <configuration>
    37.8 +        <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
    37.9 +            <code-name-base>org.netbeans.modules.python.project2</code-name-base>
   37.10 +            <module-dependencies>
   37.11 +                <dependency>
   37.12 +                    <code-name-base>org.netbeans.api.annotations.common</code-name-base>
   37.13 +                    <build-prerequisite/>
   37.14 +                    <compile-dependency/>
   37.15 +                    <run-dependency>
   37.16 +                        <release-version>1</release-version>
   37.17 +                        <specification-version>1.25</specification-version>
   37.18 +                    </run-dependency>
   37.19 +                </dependency>
   37.20 +                <dependency>
   37.21 +                    <code-name-base>org.netbeans.api.java.classpath</code-name-base>
   37.22 +                    <build-prerequisite/>
   37.23 +                    <compile-dependency/>
   37.24 +                    <run-dependency>
   37.25 +                        <release-version>1</release-version>
   37.26 +                        <specification-version>1.48</specification-version>
   37.27 +                    </run-dependency>
   37.28 +                </dependency>
   37.29 +                <dependency>
   37.30 +                    <code-name-base>org.netbeans.api.progress</code-name-base>
   37.31 +                    <build-prerequisite/>
   37.32 +                    <compile-dependency/>
   37.33 +                    <run-dependency>
   37.34 +                        <release-version>1</release-version>
   37.35 +                        <specification-version>1.40</specification-version>
   37.36 +                    </run-dependency>
   37.37 +                </dependency>
   37.38 +                <dependency>
   37.39 +                    <code-name-base>org.netbeans.modules.projectapi</code-name-base>
   37.40 +                    <build-prerequisite/>
   37.41 +                    <compile-dependency/>
   37.42 +                    <run-dependency>
   37.43 +                        <release-version>1</release-version>
   37.44 +                        <specification-version>1.62</specification-version>
   37.45 +                    </run-dependency>
   37.46 +                </dependency>
   37.47 +                <dependency>
   37.48 +                    <code-name-base>org.netbeans.modules.projectuiapi</code-name-base>
   37.49 +                    <build-prerequisite/>
   37.50 +                    <compile-dependency/>
   37.51 +                    <run-dependency>
   37.52 +                        <release-version>1</release-version>
   37.53 +                        <specification-version>1.79</specification-version>
   37.54 +                    </run-dependency>
   37.55 +                </dependency>
   37.56 +                <dependency>
   37.57 +                    <code-name-base>org.netbeans.modules.python.core</code-name-base>
   37.58 +                    <build-prerequisite/>
   37.59 +                    <compile-dependency/>
   37.60 +                    <run-dependency>
   37.61 +                        <specification-version>1.0</specification-version>
   37.62 +                    </run-dependency>
   37.63 +                </dependency>
   37.64 +                <dependency>
   37.65 +                    <code-name-base>org.netbeans.modules.python.editor</code-name-base>
   37.66 +                    <build-prerequisite/>
   37.67 +                    <compile-dependency/>
   37.68 +                    <run-dependency>
   37.69 +                        <specification-version>1.0</specification-version>
   37.70 +                    </run-dependency>
   37.71 +                </dependency>
   37.72 +                <dependency>
   37.73 +                    <code-name-base>org.netbeans.modules.python.source</code-name-base>
   37.74 +                    <build-prerequisite/>
   37.75 +                    <compile-dependency/>
   37.76 +                    <run-dependency>
   37.77 +                        <specification-version>1.0</specification-version>
   37.78 +                    </run-dependency>
   37.79 +                </dependency>
   37.80 +                <dependency>
   37.81 +                    <code-name-base>org.netbeans.modules.queries</code-name-base>
   37.82 +                    <build-prerequisite/>
   37.83 +                    <compile-dependency/>
   37.84 +                    <run-dependency>
   37.85 +                        <release-version>1</release-version>
   37.86 +                        <specification-version>1.39.1</specification-version>
   37.87 +                    </run-dependency>
   37.88 +                </dependency>
   37.89 +                <dependency>
   37.90 +                    <code-name-base>org.openide.actions</code-name-base>
   37.91 +                    <build-prerequisite/>
   37.92 +                    <compile-dependency/>
   37.93 +                    <run-dependency>
   37.94 +                        <specification-version>6.8.0.1</specification-version>
   37.95 +                    </run-dependency>
   37.96 +                </dependency>
   37.97 +                <dependency>
   37.98 +                    <code-name-base>org.openide.awt</code-name-base>
   37.99 +                    <build-prerequisite/>
  37.100 +                    <compile-dependency/>
  37.101 +                    <run-dependency>
  37.102 +                        <specification-version>7.62.1</specification-version>
  37.103 +                    </run-dependency>
  37.104 +                </dependency>
  37.105 +                <dependency>
  37.106 +                    <code-name-base>org.openide.dialogs</code-name-base>
  37.107 +                    <build-prerequisite/>
  37.108 +                    <compile-dependency/>
  37.109 +                    <run-dependency>
  37.110 +                        <specification-version>7.6</specification-version>
  37.111 +                    </run-dependency>
  37.112 +                </dependency>
  37.113 +                <dependency>
  37.114 +                    <code-name-base>org.openide.filesystems</code-name-base>
  37.115 +                    <build-prerequisite/>
  37.116 +                    <compile-dependency/>
  37.117 +                    <run-dependency>
  37.118 +                        <specification-version>9.0</specification-version>
  37.119 +                    </run-dependency>
  37.120 +                </dependency>
  37.121 +                <dependency>
  37.122 +                    <code-name-base>org.openide.loaders</code-name-base>
  37.123 +                    <build-prerequisite/>
  37.124 +                    <compile-dependency/>
  37.125 +                    <run-dependency>
  37.126 +                        <specification-version>7.61</specification-version>
  37.127 +                    </run-dependency>
  37.128 +                </dependency>
  37.129 +                <dependency>
  37.130 +                    <code-name-base>org.openide.nodes</code-name-base>
  37.131 +                    <build-prerequisite/>
  37.132 +                    <compile-dependency/>
  37.133 +                    <run-dependency>
  37.134 +                        <specification-version>7.39.1</specification-version>
  37.135 +                    </run-dependency>
  37.136 +                </dependency>
  37.137 +                <dependency>
  37.138 +                    <code-name-base>org.openide.util</code-name-base>
  37.139 +                    <build-prerequisite/>
  37.140 +                    <compile-dependency/>
  37.141 +                    <run-dependency>
  37.142 +                        <specification-version>9.3</specification-version>
  37.143 +                    </run-dependency>
  37.144 +                </dependency>
  37.145 +                <dependency>
  37.146 +                    <code-name-base>org.openide.util.lookup</code-name-base>
  37.147 +                    <build-prerequisite/>
  37.148 +                    <compile-dependency/>
  37.149 +                    <run-dependency>
  37.150 +                        <specification-version>8.25.1</specification-version>
  37.151 +                    </run-dependency>
  37.152 +                </dependency>
  37.153 +                <dependency>
  37.154 +                    <code-name-base>org.openide.windows</code-name-base>
  37.155 +                    <build-prerequisite/>
  37.156 +                    <compile-dependency/>
  37.157 +                    <run-dependency>
  37.158 +                        <specification-version>6.71.1</specification-version>
  37.159 +                    </run-dependency>
  37.160 +                </dependency>
  37.161 +            </module-dependencies>
  37.162 +            <public-packages/>
  37.163 +        </data>
  37.164 +    </configuration>
  37.165 +</project>
    38.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    38.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/Bundle.properties	Tue Feb 24 01:58:36 2015 -0800
    38.3 @@ -0,0 +1,2 @@
    38.4 +OpenIDE-Module-Name=Python Project 2
    38.5 +Templates/Project/Python/PythonProject2.xml=Python Project
    39.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    39.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/Python2LogicalView.java	Tue Feb 24 01:58:36 2015 -0800
    39.3 @@ -0,0 +1,180 @@
    39.4 +package org.netbeans.modules.python.project2;
    39.5 +
    39.6 +import java.awt.Image;
    39.7 +import java.io.CharConversionException;
    39.8 +import java.util.ArrayList;
    39.9 +import java.util.List;
   39.10 +import javax.swing.Action;
   39.11 +import org.netbeans.api.project.FileOwnerQuery;
   39.12 +import org.netbeans.api.project.Project;
   39.13 +import org.netbeans.api.project.ProjectUtils;
   39.14 +import org.netbeans.modules.python.project2.ui.ChangePackageViewTypeAction;
   39.15 +import org.netbeans.modules.python.project2.ui.PackageView;
   39.16 +import org.netbeans.spi.project.ActionProvider;
   39.17 +import org.netbeans.spi.project.ui.LogicalViewProvider;
   39.18 +import org.netbeans.spi.project.ui.support.CommonProjectActions;
   39.19 +import org.netbeans.spi.project.ui.support.DefaultProjectOperations;
   39.20 +import org.netbeans.spi.project.ui.support.NodeFactorySupport;
   39.21 +import org.netbeans.spi.project.ui.support.ProjectSensitiveActions;
   39.22 +import org.openide.actions.FindAction;
   39.23 +import org.openide.filesystems.FileObject;
   39.24 +import org.openide.filesystems.FileUtil;
   39.25 +import org.openide.nodes.AbstractNode;
   39.26 +import org.openide.nodes.Node;
   39.27 +import org.openide.util.HelpCtx;
   39.28 +import org.openide.util.ImageUtilities;
   39.29 +import org.openide.util.NbBundle;
   39.30 +import org.openide.util.Utilities;
   39.31 +import org.openide.util.actions.SystemAction;
   39.32 +import org.openide.util.lookup.Lookups;
   39.33 +import org.openide.xml.XMLUtil;
   39.34 +import static org.netbeans.modules.python.project2.Bundle.*;
   39.35 +
   39.36 +/**
   39.37 + *
   39.38 + * @author alley
   39.39 + * @author Tomas Zezula
   39.40 + */
   39.41 +@NbBundle.Messages({"# {0} - Path to of project",
   39.42 +    "PythonLogicalView.ProjectTooltipDescription=Python project in {0}",
   39.43 +    "LBL_RunAction_Name=Run",
   39.44 +    "LBL_DebugAction_Name=Debug",
   39.45 +    "LBL_TestAction_Name=Test",
   39.46 +    "LBL_BuildAction_Name=Build Egg",
   39.47 +    "LBL_CleanBuildAction_Name=Clean and Build Egg"})
   39.48 +class Python2LogicalView implements LogicalViewProvider {
   39.49 +
   39.50 +    private static final Image brokenProjectBadge = ImageUtilities.loadImage("org/netbeans/modules/python/project2/resources/brokenProjectBadge.gif", true);
   39.51 +    private final PythonProject2 project;
   39.52 +
   39.53 +    public Python2LogicalView(PythonProject2 project) {
   39.54 +        this.project = project;
   39.55 +    }
   39.56 +
   39.57 +    @Override
   39.58 +    public Node createLogicalView() {
   39.59 +        return new PythonProjectNode();
   39.60 +    }
   39.61 +
   39.62 +    @Override
   39.63 +    public Node findPath(Node root, Object target) {
   39.64 +        Project p = root.getLookup().lookup(Project.class);
   39.65 +        if (p == null) {
   39.66 +            return null;
   39.67 +        }
   39.68 +        if (target instanceof FileObject) {
   39.69 +            FileObject targetFO = (FileObject) target;
   39.70 +            Project owner = FileOwnerQuery.getOwner(targetFO);
   39.71 +            if (!p.equals(owner)) {
   39.72 +                return null; // Don't waste time if project does not own the fo
   39.73 +            }
   39.74 +
   39.75 +            for (Node n : root.getChildren().getNodes(true)) {
   39.76 +                Node result = PackageView.findPath(n, target);
   39.77 +                if (result != null) {
   39.78 +                    return result;
   39.79 +                }
   39.80 +            }
   39.81 +        }
   39.82 +
   39.83 +        return null;
   39.84 +    }
   39.85 +    
   39.86 +    private final class PythonProjectNode extends AbstractNode {
   39.87 +
   39.88 +        private boolean broken; //for future use, marks the project as broken
   39.89 +
   39.90 +        public PythonProjectNode() {
   39.91 +            super(NodeFactorySupport.createCompositeChildren(project, "Projects/org-netbeans-modules-python-project2/Nodes"),
   39.92 +                    Lookups.singleton(project));
   39.93 +            setIconBaseWithExtension("org/netbeans/modules/python/project2/resources/py_25_16.png");
   39.94 +            super.setName(ProjectUtils.getInformation(project).getDisplayName());
   39.95 +        }
   39.96 +
   39.97 +        public 
   39.98 +        @Override
   39.99 +        String getShortDescription() {
  39.100 +            //todo: Add python platform description
  39.101 +            String dirName = FileUtil.getFileDisplayName(project.getProjectDirectory());
  39.102 +            return PythonLogicalView_ProjectTooltipDescription(dirName);
  39.103 +        }
  39.104 +
  39.105 +        public 
  39.106 +        @Override
  39.107 +        String getHtmlDisplayName() {
  39.108 +            String dispName = super.getDisplayName();
  39.109 +            try {
  39.110 +                dispName = XMLUtil.toElementContent(dispName);
  39.111 +            } catch (CharConversionException ex) {
  39.112 +                return dispName;
  39.113 +            }
  39.114 +            // XXX text colors should be taken from UIManager, not hard-coded!
  39.115 +            return broken ? "<font color=\"#A40000\">" + dispName + "</font>" : null; //NOI18N
  39.116 +        }
  39.117 +
  39.118 +        @Override
  39.119 +        public Image getIcon(int type) {
  39.120 +            Image original = super.getIcon(type);
  39.121 +            return broken ? ImageUtilities.mergeImages(original, brokenProjectBadge, 8, 0) : original;
  39.122 +        }
  39.123 +
  39.124 +        @Override
  39.125 +        public Image getOpenedIcon(int type) {
  39.126 +            Image original = super.getOpenedIcon(type);
  39.127 +            return broken ? ImageUtilities.mergeImages(original, brokenProjectBadge, 8, 0) : original;
  39.128 +        }
  39.129 +
  39.130 +        @Override
  39.131 +        public Action[] getActions(boolean context) {
  39.132 +            return getAdditionalActions();
  39.133 +        }
  39.134 +
  39.135 +        @Override
  39.136 +        public boolean canRename() {
  39.137 +            return true;
  39.138 +        }
  39.139 +
  39.140 +        @Override
  39.141 +        public void setName(String s) {
  39.142 +            DefaultProjectOperations.performDefaultRenameOperation(project, s);
  39.143 +        }
  39.144 +
  39.145 +        @Override
  39.146 +        public HelpCtx getHelpCtx() {
  39.147 +            return new HelpCtx(PythonProjectNode.class);
  39.148 +        }
  39.149 +
  39.150 +        private Action[] getAdditionalActions() {
  39.151 +            final List<Action> actions = new ArrayList<>();
  39.152 +            actions.add(CommonProjectActions.newFileAction());
  39.153 +            actions.add(null);
  39.154 +            actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_RUN, LBL_RunAction_Name(), null)); // NOI18N
  39.155 +            actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_DEBUG, LBL_DebugAction_Name(), null)); // NOI18N
  39.156 +//            actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_TEST, LBL_TestAction_Name(), null)); // NOI18N
  39.157 +            actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_BUILD, LBL_BuildAction_Name(), null)); // NOI18N
  39.158 +            actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_REBUILD, LBL_CleanBuildAction_Name(), null)); // NOI18N
  39.159 +            actions.add(null);
  39.160 +//            actions.add(CoverageActionFactory.createCollectorAction(null, null));
  39.161 +//            actions.add(null);
  39.162 +            actions.add(CommonProjectActions.setAsMainProjectAction());
  39.163 +            actions.add(CommonProjectActions.openSubprojectsAction());
  39.164 +            actions.add(CommonProjectActions.closeProjectAction());
  39.165 +            actions.add(null);
  39.166 +            actions.add(CommonProjectActions.renameProjectAction());
  39.167 +            actions.add(CommonProjectActions.moveProjectAction());
  39.168 +            actions.add(CommonProjectActions.copyProjectAction());
  39.169 +            actions.add(CommonProjectActions.deleteProjectAction());
  39.170 +            actions.add(null);
  39.171 +            actions.add(new ChangePackageViewTypeAction());
  39.172 +            actions.add(null);
  39.173 +            actions.add(SystemAction.get(FindAction.class));
  39.174 +
  39.175 +            // honor 57874 contact
  39.176 +            actions.addAll(Utilities.actionsForPath("Projects/Actions")); //NOI18N
  39.177 +
  39.178 +            actions.add(null);
  39.179 +            actions.add(CommonProjectActions.customizeProjectAction());
  39.180 +            return actions.toArray(new Action[actions.size()]);
  39.181 +        }
  39.182 +    }
  39.183 +}
    40.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    40.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/PythonActionProvider.java	Tue Feb 24 01:58:36 2015 -0800
    40.3 @@ -0,0 +1,93 @@
    40.4 +package org.netbeans.modules.python.project2;
    40.5 +
    40.6 +import java.util.LinkedHashMap;
    40.7 +import java.util.Map;
    40.8 +import java.util.Set;
    40.9 +import org.netbeans.modules.python.project2.ui.actions.Command;
   40.10 +import org.netbeans.modules.python.project2.ui.actions.RunCommand;
   40.11 +import org.netbeans.modules.python.project2.ui.actions.RunSingleCommand;
   40.12 +import org.netbeans.spi.project.ActionProvider;
   40.13 +import org.openide.LifecycleManager;
   40.14 +import org.openide.util.Lookup;
   40.15 +import org.openide.util.RequestProcessor;
   40.16 +
   40.17 +/**
   40.18 + *
   40.19 + * @author alley
   40.20 + * @author Tomas Zezula
   40.21 + */
   40.22 +public class PythonActionProvider implements ActionProvider {
   40.23 +
   40.24 +    private final Map<String, Command> commands;
   40.25 +
   40.26 +    public PythonActionProvider(PythonProject2 project) {
   40.27 +        assert project != null;
   40.28 +        commands = new LinkedHashMap<String, Command>();
   40.29 +        Command[] commandArray = new Command[]{
   40.30 +            //            new DeleteCommand(project),
   40.31 +            //            new CopyCommand(project),
   40.32 +            //            new MoveCommand(project),
   40.33 +            //            new RenameCommand(project),
   40.34 +            //            new CleanCommand(project),
   40.35 +            new RunSingleCommand(project, false),
   40.36 +            new RunSingleCommand(project, true), // Run as Test
   40.37 +            new RunCommand(project, false),
   40.38 +            new RunCommand(project, true), // Run project as Test
   40.39 +        //            new DebugCommand(project) ,
   40.40 +        //            new DebugSingleCommand(project, false),
   40.41 +        //            new DebugSingleCommand(project, true), // Debug as Test
   40.42 +        //            new BuildCommand(project), //Build Egg
   40.43 +        //            new CleanBuildCommand(project) //Clean and Build Egg
   40.44 +        };
   40.45 +        for (Command command : commandArray) {
   40.46 +            commands.put(command.getCommandId(), command);
   40.47 +        }
   40.48 +    }
   40.49 +
   40.50 +//    public static TestRunner getTestRunner(TestRunner.TestType testType) {
   40.51 +//        Collection<? extends TestRunner> testRunners = Lookup.getDefault().lookupAll(TestRunner.class);
   40.52 +//        for (TestRunner each : testRunners) {
   40.53 +//            if (each.supports(testType)) {
   40.54 +//                return each;
   40.55 +//            }
   40.56 +//        }
   40.57 +//        return null;
   40.58 +//    }
   40.59 +    @Override
   40.60 +    public String[] getSupportedActions() {
   40.61 +        final Set<String> names = commands.keySet();
   40.62 +        return names.toArray(new String[names.size()]);
   40.63 +    }
   40.64 +
   40.65 +    @Override
   40.66 +    public void invokeAction(final String commandName, final Lookup context) throws IllegalArgumentException {
   40.67 +        final Command command = findCommand(commandName);
   40.68 +        assert command != null;
   40.69 +        if (command.saveRequired()) {
   40.70 +            LifecycleManager.getDefault().saveAll();
   40.71 +        }
   40.72 +        if (!command.asyncCallRequired()) {
   40.73 +            command.invokeAction(context);
   40.74 +        } else {
   40.75 +            RequestProcessor.getDefault().post(new Runnable() {
   40.76 +                @Override
   40.77 +                public void run() {
   40.78 +                    command.invokeAction(context);
   40.79 +                }
   40.80 +            });
   40.81 +        }
   40.82 +    }
   40.83 +
   40.84 +    @Override
   40.85 +    public boolean isActionEnabled(String commandName, Lookup context) throws IllegalArgumentException {
   40.86 +        final Command command = findCommand(commandName);
   40.87 +        assert command != null;
   40.88 +        return command.isActionEnabled(context);
   40.89 +    }
   40.90 +
   40.91 +    private Command findCommand(final String commandName) {
   40.92 +        assert commandName != null;
   40.93 +        return commands.get(commandName);
   40.94 +    }
   40.95 +
   40.96 +}
    41.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    41.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/PythonAuxilaryConfig.java	Tue Feb 24 01:58:36 2015 -0800
    41.3 @@ -0,0 +1,524 @@
    41.4 +/*
    41.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    41.6 + *
    41.7 + * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
    41.8 + *
    41.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   41.10 + * Other names may be trademarks of their respective owners.
   41.11 + *
   41.12 + * The contents of this file are subject to the terms of either the GNU
   41.13 + * General Public License Version 2 only ("GPL") or the Common
   41.14 + * Development and Distribution License("CDDL") (collectively, the
   41.15 + * "License"). You may not use this file except in compliance with the
   41.16 + * License. You can obtain a copy of the License at
   41.17 + * http://www.netbeans.org/cddl-gplv2.html
   41.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   41.19 + * specific language governing permissions and limitations under the
   41.20 + * License.  When distributing the software, include this License Header
   41.21 + * Notice in each file and include the License file at
   41.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   41.23 + * particular file as subject to the "Classpath" exception as provided
   41.24 + * by Oracle in the GPL Version 2 section of the License file that
   41.25 + * accompanied this code. If applicable, add the following below the
   41.26 + * License Header, with the fields enclosed by brackets [] replaced by
   41.27 + * your own identifying information:
   41.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   41.29 + *
   41.30 + * If you wish your version of this file to be governed by only the CDDL
   41.31 + * or only the GPL Version 2, indicate your decision by adding
   41.32 + * "[Contributor] elects to include this software in this distribution
   41.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   41.34 + * single choice of license, a recipient has the option to distribute
   41.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   41.36 + * to extend the choice of license to its licensees as provided above.
   41.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   41.38 + * Version 2 license, then the option applies only if the new code is
   41.39 + * made subject to such option by the copyright holder.
   41.40 + *
   41.41 + * Contributor(s):
   41.42 + *
   41.43 + * Portions Copyrighted 2008 Sun Microsystems, Inc.
   41.44 + */
   41.45 +
   41.46 +package org.netbeans.modules.python.project2;
   41.47 +
   41.48 +import java.beans.PropertyChangeListener;
   41.49 +import java.beans.PropertyChangeSupport;
   41.50 +import java.io.ByteArrayOutputStream;
   41.51 +import java.io.IOException;
   41.52 +import java.io.OutputStream;
   41.53 +import java.io.StringReader;
   41.54 +import java.util.Collection;
   41.55 +import java.util.Collections;
   41.56 +import java.util.HashSet;
   41.57 +import java.util.Set;
   41.58 +import java.util.concurrent.atomic.AtomicBoolean;
   41.59 +import java.util.logging.Level;
   41.60 +import java.util.logging.Logger;
   41.61 +import javax.xml.parsers.DocumentBuilder;
   41.62 +import javax.xml.parsers.DocumentBuilderFactory;
   41.63 +import javax.xml.parsers.ParserConfigurationException;
   41.64 +import org.netbeans.api.annotations.common.NonNull;
   41.65 +import static org.netbeans.modules.python.project2.Bundle.*;
   41.66 +import org.netbeans.spi.project.AuxiliaryConfiguration;
   41.67 +import org.netbeans.spi.project.ui.ProjectProblemsProvider;
   41.68 +import org.netbeans.spi.project.ui.ProjectProblemsProvider.ProjectProblem;
   41.69 +import org.openide.filesystems.FileChangeAdapter;
   41.70 +import org.openide.filesystems.FileEvent;
   41.71 +import org.openide.filesystems.FileObject;
   41.72 +import org.openide.filesystems.FileRenameEvent;
   41.73 +import org.openide.filesystems.FileSystem.AtomicAction;
   41.74 +import org.openide.filesystems.FileUtil;
   41.75 +import org.openide.util.Exceptions;
   41.76 +import org.openide.util.NbBundle.Messages;
   41.77 +import org.openide.util.RequestProcessor;
   41.78 +import org.openide.xml.XMLUtil;
   41.79 +import org.w3c.dom.DOMException;
   41.80 +import org.w3c.dom.Document;
   41.81 +import org.w3c.dom.Element;
   41.82 +import org.w3c.dom.Node;
   41.83 +import org.w3c.dom.NodeList;
   41.84 +import org.xml.sax.InputSource;
   41.85 +import org.xml.sax.SAXException;
   41.86 +
   41.87 +/**
   41.88 + * implementation of AuxiliaryConfiguration that relies on FileObject's attributes
   41.89 + * for the non shared elements and on ${basedir}/nb-configuration file for share ones.
   41.90 + * @author mkleint
   41.91 + */
   41.92 +public class PythonAuxilaryConfig implements AuxiliaryConfiguration {
   41.93 +    public static final String BROKEN_NBCONFIG = "BROKENNBCONFIG"; //NOI18N
   41.94 +
   41.95 +    private static final String AUX_CONFIG = "AuxilaryConfiguration"; //NOI18N
   41.96 +    public static final String CONFIG_FILE_NAME = "nb-configuration.xml"; //NOI18N
   41.97 +
   41.98 +    private static final Logger LOG = Logger.getLogger(PythonAuxilaryConfig.class.getName());
   41.99 +    private static final RequestProcessor RP = new RequestProcessor(PythonAuxilaryConfig.class);
  41.100 +    private static final int SAVING_DELAY = 100;
  41.101 +    private RequestProcessor.Task savingTask;
  41.102 +    private Document scheduledDocument;
  41.103 +    private Document cachedDoc;
  41.104 +    private static final Document DELETED_FILE_DOCUMENT = XMLUtil.createDocument(AUX_CONFIG, null, null, null);
  41.105 +    private static final Document BROKEN_DOCUMENT = XMLUtil.createDocument(AUX_CONFIG, null, null, null);
  41.106 +    private final Object configIOLock = new Object();
  41.107 +    private final FileObject projectDirectory;
  41.108 +    private ProblemProvider pp;
  41.109 +    private final FileChangeAdapter fileChange;
  41.110 +    private final AtomicBoolean fileChangeSet = new AtomicBoolean(false);
  41.111 +    
  41.112 +    public PythonAuxilaryConfig(FileObject dir, boolean longtermInstance) {
  41.113 +        this.projectDirectory = dir;
  41.114 +        
  41.115 +        if (longtermInstance) {
  41.116 +            pp = new ProblemProvider();
  41.117 +            fileChange = new FileChangeAdapter() {
  41.118 +
  41.119 +                @Override
  41.120 +                public void fileRenamed(FileRenameEvent fe) {
  41.121 +                    if (CONFIG_FILE_NAME.equals(fe.getName() + "." + fe.getExt())) {
  41.122 +                        resetCache();
  41.123 +                    }
  41.124 +                }
  41.125 +
  41.126 +                @Override
  41.127 +                public void fileDeleted(FileEvent fe) {
  41.128 +                    if (CONFIG_FILE_NAME.equals(fe.getFile().getNameExt())) {
  41.129 +                        resetCache();
  41.130 +                    }
  41.131 +                }
  41.132 +
  41.133 +                @Override
  41.134 +                public void fileChanged(FileEvent fe) {
  41.135 +                    if (CONFIG_FILE_NAME.equals(fe.getFile().getNameExt())) {
  41.136 +                        resetCache();
  41.137 +                    }
  41.138 +                }
  41.139 +
  41.140 +                @Override
  41.141 +                public void fileDataCreated(FileEvent fe) {
  41.142 +                    if (CONFIG_FILE_NAME.equals(fe.getFile().getNameExt())) {
  41.143 +                        resetCache();
  41.144 +                    }
  41.145 +                }
  41.146 +            };
  41.147 +            
  41.148 +        savingTask = RP.create(new Runnable() {
  41.149 +            public @Override void run() {
  41.150 +                try {
  41.151 +                    projectDirectory.getFileSystem().runAtomicAction(new AtomicAction() {
  41.152 +                        public @Override void run() throws IOException {
  41.153 +                            Document doc;
  41.154 +                            synchronized (PythonAuxilaryConfig.this) {
  41.155 +                                doc = scheduledDocument;
  41.156 +                                if (doc == null) {
  41.157 +                                    return;
  41.158 +                                }
  41.159 +                                scheduledDocument = null;
  41.160 +                            }
  41.161 +                            synchronized (configIOLock) {
  41.162 +                                FileObject config = projectDirectory.getFileObject(CONFIG_FILE_NAME);
  41.163 +                                if (doc.getDocumentElement().getElementsByTagName("*").getLength() > 0) {
  41.164 +                                    OutputStream out = config == null ? projectDirectory.createAndOpen(CONFIG_FILE_NAME) : config.getOutputStream();
  41.165 +                                    LOG.log(Level.FINEST, "Write configuration file for {0}", projectDirectory);
  41.166 +                                    try {
  41.167 +                                        XMLUtil.write(doc, out, "UTF-8"); //NOI18N
  41.168 +                                    } finally {
  41.169 +                                        out.close();
  41.170 +                                    }
  41.171 +                                } else if (config != null) {
  41.172 +                                    LOG.log(Level.FINEST, "Delete empty configuration file for {0}", projectDirectory);
  41.173 +                                    config.delete();
  41.174 +                                }
  41.175 +                            }
  41.176 +                        }
  41.177 +                    });
  41.178 +                } catch (IOException ex) {
  41.179 +                    LOG.log(Level.INFO, "IO Error while saving " + projectDirectory.getFileObject(CONFIG_FILE_NAME), ex);
  41.180 +                }
  41.181 +            }
  41.182 +        });
  41.183 +        } else {
  41.184 +            fileChange = null;
  41.185 +            fileChangeSet.set(true);
  41.186 +        }
  41.187 +    }
  41.188 +    
  41.189 +    private synchronized void resetCache() {
  41.190 +        cachedDoc = null;
  41.191 +    }
  41.192 +    
  41.193 +    
  41.194 +    public ProjectProblemsProvider getProblemProvider() {
  41.195 +        return pp;
  41.196 +    }
  41.197 +
  41.198 +    private Document loadConfig(FileObject config) throws IOException, SAXException {
  41.199 +        synchronized (configIOLock) {
  41.200 +            return XMLUtil.parse(new InputSource(config.toURL().toString()), false, true, null, null);
  41.201 +        }
  41.202 +    }
  41.203 +
  41.204 +    public @Override Element getConfigurationFragment(String elementName, String namespace, boolean shared) {
  41.205 +        Element e = doGetConfigurationFragment(elementName, namespace, shared);
  41.206 +        return e != null ? cloneSafely(e) : null;
  41.207 +    }
  41.208 +    // Copied from AntProjectHelper.
  41.209 +    private static final DocumentBuilder db;
  41.210 +    static {
  41.211 +        try {
  41.212 +            db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
  41.213 +        } catch (ParserConfigurationException e) {
  41.214 +            throw new AssertionError(e);
  41.215 +        }
  41.216 +    }
  41.217 +    private static Element cloneSafely(Element el) { // #190845
  41.218 +        // #50198: for thread safety, use a separate document.
  41.219 +        // Using XMLUtil.createDocument is much too slow.
  41.220 +        synchronized (db) {
  41.221 +            Document dummy = db.newDocument();
  41.222 +            return (Element) dummy.importNode(el, true);
  41.223 +        }
  41.224 +    }
  41.225 +    @Messages({
  41.226 +        "TXT_Problem_Broken_Config=Broken nb-configuration.xml file.",
  41.227 +        "# {0} - parser error message", 
  41.228 +        "DESC_Problem_Broken_Config=The $project_basedir/nb-configuration.xml file cannot be parsed. "
  41.229 +            + "The information contained in the file will be ignored until fixed. "
  41.230 +            + "This affects several features in the IDE that will not work properly as a result.\n\n "
  41.231 +            + "The parsing exception follows:\n{0}",
  41.232 +        "TXT_Problem_Broken_Config2=Duplicate entries found in nb-configuration.xml file.",
  41.233 +        "DESC_Problem_Broken_Config2=The $project_basedir/nb-configuration.xml file contains some elements multiple times. "
  41.234 +            + "That can happen when concurrent changes get merged by version control for example. The IDE however cannot decide which one to use. "
  41.235 +            + "So until the problem is resolved manually, the affected configuration will be ignored."
  41.236 +    })
  41.237 +    private synchronized Element doGetConfigurationFragment(final String elementName, final String namespace, boolean shared) {
  41.238 +        lazyAttachListener();
  41.239 +        if (shared) {
  41.240 +            //first check the document schedule for persistence
  41.241 +            if (scheduledDocument != null) {
  41.242 +                try {
  41.243 +                    Element el = XMLUtil.findElement(scheduledDocument.getDocumentElement(), elementName, namespace);
  41.244 +                    if (el != null) {
  41.245 +                        el = (Element) el.cloneNode(true);
  41.246 +                    }
  41.247 +                    return el;
  41.248 +                } catch (IllegalArgumentException iae) {
  41.249 +                    //thrown from XmlUtil.findElement when more than 1 equal elements are present.
  41.250 +                    LOG.log(Level.INFO, iae.getMessage(), iae);
  41.251 +                }
  41.252 +            }
  41.253 +            if (cachedDoc == null) {
  41.254 +                final FileObject config = projectDirectory.getFileObject(CONFIG_FILE_NAME);
  41.255 +                if (config != null) {
  41.256 +                    // we need to re-read the config file..
  41.257 +                    try {
  41.258 +                        Document doc = loadConfig(config);
  41.259 +                        cachedDoc = doc;
  41.260 +                        if (pp != null) {
  41.261 +                            pp.setProblem(null);
  41.262 +                            findDuplicateElements(doc.getDocumentElement(), pp, config);
  41.263 +                        }
  41.264 +                        return XMLUtil.findElement(doc.getDocumentElement(), elementName, namespace);
  41.265 +                    } catch (final SAXException ex) {
  41.266 +                        if (pp != null) {
  41.267 +                            RP.post(new Runnable() {
  41.268 +                                @Override
  41.269 +                                public void run() {
  41.270 +                                    pp.setProblem(ProjectProblem.createWarning(
  41.271 +                                        TXT_Problem_Broken_Config(),
  41.272 +                                            DESC_Problem_Broken_Config(ex.getMessage())/*,
  41.273 +                                    new ProblemReporterImpl.MavenProblemResolver(ProblemReporterImpl.createOpenFileAction(config), BROKEN_NBCONFIG)*/));
  41.274 +                                }
  41.275 +                            });
  41.276 +                        }
  41.277 +                        LOG.log(Level.INFO, ex.getMessage(), ex);
  41.278 +                        cachedDoc = BROKEN_DOCUMENT;
  41.279 +                    } catch (IOException ex) {
  41.280 +                        LOG.log(Level.INFO, "IO Error while loading " + config.getPath(), ex);
  41.281 +                        cachedDoc = BROKEN_DOCUMENT;
  41.282 +                    } catch (IllegalArgumentException iae) {
  41.283 +                        //thrown from XmlUtil.findElement when more than 1 equal elements are present.
  41.284 +                        LOG.log(Level.INFO, iae.getMessage(), iae);
  41.285 +                    }
  41.286 +                    return null;
  41.287 +                } else {
  41.288 +                    // no file.. remove possible cache
  41.289 +                    cachedDoc = DELETED_FILE_DOCUMENT;
  41.290 +                    return null;
  41.291 +                }
  41.292 +            } else {
  41.293 +                if (cachedDoc == DELETED_FILE_DOCUMENT || cachedDoc == BROKEN_DOCUMENT) {
  41.294 +                    return null;
  41.295 +                }
  41.296 +                //reuse cached value if available;
  41.297 +                try {
  41.298 +                    return XMLUtil.findElement(cachedDoc.getDocumentElement(), elementName, namespace);
  41.299 +                } catch (IllegalArgumentException iae) {
  41.300 +                    //thrown from XmlUtil.findElement when more than 1 equal elements are present.
  41.301 +                    LOG.log(Level.INFO, iae.getMessage(), iae);
  41.302 +                }
  41.303 +            }
  41.304 +            return null;
  41.305 +        } else {
  41.306 +            String str = (String) projectDirectory.getAttribute(AUX_CONFIG);
  41.307 +            if (str != null) {
  41.308 +                Document doc;
  41.309 +                try {
  41.310 +                    doc = XMLUtil.parse(new InputSource(new StringReader(str)), false, true, null, null);
  41.311 +                    return XMLUtil.findElement(doc.getDocumentElement(), elementName, namespace);
  41.312 +                } catch (SAXException ex) {
  41.313 +                    LOG.log(Level.FINE, "cannot parse", ex);
  41.314 +                } catch (IOException ex) {
  41.315 +                    LOG.log(Level.FINE, "error reading private auxiliary configuration", ex);
  41.316 +                }
  41.317 +            }
  41.318 +            return null;
  41.319 +        }
  41.320 +    }
  41.321 +
  41.322 +    private void lazyAttachListener() {
  41.323 +        if (fileChangeSet.compareAndSet(false, true)) {
  41.324 +            projectDirectory.addFileChangeListener(FileUtil.weakFileChangeListener(fileChange, projectDirectory));
  41.325 +        }
  41.326 +    }
  41.327 +
  41.328 +    public @Override synchronized void putConfigurationFragment(final Element fragment, final boolean shared) throws IllegalArgumentException {
  41.329 +        lazyAttachListener();
  41.330 +        Document doc = null;
  41.331 +        if (shared) {
  41.332 +            if (scheduledDocument != null) {
  41.333 +                doc = scheduledDocument;
  41.334 +            } else {
  41.335 +                FileObject config = projectDirectory.getFileObject(CONFIG_FILE_NAME);
  41.336 +                if (config != null) {
  41.337 +                    try {
  41.338 +                        doc = loadConfig(config);
  41.339 +                    } catch (SAXException ex) {
  41.340 +                        LOG.log(Level.INFO, "Cannot parse file " + config.getPath(), ex);
  41.341 +                        if (config.getSize() == 0) {
  41.342 +                            //something got wrong in the past..
  41.343 +                            doc = createNewSharedDocument();
  41.344 +                        }
  41.345 +                    } catch (IOException ex) {
  41.346 +                        LOG.log(Level.INFO, "IO Error with " + config.getPath(), ex);
  41.347 +                    }
  41.348 +                } else {
  41.349 +                    doc = createNewSharedDocument();
  41.350 +                }
  41.351 +            }
  41.352 +        } else {
  41.353 +            String str = (String) projectDirectory.getAttribute(AUX_CONFIG);
  41.354 +            if (str != null) {
  41.355 +                try {
  41.356 +                    doc = XMLUtil.parse(new InputSource(new StringReader(str)), false, true, null, null);
  41.357 +                } catch (SAXException ex) {
  41.358 +                    LOG.log(Level.FINE, "cannot parse", ex);
  41.359 +                } catch (IOException ex) {
  41.360 +                    LOG.log(Level.FINE, "error reading private auxiliary configuration", ex);
  41.361 +                }
  41.362 +            }
  41.363 +            if (doc == null) {
  41.364 +                String element = "project-private"; // NOI18N
  41.365 +                doc = XMLUtil.createDocument(element, null, null, null);
  41.366 +            }
  41.367 +        }
  41.368 +        if (doc != null) {
  41.369 +            Element el = XMLUtil.findElement(doc.getDocumentElement(), fragment.getNodeName(), fragment.getNamespaceURI());
  41.370 +            if (el != null) {
  41.371 +                doc.getDocumentElement().removeChild(el);
  41.372 +            }
  41.373 +            doc.getDocumentElement().appendChild(doc.importNode(fragment, true));
  41.374 +
  41.375 +            if (shared) {
  41.376 +                if (scheduledDocument == null) {
  41.377 +                    scheduledDocument = doc;
  41.378 +                }
  41.379 +                LOG.log(Level.FINEST, "Schedule saving of configuration fragment for " + projectDirectory, new Exception());
  41.380 +                savingTask.schedule(SAVING_DELAY);
  41.381 +            } else {
  41.382 +                try {
  41.383 +                    ByteArrayOutputStream wr = new ByteArrayOutputStream();
  41.384 +                    XMLUtil.write(doc, wr, "UTF-8"); //NOI18N
  41.385 +                    projectDirectory.setAttribute(AUX_CONFIG, wr.toString("UTF-8"));
  41.386 +                } catch (IOException ex) {
  41.387 +                    LOG.log(Level.FINE, "error writing private auxiliary configuration", ex);
  41.388 +                }
  41.389 +            }
  41.390 +        }
  41.391 +
  41.392 +    }
  41.393 +
  41.394 +    public @Override synchronized boolean removeConfigurationFragment(final String elementName, final String namespace, final boolean shared) throws IllegalArgumentException {
  41.395 +        lazyAttachListener();
  41.396 +        Document doc = null;
  41.397 +        FileObject config = projectDirectory.getFileObject(CONFIG_FILE_NAME);
  41.398 +        if (shared) {
  41.399 +            if (scheduledDocument != null) {
  41.400 +                doc = scheduledDocument;
  41.401 +            } else {
  41.402 +                if (config != null) {
  41.403 +                    try {
  41.404 +                        try {
  41.405 +                            doc = loadConfig(config);
  41.406 +                        } catch (SAXException ex) {
  41.407 +                            LOG.log(Level.INFO, "Cannot parse file " + config.getPath(), ex);
  41.408 +                            if (config.getSize() == 0) {
  41.409 +                                //just delete the empty file, something got wrong a while back..
  41.410 +                                config.delete();
  41.411 +                            }
  41.412 +                            return true;
  41.413 +                        }
  41.414 +                    } catch (IOException ex) {
  41.415 +                        LOG.log(Level.INFO, "IO Error with " + config.getPath(), ex);
  41.416 +                    }
  41.417 +                } else {
  41.418 +                    return false;
  41.419 +                }
  41.420 +            }
  41.421 +        } else {
  41.422 +            String str = (String) projectDirectory.getAttribute(AUX_CONFIG);
  41.423 +            if (str != null) {
  41.424 +                try {
  41.425 +                    doc = XMLUtil.parse(new InputSource(new StringReader(str)), false, true, null, null);
  41.426 +                } catch (SAXException ex) {
  41.427 +                    Exceptions.printStackTrace(ex);
  41.428 +                } catch (IOException ex) {
  41.429 +                    Exceptions.printStackTrace(ex);
  41.430 +                }
  41.431 +            } else {
  41.432 +                return false;
  41.433 +            }
  41.434 +        }
  41.435 +        if (doc != null) {
  41.436 +            Element el = XMLUtil.findElement(doc.getDocumentElement(), elementName, namespace);
  41.437 +            if (el != null) {
  41.438 +                doc.getDocumentElement().removeChild(el);
  41.439 +            }
  41.440 +            if (shared) {
  41.441 +                if (scheduledDocument == null) {
  41.442 +                    scheduledDocument = doc;
  41.443 +                }
  41.444 +                LOG.log(Level.FINEST, "Schedule saving of configuration fragment for " + projectDirectory, new Exception());
  41.445 +                savingTask.schedule(SAVING_DELAY);
  41.446 +            } else {
  41.447 +                try {
  41.448 +                    ByteArrayOutputStream wr = new ByteArrayOutputStream();
  41.449 +                    XMLUtil.write(doc, wr, "UTF-8"); //NOI18N
  41.450 +                    projectDirectory.setAttribute(AUX_CONFIG, wr.toString("UTF-8"));
  41.451 +                } catch (IOException ex) {
  41.452 +                    Exceptions.printStackTrace(ex);
  41.453 +                }
  41.454 +            }
  41.455 +        }
  41.456 +        return true;
  41.457 +    }
  41.458 +
  41.459 +    private Document createNewSharedDocument() throws DOMException {
  41.460 +        String element = "project-shared-configuration";
  41.461 +        Document doc = XMLUtil.createDocument(element, null, null, null);
  41.462 +        doc.getDocumentElement().appendChild(doc.createComment(
  41.463 +                "\nThis file contains additional configuration written by modules in the NetBeans IDE.\n" +
  41.464 +                "The configuration is intended to be shared among all the users of project and\n" +
  41.465 +                "therefore it is assumed to be part of version control checkout.\n" +
  41.466 +                "Without this configuration present, some functionality in the IDE may be limited or fail altogether.\n"));
  41.467 +        return doc;
  41.468 +    }
  41.469 +    
  41.470 +    static void findDuplicateElements(@NonNull Element parent, @NonNull ProblemProvider pp, FileObject config) {
  41.471 +        NodeList l = parent.getChildNodes();
  41.472 +        int nodeCount = l.getLength();
  41.473 +        Set<String> known = new HashSet<String>();
  41.474 +        for (int i = 0; i < nodeCount; i++) {
  41.475 +            if (l.item(i).getNodeType() == Node.ELEMENT_NODE) {
  41.476 +                Node node = l.item(i);
  41.477 +                String localName = node.getLocalName();
  41.478 +                localName = localName == null ? node.getNodeName() : localName;
  41.479 +                String id = localName + "|" + node.getNamespaceURI();
  41.480 +                if (!known.add(id)) {
  41.481 +                    //we have a duplicate;
  41.482 +                    pp.setProblem(ProjectProblem.createWarning(
  41.483 +                                    TXT_Problem_Broken_Config2(), 
  41.484 +                            DESC_Problem_Broken_Config2()/*,
  41.485 +                    new ProblemReporterImpl.MavenProblemResolver(ProblemReporterImpl.createOpenFileAction(config), BROKEN_NBCONFIG)*/));
  41.486 +                }
  41.487 +            }
  41.488 +        }
  41.489 +    }    
  41.490 +    
  41.491 +    private class ProblemProvider implements ProjectProblemsProvider {
  41.492 +
  41.493 +        private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
  41.494 +        private ProjectProblem pp;
  41.495 +
  41.496 +        public ProblemProvider() {
  41.497 +        }
  41.498 +        
  41.499 +        void setProblem(ProjectProblem pp) {
  41.500 +            this.pp = pp;
  41.501 +            if (pp == null && this.pp == null) {
  41.502 +                return; //ignore this case, dont' fire change..
  41.503 +            }
  41.504 +            pcs.firePropertyChange(ProjectProblemsProvider.PROP_PROBLEMS, null, null);
  41.505 +        }
  41.506 +        
  41.507 +        @Override
  41.508 +        public void addPropertyChangeListener(PropertyChangeListener listener) {
  41.509 +            pcs.addPropertyChangeListener(listener);
  41.510 +        }
  41.511 +
  41.512 +        @Override
  41.513 +        public void removePropertyChangeListener(PropertyChangeListener listener) {
  41.514 +            pcs.removePropertyChangeListener(listener);
  41.515 +        }
  41.516 +
  41.517 +        @Override
  41.518 +        public Collection<? extends ProjectProblem> getProblems() {
  41.519 +            if (pp != null) {
  41.520 +                return Collections.singleton(pp);
  41.521 +            } else {
  41.522 +                return Collections.emptyList();
  41.523 +            }
  41.524 +        }
  41.525 +        
  41.526 +    }
  41.527 +}
    42.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    42.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/PythonProject2.java	Tue Feb 24 01:58:36 2015 -0800
    42.3 @@ -0,0 +1,368 @@
    42.4 +package org.netbeans.modules.python.project2;
    42.5 +
    42.6 +import java.beans.PropertyChangeListener;
    42.7 +import java.beans.PropertyChangeSupport;
    42.8 +import java.io.IOException;
    42.9 +import java.util.Properties;
   42.10 +import java.util.Scanner;
   42.11 +import java.util.concurrent.ExecutionException;
   42.12 +import java.util.concurrent.Future;
   42.13 +import javax.swing.Icon;
   42.14 +import javax.swing.ImageIcon;
   42.15 +import org.netbeans.api.project.Project;
   42.16 +import org.netbeans.api.project.ProjectInformation;
   42.17 +import org.netbeans.api.project.ProjectManager;
   42.18 +import org.netbeans.api.project.ProjectUtils;
   42.19 +import org.netbeans.modules.python.api.PythonException;
   42.20 +import org.netbeans.modules.python.api.PythonExecution;
   42.21 +import org.netbeans.modules.python.api.PythonPlatform;
   42.22 +import org.netbeans.modules.python.api.PythonPlatformManager;
   42.23 +import org.netbeans.modules.python.editor.codecoverage.PythonCoverageProvider;
   42.24 +import org.netbeans.modules.python.project2.classpath.ClassPathProviderImpl;
   42.25 +import org.netbeans.modules.python.project2.ui.customizer.PythonCustomizerProvider;
   42.26 +import org.netbeans.spi.project.AuxiliaryConfiguration;
   42.27 +import org.netbeans.spi.project.ProjectState;
   42.28 +import org.netbeans.spi.project.ui.LogicalViewProvider;
   42.29 +import org.netbeans.spi.project.ui.ProjectOpenedHook;
   42.30 +import org.openide.filesystems.FileAttributeEvent;
   42.31 +import org.openide.filesystems.FileChangeListener;
   42.32 +import org.openide.filesystems.FileEvent;
   42.33 +import org.openide.filesystems.FileObject;
   42.34 +import org.openide.filesystems.FileRenameEvent;
   42.35 +import org.openide.filesystems.FileUtil;
   42.36 +import org.openide.util.Exceptions;
   42.37 +import org.openide.util.ImageUtilities;
   42.38 +import org.openide.util.Lookup;
   42.39 +import org.openide.util.Mutex;
   42.40 +import org.openide.util.io.ReaderInputStream;
   42.41 +import org.openide.util.lookup.Lookups;
   42.42 +import org.openide.xml.XMLUtil;
   42.43 +import org.w3c.dom.Element;
   42.44 +import org.w3c.dom.Node;
   42.45 +import org.w3c.dom.NodeList;
   42.46 +import org.w3c.dom.Text;
   42.47 +
   42.48 +/**
   42.49 + *
   42.50 + * @author Ralph Benjamin Ruijs <ralphbenjamin@netbeans.org>
   42.51 + */
   42.52 +public class PythonProject2 implements Project {
   42.53 +
   42.54 +    /**
   42.55 +     * the only property change fired by the class, means that the setup.py file
   42.56 +     * has changed.
   42.57 +     */
   42.58 +    public static final String PROP_PROJECT = "MavenProject"; //NOI18N
   42.59 +
   42.60 +    private static final String NS_PYTHON_1 = "http://nbpython.dev.java.net/ns/php-project/1"; // NOI18N
   42.61 +    private static final String EL_PYTHON = "python-data"; // NOI18N
   42.62 +    private static final ImageIcon PROJECT_ICON = ImageUtilities.loadImageIcon("org/netbeans/modules/python/project/resources/py_25_16.png", false);
   42.63 +
   42.64 +    private static final String MAIN_MODULE = "main.file"; //NOI18N
   42.65 +    private static final String APPLICATION_ARGS = "application.args";   //NOI18N
   42.66 +    private static final String ACTIVE_PLATFORM = "platform.active"; //NOI18N
   42.67 +    public static final String SOURCES_TYPE_PYTHON = "python"; //NOI18N
   42.68 +    static final String SETUPPY = "setup.py"; //NOI18N
   42.69 +
   42.70 +    private final FileObject projectDirectory;
   42.71 +    protected Lookup lkp;
   42.72 +    protected AuxiliaryConfiguration aux;
   42.73 +    protected LogicalViewProvider logicalView;
   42.74 +    private final Info info;
   42.75 +    private final PropertyChangeSupport support;
   42.76 +    private final PythonSources sources;
   42.77 +
   42.78 +    public PythonProject2(FileObject projectDirectory, ProjectState state) {
   42.79 +        support = new PropertyChangeSupport(this);
   42.80 +        this.logicalView = new Python2LogicalView(this);
   42.81 +        this.projectDirectory = projectDirectory;
   42.82 +        info = new PythonProject2.Info();
   42.83 +        aux = new PythonAuxilaryConfig(projectDirectory, true);
   42.84 +        sources = new PythonSources(this);
   42.85 +        this.lkp = createLookup(state);
   42.86 +    }
   42.87 +
   42.88 +    @Override
   42.89 +    public FileObject getProjectDirectory() {
   42.90 +        return projectDirectory;
   42.91 +    }
   42.92 +
   42.93 +    @Override
   42.94 +    public Lookup getLookup() {
   42.95 +        return lkp;
   42.96 +    }
   42.97 +
   42.98 +    private Lookup createLookup(ProjectState state) {
   42.99 +        return Lookups.fixed(new Object[]{
  42.100 +            this, //project spec requires a project be in it's own lookup
  42.101 +            aux, //Auxiliary configuartion to store bookmarks and so on
  42.102 +            new PythonActionProvider(this), //Provides Standard like build and cleen
  42.103 +            info, // Project information Implementation
  42.104 +            logicalView, // Logical view if project implementation
  42.105 +            new PythonProject2.PythonOpenedHook(), //Called by project framework when project is opened (closed)
  42.106 +            sources, //Python source grops - used by package view, factories, refactoring, ...
  42.107 +            new ClassPathProviderImpl(this, sources),
  42.108 +//            new PythonProjectOperations(this), //move, rename, copy of project
  42.109 +//            new PythonProject.RecommendedTemplatesImpl(this.updateHelper), // Recommended Templates
  42.110 +            new PythonCustomizerProvider(this), //Project custmoizer
  42.111 +//            new PythonProjectFileEncodingQuery(getEvaluator()), //Provides encoding of the project - used by editor, runtime
  42.112 +//            new PythonSharabilityQuery(helper, getEvaluator(), getSourceRoots(), getTestRoots()), //Sharabilit info - used by VCS
  42.113 +//            helper.createCacheDirectoryProvider(), //Cache provider
  42.114 +//            helper.createAuxiliaryProperties(), // AuxiliaryConfiguraion provider - used by bookmarks, project Preferences, etc
  42.115 +//            new PythonPlatformProvider(getEvaluator()),
  42.116 +            new PythonCoverageProvider(this),
  42.117 +            new PythonProjectSourceLevelQuery(this),
  42.118 +            state
  42.119 +        });
  42.120 +    }
  42.121 +
  42.122 +    public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) {
  42.123 +        support.addPropertyChangeListener(propertyChangeListener);
  42.124 +    }
  42.125 +
  42.126 +    public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener) {
  42.127 +        support.removePropertyChangeListener(propertyChangeListener);
  42.128 +    }
  42.129 +
  42.130 +    public PythonPlatform getActivePlatform() {
  42.131 +        String pid = getProp(ProjectUtils.getAuxiliaryConfiguration(this), PythonProject2.APPLICATION_ARGS);
  42.132 +        final PythonPlatformManager manager = PythonPlatformManager.getInstance();
  42.133 +        if (pid == null) {
  42.134 +            pid = manager.getDefaultPlatform();
  42.135 +        }
  42.136 +        return manager.getPlatform(pid);
  42.137 +    }
  42.138 +
  42.139 +    public void setActivePlatform(final PythonPlatform platform) {
  42.140 +        storeProp(ProjectUtils.getAuxiliaryConfiguration(this), PythonProject2.ACTIVE_PLATFORM, platform.getId());
  42.141 +    }
  42.142 +
  42.143 +    public String getApplicationArgs() {
  42.144 +        return getProp(ProjectUtils.getAuxiliaryConfiguration(this), PythonProject2.APPLICATION_ARGS);
  42.145 +    }
  42.146 +
  42.147 +    public void setApplicationArgs(final String args) {
  42.148 +        storeProp(ProjectUtils.getAuxiliaryConfiguration(this), PythonProject2.APPLICATION_ARGS, args);
  42.149 +    }
  42.150 +
  42.151 +    public String getMainModule() {
  42.152 +        return getProp(ProjectUtils.getAuxiliaryConfiguration(this), PythonProject2.MAIN_MODULE);
  42.153 +    }
  42.154 +
  42.155 +    public void setMainModule(final String main) {
  42.156 +        storeProp(ProjectUtils.getAuxiliaryConfiguration(this), PythonProject2.MAIN_MODULE, main);
  42.157 +    }
  42.158 +
  42.159 +    private String getProp(final AuxiliaryConfiguration auxiliaryConfiguration, final String key) {
  42.160 +        return ProjectManager.mutex().readAccess(new Mutex.Action<String>() {
  42.161 +            @Override
  42.162 +            public String run() {
  42.163 +                Element data = auxiliaryConfiguration.getConfigurationFragment(PythonProject2.EL_PYTHON, PythonProject2.NS_PYTHON_1, true);
  42.164 +                if (data == null) {
  42.165 +                    return null;
  42.166 +                }
  42.167 +                NodeList nl = data.getElementsByTagNameNS(PythonProject2.NS_PYTHON_1, key);
  42.168 +                if (nl.getLength() == 1) {
  42.169 +                    nl = nl.item(0).getChildNodes();
  42.170 +                    if (nl.getLength() == 1 && nl.item(0).getNodeType() == Node.TEXT_NODE) {
  42.171 +                        return ((Text) nl.item(0)).getNodeValue();
  42.172 +                    }
  42.173 +                }
  42.174 +                return null; // NOI18N
  42.175 +            }
  42.176 +        });
  42.177 +    }
  42.178 +
  42.179 +    private void storeProp(final AuxiliaryConfiguration auxiliaryConfiguration, final String key, final String main) {
  42.180 +        ProjectManager.mutex().writeAccess(new Mutex.Action<Void>() {
  42.181 +            @Override
  42.182 +            public Void run() {
  42.183 +                Element data = auxiliaryConfiguration.getConfigurationFragment(PythonProject2.EL_PYTHON, PythonProject2.NS_PYTHON_1, true);
  42.184 +                if (data == null) {
  42.185 +                    data = XMLUtil.createDocument(PythonProject2.EL_PYTHON, PythonProject2.NS_PYTHON_1, null, null).getDocumentElement();
  42.186 +                    auxiliaryConfiguration.putConfigurationFragment(data, false);
  42.187 +                }
  42.188 +                NodeList nl = data.getElementsByTagNameNS(PythonProject2.NS_PYTHON_1, key);
  42.189 +                Element nameEl;
  42.190 +                if (nl.getLength() == 1) {
  42.191 +                    nameEl = (Element) nl.item(0);
  42.192 +                    NodeList deadKids = nameEl.getChildNodes();
  42.193 +                    while (deadKids.getLength() > 0) {
  42.194 +                        nameEl.removeChild(deadKids.item(0));
  42.195 +                    }
  42.196 +                } else {
  42.197 +                    nameEl = data.getOwnerDocument().createElementNS(PythonProject2.NS_PYTHON_1, key);
  42.198 +                    data.insertBefore(nameEl, data.getChildNodes().item(0));
  42.199 +                }
  42.200 +                nameEl.appendChild(data.getOwnerDocument().createTextNode(main));
  42.201 +                auxiliaryConfiguration.putConfigurationFragment(data, true);
  42.202 +                return null;
  42.203 +            }
  42.204 +        });
  42.205 +    }
  42.206 +
  42.207 +    private final class Info implements ProjectInformation, FileChangeListener {
  42.208 +
  42.209 +        private static final String PROP_VERSION = "version";
  42.210 +
  42.211 +        private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
  42.212 +        private final Properties properties;
  42.213 +
  42.214 +        public Info() {
  42.215 +            properties = findProjectProperties();
  42.216 +        }
  42.217 +
  42.218 +        @Override
  42.219 +        public void addPropertyChangeListener(PropertyChangeListener listener) {
  42.220 +            propertyChangeSupport.addPropertyChangeListener(listener);
  42.221 +        }
  42.222 +
  42.223 +        @Override
  42.224 +        public void removePropertyChangeListener(PropertyChangeListener listener) {
  42.225 +            propertyChangeSupport.removePropertyChangeListener(listener);
  42.226 +        }
  42.227 +
  42.228 +        public Properties getProperties() {
  42.229 +            return properties;
  42.230 +        }
  42.231 +
  42.232 +        @Override
  42.233 +        public String getDisplayName() {
  42.234 +            return properties.getProperty(PROP_DISPLAY_NAME);
  42.235 +        }
  42.236 +
  42.237 +        @Override
  42.238 +        public Icon getIcon() {
  42.239 +            return PROJECT_ICON;
  42.240 +        }
  42.241 +
  42.242 +        @Override
  42.243 +        public String getName() {
  42.244 +            return projectDirectory.getName();
  42.245 +//            return name;
  42.246 +        }
  42.247 +
  42.248 +        public String getVersion() {
  42.249 +            return properties.getProperty(PROP_VERSION);
  42.250 +//            return version;
  42.251 +        }
  42.252 +
  42.253 +        @Override
  42.254 +        public Project getProject() {
  42.255 +            return PythonProject2.this;
  42.256 +        }
  42.257 +
  42.258 +        public Properties findProjectProperties() {
  42.259 +            Properties props = new Properties();
  42.260 +            PythonPlatform platform = PythonPlatformManager.getInstance().getPlatforms().get(0);
  42.261 +            PythonExecution pye;
  42.262 +            try {
  42.263 +                pye = new PythonExecution();
  42.264 +                pye.setCommand(platform.getInterpreterCommand());
  42.265 +                pye.setDisplayName("Python Project Info");
  42.266 +                FileObject setuppy = projectDirectory.getFileObject(SETUPPY);
  42.267 +                setuppy.addFileChangeListener(this);
  42.268 +                pye.setScript(FileUtil.toFile(setuppy).getAbsolutePath());
  42.269 +                pye.setScriptArgs("--name --version"); //NOI18N
  42.270 +                pye.setShowControls(false);
  42.271 +                pye.setShowInput(false);
  42.272 +                pye.setShowWindow(false);
  42.273 +                pye.setShowProgress(false);
  42.274 +                pye.setShowSuspended(false);
  42.275 +                //pye.setWorkingDirectory(info.getAbsolutePath().substring(0, info.getAbsolutePath().lastIndexOf(File.separator)));
  42.276 +                pye.setWorkingDirectory(FileUtil.toFile(projectDirectory).getAbsolutePath());
  42.277 +                pye.attachOutputProcessor();
  42.278 +                Future<Integer> result = pye.run();
  42.279 +                Integer value = result.get();
  42.280 +                if (value.intValue() == 0) {
  42.281 +                    // Problem with encoding?
  42.282 +//                    prop.load(new ReaderInputStream(pye.getOutput()));
  42.283 +                    try (Scanner sc = new Scanner(new ReaderInputStream(pye.getOutput()))) {
  42.284 +                        String newName = sc.nextLine();
  42.285 +                        props.setProperty(PROP_DISPLAY_NAME, newName);
  42.286 +                        String newVersion = sc.nextLine();
  42.287 +                        props.setProperty(PROP_VERSION, newVersion);
  42.288 +                    }
  42.289 +                } else {
  42.290 +                    throw new PythonException("Could not discover Python Project Info");
  42.291 +                }
  42.292 +            } catch (PythonException | InterruptedException | ExecutionException | IOException ex) {
  42.293 +                Exceptions.printStackTrace(ex);
  42.294 +            }
  42.295 +            return props;
  42.296 +        }
  42.297 +
  42.298 +        @Override
  42.299 +        public void fileFolderCreated(FileEvent fe) {
  42.300 +        }
  42.301 +
  42.302 +        @Override
  42.303 +        public void fileDataCreated(FileEvent fe) {
  42.304 +        }
  42.305 +
  42.306 +        @Override
  42.307 +        public void fileChanged(FileEvent fe) {
  42.308 +            ProjectManager.mutex().writeAccess(new Mutex.Action<Void>() {
  42.309 +
  42.310 +                @Override
  42.311 +                public Void run() {
  42.312 +                    Properties newProps = findProjectProperties();
  42.313 +                    for (String propName : newProps.stringPropertyNames()) {
  42.314 +                        String value = newProps.getProperty(propName);
  42.315 +                        Object oldValue = properties.setProperty(propName, value);
  42.316 +                        if (!value.equals(oldValue)) {
  42.317 +                            propertyChangeSupport.firePropertyChange(propName, oldValue, value);
  42.318 +                        }
  42.319 +                    }
  42.320 +                    return null;
  42.321 +                }
  42.322 +            });
  42.323 +        }
  42.324 +
  42.325 +        @Override
  42.326 +        public void fileDeleted(FileEvent fe) {
  42.327 +            //TODO: What when it is deleted?
  42.328 +        }
  42.329 +
  42.330 +        @Override
  42.331 +        public void fileRenamed(FileRenameEvent fe) {
  42.332 +            //TODO: What when it is renamed?
  42.333 +        }
  42.334 +
  42.335 +        @Override
  42.336 +        public void fileAttributeChanged(FileAttributeEvent fe) {
  42.337 +        }
  42.338 +    }
  42.339 +
  42.340 +    public final class PythonOpenedHook extends ProjectOpenedHook {
  42.341 +
  42.342 +        @Override
  42.343 +        protected void projectOpened() {
  42.344 +            // register project's classpaths to GlobalPathRegistry
  42.345 +//            final ClassPathProviderImpl cpProvider = getLookup().lookup(ClassPathProviderImpl.class);
  42.346 +//            assert cpProvider != null;
  42.347 +//            GlobalPathRegistry.getDefault().register(ClassPath.BOOT, cpProvider.getProjectClassPaths(ClassPath.BOOT));
  42.348 +//            GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, cpProvider.getProjectClassPaths(ClassPath.SOURCE));
  42.349 +
  42.350 +            // Ensure that code coverage is initialized in case it's enabled...
  42.351 +//            PythonCoverageProvider codeCoverage = getLookup().lookup(PythonCoverageProvider.class);
  42.352 +//            if (codeCoverage.isEnabled()) {
  42.353 +//                codeCoverage.notifyProjectOpened();
  42.354 +//            }
  42.355 +        }
  42.356 +
  42.357 +        @Override
  42.358 +        protected void projectClosed() {
  42.359 +            // unregister project's classpaths to GlobalPathRegistry
  42.360 +//            final ClassPathProviderImpl cpProvider = getLookup().lookup(ClassPathProviderImpl.class);
  42.361 +//            assert cpProvider != null;
  42.362 +//            //GlobalPathRegistry.getDefault().unregister(ClassPath.BOOT, cpProvider.getProjectClassPaths(ClassPath.BOOT));
  42.363 +//            GlobalPathRegistry.getDefault().unregister(ClassPath.SOURCE, cpProvider.getProjectClassPaths(ClassPath.SOURCE));
  42.364 +//            try {
  42.365 +//                ProjectManager.getDefault().saveProject(PythonProject2.this);
  42.366 +//            } catch (IOException e) {
  42.367 +//                Exceptions.printStackTrace(e);
  42.368 +//            }
  42.369 +        }
  42.370 +    }
  42.371 +}
    43.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    43.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/PythonProjectFactory.java	Tue Feb 24 01:58:36 2015 -0800
    43.3 @@ -0,0 +1,39 @@
    43.4 +/*
    43.5 + * To change this license header, choose License Headers in Project Properties.
    43.6 + * To change this template file, choose Tools | Templates
    43.7 + * and open the template in the editor.
    43.8 + */
    43.9 +package org.netbeans.modules.python.project2;
   43.10 +
   43.11 +import java.io.IOException;
   43.12 +import org.netbeans.api.project.Project;
   43.13 +import org.netbeans.spi.project.ProjectFactory;
   43.14 +import org.netbeans.spi.project.ProjectState;
   43.15 +import org.openide.filesystems.FileObject;
   43.16 +import org.openide.util.lookup.ServiceProvider;
   43.17 +
   43.18 +/**
   43.19 + *
   43.20 + * @author Ralph Benjamin Ruijs <ralphbenjamin@netbeans.org>
   43.21 + */
   43.22 +@ServiceProvider(service=ProjectFactory.class)
   43.23 +public class PythonProjectFactory implements ProjectFactory {
   43.24 +    
   43.25 +
   43.26 +    @Override
   43.27 +    public boolean isProject(FileObject projectDirectory) {
   43.28 +        return projectDirectory.getFileObject(PythonProject2.SETUPPY) != null;
   43.29 +    }
   43.30 +
   43.31 +    //Specifies when the project will be opened, i.e., if the project exists:
   43.32 +    @Override
   43.33 +    public Project loadProject(FileObject dir, ProjectState state) throws IOException {
   43.34 +        return isProject(dir) ? new PythonProject2(dir, state) : null;
   43.35 +    }
   43.36 +
   43.37 +    @Override
   43.38 +    public void saveProject(final Project project) throws IOException, ClassCastException {
   43.39 +        // leave unimplemented for the moment
   43.40 +    }
   43.41 +
   43.42 +}
    44.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    44.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/PythonProjectSourceLevelQuery.java	Tue Feb 24 01:58:36 2015 -0800
    44.3 @@ -0,0 +1,88 @@
    44.4 +package org.netbeans.modules.python.project2;
    44.5 +
    44.6 +import java.beans.PropertyChangeEvent;
    44.7 +import java.beans.PropertyChangeListener;
    44.8 +import javax.swing.event.ChangeListener;
    44.9 +import org.netbeans.api.annotations.common.CheckForNull;
   44.10 +import org.netbeans.api.annotations.common.NonNull;
   44.11 +import org.netbeans.api.project.ProjectUtils;
   44.12 +import org.netbeans.modules.python.api.PythonPlatform;
   44.13 +import org.netbeans.modules.python.source.queries.SourceLevelQueryImplementation;
   44.14 +import org.openide.filesystems.FileObject;
   44.15 +import org.openide.util.ChangeSupport;
   44.16 +import org.openide.util.WeakListeners;
   44.17 +
   44.18 +/**
   44.19 + *
   44.20 + * @author Ralph Benjamin Ruijs <ralphbenjamin@netbeans.org>
   44.21 + */
   44.22 +class PythonProjectSourceLevelQuery implements SourceLevelQueryImplementation {
   44.23 +
   44.24 +    private final PythonProject2 project;
   44.25 +    private final Result result;
   44.26 +
   44.27 +    PythonProjectSourceLevelQuery(@NonNull final PythonProject2 project) {
   44.28 +        assert project != null;
   44.29 +        this.project = project;
   44.30 +        this.result = new R(project);
   44.31 +    }
   44.32 +
   44.33 +    @Override
   44.34 +    public Result getSourceLevel(FileObject javaFile) {
   44.35 +        return this.result;
   44.36 +    }
   44.37 +
   44.38 +    @CheckForNull
   44.39 +    static String findSourceLevel (@NonNull final PythonProject2 project) {
   44.40 +        return findValue(project);
   44.41 +    }
   44.42 +
   44.43 +    @CheckForNull
   44.44 +    private static String findValue(
   44.45 +            @NonNull final PythonProject2 project) {
   44.46 +        final PythonPlatform activePlatform = project.getActivePlatform();
   44.47 +        return activePlatform.getSourceLevel();
   44.48 +    }
   44.49 +
   44.50 +    private class R implements Result, PropertyChangeListener {
   44.51 +
   44.52 +        private final ChangeSupport cs = new ChangeSupport(this);
   44.53 +
   44.54 +        @SuppressWarnings("LeakingThisInConstructor")
   44.55 +        private R(final PythonProject2 project) {
   44.56 +            project.addPropertyChangeListener(WeakListeners.propertyChange(this, project));
   44.57 +        }
   44.58 +
   44.59 +        @Override
   44.60 +        public String getSourceLevel() {
   44.61 +            return findSourceLevel(project);
   44.62 +        }
   44.63 +
   44.64 +        @Override
   44.65 +        public void addChangeListener(ChangeListener listener) {
   44.66 +            this.cs.addChangeListener(listener);
   44.67 +        }
   44.68 +
   44.69 +        @Override
   44.70 +        public void removeChangeListener(ChangeListener listener) {
   44.71 +            this.cs.removeChangeListener(listener);
   44.72 +        }
   44.73 +
   44.74 +        @Override
   44.75 +        public void propertyChange(PropertyChangeEvent evt) {
   44.76 +//            final String name = evt.getPropertyName();
   44.77 +//            if (name == null ||
   44.78 +//                PLATFORM_ACTIVE.equals(name)) {
   44.79 +                this.cs.fireChange();
   44.80 +//            }
   44.81 +        }
   44.82 +
   44.83 +        @Override
   44.84 +        public String toString() {
   44.85 +            final String sl = getSourceLevel();
   44.86 +            return sl == null ? "" : sl; //NOI18M
   44.87 +        }
   44.88 +
   44.89 +    }
   44.90 +
   44.91 +}
    45.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    45.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/PythonSources.java	Tue Feb 24 01:58:36 2015 -0800
    45.3 @@ -0,0 +1,45 @@
    45.4 +/*
    45.5 + * To change this license header, choose License Headers in Project Properties.
    45.6 + * To change this template file, choose Tools | Templates
    45.7 + * and open the template in the editor.
    45.8 + */
    45.9 +package org.netbeans.modules.python.project2;
   45.10 +
   45.11 +import javax.swing.event.ChangeListener;
   45.12 +import org.netbeans.api.project.SourceGroup;
   45.13 +import org.netbeans.api.project.Sources;
   45.14 +import org.netbeans.spi.project.support.GenericSources;
   45.15 +import org.openide.filesystems.FileObject;
   45.16 +
   45.17 +/**
   45.18 + *
   45.19 + * @author Ralph Benjamin Ruijs <ralphbenjamin@netbeans.org>
   45.20 + */
   45.21 +public class PythonSources implements Sources {
   45.22 +    private final PythonProject2 project;
   45.23 +    private SourceGroup[] roots;
   45.24 +    
   45.25 +
   45.26 +    public PythonSources(PythonProject2 project) {
   45.27 +        this.project = project;
   45.28 +    }
   45.29 +
   45.30 +    @Override
   45.31 +    public SourceGroup[] getSourceGroups(String type) {
   45.32 +        synchronized(this) {
   45.33 +            if(roots == null) {
   45.34 +                FileObject fo = project.getProjectDirectory();
   45.35 +                roots = new SourceGroup[]{GenericSources.group(project, fo, fo.getPath(), "Source Packages", null, null)};
   45.36 +            }
   45.37 +        }
   45.38 +        return roots;
   45.39 +    }
   45.40 +
   45.41 +    @Override
   45.42 +    public void addChangeListener(ChangeListener listener) {
   45.43 +    }
   45.44 +
   45.45 +    @Override
   45.46 +    public void removeChangeListener(ChangeListener listener) {
   45.47 +    }
   45.48 +}
    46.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    46.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/classpath/BootClassPathImplementation.java	Tue Feb 24 01:58:36 2015 -0800
    46.3 @@ -0,0 +1,127 @@
    46.4 +/*
    46.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    46.6 + *
    46.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    46.8 + *
    46.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   46.10 + * Other names may be trademarks of their respective owners.
   46.11 + *
   46.12 + * The contents of this file are subject to the terms of either the GNU
   46.13 + * General Public License Version 2 only ("GPL") or the Common
   46.14 + * Development and Distribution License("CDDL") (collectively, the
   46.15 + * "License"). You may not use this file except in compliance with the
   46.16 + * License. You can obtain a copy of the License at
   46.17 + * http://www.netbeans.org/cddl-gplv2.html
   46.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   46.19 + * specific language governing permissions and limitations under the
   46.20 + * License.  When distributing the software, include this License Header
   46.21 + * Notice in each file and include the License file at
   46.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   46.23 + * particular file as subject to the "Classpath" exception as provided
   46.24 + * by Oracle in the GPL Version 2 section of the License file that
   46.25 + * accompanied this code. If applicable, add the following below the
   46.26 + * License Header, with the fields enclosed by brackets [] replaced by
   46.27 + * your own identifying information:
   46.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   46.29 + *
   46.30 + * Contributor(s):
   46.31 + *
   46.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   46.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   46.34 + * Microsystems, Inc. All Rights Reserved.
   46.35 + *
   46.36 + * If you wish your version of this file to be governed by only the CDDL
   46.37 + * or only the GPL Version 2, indicate your decision by adding
   46.38 + * "[Contributor] elects to include this software in this distribution
   46.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   46.40 + * single choice of license, a recipient has the option to distribute
   46.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   46.42 + * to extend the choice of license to its licensees as provided above.
   46.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   46.44 + * Version 2 license, then the option applies only if the new code is
   46.45 + * made subject to such option by the copyright holder.
   46.46 + */
   46.47 +package org.netbeans.modules.python.project2.classpath;
   46.48 +
   46.49 +import java.beans.PropertyChangeEvent;
   46.50 +import java.beans.PropertyChangeListener;
   46.51 +import java.beans.PropertyChangeSupport;
   46.52 +import java.net.URL;
   46.53 +import java.util.List;
   46.54 +import java.util.ArrayList;
   46.55 +import java.util.Collections;
   46.56 +import org.netbeans.modules.python.api.PythonPlatform;
   46.57 +import org.netbeans.modules.python.api.PythonPlatformManager;
   46.58 +import org.netbeans.modules.python.project2.PythonProject2;
   46.59 +import org.netbeans.spi.java.classpath.ClassPathImplementation;
   46.60 +import org.netbeans.spi.java.classpath.PathResourceImplementation;
   46.61 +import org.netbeans.spi.java.classpath.support.ClassPathSupport;
   46.62 +import org.openide.util.Parameters;
   46.63 +
   46.64 +final class BootClassPathImplementation implements ClassPathImplementation, PropertyChangeListener {
   46.65 +    private List<PathResourceImplementation> resourcesCache;
   46.66 +    private final PropertyChangeSupport support = new PropertyChangeSupport(this);
   46.67 +    private final PythonProject2 project;
   46.68 +
   46.69 +    public BootClassPathImplementation (final PythonProject2 project) {
   46.70 +        assert project != null;
   46.71 +        this.project = project;
   46.72 +//        this.eval.addPropertyChangeListener(WeakListeners.propertyChange(this, this.eval));
   46.73 +    }
   46.74 +
   46.75 +    @Override
   46.76 +    public synchronized List<PathResourceImplementation> getResources() {
   46.77 +        if (this.resourcesCache == null) {
   46.78 +            List<URL> urls = getUrls(project);
   46.79 +            List<PathResourceImplementation> result = new ArrayList<>(1);
   46.80 +            for (URL url : urls) {
   46.81 +                result.add(ClassPathSupport.createResource(url));
   46.82 +            }
   46.83 +            resourcesCache = Collections.unmodifiableList(result);
   46.84 +        }
   46.85 +        return this.resourcesCache;
   46.86 +    }
   46.87 +
   46.88 +    @Override
   46.89 +    public void addPropertyChangeListener(PropertyChangeListener listener) {
   46.90 +        Parameters.notNull("listener", listener);
   46.91 +        this.support.addPropertyChangeListener (listener);
   46.92 +    }
   46.93 +
   46.94 +    @Override
   46.95 +    public void removePropertyChangeListener(PropertyChangeListener listener) {
   46.96 +        Parameters.notNull("listener", listener);
   46.97 +        this.support.removePropertyChangeListener (listener);
   46.98 +    }
   46.99 +
  46.100 +    @Override
  46.101 +    public void propertyChange(final PropertyChangeEvent evt) {
  46.102 +//        if (evt.getSource() == this.eval &&
  46.103 +//            (evt.getPropertyName() == null || evt.getPropertyName().equals(PythonProjectProperties.ACTIVE_PLATFORM))) {
  46.104 +//            //Active platform was changed
  46.105 +//            RequestProcessor.getDefault().post(new Runnable() {
  46.106 +//                @Override
  46.107 +//              public void run() {
  46.108 +//                resetCache ();
  46.109 +//              }
  46.110 +//            }) ;
  46.111 +//        }
  46.112 +    }
  46.113 +    
  46.114 +    private void resetCache () {
  46.115 +        synchronized (this) {
  46.116 +            resourcesCache = null;
  46.117 +        }
  46.118 +        support.firePropertyChange(PROP_RESOURCES, null, null);
  46.119 +    }
  46.120 +
  46.121 +    private List<URL> getUrls(PythonProject2 project) {
  46.122 +        PythonPlatform activePlatform = project.getActivePlatform();
  46.123 +        if (activePlatform == null) {
  46.124 +            final PythonPlatformManager manager = PythonPlatformManager.getInstance();
  46.125 +            final String platformName = manager.getDefaultPlatform();
  46.126 +            activePlatform = manager.getPlatform(platformName);
  46.127 +        }
  46.128 +        return activePlatform.getUrls();
  46.129 +    }
  46.130 +}
    47.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    47.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/classpath/ClassPathProviderImpl.java	Tue Feb 24 01:58:36 2015 -0800
    47.3 @@ -0,0 +1,205 @@
    47.4 +/*
    47.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    47.6 + *
    47.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    47.8 + *
    47.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   47.10 + * Other names may be trademarks of their respective owners.
   47.11 + *
   47.12 + * The contents of this file are subject to the terms of either the GNU
   47.13 + * General Public License Version 2 only ("GPL") or the Common
   47.14 + * Development and Distribution License("CDDL") (collectively, the
   47.15 + * "License"). You may not use this file except in compliance with the
   47.16 + * License. You can obtain a copy of the License at
   47.17 + * http://www.netbeans.org/cddl-gplv2.html
   47.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   47.19 + * specific language governing permissions and limitations under the
   47.20 + * License.  When distributing the software, include this License Header
   47.21 + * Notice in each file and include the License file at
   47.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   47.23 + * particular file as subject to the "Classpath" exception as provided
   47.24 + * by Oracle in the GPL Version 2 section of the License file that
   47.25 + * accompanied this code. If applicable, add the following below the
   47.26 + * License Header, with the fields enclosed by brackets [] replaced by
   47.27 + * your own identifying information:
   47.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   47.29 + *
   47.30 + * Contributor(s):
   47.31 + *
   47.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   47.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
   47.34 + * Microsystems, Inc. All Rights Reserved.
   47.35 + *
   47.36 + * If you wish your version of this file to be governed by only the CDDL
   47.37 + * or only the GPL Version 2, indicate your decision by adding
   47.38 + * "[Contributor] elects to include this software in this distribution
   47.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   47.40 + * single choice of license, a recipient has the option to distribute
   47.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   47.42 + * to extend the choice of license to its licensees as provided above.
   47.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   47.44 + * Version 2 license, then the option applies only if the new code is
   47.45 + * made subject to such option by the copyright holder.
   47.46 + */
   47.47 +package org.netbeans.modules.python.project2.classpath;
   47.48 +
   47.49 +import java.util.HashMap;
   47.50 +import java.util.Map;
   47.51 +import org.netbeans.api.java.classpath.ClassPath;
   47.52 +import org.netbeans.api.project.ProjectUtils;
   47.53 +import org.netbeans.api.project.SourceGroup;
   47.54 +import org.netbeans.api.project.Sources;
   47.55 +import org.netbeans.modules.python.project2.PythonProject2;
   47.56 +import org.netbeans.spi.java.classpath.ClassPathFactory;
   47.57 +import org.netbeans.spi.java.classpath.ClassPathImplementation;
   47.58 +import org.netbeans.spi.java.classpath.ClassPathProvider;
   47.59 +import org.netbeans.spi.java.classpath.support.ClassPathSupport;
   47.60 +import org.openide.filesystems.FileObject;
   47.61 +import org.openide.filesystems.FileUtil;
   47.62 +import org.openide.util.Pair;
   47.63 +
   47.64 +/**
   47.65 + * Defines various paths for the Python Project.
   47.66 + * Based on the Ruby project, in turn based on the J2SE project.
   47.67 + * Greatly simplified at the moment since Python projects don't have a Sources object,
   47.68 + * and there's no separate source/test folders.
   47.69 + * @author Tor Norbye
   47.70 + * @author Tomas Zezula
   47.71 + */
   47.72 +public final class ClassPathProviderImpl implements ClassPathProvider {
   47.73 +    private final PythonProject2 project;
   47.74 +    private final Sources sources;
   47.75 +    private final Map<Pair<String,Integer>,ClassPath> cache = new HashMap<>();
   47.76 +
   47.77 +    public ClassPathProviderImpl(final PythonProject2 project, Sources sources) {
   47.78 +        assert project != null;
   47.79 +        this.project = project;
   47.80 +        this.sources = sources;
   47.81 +        assert this.sources != null;
   47.82 +    }
   47.83 +
   47.84 +    private static final int MAX_TYPES = 3;
   47.85 +    /**
   47.86 +     * Find what a given file represents.
   47.87 +     * @param file a file in the project
   47.88 +     * @return one of: <dl>
   47.89 +     *         <dt>0</dt> <dd>normal source</dd>
   47.90 +     *         <dt>1</dt> <dd>test source</dd>
   47.91 +     *         <dt>2</dt> <dd>the project root</dd>
   47.92 +     *         <dt>-1</dt> <dd>something else</dd>
   47.93 +     *         </dl>
   47.94 +     */
   47.95 +    private int getType(FileObject file) {
   47.96 +        if (file == project.getProjectDirectory()) {
   47.97 +            return 2;
   47.98 +        }
   47.99 +        for (SourceGroup sourceGroup : sources.getSourceGroups(PythonProject2.SOURCES_TYPE_PYTHON)) {
  47.100 +            FileObject root = sourceGroup.getRootFolder();
  47.101 +            if (root.equals(file) || FileUtil.isParentOf(root, file)) {
  47.102 +                return 0;
  47.103 +            }
  47.104 +        }
  47.105 +        return -1;
  47.106 +    }
  47.107 +
  47.108 +    private synchronized ClassPath getSourcepath(FileObject file) {
  47.109 +        int type = getType(file);
  47.110 +        return this.getSourcepath(type);
  47.111 +    }
  47.112 +
  47.113 +    private ClassPath getSourcepath(int type) {
  47.114 +        if (type < 0 || type > MAX_TYPES) {
  47.115 +            return null;
  47.116 +        }
  47.117 +        final Pair<String,Integer> key = Pair.of(ClassPath.SOURCE, type);
  47.118 +        ClassPath cp = cache.get(key);
  47.119 +        if (cp == null) {
  47.120 +            switch (type) {
  47.121 +                case 0:
  47.122 +                    cp = ClassPathFactory.createClassPath(new SourcePathImplementation(sources));
  47.123 +                    break;
  47.124 +//                case 1:
  47.125 +//                    cp = ClassPathFactory.createClassPath(new SourcePathImplementation(tests));
  47.126 +//                    break;
  47.127 +                case 2:
  47.128 +                    // Classpath for the "whole project" - for now just use the sources
  47.129 +                    // Used from the tasklist for example.
  47.130 +                    cp = ClassPathFactory.createClassPath(new SourcePathImplementation(sources));
  47.131 +                    break;
  47.132 +
  47.133 +                default:
  47.134 +                    throw new UnsupportedOperationException("Only sources are available in the Python project at this point");
  47.135 +           }
  47.136 +           cache.put (key,cp);
  47.137 +        }
  47.138 +        return cp;
  47.139 +    }
  47.140 +
  47.141 +    private synchronized ClassPath getBootClassPath() {        
  47.142 +        final Pair<String,Integer> key = Pair.of(ClassPath.BOOT, 0);
  47.143 +        ClassPath cp = cache.get(key);
  47.144 +        if (cp == null) {
  47.145 +            //todo: For now merge compile and platform class paths
  47.146 +            //under parsing api they should be separated
  47.147 +            final ClassPathImplementation boot = new BootClassPathImplementation(project);
  47.148 +            final ClassPathImplementation compile = new CompilePathImplementation(project);
  47.149 +            cp = ClassPathFactory.createClassPath(ClassPathSupport.createProxyClassPathImplementation(boot,compile));
  47.150 +           cache.put (key,cp);
  47.151 +        }        
  47.152 +        return cp;
  47.153 +    }
  47.154 +
  47.155 +//    private synchronized ClassPath getCompileClassPath() {
  47.156 +//        final Pair<String,Integer> key = Pair.of(ClassPath.COMPILE, 0);
  47.157 +//        ClassPath cp = cache.get(key);
  47.158 +//        if (cp == null) {
  47.159 +//            cp = ClassPathFactory.createClassPath(new CompilePathImplementation(this.project));
  47.160 +//           cache.put (key,cp);
  47.161 +//        }
  47.162 +//        return cp;
  47.163 +//    }
  47.164 +
  47.165 +    @Override
  47.166 +    public ClassPath findClassPath(FileObject file, String type) {
  47.167 +        if (type.equals(ClassPath.SOURCE)) {
  47.168 +            return getSourcepath(file);
  47.169 +        } else if (type.equals(ClassPath.BOOT)) {
  47.170 +            return getBootClassPath();
  47.171 +        } else if (type.equals(ClassPath.COMPILE)) {
  47.172 +            // Bogus
  47.173 +            return getBootClassPath();
  47.174 +        } else {
  47.175 +            return null;
  47.176 +        }
  47.177 +    }
  47.178 +
  47.179 +    /**
  47.180 +     * Returns array of all classpaths of the given type in the project.
  47.181 +     * The result is used for example for GlobalPathRegistry registrations.
  47.182 +     */
  47.183 +    public ClassPath[] getProjectClassPaths(String type) {
  47.184 +        if (ClassPath.BOOT.equals(type)) {
  47.185 +            return new ClassPath[]{getBootClassPath()};
  47.186 +        }
  47.187 +        if (ClassPath.SOURCE.equals(type)) {
  47.188 +            ClassPath[] l = new ClassPath[1];
  47.189 +            l[0] = getSourcepath(0);
  47.190 +            return l;
  47.191 +        }
  47.192 +        return null;
  47.193 +    }
  47.194 +
  47.195 +    /**
  47.196 +     * Returns the given type of the classpath for the project sources
  47.197 +     * (i.e., excluding tests roots). Valid types are BOOT, SOURCE and COMPILE.
  47.198 +     */
  47.199 +    public ClassPath getProjectSourcesClassPath(String type) {
  47.200 +        if (ClassPath.BOOT.equals(type)) {
  47.201 +             return getBootClassPath();
  47.202 +        }
  47.203 +        if (ClassPath.SOURCE.equals(type)) {
  47.204 +            return getSourcepath(0);
  47.205 +        }
  47.206 +        return null;
  47.207 +    }            
  47.208 +}
    48.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    48.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/classpath/CompilePathImplementation.java	Tue Feb 24 01:58:36 2015 -0800
    48.3 @@ -0,0 +1,147 @@
    48.4 +/*
    48.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    48.6 + *
    48.7 + * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
    48.8 + *
    48.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   48.10 + * Other names may be trademarks of their respective owners.
   48.11 + *
   48.12 + * The contents of this file are subject to the terms of either the GNU
   48.13 + * General Public License Version 2 only ("GPL") or the Common
   48.14 + * Development and Distribution License("CDDL") (collectively, the
   48.15 + * "License"). You may not use this file except in compliance with the
   48.16 + * License. You can obtain a copy of the License at
   48.17 + * http://www.netbeans.org/cddl-gplv2.html
   48.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   48.19 + * specific language governing permissions and limitations under the
   48.20 + * License.  When distributing the software, include this License Header
   48.21 + * Notice in each file and include the License file at
   48.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   48.23 + * particular file as subject to the "Classpath" exception as provided
   48.24 + * by Oracle in the GPL Version 2 section of the License file that
   48.25 + * accompanied this code. If applicable, add the following below the
   48.26 + * License Header, with the fields enclosed by brackets [] replaced by
   48.27 + * your own identifying information:
   48.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   48.29 + *
   48.30 + * If you wish your version of this file to be governed by only the CDDL
   48.31 + * or only the GPL Version 2, indicate your decision by adding
   48.32 + * "[Contributor] elects to include this software in this distribution
   48.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   48.34 + * single choice of license, a recipient has the option to distribute
   48.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   48.36 + * to extend the choice of license to its licensees as provided above.
   48.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   48.38 + * Version 2 license, then the option applies only if the new code is
   48.39 + * made subject to such option by the copyright holder.
   48.40 + *
   48.41 + * Contributor(s):
   48.42 + *
   48.43 + * Portions Copyrighted 2008 Sun Microsystems, Inc.
   48.44 + */
   48.45 +package org.netbeans.modules.python.project2.classpath;
   48.46 +
   48.47 +import java.beans.PropertyChangeEvent;
   48.48 +import java.beans.PropertyChangeListener;
   48.49 +import java.beans.PropertyChangeSupport;
   48.50 +import java.io.File;
   48.51 +import java.util.ArrayList;
   48.52 +import java.util.Collections;
   48.53 +import java.util.List;
   48.54 +import java.util.concurrent.atomic.AtomicBoolean;
   48.55 +import org.netbeans.api.project.ProjectManager;
   48.56 +import org.netbeans.modules.python.project2.PythonProject2;
   48.57 +import org.netbeans.spi.java.classpath.ClassPathImplementation;
   48.58 +import org.netbeans.spi.java.classpath.PathResourceImplementation;
   48.59 +import org.openide.filesystems.FileObject;
   48.60 +import org.openide.filesystems.FileUtil;
   48.61 +import org.openide.util.Parameters;
   48.62 +
   48.63 +/**
   48.64 + *
   48.65 + * @author Tomas Zezula
   48.66 + */
   48.67 +public final class CompilePathImplementation implements ClassPathImplementation, PropertyChangeListener, Runnable {
   48.68 +
   48.69 +    private final PropertyChangeSupport support = new PropertyChangeSupport(this);
   48.70 +    private final File projectFolder;
   48.71 +    private List<PathResourceImplementation> resources;
   48.72 +    private AtomicBoolean dirty = new AtomicBoolean();
   48.73 +
   48.74 +    CompilePathImplementation(final PythonProject2 project) {
   48.75 +        assert project != null;
   48.76 +        FileObject fo = project.getProjectDirectory();;
   48.77 +        assert fo != null;
   48.78 +        this.projectFolder = FileUtil.toFile(fo);
   48.79 +        assert projectFolder != null;
   48.80 +        this.resources = this.getPath();
   48.81 +    }
   48.82 +
   48.83 +    @Override
   48.84 +    public synchronized List<PathResourceImplementation> getResources() {
   48.85 +        assert this.resources != null;
   48.86 +        return this.resources;
   48.87 +    }
   48.88 +
   48.89 +    @Override
   48.90 +    public void addPropertyChangeListener(final PropertyChangeListener listener) {
   48.91 +        Parameters.notNull("listener", listener);
   48.92 +        support.addPropertyChangeListener(listener);
   48.93 +    }
   48.94 +
   48.95 +    @Override
   48.96 +    public void removePropertyChangeListener(PropertyChangeListener listener) {
   48.97 +        Parameters.notNull("listener", listener);
   48.98 +        support.removePropertyChangeListener(listener);
   48.99 +    }
  48.100 +
  48.101 +    @Override
  48.102 +    public void propertyChange(final PropertyChangeEvent evt) {
  48.103 +        String prop = evt.getPropertyName();
  48.104 +//        if (prop != null && !PythonProjectProperties.PYTHON_LIB_PATH.equals(evt.getPropertyName())) {
  48.105 +//            // Not interesting to us.
  48.106 +//            return;
  48.107 +//        }
  48.108 +        // Coalesce changes; can come in fast after huge CP changes (#47910):
  48.109 +        if (!dirty.getAndSet(true)) {
  48.110 +            ProjectManager.mutex().postReadRequest(this);
  48.111 +        }
  48.112 +    }
  48.113 +
  48.114 +    @Override
  48.115 +    public void run() {
  48.116 +        dirty.set(false);
  48.117 +        List<PathResourceImplementation> newRoots = getPath();
  48.118 +        boolean fire = false;
  48.119 +        synchronized (this) {
  48.120 +            if (!this.resources.equals(newRoots)) {
  48.121 +                this.resources = newRoots;
  48.122 +                fire = true;
  48.123 +            }
  48.124 +        }
  48.125 +        if (fire) {
  48.126 +            support.firePropertyChange(PROP_RESOURCES, null, null);
  48.127 +        }
  48.128 +    }
  48.129 +
  48.130 +    private List<PathResourceImplementation> getPath() {
  48.131 +        List<PathResourceImplementation> result = new ArrayList<>();
  48.132 +
  48.133 +//        String prop = evaluator.getProperty(PythonProjectProperties.PYTHON_LIB_PATH);
  48.134 +//        if (prop != null) {
  48.135 +//            //todo: Use PropertyUtil
  48.136 +//            final StringTokenizer tokenizer = new StringTokenizer(prop, "|");   //NOI18N
  48.137 +//            while (tokenizer.hasMoreTokens()) {
  48.138 +//                String piece = tokenizer.nextToken();
  48.139 +//                File f = PropertyUtils.resolveFile(this.projectFolder, piece);
  48.140 +//                URL entry = FileUtil.urlForArchiveOrDir(f);
  48.141 +//                if (entry != null) {
  48.142 +//                    result.add(ClassPathSupport.createResource(entry));
  48.143 +//                } else {
  48.144 +//                    Logger.getLogger(CompilePathImplementation.class.getName()).warning(f + " does not look like a valid archive file");
  48.145 +//                }
  48.146 +//            }
  48.147 +//        }
  48.148 +        return Collections.unmodifiableList(result);
  48.149 +    }
  48.150 +}
    49.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    49.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/classpath/SourcePathImplementation.java	Tue Feb 24 01:58:36 2015 -0800
    49.3 @@ -0,0 +1,109 @@
    49.4 +/*
    49.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    49.6 + *
    49.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    49.8 + *
    49.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   49.10 + * Other names may be trademarks of their respective owners.
   49.11 + *
   49.12 + * The contents of this file are subject to the terms of either the GNU
   49.13 + * General Public License Version 2 only ("GPL") or the Common
   49.14 + * Development and Distribution License("CDDL") (collectively, the
   49.15 + * "License"). You may not use this file except in compliance with the
   49.16 + * License. You can obtain a copy of the License at
   49.17 + * http://www.netbeans.org/cddl-gplv2.html
   49.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   49.19 + * specific language governing permissions and limitations under the
   49.20 + * License.  When distributing the software, include this License Header
   49.21 + * Notice in each file and include the License file at
   49.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   49.23 + * particular file as subject to the "Classpath" exception as provided
   49.24 + * by Oracle in the GPL Version 2 section of the License file that
   49.25 + * accompanied this code. If applicable, add the following below the
   49.26 + * License Header, with the fields enclosed by brackets [] replaced by
   49.27 + * your own identifying information:
   49.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   49.29 + *
   49.30 + * Contributor(s):
   49.31 + *
   49.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   49.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   49.34 + * Microsystems, Inc. All Rights Reserved.
   49.35 + *
   49.36 + * If you wish your version of this file to be governed by only the CDDL
   49.37 + * or only the GPL Version 2, indicate your decision by adding
   49.38 + * "[Contributor] elects to include this software in this distribution
   49.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   49.40 + * single choice of license, a recipient has the option to distribute
   49.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   49.42 + * to extend the choice of license to its licensees as provided above.
   49.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   49.44 + * Version 2 license, then the option applies only if the new code is
   49.45 + * made subject to such option by the copyright holder.
   49.46 + */
   49.47 +package org.netbeans.modules.python.project2.classpath;
   49.48 +
   49.49 +import java.beans.PropertyChangeEvent;
   49.50 +import java.util.List;
   49.51 +import java.util.ArrayList;
   49.52 +import java.util.Collections;
   49.53 +import java.beans.PropertyChangeListener;
   49.54 +import java.beans.PropertyChangeSupport;
   49.55 +import org.netbeans.api.project.SourceGroup;
   49.56 +import org.netbeans.api.project.Sources;
   49.57 +import org.netbeans.modules.python.project2.PythonProject2;
   49.58 +import org.netbeans.spi.java.classpath.ClassPathImplementation;
   49.59 +import org.netbeans.spi.java.classpath.PathResourceImplementation;
   49.60 +import org.netbeans.spi.java.classpath.support.ClassPathSupport;
   49.61 +
   49.62 +/**
   49.63 + * Source class path implementation
   49.64 + * @author Tor Norbye
   49.65 + * @author Tomas Zezula
   49.66 + */
   49.67 +final class SourcePathImplementation implements ClassPathImplementation, PropertyChangeListener {
   49.68 +    private final PropertyChangeSupport support = new PropertyChangeSupport(this);
   49.69 +    private List<PathResourceImplementation> resources;
   49.70 +    private final Sources src;
   49.71 +
   49.72 +    public SourcePathImplementation(Sources sources) {
   49.73 +        assert sources != null;
   49.74 +        this.src = sources;
   49.75 +//        this.src.addPropertyChangeListener(WeakListeners.propertyChange(this, this.src));
   49.76 +    }
   49.77 +
   49.78 +    @Override
   49.79 +    public List<PathResourceImplementation> getResources() {
   49.80 +        synchronized (this) {
   49.81 +            if (this.resources != null) {
   49.82 +                return this.resources;
   49.83 +            }
   49.84 +        }
   49.85 +        final SourceGroup[] urls = this.src.getSourceGroups(PythonProject2.SOURCES_TYPE_PYTHON);
   49.86 +        synchronized (this) {
   49.87 +            if (this.resources == null) {
   49.88 +                List<PathResourceImplementation> result = new ArrayList<>(urls.length);
   49.89 +                for (SourceGroup root : urls) {
   49.90 +                    result.add(ClassPathSupport.createResource(root.getRootFolder().toURL()));
   49.91 +                }
   49.92 +                this.resources = Collections.unmodifiableList(result);
   49.93 +            }
   49.94 +            return this.resources;
   49.95 +        }
   49.96 +    }
   49.97 +
   49.98 +    @Override
   49.99 +    public void addPropertyChangeListener(PropertyChangeListener listener) {
  49.100 +        support.addPropertyChangeListener (listener);
  49.101 +    }
  49.102 +
  49.103 +    @Override
  49.104 +    public void removePropertyChangeListener(PropertyChangeListener listener) {
  49.105 +        support.removePropertyChangeListener (listener);
  49.106 +    }
  49.107 +
  49.108 +    @Override
  49.109 +    public synchronized void propertyChange(PropertyChangeEvent evt) {
  49.110 +       this.resources = null;
  49.111 +    }
  49.112 +}
    50.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    50.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/layer.xml	Tue Feb 24 01:58:36 2015 -0800
    50.3 @@ -0,0 +1,39 @@
    50.4 +<?xml version="1.0" encoding="UTF-8"?>
    50.5 +<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
    50.6 +<filesystem>
    50.7 +    <folder name="Projects">
    50.8 +        <folder name="Actions"/>
    50.9 +
   50.10 +        <folder name="org-netbeans-modules-python-project2">
   50.11 +            <folder name="Nodes">
   50.12 +                <file name="org-netbeans-modules-python-project2-ui-SourceNodeFactory.instance">
   50.13 +                    <attr name="position" intvalue="200"/>
   50.14 +                </file>
   50.15 +            </folder>
   50.16 +
   50.17 +            <folder name="Customizer">
   50.18 +                <file name="PythonPath.instance">
   50.19 +                    <attr name="instanceCreate" methodvalue="org.netbeans.modules.python.project2.ui.customizer.CompositePanelProviderImpl.createPythonPath"/>
   50.20 +                    <attr name="position" intvalue="200"/>
   50.21 +                </file>
   50.22 +                <file name="Run.instance">
   50.23 +                    <attr name="instanceCreate" methodvalue="org.netbeans.modules.python.project2.ui.customizer.CompositePanelProviderImpl.createRunConfig"/>
   50.24 +                    <attr name="position" intvalue="300"/>
   50.25 +                </file>
   50.26 +                <file name="Formatting.instance">
   50.27 +                    <attr name="instanceOf" stringvalue="org.netbeans.spi.project.ui.support.ProjectCustomizer$CompositeCategoryProvider"/>
   50.28 +                    <attr name="instanceCreate" methodvalue="org.netbeans.modules.options.indentation.FormattingCustomizerPanel.createCategoryProvider"/>
   50.29 +                    <attr name="allowedMimeTypes" stringvalue="text/x-python"/>
   50.30 +                    <attr name="position" intvalue="1000"/>
   50.31 +                </file>
   50.32 +            </folder>
   50.33 +        </folder>
   50.34 +    </folder>
   50.35 +    <folder name="Templates">
   50.36 +        <folder name="Project">
   50.37 +            <folder name="Python">
   50.38 +                <attr name="position" intvalue="1412"/>
   50.39 +            </folder>
   50.40 +        </folder>
   50.41 +    </folder>
   50.42 +</filesystem>
    51.1 Binary file python.project2/src/org/netbeans/modules/python/project2/resources/brokenProjectBadge.gif has changed
    52.1 Binary file python.project2/src/org/netbeans/modules/python/project2/resources/package.gif has changed
    53.1 Binary file python.project2/src/org/netbeans/modules/python/project2/resources/packageBadge.gif has changed
    54.1 Binary file python.project2/src/org/netbeans/modules/python/project2/resources/packageEmpty.gif has changed
    55.1 Binary file python.project2/src/org/netbeans/modules/python/project2/resources/packagePrivate.gif has changed
    56.1 Binary file python.project2/src/org/netbeans/modules/python/project2/resources/packagePublic.gif has changed
    57.1 Binary file python.project2/src/org/netbeans/modules/python/project2/resources/py_25_16.png has changed
    58.1 Binary file python.project2/src/org/netbeans/modules/python/project2/resources/sourceBadge.gif has changed
    59.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    59.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/templates/Bundle.properties	Tue Feb 24 01:58:36 2015 -0800
    59.3 @@ -0,0 +1,57 @@
    59.4 +# Python project wizards
    59.5 +LBL_IteratorName={0} of {1}
    59.6 +LBL_NewPythonProjectWizardIterator_WizardProgress_CreatingProject=Creating new project
    59.7 +LBL_NewPythonProjectWizardIterator_WizardProgress_PreparingToOpen=Preparing new project to be opened
    59.8 +TXT_PythonProject=Python Project
    59.9 +TXT_ExistingPythonProject=Python Project with Existing Sources
   59.10 +LBL_ProjectNameLocation=Name and Location
   59.11 +LBL_ProjectSources=Existing Sources
   59.12 +
   59.13 +#PanelOptionsVisual
   59.14 +TXT_MainFileName={0}.py
   59.15 +ERROR_IllegalMainFileName=Illegal main file name.
   59.16 +LBL_setAsMainCheckBox=Set as &Main Project
   59.17 +ACSD_setAsMainCheckBox=N/A
   59.18 +ACSN_setAsMainCheckBox=N/A
   59.19 +ACSN_createMainCheckBox=Create Main Class
   59.20 +ACSD_createMainCheckBox=Select checkbox to create main class in project.
   59.21 +LBL_createMainCheckBox=&Create Main File
   59.22 +ASCN_mainClassTextFiled=N/A
   59.23 +ASCD_mainClassTextFiled=N/A
   59.24 +ACSN_PanelOptionsVisual=N/A
   59.25 +ACSD_PanelOptionsVisual=N/A
   59.26 +
   59.27 +#PanelConfigureProjectVisual
   59.28 +TXT_NameAndLoc=Name and Location
   59.29 +TXT_NewPythonApp=New Python Application
   59.30 +ACSD_NewPythonApp=New Python Application
   59.31 +ACSN_locationContainer=N/A
   59.32 +ACSD_locationContainer=N/A
   59.33 +ACSN_optionsContainer=N/A
   59.34 +ACSD_optionsContainer=N/A
   59.35 +TXT_PythonPlatform=&Python Platform:
   59.36 +ERROR_IllegalPlatform=Wrong Python Platform
   59.37 +TXT_ManagePlatfroms=&Manage...
   59.38 +ACSD_ExistingPythonProject=N/A
   59.39 +
   59.40 +#PanelConfigureSourcesVisual
   59.41 +LBL_SelectSources=Select Source Folder
   59.42 +LBL_SelectTests=Select Test Folder
   59.43 +
   59.44 +ExistingPythonProjectPanelVisual.projectName.text=Project &Name:
   59.45 +ExistingPythonProjectPanelVisual.projectFolder.text=Project &Folder:
   59.46 +ExistingPythonProjectPanelVisual.browse.text=Br&owse
   59.47 +LBL_SelectProjectFolder=Select project folder
   59.48 +
   59.49 +#Errors
   59.50 +ERR_WrongName=Project Name is not a valid folder name.
   59.51 +ERR_WrongProjectFolder=Project Folder is not a valid path.
   59.52 +ERR_ReadOnlyProjectFolder=Project Folder cannot be created.
   59.53 +ERR_AlreadyAnProject="There is already a NetBeans project in the project folder."
   59.54 +PanelConfigureSourcesVisual.jLabel1.text=Specify the folders containing sources and tests.
   59.55 +PanelConfigureSourcesVisual.jLabel2.text=&Source Root Folders
   59.56 +PanelConfigureSourcesVisual.jLabel3.text=&Test Root Folders
   59.57 +PanelConfigureSourcesVisual.addSource.text=&Add Folder...
   59.58 +PanelConfigureSourcesVisual.removeSource.text=&Remove
   59.59 +PanelConfigureSourcesVisual.addTest.text=Add F&older...
   59.60 +PanelConfigureSourcesVisual.removeTest.text=Re&move
    60.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    60.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/templates/EmptyPythonProjectDescription.html	Tue Feb 24 01:58:36 2015 -0800
    60.3 @@ -0,0 +1,12 @@
    60.4 +<!--
    60.5 +To change this template, choose Tools | Templates
    60.6 +and open the template in the editor.
    60.7 +-->
    60.8 +<html>
    60.9 +    <head>
   60.10 +        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   60.11 +    </head>
   60.12 +    <body>
   60.13 +        New Empty Python Project using setuptools
   60.14 +    </body>
   60.15 +</html>
    61.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    61.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/templates/EmptyPythonProjectPanelVisual.form	Tue Feb 24 01:58:36 2015 -0800
    61.3 @@ -0,0 +1,112 @@
    61.4 +<?xml version="1.0" encoding="UTF-8" ?>
    61.5 +
    61.6 +<Form version="1.3" maxVersion="1.3" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
    61.7 +  <AuxValues>
    61.8 +    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
    61.9 +    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
   61.10 +    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
   61.11 +    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
   61.12 +    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
   61.13 +    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
   61.14 +    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
   61.15 +    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
   61.16 +    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
   61.17 +  </AuxValues>
   61.18 +
   61.19 +  <Layout>
   61.20 +    <DimensionLayout dim="0">
   61.21 +      <Group type="103" groupAlignment="0" attributes="0">
   61.22 +          <Group type="102" attributes="0">
   61.23 +              <Group type="103" groupAlignment="0" attributes="0">
   61.24 +                  <Component id="projectLocationLabel" min="-2" max="-2" attributes="0"/>
   61.25 +                  <Component id="createdFolderLabel" alignment="0" min="-2" max="-2" attributes="0"/>
   61.26 +                  <Component id="projectNameLabel" min="-2" max="-2" attributes="0"/>
   61.27 +              </Group>
   61.28 +              <EmptySpace max="-2" attributes="0"/>
   61.29 +              <Group type="103" groupAlignment="0" attributes="0">
   61.30 +                  <Group type="102" attributes="0">
   61.31 +                      <Component id="projectLocationTextField" pref="238" max="32767" attributes="0"/>
   61.32 +                      <EmptySpace max="-2" attributes="0"/>
   61.33 +                      <Component id="browseButton" min="-2" max="-2" attributes="0"/>
   61.34 +                  </Group>
   61.35 +                  <Component id="projectNameTextField" pref="343" max="32767" attributes="0"/>
   61.36 +                  <Component id="createdFolderTextField" alignment="0" pref="343" max="32767" attributes="0"/>
   61.37 +              </Group>
   61.38 +          </Group>
   61.39 +      </Group>
   61.40 +    </DimensionLayout>
   61.41 +    <DimensionLayout dim="1">
   61.42 +      <Group type="103" groupAlignment="0" attributes="0">
   61.43 +          <Group type="102" attributes="0">
   61.44 +              <EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
   61.45 +              <Group type="103" groupAlignment="3" attributes="0">
   61.46 +                  <Component id="projectNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   61.47 +                  <Component id="projectNameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   61.48 +              </Group>
   61.49 +              <EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
   61.50 +              <Group type="103" groupAlignment="3" attributes="0">
   61.51 +                  <Component id="projectLocationLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   61.52 +                  <Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
   61.53 +                  <Component id="projectLocationTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   61.54 +              </Group>
   61.55 +              <EmptySpace max="-2" attributes="0"/>
   61.56 +              <Group type="103" groupAlignment="3" attributes="0">
   61.57 +                  <Component id="createdFolderLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   61.58 +                  <Component id="createdFolderTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   61.59 +              </Group>
   61.60 +              <EmptySpace pref="31" max="32767" attributes="0"/>
   61.61 +          </Group>
   61.62 +      </Group>
   61.63 +    </DimensionLayout>
   61.64 +  </Layout>
   61.65 +  <SubComponents>
   61.66 +    <Component class="javax.swing.JLabel" name="projectNameLabel">
   61.67 +      <Properties>
   61.68 +        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
   61.69 +          <ComponentRef name="projectNameTextField"/>
   61.70 +        </Property>
   61.71 +        <Property name="text" type="java.lang.String" value="Project &amp;Name:"/>
   61.72 +      </Properties>
   61.73 +      <AuxValues>
   61.74 +        <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
   61.75 +      </AuxValues>
   61.76 +    </Component>
   61.77 +    <Component class="javax.swing.JTextField" name="projectNameTextField">
   61.78 +    </Component>
   61.79 +    <Component class="javax.swing.JLabel" name="projectLocationLabel">
   61.80 +      <Properties>
   61.81 +        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
   61.82 +          <ComponentRef name="projectLocationTextField"/>
   61.83 +        </Property>
   61.84 +        <Property name="text" type="java.lang.String" value="Project &amp;Location:"/>
   61.85 +      </Properties>
   61.86 +      <AuxValues>
   61.87 +        <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
   61.88 +      </AuxValues>
   61.89 +    </Component>
   61.90 +    <Component class="javax.swing.JTextField" name="projectLocationTextField">
   61.91 +    </Component>
   61.92 +    <Component class="javax.swing.JButton" name="browseButton">
   61.93 +      <Properties>
   61.94 +        <Property name="text" type="java.lang.String" value="Br&amp;owse..."/>
   61.95 +        <Property name="actionCommand" type="java.lang.String" value="BROWSE"/>
   61.96 +      </Properties>
   61.97 +      <Events>
   61.98 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
   61.99 +      </Events>
  61.100 +    </Component>
  61.101 +    <Component class="javax.swing.JLabel" name="createdFolderLabel">
  61.102 +      <Properties>
  61.103 +        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
  61.104 +          <ComponentRef name="createdFolderTextField"/>
  61.105 +        </Property>
  61.106 +        <Property name="text" type="java.lang.String" value="Project &amp;Folder:"/>
  61.107 +      </Properties>
  61.108 +    </Component>
  61.109 +    <Component class="javax.swing.JTextField" name="createdFolderTextField">
  61.110 +      <Properties>
  61.111 +        <Property name="editable" type="boolean" value="false"/>
  61.112 +      </Properties>
  61.113 +    </Component>
  61.114 +  </SubComponents>
  61.115 +</Form>
    62.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    62.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/templates/EmptyPythonProjectPanelVisual.java	Tue Feb 24 01:58:36 2015 -0800
    62.3 @@ -0,0 +1,264 @@
    62.4 +/*
    62.5 + * To change this template, choose Tools | Templates
    62.6 + * and open the template in the editor.
    62.7 + */
    62.8 +
    62.9 +package org.netbeans.modules.python.project2.templates;
   62.10 +
   62.11 +import java.io.File;
   62.12 +import javax.swing.JFileChooser;
   62.13 +import javax.swing.event.DocumentEvent;
   62.14 +import javax.swing.event.DocumentListener;
   62.15 +import javax.swing.text.Document;
   62.16 +import org.netbeans.spi.project.ui.support.ProjectChooser;
   62.17 +import org.openide.WizardDescriptor;
   62.18 +import org.openide.WizardValidationException;
   62.19 +import org.openide.filesystems.FileUtil;
   62.20 +
   62.21 +public final class EmptyPythonProjectPanelVisual extends SettingsPanel implements DocumentListener {
   62.22 +    
   62.23 +
   62.24 +    private final PanelConfigureProject panel;
   62.25 +
   62.26 +    public EmptyPythonProjectPanelVisual(PanelConfigureProject panel) {
   62.27 +        initComponents();
   62.28 +        this.panel = panel;
   62.29 +        // Register listener on the textFields to make the automatic updates
   62.30 +        projectNameTextField.getDocument().addDocumentListener(this);
   62.31 +        projectLocationTextField.getDocument().addDocumentListener(this);
   62.32 +    }
   62.33 +
   62.34 +    public String getProjectName() {
   62.35 +        return this.projectNameTextField.getText();
   62.36 +    }
   62.37 +
   62.38 +    /** This method is called from within the constructor to
   62.39 +     * initialize the form.
   62.40 +     * WARNING: Do NOT modify this code. The content of this method is
   62.41 +     * always regenerated by the Form Editor.
   62.42 +     */
   62.43 +    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
   62.44 +    private void initComponents() {
   62.45 +
   62.46 +        projectNameLabel = new javax.swing.JLabel();
   62.47 +        projectNameTextField = new javax.swing.JTextField();
   62.48 +        projectLocationLabel = new javax.swing.JLabel();
   62.49 +        projectLocationTextField = new javax.swing.JTextField();
   62.50 +        browseButton = new javax.swing.JButton();
   62.51 +        createdFolderLabel = new javax.swing.JLabel();
   62.52 +        createdFolderTextField = new javax.swing.JTextField();
   62.53 +
   62.54 +        projectNameLabel.setLabelFor(projectNameTextField);
   62.55 +        org.openide.awt.Mnemonics.setLocalizedText(projectNameLabel, "Project &Name:");
   62.56 +
   62.57 +        projectLocationLabel.setLabelFor(projectLocationTextField);
   62.58 +        org.openide.awt.Mnemonics.setLocalizedText(projectLocationLabel, "Project &Location:");
   62.59 +
   62.60 +        org.openide.awt.Mnemonics.setLocalizedText(browseButton, "Br&owse...");
   62.61 +        browseButton.setActionCommand("BROWSE");
   62.62 +        browseButton.addActionListener(new java.awt.event.ActionListener() {
   62.63 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
   62.64 +                browseButtonActionPerformed(evt);
   62.65 +            }
   62.66 +        });
   62.67 +
   62.68 +        createdFolderLabel.setLabelFor(createdFolderTextField);
   62.69 +        org.openide.awt.Mnemonics.setLocalizedText(createdFolderLabel, "Project &Folder:");
   62.70 +
   62.71 +        createdFolderTextField.setEditable(false);
   62.72 +
   62.73 +        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
   62.74 +        this.setLayout(layout);
   62.75 +        layout.setHorizontalGroup(
   62.76 +            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
   62.77 +            .addGroup(layout.createSequentialGroup()
   62.78 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
   62.79 +                    .addComponent(projectLocationLabel)
   62.80 +                    .addComponent(createdFolderLabel)
   62.81 +                    .addComponent(projectNameLabel))
   62.82 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
   62.83 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
   62.84 +                    .addGroup(layout.createSequentialGroup()
   62.85 +                        .addComponent(projectLocationTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 238, Short.MAX_VALUE)
   62.86 +                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
   62.87 +                        .addComponent(browseButton))
   62.88 +                    .addComponent(projectNameTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 343, Short.MAX_VALUE)
   62.89 +                    .addComponent(createdFolderTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 343, Short.MAX_VALUE)))
   62.90 +        );
   62.91 +        layout.setVerticalGroup(
   62.92 +            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
   62.93 +            .addGroup(layout.createSequentialGroup()
   62.94 +                .addGap(20, 20, 20)
   62.95 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
   62.96 +                    .addComponent(projectNameLabel)
   62.97 +                    .addComponent(projectNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
   62.98 +                .addGap(8, 8, 8)
   62.99 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  62.100 +                    .addComponent(projectLocationLabel)
  62.101 +                    .addComponent(browseButton)
  62.102 +                    .addComponent(projectLocationTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
  62.103 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  62.104 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  62.105 +                    .addComponent(createdFolderLabel)
  62.106 +                    .addComponent(createdFolderTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
  62.107 +                .addContainerGap(31, Short.MAX_VALUE))
  62.108 +        );
  62.109 +    }// </editor-fold>//GEN-END:initComponents
  62.110 +
  62.111 +    private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
  62.112 +        String command = evt.getActionCommand();
  62.113 +        if ("BROWSE".equals(command)) {
  62.114 +            JFileChooser chooser = new JFileChooser();
  62.115 +            chooser.setCurrentDirectory(null);
  62.116 +            chooser.setDialogTitle("Select Project Location");
  62.117 +            chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
  62.118 +            String path = this.projectLocationTextField.getText();
  62.119 +            if (path.length() > 0) {
  62.120 +                File f = new File(path);
  62.121 +                if (f.exists()) {
  62.122 +                    chooser.setSelectedFile(f);
  62.123 +                }
  62.124 +            }
  62.125 +            if (JFileChooser.APPROVE_OPTION == chooser.showOpenDialog(this)) {
  62.126 +                File projectDir = chooser.getSelectedFile();
  62.127 +                projectLocationTextField.setText(FileUtil.normalizeFile(projectDir).getAbsolutePath());
  62.128 +            }
  62.129 +            panel.fireChangeEvent();
  62.130 +        }
  62.131 +
  62.132 +    }//GEN-LAST:event_browseButtonActionPerformed
  62.133 +    // Variables declaration - do not modify//GEN-BEGIN:variables
  62.134 +    private javax.swing.JButton browseButton;
  62.135 +    private javax.swing.JLabel createdFolderLabel;
  62.136 +    private javax.swing.JTextField createdFolderTextField;
  62.137 +    private javax.swing.JLabel projectLocationLabel;
  62.138 +    private javax.swing.JTextField projectLocationTextField;
  62.139 +    private javax.swing.JLabel projectNameLabel;
  62.140 +    private javax.swing.JTextField projectNameTextField;
  62.141 +    // End of variables declaration//GEN-END:variables
  62.142 +    @Override
  62.143 +    public void addNotify() {
  62.144 +        super.addNotify();
  62.145 +        //same problem as in 31086, initial focus on Cancel button
  62.146 +        projectNameTextField.requestFocus();
  62.147 +    }
  62.148 +
  62.149 +    @Override
  62.150 +    boolean valid(WizardDescriptor wizardDescriptor) {
  62.151 +
  62.152 +        if (projectNameTextField.getText().length() == 0) {
  62.153 +            wizardDescriptor.putProperty("WizardPanel_errorMessage",
  62.154 +                    "Project Name is not a valid folder name.");
  62.155 +            return false; // Display name not specified
  62.156 +        }
  62.157 +        File f = FileUtil.normalizeFile(new File(projectLocationTextField.getText()).getAbsoluteFile());
  62.158 +        if (!f.isDirectory()) {
  62.159 +            String message = "Project Folder is not a valid path.";
  62.160 +            wizardDescriptor.putProperty("WizardPanel_errorMessage", message);
  62.161 +            return false;
  62.162 +        }
  62.163 +        final File destFolder = FileUtil.normalizeFile(new File(createdFolderTextField.getText()).getAbsoluteFile());
  62.164 +
  62.165 +        File projLoc = destFolder;
  62.166 +        while (projLoc != null && !projLoc.exists()) {
  62.167 +            projLoc = projLoc.getParentFile();
  62.168 +        }
  62.169 +        if (projLoc == null || !projLoc.canWrite()) {
  62.170 +            wizardDescriptor.putProperty("WizardPanel_errorMessage",
  62.171 +                    "Project Folder cannot be created.");
  62.172 +            return false;
  62.173 +        }
  62.174 +
  62.175 +        if (FileUtil.toFileObject(projLoc) == null) {
  62.176 +            String message = "Project Folder is not a valid path.";
  62.177 +            wizardDescriptor.putProperty("WizardPanel_errorMessage", message);
  62.178 +            return false;
  62.179 +        }
  62.180 +
  62.181 +        File[] kids = destFolder.listFiles();
  62.182 +        if (destFolder.exists() && kids != null && kids.length > 0) {
  62.183 +            // Folder exists and is not empty
  62.184 +            wizardDescriptor.putProperty("WizardPanel_errorMessage",
  62.185 +                    "Project Folder already exists and is not empty.");
  62.186 +            return false;
  62.187 +        }
  62.188 +        wizardDescriptor.putProperty("WizardPanel_errorMessage", "");
  62.189 +        return true;
  62.190 +    }
  62.191 +
  62.192 +    @Override
  62.193 +    void store(WizardDescriptor d) {
  62.194 +        String name = projectNameTextField.getText().trim();
  62.195 +        String folder = createdFolderTextField.getText().trim();
  62.196 +
  62.197 +        d.putProperty(NewPythonProjectWizardIterator.PROP_PROJECT_LOCATION, new File(folder));
  62.198 +        d.putProperty(NewPythonProjectWizardIterator.PROP_PROJECT_NAME, name);
  62.199 +    }
  62.200 +
  62.201 +    @Override
  62.202 +    void read(WizardDescriptor settings) {
  62.203 +        File projectLocation = (File) settings.getProperty(NewPythonProjectWizardIterator.PROP_PROJECT_LOCATION);
  62.204 +        if (projectLocation == null || projectLocation.getParentFile() == null || !projectLocation.getParentFile().isDirectory()) {
  62.205 +            projectLocation = ProjectChooser.getProjectsFolder();
  62.206 +        } else {
  62.207 +            projectLocation = projectLocation.getParentFile();
  62.208 +        }
  62.209 +        this.projectLocationTextField.setText(projectLocation.getAbsolutePath());
  62.210 +
  62.211 +        String projectName = (String) settings.getProperty(NewPythonProjectWizardIterator.PROP_PROJECT_NAME);
  62.212 +        if (projectName == null) {
  62.213 +            projectName = NewPythonProjectWizardIterator.getFreeFolderName(projectLocation,"NewPythonProject"); //NOI18N
  62.214 +        }
  62.215 +        this.projectNameTextField.setText(projectName);
  62.216 +        this.projectNameTextField.selectAll();
  62.217 +    }
  62.218 +
  62.219 +    @Override
  62.220 +    void validate(WizardDescriptor d) throws WizardValidationException {
  62.221 +    // nothing to validate
  62.222 +    }
  62.223 +
  62.224 +    // Implementation of DocumentListener --------------------------------------
  62.225 +    @Override
  62.226 +    public void changedUpdate(DocumentEvent e) {
  62.227 +        updateTexts(e);
  62.228 +        if (this.projectNameTextField.getDocument() == e.getDocument()) {
  62.229 +            firePropertyChange(NewPythonProjectWizardIterator.PROP_PROJECT_NAME, null, this.projectNameTextField.getText());
  62.230 +        }
  62.231 +    }
  62.232 +
  62.233 +    @Override
  62.234 +    public void insertUpdate(DocumentEvent e) {
  62.235 +        updateTexts(e);
  62.236 +        if (this.projectNameTextField.getDocument() == e.getDocument()) {
  62.237 +            firePropertyChange(NewPythonProjectWizardIterator.PROP_PROJECT_NAME, null, this.projectNameTextField.getText());
  62.238 +        }
  62.239 +    }
  62.240 +
  62.241 +    @Override
  62.242 +    public void removeUpdate(DocumentEvent e) {
  62.243 +        updateTexts(e);
  62.244 +        if (this.projectNameTextField.getDocument() == e.getDocument()) {
  62.245 +            firePropertyChange(NewPythonProjectWizardIterator.PROP_PROJECT_NAME, null, this.projectNameTextField.getText());
  62.246 +        }
  62.247 +    }
  62.248 +
  62.249 +    /** Handles changes in the Project name and project directory, */
  62.250 +    private void updateTexts(DocumentEvent e) {
  62.251 +
  62.252 +        Document doc = e.getDocument();
  62.253 +
  62.254 +        if (doc == projectNameTextField.getDocument() || doc == projectLocationTextField.getDocument()) {
  62.255 +            // Change in the project name
  62.256 +
  62.257 +            String projectName = projectNameTextField.getText();
  62.258 +            String projectFolder = projectLocationTextField.getText();
  62.259 +
  62.260 +            //if (projectFolder.trim().length() == 0 || projectFolder.equals(oldName)) {
  62.261 +            createdFolderTextField.setText(projectFolder + File.separatorChar + projectName);
  62.262 +        //}
  62.263 +
  62.264 +        }
  62.265 +        panel.fireChangeEvent(); // Notify that the panel changed
  62.266 +    }
  62.267 +}
    63.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    63.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/templates/NewPythonProjectWizardIterator.java	Tue Feb 24 01:58:36 2015 -0800
    63.3 @@ -0,0 +1,273 @@
    63.4 +package org.netbeans.modules.python.project2.templates;
    63.5 +
    63.6 +import java.io.File;
    63.7 +import java.io.IOException;
    63.8 +import java.util.HashMap;
    63.9 +import java.util.HashSet;
   63.10 +import java.util.Map;
   63.11 +import java.util.NoSuchElementException;
   63.12 +import java.util.Set;
   63.13 +import javax.swing.event.ChangeListener;
   63.14 +import org.netbeans.api.progress.ProgressHandle;
   63.15 +import org.netbeans.api.project.Project;
   63.16 +import org.netbeans.api.project.ProjectManager;
   63.17 +import org.netbeans.api.templates.TemplateRegistration;
   63.18 +import org.netbeans.modules.python.api.PythonPlatform;
   63.19 +import org.netbeans.modules.python.api.PythonPlatformManager;
   63.20 +import org.netbeans.modules.python.project2.PythonProject2;
   63.21 +import org.netbeans.spi.project.ui.support.ProjectChooser;
   63.22 +import org.openide.WizardDescriptor;
   63.23 +import org.openide.filesystems.FileObject;
   63.24 +import org.openide.filesystems.FileUtil;
   63.25 +import org.openide.loaders.DataFolder;
   63.26 +import org.openide.loaders.DataObject;
   63.27 +import org.openide.util.Exceptions;
   63.28 +import org.openide.util.Mutex;
   63.29 +import org.openide.util.MutexException;
   63.30 +import org.openide.util.NbBundle;
   63.31 +
   63.32 +public class NewPythonProjectWizardIterator implements WizardDescriptor.ProgressInstantiatingIterator {
   63.33 +
   63.34 +    static final String PROP_MAIN_FILE = "mainFile";      //NOI18N
   63.35 +    static final String PROP_PROJECT_NAME = "projectName";  //NOI18N
   63.36 +    static final String PROP_PROJECT_LOCATION = "pojectLocation";   //NOI18N
   63.37 +    static final String PROP_PLATFORM_ID = "platform";              //NOI18N
   63.38 +
   63.39 +    public static enum WizardType {
   63.40 +        APP
   63.41 +    }
   63.42 +
   63.43 +    private final WizardType wizardType;
   63.44 +    private WizardDescriptor descriptor;
   63.45 +    private WizardDescriptor.Panel[] panels;
   63.46 +    private int index;
   63.47 +
   63.48 +    public NewPythonProjectWizardIterator() {
   63.49 +        this(WizardType.APP);
   63.50 +    }
   63.51 +
   63.52 +    private NewPythonProjectWizardIterator(WizardType wizardType) {
   63.53 +        this.wizardType = wizardType;
   63.54 +    }
   63.55 +
   63.56 +    @TemplateRegistration(folder = "Project/Python", position = 152, displayName = "org.netbeans.modules.python.project2.Bundle#Templates/Project/Python/PythonProject2.xml", iconBase = "org/netbeans/modules/python/project2/resources/py_25_16.png", description = "/org/netbeans/modules/python/project2/templates/EmptyPythonProjectDescription.html")
   63.57 +    public static NewPythonProjectWizardIterator createApplication() {
   63.58 +        return new NewPythonProjectWizardIterator();
   63.59 +    }
   63.60 +
   63.61 +    @Override
   63.62 +    public void initialize(WizardDescriptor wizard) {
   63.63 +        descriptor = wizard;
   63.64 +        index = 0;
   63.65 +        panels = createPanels();
   63.66 +        // normally we would do it in uninitialize but we have listener on ide options (=> NPE)
   63.67 +        initDescriptor(wizard);
   63.68 +    }
   63.69 +
   63.70 +    @Override
   63.71 +    public void uninitialize(WizardDescriptor wizard) {
   63.72 +        panels = null;
   63.73 +        descriptor = null;
   63.74 +    }
   63.75 +
   63.76 +    @Override
   63.77 +    public Set instantiate() throws IOException {
   63.78 +        assert false : "Cannot call this method if implements WizardDescriptor.ProgressInstantiatingIterator.";
   63.79 +        return null;
   63.80 +    }
   63.81 +
   63.82 +    @Override
   63.83 +    public Set instantiate(ProgressHandle handle) throws IOException {
   63.84 +        final Set<FileObject> resultSet = new HashSet<>();
   63.85 +
   63.86 +        handle.start(5);
   63.87 +
   63.88 +        String msg = NbBundle.getMessage(
   63.89 +                NewPythonProjectWizardIterator.class, "LBL_NewPythonProjectWizardIterator_WizardProgress_CreatingProject");
   63.90 +        handle.progress(msg, 3);
   63.91 +        try {
   63.92 +            ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction<Void>() {
   63.93 +
   63.94 +                @Override
   63.95 +                public Void run() throws MutexException {
   63.96 +                    try {
   63.97 +                        // project
   63.98 +                        File projectDirectory = (File) descriptor.getProperty(PROP_PROJECT_LOCATION);
   63.99 +                        ProjectChooser.setProjectsFolder(projectDirectory.getParentFile());
  63.100 +
  63.101 +                        final FileObject projectFO = FileUtil.createFolder(projectDirectory);
  63.102 +                        String projectName = (String) descriptor.getProperty(PROP_PROJECT_NAME);
  63.103 +
  63.104 +                        Map<String, Object> params = new HashMap<>(2);
  63.105 +                        /* org/netbeans/modules/python/editor/templates/setup.py
  63.106 +                         * __author__="${user}"
  63.107 +                         * __date__ ="$${date} ${time}$"
  63.108 +                         *
  63.109 +                         * from setuptools import setup,find_packages
  63.110 +                         *
  63.111 +                         * setup (
  63.112 +                         *   name = '${project_name}',
  63.113 +                         *   version = '0.1',
  63.114 +                         *   packages = find_packages(),
  63.115 +                         *
  63.116 +                         *   # Declare your packages' dependencies here, for eg:
  63.117 +                         *   install_requires=[],
  63.118 +                         *
  63.119 +                         *   # Fill in these to make your Egg ready for upload to
  63.120 +                         *   # PyPI
  63.121 +                         *   author = '${user}',
  63.122 +                         *   author_email = '',
  63.123 +                         *
  63.124 +                         *   summary = 'Just another Python package for the cheese shop',
  63.125 +                         *   url = '',
  63.126 +                         *   license = '',
  63.127 +                         *   long_description= 'Long description of the package',
  63.128 +                         *
  63.129 +                         *   # could also include long_description, download_url, classifiers, etc.
  63.130 +                         * )
  63.131 +                         */
  63.132 +                        params.put("project_name", projectName); // NOI18N
  63.133 +                        params.put("python3style", Boolean.TRUE);
  63.134 +                        DataObject setuppy = createFromTemplate(FileUtil.getConfigFile("Templates/Python/_setup.py"), projectFO, "setup.py", params); // NOI18N
  63.135 +                        // Do we want to open the setup.py?
  63.136 +//        resultSet.add(setuppy.getPrimaryFile());
  63.137 +                        resultSet.add(projectFO);
  63.138 +
  63.139 +                        if (wizardType == WizardType.APP) {
  63.140 +                            // package
  63.141 +                            final String packageName = projectFO.getName().toLowerCase();
  63.142 +                            FileObject packageFO = projectFO.createFolder(packageName);
  63.143 +                            createFromTemplate(FileUtil.getConfigFile("Templates/Python/_init.py"), packageFO, "__init__.py", null); //NOI18N
  63.144 +                            ProjectManager.getDefault().clearNonProjectCache();
  63.145 +                            Project project = ProjectManager.getDefault().findProject(projectFO);
  63.146 +                            // TODO: Change with provider in lookup cast is known to break
  63.147 +                            PythonProject2 pyProj = (PythonProject2) project;
  63.148 +                            final String platformId = (String) descriptor.getProperty(NewPythonProjectWizardIterator.PROP_PLATFORM_ID);
  63.149 +                            PythonPlatform platform = PythonPlatformManager.getInstance().getPlatform(platformId);
  63.150 +                            if(platform != null) {
  63.151 +                                pyProj.setActivePlatform(platform);
  63.152 +                            }
  63.153 +                            // main file
  63.154 +                            final String mainName = (String) descriptor.getProperty(NewPythonProjectWizardIterator.PROP_MAIN_FILE);
  63.155 +                            if (mainName != null) {
  63.156 +                                final FileObject mainFile = createFromTemplate(FileUtil.getConfigFile("Templates/Python/_module.py"), //NOI18N
  63.157 +                                        packageFO, mainName, params).getPrimaryFile();
  63.158 +                                resultSet.add(mainFile);
  63.159 +                                pyProj.setMainModule(FileUtil.getRelativePath(projectFO, mainFile));
  63.160 +                            }
  63.161 +                            ProjectManager.getDefault().saveProject(project);
  63.162 +                        }
  63.163 +                    } catch (IOException ex) {
  63.164 +                        throw new MutexException(ex);
  63.165 +                    }
  63.166 +                    return null;
  63.167 +                }
  63.168 +            });
  63.169 +        } catch (MutexException ex) {
  63.170 +            Exceptions.printStackTrace(ex);
  63.171 +        }
  63.172 +
  63.173 +        msg = NbBundle.getMessage(NewPythonProjectWizardIterator.class, "LBL_NewPythonProjectWizardIterator_WizardProgress_PreparingToOpen");
  63.174 +        handle.progress(msg, 5);
  63.175 +        return resultSet;
  63.176 +    }
  63.177 +
  63.178 +    @Override
  63.179 +    public String name() {
  63.180 +        return NbBundle.getMessage(NewPythonProjectWizardIterator.class, "LBL_IteratorName", index + 1, panels.length);
  63.181 +    }
  63.182 +
  63.183 +    @Override
  63.184 +    public boolean hasNext() {
  63.185 +        return index < panels.length - 1;
  63.186 +    }
  63.187 +
  63.188 +    @Override
  63.189 +    public boolean hasPrevious() {
  63.190 +        return index > 0;
  63.191 +    }
  63.192 +
  63.193 +    @Override
  63.194 +    public void nextPanel() {
  63.195 +        if (!hasNext()) {
  63.196 +            throw new NoSuchElementException();
  63.197 +        }
  63.198 +        index++;
  63.199 +    }
  63.200 +
  63.201 +    @Override
  63.202 +    public void previousPanel() {
  63.203 +        if (!hasPrevious()) {
  63.204 +            throw new NoSuchElementException();
  63.205 +        }
  63.206 +        index--;
  63.207 +    }
  63.208 +
  63.209 +    @Override
  63.210 +    public WizardDescriptor.Panel current() {
  63.211 +        // wizard title
  63.212 +        String title = NbBundle.getMessage(NewPythonProjectWizardIterator.class, wizardType == WizardType.APP ? "TXT_PythonProject" : "TXT_ExistingPythonProject");
  63.213 +        descriptor.putProperty(
  63.214 +                "NewProjectWizard_Title", title); // NOI18N
  63.215 +        return panels[index];
  63.216 +    }
  63.217 +
  63.218 +    @Override
  63.219 +    public void addChangeListener(ChangeListener l) {
  63.220 +    }
  63.221 +
  63.222 +    @Override
  63.223 +    public void removeChangeListener(ChangeListener l) {
  63.224 +    }
  63.225 +
  63.226 +    static String getFreeFolderName(final File owner, final String proposal) {
  63.227 +        assert owner != null;
  63.228 +        assert proposal != null;
  63.229 +        String freeName = proposal;
  63.230 +        File f = new File(owner, freeName);
  63.231 +        int counter = 1;
  63.232 +        while (f.exists()) {
  63.233 +            counter++;
  63.234 +            freeName = proposal + counter;
  63.235 +            f = new File(owner, freeName);
  63.236 +        }
  63.237 +        return freeName;
  63.238 +
  63.239 +    }
  63.240 +
  63.241 +    private WizardDescriptor.Panel[] createPanels() {
  63.242 +        switch (wizardType) {
  63.243 +            case APP: {
  63.244 +                String[] steps = new String[]{
  63.245 +                    NbBundle.getMessage(NewPythonProjectWizardIterator.class, "LBL_ProjectNameLocation"),};
  63.246 +
  63.247 +                PanelConfigureProject configureProjectPanel = new PanelConfigureProject(wizardType, steps);
  63.248 +                return new WizardDescriptor.Panel[]{
  63.249 +                    configureProjectPanel,};
  63.250 +            }
  63.251 +            default:
  63.252 +                throw new IllegalStateException(wizardType.toString());
  63.253 +        }
  63.254 +    }
  63.255 +
  63.256 +    // prevent incorrect default values (empty project => back => existing project)
  63.257 +    private void initDescriptor(WizardDescriptor settings) {
  63.258 +        settings.putProperty(PROP_PROJECT_NAME, null);
  63.259 +        settings.putProperty(PROP_PROJECT_LOCATION, null);
  63.260 +    }
  63.261 +
  63.262 +    private DataObject createFromTemplate(FileObject template, FileObject sourceDir, String name, Map<String, ? extends Object> parameters) throws IOException {
  63.263 +        DataFolder dataFolder = DataFolder.findFolder(sourceDir);
  63.264 +        DataObject dataTemplate = DataObject.find(template);
  63.265 +        //Strip extension when needed
  63.266 +        int dot = name.lastIndexOf('.');
  63.267 +        if (dot > 0 && dot < name.length() - 1 && "py".equalsIgnoreCase(name.substring(dot + 1))) { //NOI18N
  63.268 +            name = name.substring(0, dot);
  63.269 +        }
  63.270 +        if (parameters != null) {
  63.271 +            return dataTemplate.createFromTemplate(dataFolder, name, parameters);
  63.272 +        } else {
  63.273 +            return dataTemplate.createFromTemplate(dataFolder, name);
  63.274 +        }
  63.275 +    }
  63.276 +}
    64.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    64.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/templates/PanelConfigureProject.java	Tue Feb 24 01:58:36 2015 -0800
    64.3 @@ -0,0 +1,101 @@
    64.4 +/*
    64.5 + * To change this template, choose Tools | Templates
    64.6 + * and open the template in the editor.
    64.7 + */
    64.8 +package org.netbeans.modules.python.project2.templates;
    64.9 +
   64.10 +import java.util.HashSet;
   64.11 +import java.util.Set;
   64.12 +import javax.swing.event.ChangeEvent;
   64.13 +import javax.swing.event.ChangeListener;
   64.14 +import org.openide.WizardDescriptor;
   64.15 +import org.openide.WizardValidationException;
   64.16 +import org.openide.util.HelpCtx;
   64.17 +
   64.18 +/**
   64.19 + * Panel just asking for basic info.
   64.20 + */
   64.21 +public class PanelConfigureProject implements WizardDescriptor.Panel,
   64.22 +        WizardDescriptor.ValidatingPanel, WizardDescriptor.FinishablePanel {
   64.23 +
   64.24 +    private WizardDescriptor wizardDescriptor;
   64.25 +    private PanelConfigureProjectVisual component;
   64.26 +    private final NewPythonProjectWizardIterator.WizardType type;
   64.27 +    private final String[] steps;
   64.28 +
   64.29 +    public PanelConfigureProject (final NewPythonProjectWizardIterator.WizardType type, String[] steps) {
   64.30 +        assert type != null;
   64.31 +        assert steps != null;
   64.32 +        this.type = type;
   64.33 +        this.steps = steps;
   64.34 +    }
   64.35 +
   64.36 +    @Override
   64.37 +    public PanelConfigureProjectVisual getComponent() {
   64.38 +        if (component == null) {
   64.39 +            component = new PanelConfigureProjectVisual(this, type);            
   64.40 +            component.putClientProperty(WizardDescriptor.PROP_CONTENT_DATA, steps);
   64.41 +        }
   64.42 +        return component;
   64.43 +    }
   64.44 +
   64.45 +    @Override
   64.46 +    public HelpCtx getHelp() {
   64.47 +        return new HelpCtx("org.netbeans.modules.python.project2.templates.PanelConfigureProject");
   64.48 +    }
   64.49 +
   64.50 +    @Override
   64.51 +    public boolean isValid() {
   64.52 +        getComponent();
   64.53 +        return getComponent().valid(wizardDescriptor);
   64.54 +    }
   64.55 +    private final Set<ChangeListener> listeners = new HashSet<>(1); // or can use ChangeSupport in NB 6.0
   64.56 +
   64.57 +    @Override
   64.58 +    public final void addChangeListener(ChangeListener l) {
   64.59 +        synchronized (listeners) {
   64.60 +            listeners.add(l);
   64.61 +        }
   64.62 +    }
   64.63 +
   64.64 +    @Override
   64.65 +    public final void removeChangeListener(ChangeListener l) {
   64.66 +        synchronized (listeners) {
   64.67 +            listeners.remove(l);
   64.68 +        }
   64.69 +    }
   64.70 +
   64.71 +    protected final void fireChangeEvent() {
   64.72 +        Set<ChangeListener> ls;
   64.73 +        synchronized (listeners) {
   64.74 +            ls = new HashSet<>(listeners);
   64.75 +        }
   64.76 +        ChangeEvent ev = new ChangeEvent(this);
   64.77 +        for (ChangeListener l : ls) {
   64.78 +            l.stateChanged(ev);
   64.79 +        }
   64.80 +    }
   64.81 +
   64.82 +    @Override
   64.83 +    public void readSettings(Object settings) {
   64.84 +        wizardDescriptor = (WizardDescriptor) settings;
   64.85 +        getComponent().read(wizardDescriptor);
   64.86 +    }
   64.87 +
   64.88 +    @Override
   64.89 +    public void storeSettings(Object settings) {
   64.90 +        WizardDescriptor d = (WizardDescriptor) settings;
   64.91 +        getComponent().store(d);
   64.92 +    }
   64.93 +
   64.94 +    @Override
   64.95 +    public boolean isFinishPanel() {
   64.96 +        return true;
   64.97 +    }
   64.98 +
   64.99 +    @Override
  64.100 +    public void validate() throws WizardValidationException {
  64.101 +        getComponent();
  64.102 +        getComponent().validate(wizardDescriptor);
  64.103 +    }
  64.104 +}
    65.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    65.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/templates/PanelConfigureProjectVisual.form	Tue Feb 24 01:58:36 2015 -0800
    65.3 @@ -0,0 +1,60 @@
    65.4 +<?xml version="1.0" encoding="UTF-8" ?>
    65.5 +
    65.6 +<Form version="1.2" maxVersion="1.2" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
    65.7 +  <AuxValues>
    65.8 +    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
    65.9 +    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
   65.10 +    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
   65.11 +    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
   65.12 +    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
   65.13 +    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
   65.14 +    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
   65.15 +    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
   65.16 +    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
   65.17 +  </AuxValues>
   65.18 +
   65.19 +  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
   65.20 +  <SubComponents>
   65.21 +    <Container class="javax.swing.JPanel" name="locationContainer">
   65.22 +      <AccessibilityProperties>
   65.23 +        <Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   65.24 +          <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="ACSN_locationContainer" replaceFormat="org.openide.util.NbBundle.getBundle({sourceFileName}.class).getString(&quot;{key}&quot;)"/>
   65.25 +        </Property>
   65.26 +        <Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   65.27 +          <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="ACSD_locationContainer" replaceFormat="org.openide.util.NbBundle.getBundle({sourceFileName}.class).getString(&quot;{key}&quot;)"/>
   65.28 +        </Property>
   65.29 +      </AccessibilityProperties>
   65.30 +      <Constraints>
   65.31 +        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
   65.32 +          <GridBagConstraints gridX="-1" gridY="-1" gridWidth="0" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
   65.33 +        </Constraint>
   65.34 +      </Constraints>
   65.35 +
   65.36 +      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
   65.37 +    </Container>
   65.38 +    <Component class="javax.swing.JSeparator" name="jSeparator1">
   65.39 +      <Constraints>
   65.40 +        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
   65.41 +          <GridBagConstraints gridX="-1" gridY="-1" gridWidth="0" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="12" insetsLeft="0" insetsBottom="12" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
   65.42 +        </Constraint>
   65.43 +      </Constraints>
   65.44 +    </Component>
   65.45 +    <Container class="javax.swing.JPanel" name="optionsContainer">
   65.46 +      <AccessibilityProperties>
   65.47 +        <Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   65.48 +          <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="ACSN_optionsContainer" replaceFormat="org.openide.util.NbBundle.getBundle({sourceFileName}.class).getString(&quot;{key}&quot;)"/>
   65.49 +        </Property>
   65.50 +        <Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   65.51 +          <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="ACSD_optionsContainer" replaceFormat="org.openide.util.NbBundle.getBundle({sourceFileName}.class).getString(&quot;{key}&quot;)"/>
   65.52 +        </Property>
   65.53 +      </AccessibilityProperties>
   65.54 +      <Constraints>
   65.55 +        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
   65.56 +          <GridBagConstraints gridX="-1" gridY="-1" gridWidth="0" gridHeight="0" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
   65.57 +        </Constraint>
   65.58 +      </Constraints>
   65.59 +
   65.60 +      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
   65.61 +    </Container>
   65.62 +  </SubComponents>
   65.63 +</Form>
    66.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    66.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/templates/PanelConfigureProjectVisual.java	Tue Feb 24 01:58:36 2015 -0800
    66.3 @@ -0,0 +1,169 @@
    66.4 +/*
    66.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    66.6 + *
    66.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    66.8 + *
    66.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   66.10 + * Other names may be trademarks of their respective owners.
   66.11 + *
   66.12 + * The contents of this file are subject to the terms of either the GNU
   66.13 + * General Public License Version 2 only ("GPL") or the Common
   66.14 + * Development and Distribution License("CDDL") (collectively, the
   66.15 + * "License"). You may not use this file except in compliance with the
   66.16 + * License. You can obtain a copy of the License at
   66.17 + * http://www.netbeans.org/cddl-gplv2.html
   66.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   66.19 + * specific language governing permissions and limitations under the
   66.20 + * License.  When distributing the software, include this License Header
   66.21 + * Notice in each file and include the License file at
   66.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   66.23 + * particular file as subject to the "Classpath" exception as provided
   66.24 + * by Oracle in the GPL Version 2 section of the License file that
   66.25 + * accompanied this code. If applicable, add the following below the
   66.26 + * License Header, with the fields enclosed by brackets [] replaced by
   66.27 + * your own identifying information:
   66.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   66.29 + *
   66.30 + * Contributor(s):
   66.31 + *
   66.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   66.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   66.34 + * Microsystems, Inc. All Rights Reserved.
   66.35 + *
   66.36 + * If you wish your version of this file to be governed by only the CDDL
   66.37 + * or only the GPL Version 2, indicate your decision by adding
   66.38 + * "[Contributor] elects to include this software in this distribution
   66.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   66.40 + * single choice of license, a recipient has the option to distribute
   66.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   66.42 + * to extend the choice of license to its licensees as provided above.
   66.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   66.44 + * Version 2 license, then the option applies only if the new code is
   66.45 + * made subject to such option by the copyright holder.
   66.46 + */
   66.47 +
   66.48 +package org.netbeans.modules.python.project2.templates;
   66.49 +
   66.50 +import javax.swing.JPanel;
   66.51 +import org.openide.WizardDescriptor;
   66.52 +import org.openide.WizardValidationException;
   66.53 +import org.openide.util.NbBundle;
   66.54 +
   66.55 +/** First panel in the NewProject wizard. Used for filling in
   66.56 + * name, and directory of the project.
   66.57 + *
   66.58 + * @author Tomas Zezula
   66.59 + */
   66.60 +public class PanelConfigureProjectVisual extends JPanel {
   66.61 +
   66.62 +    private PanelConfigureProject panel;
   66.63 +
   66.64 +    private SettingsPanel projectLocationPanel;
   66.65 +
   66.66 +    private PanelOptionsVisual optionsPanel;
   66.67 +
   66.68 +    private NewPythonProjectWizardIterator.WizardType type;
   66.69 +
   66.70 +    public PanelConfigureProjectVisual(PanelConfigureProject panel, NewPythonProjectWizardIterator.WizardType type) {
   66.71 +        this.panel = panel;
   66.72 +        initComponents();
   66.73 +        this.type = type;
   66.74 +        setName(NbBundle.getMessage(PanelConfigureProjectVisual.class,"TXT_NameAndLoc")); // NOI18N
   66.75 +        switch (type) {
   66.76 +        case APP:
   66.77 +            projectLocationPanel = new EmptyPythonProjectPanelVisual(panel);
   66.78 +            putClientProperty ("NewProjectWizard_Title", NbBundle.getMessage(PanelConfigureProjectVisual.class,"TXT_NewPythonApp")); // NOI18N
   66.79 +            jSeparator1.setVisible(true);
   66.80 +            getAccessibleContext ().setAccessibleName (NbBundle.getMessage(PanelConfigureProjectVisual.class,"TXT_NewPythonApp")); // NOI18N
   66.81 +            getAccessibleContext ().setAccessibleDescription (NbBundle.getMessage(PanelConfigureProjectVisual.class,"ACSD_NewPythonApp")); // NOI18N
   66.82 +            break;
   66.83 +        default:
   66.84 +                throw new UnsupportedOperationException();
   66.85 +        }
   66.86 +        locationContainer.add( projectLocationPanel, java.awt.BorderLayout.CENTER );
   66.87 +        optionsPanel = new PanelOptionsVisual( panel, type );
   66.88 +        projectLocationPanel.addPropertyChangeListener(optionsPanel);
   66.89 +        optionsContainer.add( optionsPanel, java.awt.BorderLayout.CENTER );
   66.90 +    }
   66.91 +
   66.92 +    boolean valid( WizardDescriptor wizardDescriptor ) {
   66.93 +        wizardDescriptor.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, ""); //NOI18N
   66.94 +        return projectLocationPanel.valid( wizardDescriptor ) && optionsPanel.valid(wizardDescriptor);
   66.95 +    }
   66.96 +
   66.97 +    void read (WizardDescriptor d) {
   66.98 +        Object lastType =  d.getProperty("wizard-type");  //NOI18N
   66.99 +        if (lastType == null || lastType != type) {
  66.100 +            //bugfix #46387 The type of project changed, reset values to defaults
  66.101 +            d.putProperty ("name", null);
  66.102 +            d.putProperty ("projdir",null);
  66.103 +        }
  66.104 +        projectLocationPanel.read (d);
  66.105 +        optionsPanel.read (d);
  66.106 +    }
  66.107 +
  66.108 +    void store( WizardDescriptor d ) {
  66.109 +        d.putProperty("wizard-type", type);   //NOI18N
  66.110 +        projectLocationPanel.store( d );
  66.111 +        optionsPanel.store( d );
  66.112 +    }
  66.113 +
  66.114 +    void validate (WizardDescriptor d) throws WizardValidationException {
  66.115 +        projectLocationPanel.validate (d);
  66.116 +    }
  66.117 +
  66.118 +
  66.119 +    /** This method is called from within the constructor to
  66.120 +     * initialize the form.
  66.121 +     * WARNING: Do NOT modify this code. The content of this method is
  66.122 +     * always regenerated by the Form Editor.
  66.123 +     */
  66.124 +    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  66.125 +    private void initComponents() {
  66.126 +        java.awt.GridBagConstraints gridBagConstraints;
  66.127 +
  66.128 +        locationContainer = new javax.swing.JPanel();
  66.129 +        jSeparator1 = new javax.swing.JSeparator();
  66.130 +        optionsContainer = new javax.swing.JPanel();
  66.131 +
  66.132 +        setLayout(new java.awt.GridBagLayout());
  66.133 +
  66.134 +        locationContainer.setLayout(new java.awt.BorderLayout());
  66.135 +        gridBagConstraints = new java.awt.GridBagConstraints();
  66.136 +        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
  66.137 +        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
  66.138 +        gridBagConstraints.weightx = 1.0;
  66.139 +        add(locationContainer, gridBagConstraints);
  66.140 +        locationContainer.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getBundle(PanelConfigureProjectVisual.class).getString("ACSN_locationContainer")); // NOI18N
  66.141 +        locationContainer.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getBundle(PanelConfigureProjectVisual.class).getString("ACSD_locationContainer")); // NOI18N
  66.142 +
  66.143 +        gridBagConstraints = new java.awt.GridBagConstraints();
  66.144 +        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
  66.145 +        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
  66.146 +        gridBagConstraints.weightx = 1.0;
  66.147 +        gridBagConstraints.insets = new java.awt.Insets(12, 0, 12, 0);
  66.148 +        add(jSeparator1, gridBagConstraints);
  66.149 +
  66.150 +        optionsContainer.setLayout(new java.awt.BorderLayout());
  66.151 +        gridBagConstraints = new java.awt.GridBagConstraints();
  66.152 +        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
  66.153 +        gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER;
  66.154 +        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
  66.155 +        gridBagConstraints.weightx = 1.0;
  66.156 +        gridBagConstraints.weighty = 1.0;
  66.157 +        add(optionsContainer, gridBagConstraints);
  66.158 +        optionsContainer.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getBundle(PanelConfigureProjectVisual.class).getString("ACSN_optionsContainer")); // NOI18N
  66.159 +        optionsContainer.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getBundle(PanelConfigureProjectVisual.class).getString("ACSD_optionsContainer")); // NOI18N
  66.160 +    }// </editor-fold>//GEN-END:initComponents
  66.161 +
  66.162 +    /** Currently only handles the "Browse..." button
  66.163 +     */
  66.164 +
  66.165 +    // Variables declaration - do not modify//GEN-BEGIN:variables
  66.166 +    private javax.swing.JSeparator jSeparator1;
  66.167 +    private javax.swing.JPanel locationContainer;
  66.168 +    private javax.swing.JPanel optionsContainer;
  66.169 +    // End of variables declaration//GEN-END:variables
  66.170 +
  66.171 +
  66.172 +}
    67.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    67.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/templates/PanelOptionsVisual.form	Tue Feb 24 01:58:36 2015 -0800
    67.3 @@ -0,0 +1,125 @@
    67.4 +<?xml version="1.0" encoding="UTF-8" ?>
    67.5 +
    67.6 +<Form version="1.2" maxVersion="1.2" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
    67.7 +  <AccessibilityProperties>
    67.8 +    <Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
    67.9 +      <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="ACSN_PanelOptionsVisual" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   67.10 +    </Property>
   67.11 +    <Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   67.12 +      <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="ACSD_PanelOptionsVisual" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   67.13 +    </Property>
   67.14 +  </AccessibilityProperties>
   67.15 +  <AuxValues>
   67.16 +    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
   67.17 +    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
   67.18 +    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
   67.19 +    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
   67.20 +    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
   67.21 +    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
   67.22 +    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
   67.23 +    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
   67.24 +    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
   67.25 +  </AuxValues>
   67.26 +
   67.27 +  <Layout>
   67.28 +    <DimensionLayout dim="0">
   67.29 +      <Group type="103" groupAlignment="0" attributes="0">
   67.30 +          <Group type="102" alignment="0" attributes="0">
   67.31 +              <Group type="103" groupAlignment="0" attributes="0">
   67.32 +                  <Component id="createMainCheckBox" alignment="0" min="-2" max="-2" attributes="0"/>
   67.33 +                  <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
   67.34 +              </Group>
   67.35 +              <EmptySpace type="separate" max="-2" attributes="0"/>
   67.36 +              <Group type="103" groupAlignment="0" attributes="0">
   67.37 +                  <Group type="102" attributes="0">
   67.38 +                      <Component id="platforms" min="-2" pref="233" max="-2" attributes="0"/>
   67.39 +                      <EmptySpace type="unrelated" max="-2" attributes="0"/>
   67.40 +                      <Component id="manage" min="-2" max="-2" attributes="0"/>
   67.41 +                      <EmptySpace min="-2" pref="4" max="-2" attributes="0"/>
   67.42 +                  </Group>
   67.43 +                  <Component id="mainFileTextField" alignment="0" max="32767" attributes="0"/>
   67.44 +              </Group>
   67.45 +          </Group>
   67.46 +      </Group>
   67.47 +    </DimensionLayout>
   67.48 +    <DimensionLayout dim="1">
   67.49 +      <Group type="103" groupAlignment="0" attributes="0">
   67.50 +          <Group type="102" attributes="0">
   67.51 +              <EmptySpace max="-2" attributes="0"/>
   67.52 +              <Group type="103" groupAlignment="3" attributes="0">
   67.53 +                  <Component id="createMainCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
   67.54 +                  <Component id="mainFileTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   67.55 +              </Group>
   67.56 +              <EmptySpace max="-2" attributes="0"/>
   67.57 +              <Group type="103" groupAlignment="0" attributes="0">
   67.58 +                  <Group type="103" groupAlignment="3" attributes="0">
   67.59 +                      <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
   67.60 +                      <Component id="platforms" alignment="3" min="-2" max="-2" attributes="0"/>
   67.61 +                  </Group>
   67.62 +                  <Component id="manage" alignment="0" min="-2" max="-2" attributes="0"/>
   67.63 +              </Group>
   67.64 +              <EmptySpace max="32767" attributes="0"/>
   67.65 +          </Group>
   67.66 +      </Group>
   67.67 +    </DimensionLayout>
   67.68 +  </Layout>
   67.69 +  <SubComponents>
   67.70 +    <Component class="javax.swing.JCheckBox" name="createMainCheckBox">
   67.71 +      <Properties>
   67.72 +        <Property name="selected" type="boolean" value="true"/>
   67.73 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   67.74 +          <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="LBL_createMainCheckBox" replaceFormat="org.openide.util.NbBundle.getBundle({sourceFileName}.class).getString(&quot;{key}&quot;)"/>
   67.75 +        </Property>
   67.76 +      </Properties>
   67.77 +      <AccessibilityProperties>
   67.78 +        <Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   67.79 +          <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="ACSN_createMainCheckBox" replaceFormat="org.openide.util.NbBundle.getBundle({sourceFileName}.class).getString(&quot;{key}&quot;)"/>
   67.80 +        </Property>
   67.81 +        <Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   67.82 +          <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="ACSD_createMainCheckBox" replaceFormat="org.openide.util.NbBundle.getBundle({sourceFileName}.class).getString(&quot;{key}&quot;)"/>
   67.83 +        </Property>
   67.84 +      </AccessibilityProperties>
   67.85 +      <AuxValues>
   67.86 +        <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
   67.87 +      </AuxValues>
   67.88 +    </Component>
   67.89 +    <Component class="javax.swing.JTextField" name="mainFileTextField">
   67.90 +      <Properties>
   67.91 +        <Property name="text" type="java.lang.String" value="main"/>
   67.92 +      </Properties>
   67.93 +      <AccessibilityProperties>
   67.94 +        <Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   67.95 +          <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="ASCN_mainClassTextFiled" replaceFormat="org.openide.util.NbBundle.getBundle({sourceFileName}.class).getString(&quot;{key}&quot;)"/>
   67.96 +        </Property>
   67.97 +        <Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   67.98 +          <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="ASCD_mainClassTextFiled" replaceFormat="org.openide.util.NbBundle.getBundle({sourceFileName}.class).getString(&quot;{key}&quot;)"/>
   67.99 +        </Property>
  67.100 +      </AccessibilityProperties>
  67.101 +    </Component>
  67.102 +    <Component class="javax.swing.JLabel" name="jLabel1">
  67.103 +      <Properties>
  67.104 +        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
  67.105 +          <ComponentRef name="platforms"/>
  67.106 +        </Property>
  67.107 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  67.108 +          <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="TXT_PythonPlatform" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  67.109 +        </Property>
  67.110 +      </Properties>
  67.111 +    </Component>
  67.112 +    <Component class="javax.swing.JComboBox" name="platforms">
  67.113 +      <AuxValues>
  67.114 +        <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="org.netbeans.modules.python.api.PlatformComponentFactory.getPythonPlatformsComboxBox()"/>
  67.115 +      </AuxValues>
  67.116 +    </Component>
  67.117 +    <Component class="javax.swing.JButton" name="manage">
  67.118 +      <Properties>
  67.119 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  67.120 +          <ResourceString bundle="org/netbeans/modules/python/project2/templates/Bundle.properties" key="TXT_ManagePlatfroms" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  67.121 +        </Property>
  67.122 +      </Properties>
  67.123 +      <Events>
  67.124 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="manageActionPerformed"/>
  67.125 +      </Events>
  67.126 +    </Component>
  67.127 +  </SubComponents>
  67.128 +</Form>
    68.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    68.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/templates/PanelOptionsVisual.java	Tue Feb 24 01:58:36 2015 -0800
    68.3 @@ -0,0 +1,349 @@
    68.4 +/*
    68.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    68.6 + *
    68.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    68.8 + *
    68.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   68.10 + * Other names may be trademarks of their respective owners.
   68.11 + *
   68.12 + * The contents of this file are subject to the terms of either the GNU
   68.13 + * General Public License Version 2 only ("GPL") or the Common
   68.14 + * Development and Distribution License("CDDL") (collectively, the
   68.15 + * "License"). You may not use this file except in compliance with the
   68.16 + * License. You can obtain a copy of the License at
   68.17 + * http://www.netbeans.org/cddl-gplv2.html
   68.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   68.19 + * specific language governing permissions and limitations under the
   68.20 + * License.  When distributing the software, include this License Header
   68.21 + * Notice in each file and include the License file at
   68.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   68.23 + * particular file as subject to the "Classpath" exception as provided
   68.24 + * by Oracle in the GPL Version 2 section of the License file that
   68.25 + * accompanied this code. If applicable, add the following below the
   68.26 + * License Header, with the fields enclosed by brackets [] replaced by
   68.27 + * your own identifying information:
   68.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   68.29 + *
   68.30 + * Contributor(s):
   68.31 + *
   68.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   68.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   68.34 + * Microsystems, Inc. All Rights Reserved.
   68.35 + *
   68.36 + * If you wish your version of this file to be governed by only the CDDL
   68.37 + * or only the GPL Version 2, indicate your decision by adding
   68.38 + * "[Contributor] elects to include this software in this distribution
   68.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   68.40 + * single choice of license, a recipient has the option to distribute
   68.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   68.42 + * to extend the choice of license to its licensees as provided above.
   68.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   68.44 + * Version 2 license, then the option applies only if the new code is
   68.45 + * made subject to such option by the copyright holder.
   68.46 + */
   68.47 +
   68.48 +
   68.49 +package org.netbeans.modules.python.project2.templates;
   68.50 +
   68.51 +import java.awt.event.ActionEvent;
   68.52 +import java.awt.event.ActionListener;
   68.53 +import java.beans.PropertyChangeEvent;
   68.54 +import java.beans.PropertyChangeListener;
   68.55 +import java.io.IOException;
   68.56 +import java.util.StringTokenizer;
   68.57 +import javax.swing.JComboBox;
   68.58 +import javax.swing.event.DocumentListener;
   68.59 +import javax.swing.event.DocumentEvent;
   68.60 +import org.netbeans.modules.python.api.PlatformComponentFactory;
   68.61 +import org.netbeans.modules.python.api.PythonPlatform;
   68.62 +import org.netbeans.modules.python.api.PythonPlatformManager;
   68.63 +import org.netbeans.modules.python.api.Util;
   68.64 +import org.netbeans.modules.python.project2.ui.Utils;
   68.65 +import org.openide.WizardDescriptor;
   68.66 +import org.openide.WizardValidationException;
   68.67 +import org.openide.filesystems.FileObject;
   68.68 +import org.openide.filesystems.FileUtil;
   68.69 +import org.openide.loaders.DataObject;
   68.70 +import org.openide.loaders.InstanceDataObject;
   68.71 +import org.openide.util.Exceptions;
   68.72 +import org.openide.util.NbBundle;
   68.73 +import org.openide.util.Utilities;
   68.74 +import org.openide.util.actions.CallableSystemAction;
   68.75 +
   68.76 +/**
   68.77 + * @author  Tomas Zezula
   68.78 + */
   68.79 +public final class PanelOptionsVisual extends SettingsPanel implements ActionListener, PropertyChangeListener {
   68.80 +
   68.81 +    private static boolean lastMainClassCheck = true; // XXX Store somewhere
   68.82 +
   68.83 +    private PanelConfigureProject panel;
   68.84 +    private boolean valid;
   68.85 +    private String projectLocation;
   68.86 +
   68.87 +    public PanelOptionsVisual(PanelConfigureProject panel, NewPythonProjectWizardIterator.WizardType type) {
   68.88 +        initComponents();
   68.89 +        this.panel = panel;
   68.90 +        PlatformComponentFactory.addPlatformChangeListener(platforms, new PlatformComponentFactory.PlatformChangeListener() {
   68.91 +            @Override
   68.92 +            public void platformChanged() {
   68.93 +                fireChangeEvent();
   68.94 +            }
   68.95 +        });
   68.96 +
   68.97 +
   68.98 +        switch (type) {
   68.99 +            case APP:
  68.100 +                createMainCheckBox.addActionListener( this );
  68.101 +                createMainCheckBox.setSelected( lastMainClassCheck );
  68.102 +                mainFileTextField.setEnabled( lastMainClassCheck );
  68.103 +                break;
  68.104 +        }
  68.105 +
  68.106 +        this.mainFileTextField.getDocument().addDocumentListener( new DocumentListener () {
  68.107 +
  68.108 +            @Override
  68.109 +            public void insertUpdate(DocumentEvent e) {
  68.110 +                mainFileChanged ();
  68.111 +            }
  68.112 +
  68.113 +            @Override
  68.114 +            public void removeUpdate(DocumentEvent e) {
  68.115 +                mainFileChanged ();
  68.116 +            }
  68.117 +
  68.118 +            @Override
  68.119 +            public void changedUpdate(DocumentEvent e) {
  68.120 +                mainFileChanged ();
  68.121 +            }
  68.122 +
  68.123 +        });
  68.124 +
  68.125 +    }
  68.126 +
  68.127 +    @Override
  68.128 +    public void actionPerformed( ActionEvent e ) {
  68.129 +        if ( e.getSource() == createMainCheckBox ) {
  68.130 +            lastMainClassCheck = createMainCheckBox.isSelected();
  68.131 +            mainFileTextField.setEnabled( lastMainClassCheck );
  68.132 +            this.panel.fireChangeEvent();
  68.133 +        }
  68.134 +    }
  68.135 +
  68.136 +    @Override
  68.137 +    public void propertyChange (PropertyChangeEvent event) {
  68.138 +        // The project name isn't very python'y
  68.139 +        if (NewPythonProjectWizardIterator.PROP_PROJECT_NAME.equals(event.getPropertyName())) {
  68.140 +            String newProjectName = (String) event.getNewValue();
  68.141 +            this.mainFileTextField.setText (NbBundle.getMessage(PanelOptionsVisual.class, "TXT_MainFileName",newProjectName.toLowerCase()));
  68.142 +        }
  68.143 +        if (NewPythonProjectWizardIterator.PROP_PROJECT_LOCATION.equals(event.getPropertyName())) {
  68.144 +            projectLocation = (String)event.getNewValue();
  68.145 +        }
  68.146 +    }
  68.147 +
  68.148 +    /** This method is called from within the constructor to
  68.149 +     * initialize the form.
  68.150 +     * WARNING: Do NOT modify this code. The content of this method is
  68.151 +     * always regenerated by the Form Editor.
  68.152 +     */
  68.153 +    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  68.154 +    private void initComponents() {
  68.155 +
  68.156 +        createMainCheckBox = new javax.swing.JCheckBox();
  68.157 +        mainFileTextField = new javax.swing.JTextField();
  68.158 +        jLabel1 = new javax.swing.JLabel();
  68.159 +        platforms = org.netbeans.modules.python.api.PlatformComponentFactory.getPythonPlatformsComboxBox();
  68.160 +        manage = new javax.swing.JButton();
  68.161 +
  68.162 +        createMainCheckBox.setSelected(true);
  68.163 +        org.openide.awt.Mnemonics.setLocalizedText(createMainCheckBox, org.openide.util.NbBundle.getBundle(PanelOptionsVisual.class).getString("LBL_createMainCheckBox")); // NOI18N
  68.164 +
  68.165 +        mainFileTextField.setText("main");
  68.166 +
  68.167 +        jLabel1.setLabelFor(platforms);
  68.168 +        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(PanelOptionsVisual.class, "TXT_PythonPlatform")); // NOI18N
  68.169 +
  68.170 +        org.openide.awt.Mnemonics.setLocalizedText(manage, org.openide.util.NbBundle.getMessage(PanelOptionsVisual.class, "TXT_ManagePlatfroms")); // NOI18N
  68.171 +        manage.addActionListener(new java.awt.event.ActionListener() {
  68.172 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
  68.173 +                manageActionPerformed(evt);
  68.174 +            }
  68.175 +        });
  68.176 +
  68.177 +        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
  68.178 +        this.setLayout(layout);
  68.179 +        layout.setHorizontalGroup(
  68.180 +            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  68.181 +            .addGroup(layout.createSequentialGroup()
  68.182 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  68.183 +                    .addComponent(createMainCheckBox)
  68.184 +                    .addComponent(jLabel1))
  68.185 +                .addGap(18, 18, 18)
  68.186 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  68.187 +                    .addGroup(layout.createSequentialGroup()
  68.188 +                        .addComponent(platforms, javax.swing.GroupLayout.PREFERRED_SIZE, 233, javax.swing.GroupLayout.PREFERRED_SIZE)
  68.189 +                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
  68.190 +                        .addComponent(manage)
  68.191 +                        .addGap(4, 4, 4))
  68.192 +                    .addComponent(mainFileTextField)))
  68.193 +        );
  68.194 +        layout.setVerticalGroup(
  68.195 +            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  68.196 +            .addGroup(layout.createSequentialGroup()
  68.197 +                .addContainerGap()
  68.198 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  68.199 +                    .addComponent(createMainCheckBox)
  68.200 +                    .addComponent(mainFileTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
  68.201 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  68.202 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  68.203 +                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  68.204 +                        .addComponent(jLabel1)
  68.205 +                        .addComponent(platforms, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
  68.206 +                    .addComponent(manage))
  68.207 +                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
  68.208 +        );
  68.209 +
  68.210 +        createMainCheckBox.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getBundle(PanelOptionsVisual.class).getString("ACSN_createMainCheckBox")); // NOI18N
  68.211 +        createMainCheckBox.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getBundle(PanelOptionsVisual.class).getString("ACSD_createMainCheckBox")); // NOI18N
  68.212 +        mainFileTextField.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getBundle(PanelOptionsVisual.class).getString("ASCN_mainClassTextFiled")); // NOI18N
  68.213 +        mainFileTextField.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getBundle(PanelOptionsVisual.class).getString("ASCD_mainClassTextFiled")); // NOI18N
  68.214 +
  68.215 +        getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(PanelOptionsVisual.class, "ACSN_PanelOptionsVisual")); // NOI18N
  68.216 +        getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(PanelOptionsVisual.class, "ACSD_PanelOptionsVisual")); // NOI18N
  68.217 +    }// </editor-fold>//GEN-END:initComponents
  68.218 +
  68.219 +private void manageActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_manageActionPerformed
  68.220 +//Workaround, Needs an API to display platform customizer
  68.221 +    final FileObject fo = FileUtil.getConfigFile("Actions/Python/org-netbeans-modules-python-platform-PythonManagerAction.instance");  //NOI18N
  68.222 +    if (fo != null) {
  68.223 +        try {
  68.224 +            InstanceDataObject ido = (InstanceDataObject) DataObject.find(fo);
  68.225 +            CallableSystemAction action = (CallableSystemAction) ido.instanceCreate();
  68.226 +            action.performAction();
  68.227 +            platforms.setModel(Utils.createPlatformModel()); //Currentl the PythonManager doesn't fire events, we need to replace model.
  68.228 +        } catch (IOException | ClassNotFoundException ex) {
  68.229 +            Exceptions.printStackTrace(ex);
  68.230 +        }
  68.231 +    }
  68.232 +}//GEN-LAST:event_manageActionPerformed
  68.233 +
  68.234 +
  68.235 +
  68.236 +    @Override
  68.237 +    boolean valid(WizardDescriptor settings) {
  68.238 +        if (PlatformComponentFactory.getPlatform(platforms) == null) {
  68.239 +            // Only complain if there are no available platforms since most likely there's
  68.240 +            // no selection yet because it's busy auto-detecting
  68.241 +            if (PythonPlatformManager.getInstance().getPlatformList().isEmpty()) {
  68.242 +                settings.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE,
  68.243 +                        NbBundle.getMessage(PanelOptionsVisual.class,"ERROR_IllegalPlatform"));
  68.244 +            }
  68.245 +            return false;
  68.246 +        }
  68.247 +        if (mainFileTextField.isVisible () && mainFileTextField.isEnabled ()) {
  68.248 +            if (!valid) {
  68.249 +                settings.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE,
  68.250 +                    NbBundle.getMessage(PanelOptionsVisual.class,"ERROR_IllegalMainFileName")); //NOI18N
  68.251 +            }
  68.252 +            return this.valid;
  68.253 +        }
  68.254 +        else {
  68.255 +            return true;
  68.256 +        }
  68.257 +    }
  68.258 +
  68.259 +    @Override
  68.260 +    void read (WizardDescriptor d) {
  68.261 +        final PythonPlatformManager manager = PythonPlatformManager.getInstance();
  68.262 +        String pid = (String) d.getProperty(NewPythonProjectWizardIterator.PROP_PLATFORM_ID);
  68.263 +        if (pid == null) {
  68.264 +            pid = Util.getPythonPreferences().get(LAST_PLATFORM_ID, null);
  68.265 +            if (pid == null) {
  68.266 +                pid = manager.getDefaultPlatform();
  68.267 +            }
  68.268 +        }
  68.269 +        final PythonPlatform activePlatform = manager.getPlatform(pid);
  68.270 +        if (activePlatform != null) {
  68.271 +            platforms.setSelectedItem(activePlatform);
  68.272 +        }
  68.273 +    }
  68.274 +
  68.275 +    @Override
  68.276 +    void validate (WizardDescriptor d) throws WizardValidationException {
  68.277 +        // nothing to validate
  68.278 +    }
  68.279 +
  68.280 +    @Override
  68.281 +    void store( WizardDescriptor d ) {
  68.282 +        d.putProperty(NewPythonProjectWizardIterator.PROP_MAIN_FILE, createMainCheckBox.isSelected() && createMainCheckBox.isVisible() ? mainFileTextField.getText() : null ); // NOI18N
  68.283 +        PythonPlatform platform = PlatformComponentFactory.getPlatform(platforms);
  68.284 +        if (platform != null) {
  68.285 +            d.putProperty(NewPythonProjectWizardIterator.PROP_PLATFORM_ID, platform.getId());
  68.286 +        }
  68.287 +    }
  68.288 +
  68.289 +    // Variables declaration - do not modify//GEN-BEGIN:variables
  68.290 +    private javax.swing.JCheckBox createMainCheckBox;
  68.291 +    private javax.swing.JLabel jLabel1;
  68.292 +    private javax.swing.JTextField mainFileTextField;
  68.293 +    private javax.swing.JButton manage;
  68.294 +    private javax.swing.JComboBox platforms;
  68.295 +    // End of variables declaration//GEN-END:variables
  68.296 +
  68.297 +    private void mainFileChanged () {
  68.298 +        String mainClassName = this.mainFileTextField.getText ();
  68.299 +        StringTokenizer tk = new StringTokenizer (mainClassName, "."); //NOI18N
  68.300 +        boolean isJavaIdentifier = true;
  68.301 +        while (tk.hasMoreTokens()) {
  68.302 +            String token = tk.nextToken();
  68.303 +            if (token.length() == 0 || !Utilities.isJavaIdentifier(token)) {
  68.304 +                isJavaIdentifier = false;
  68.305 +                break;
  68.306 +            }
  68.307 +        }
  68.308 +        this.valid = isJavaIdentifier;
  68.309 +        this.panel.fireChangeEvent();
  68.310 +    }
  68.311 +
  68.312 +    public @Override void removeNotify() {
  68.313 +        storeWizardPlatform(platforms);
  68.314 +        super.removeNotify();
  68.315 +    }
  68.316 +
  68.317 +    private void fireChangeEvent() {
  68.318 +        this.panel.fireChangeEvent();
  68.319 +    }
  68.320 +
  68.321 +    private static final String LAST_PLATFORM_ID = "projectPanelLastPlatformID"; // NOI18N
  68.322 +
  68.323 +    public static void preselectWizardPlatform(final JComboBox platforms) {
  68.324 +        preselectPlatform(platforms, LAST_PLATFORM_ID);
  68.325 +    }
  68.326 +
  68.327 +    public static void preselectPlatform(final JComboBox platforms, final String preferencePlatformIDKey) {
  68.328 +        String lastPlatformID = Util.getPythonPreferences().get(preferencePlatformIDKey, null);
  68.329 +        if (lastPlatformID != null) {
  68.330 +            PythonPlatform platform = PythonPlatformManager.getInstance().getPlatform(lastPlatformID);
  68.331 +            if (platform != null) {
  68.332 +                platforms.setSelectedItem(platform);
  68.333 +            }
  68.334 +        }
  68.335 +    }
  68.336 +
  68.337 +    public static void storeWizardPlatform(JComboBox platforms) {
  68.338 +        PythonPlatform selectedPlatform = PlatformComponentFactory.getPlatform(platforms);
  68.339 +        if (selectedPlatform != null) {
  68.340 +            Util.getPythonPreferences().put(LAST_PLATFORM_ID, selectedPlatform.getId());
  68.341 +        }
  68.342 +    }
  68.343 +
  68.344 +//
  68.345 +//    public static void storeWizardPlatform(JComboBox platforms) {
  68.346 +//        PythonPlatform selectedPlatform = PlatformComponentFactory.getPlatform(platforms);
  68.347 +//        if (selectedPlatform != null) {
  68.348 +//            RubyPreferences.getPreferences().put(LAST_PLATFORM_ID, selectedPlatform.getID());
  68.349 +//        }
  68.350 +//    }
  68.351 +}
  68.352 +
    69.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    69.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/templates/SettingsPanel.java	Tue Feb 24 01:58:36 2015 -0800
    69.3 @@ -0,0 +1,64 @@
    69.4 +/*
    69.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    69.6 + *
    69.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    69.8 + *
    69.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   69.10 + * Other names may be trademarks of their respective owners.
   69.11 + *
   69.12 + * The contents of this file are subject to the terms of either the GNU
   69.13 + * General Public License Version 2 only ("GPL") or the Common
   69.14 + * Development and Distribution License("CDDL") (collectively, the
   69.15 + * "License"). You may not use this file except in compliance with the
   69.16 + * License. You can obtain a copy of the License at
   69.17 + * http://www.netbeans.org/cddl-gplv2.html
   69.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   69.19 + * specific language governing permissions and limitations under the
   69.20 + * License.  When distributing the software, include this License Header
   69.21 + * Notice in each file and include the License file at
   69.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   69.23 + * particular file as subject to the "Classpath" exception as provided
   69.24 + * by Oracle in the GPL Version 2 section of the License file that
   69.25 + * accompanied this code. If applicable, add the following below the
   69.26 + * License Header, with the fields enclosed by brackets [] replaced by
   69.27 + * your own identifying information:
   69.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   69.29 + *
   69.30 + * Contributor(s):
   69.31 + *
   69.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   69.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   69.34 + * Microsystems, Inc. All Rights Reserved.
   69.35 + *
   69.36 + * If you wish your version of this file to be governed by only the CDDL
   69.37 + * or only the GPL Version 2, indicate your decision by adding
   69.38 + * "[Contributor] elects to include this software in this distribution
   69.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   69.40 + * single choice of license, a recipient has the option to distribute
   69.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   69.42 + * to extend the choice of license to its licensees as provided above.
   69.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   69.44 + * Version 2 license, then the option applies only if the new code is
   69.45 + * made subject to such option by the copyright holder.
   69.46 + */
   69.47 +
   69.48 +package org.netbeans.modules.python.project2.templates;
   69.49 +
   69.50 +import javax.swing.JPanel;
   69.51 +import org.openide.WizardDescriptor;
   69.52 +import org.openide.WizardValidationException;
   69.53 +
   69.54 +/**
   69.55 + * 
   69.56 + * @author Tomas Zezula
   69.57 + */
   69.58 +abstract class SettingsPanel extends JPanel {
   69.59 +
   69.60 +    abstract void store (WizardDescriptor settings);
   69.61 +
   69.62 +    abstract void read (WizardDescriptor settings);
   69.63 +
   69.64 +    abstract boolean valid (WizardDescriptor settings);
   69.65 +
   69.66 +    abstract void validate (WizardDescriptor settings) throws WizardValidationException;
   69.67 +}
    70.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    70.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/Bundle.properties	Tue Feb 24 01:58:36 2015 -0800
    70.3 @@ -0,0 +1,5 @@
    70.4 +# To change this license header, choose License Headers in Project Properties.
    70.5 +# To change this template file, choose Tools | Templates
    70.6 +# and open the template in the editor.
    70.7 +
    70.8 +MainModuleChooser.jLabel1.text=&Main Modules:
    70.9 \ No newline at end of file
    71.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    71.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/ChangePackageViewTypeAction.java	Tue Feb 24 01:58:36 2015 -0800
    71.3 @@ -0,0 +1,101 @@
    71.4 +/*
    71.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    71.6 + *
    71.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    71.8 + *
    71.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   71.10 + * Other names may be trademarks of their respective owners.
   71.11 + *
   71.12 + * The contents of this file are subject to the terms of either the GNU
   71.13 + * General Public License Version 2 only ("GPL") or the Common
   71.14 + * Development and Distribution License("CDDL") (collectively, the
   71.15 + * "License"). You may not use this file except in compliance with the
   71.16 + * License. You can obtain a copy of the License at
   71.17 + * http://www.netbeans.org/cddl-gplv2.html
   71.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   71.19 + * specific language governing permissions and limitations under the
   71.20 + * License.  When distributing the software, include this License Header
   71.21 + * Notice in each file and include the License file at
   71.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   71.23 + * particular file as subject to the "Classpath" exception as provided
   71.24 + * by Oracle in the GPL Version 2 section of the License file that
   71.25 + * accompanied this code. If applicable, add the following below the
   71.26 + * License Header, with the fields enclosed by brackets [] replaced by
   71.27 + * your own identifying information:
   71.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   71.29 + *
   71.30 + * Contributor(s):
   71.31 + *
   71.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   71.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   71.34 + * Microsystems, Inc. All Rights Reserved.
   71.35 + *
   71.36 + * If you wish your version of this file to be governed by only the CDDL
   71.37 + * or only the GPL Version 2, indicate your decision by adding
   71.38 + * "[Contributor] elects to include this software in this distribution
   71.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   71.40 + * single choice of license, a recipient has the option to distribute
   71.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   71.42 + * to extend the choice of license to its licensees as provided above.
   71.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   71.44 + * Version 2 license, then the option applies only if the new code is
   71.45 + * made subject to such option by the copyright holder.
   71.46 + */
   71.47 +
   71.48 +package org.netbeans.modules.python.project2.ui;
   71.49 +
   71.50 +import java.awt.event.ActionEvent;
   71.51 +import java.awt.event.ActionListener;
   71.52 +import javax.swing.AbstractAction;
   71.53 +import javax.swing.JMenu;
   71.54 +import javax.swing.JMenuItem;
   71.55 +import javax.swing.JRadioButtonMenuItem;
   71.56 +import org.openide.awt.Mnemonics;
   71.57 +import org.openide.util.NbBundle;
   71.58 +import org.openide.util.actions.Presenter;
   71.59 +import static org.netbeans.modules.python.project2.ui.Bundle.*;
   71.60 +
   71.61 +/**
   71.62 + * Popup menu in Projects tab permitting you to change the package view type.
   71.63 + *
   71.64 + * <p>
   71.65 + * <b>This is copied from the corresponding Java action in java.projects</b>
   71.66 + * </p>
   71.67 + *
   71.68 + * @author Jesse Glick
   71.69 + */
   71.70 +@NbBundle.Messages({"LBL_change_package_type=&View Python Packages as",
   71.71 +    "ChangePackageViewTypeAction_list=&List",
   71.72 +    "ChangePackageViewTypeAction_tree=&Tree"})
   71.73 +public final class ChangePackageViewTypeAction extends AbstractAction implements Presenter.Popup {
   71.74 +    
   71.75 +    public ChangePackageViewTypeAction() {}
   71.76 +
   71.77 +    @Override
   71.78 +    public void actionPerformed(ActionEvent e) {
   71.79 +        assert false : e;
   71.80 +    }
   71.81 +
   71.82 +    @Override
   71.83 +    public JMenuItem getPopupPresenter() {
   71.84 +        JMenu menu = new JMenu();
   71.85 +        Mnemonics.setLocalizedText(menu, LBL_change_package_type());
   71.86 +        menu.add(createChoice(PythonProjectSettings.TYPE_PACKAGE_VIEW, ChangePackageViewTypeAction_list()));
   71.87 +        menu.add(createChoice(PythonProjectSettings.TYPE_TREE, ChangePackageViewTypeAction_tree()));
   71.88 +        return menu;
   71.89 +    }
   71.90 +    
   71.91 +    private JMenuItem createChoice(final int type, String label) {
   71.92 +        JRadioButtonMenuItem item = new JRadioButtonMenuItem();
   71.93 +        Mnemonics.setLocalizedText(item, label);
   71.94 +        item.setSelected(PythonProjectSettings.getPackageViewType() == type);
   71.95 +        item.addActionListener(new ActionListener() {
   71.96 +            @Override
   71.97 +            public void actionPerformed(ActionEvent e) {
   71.98 +                PythonProjectSettings.setPackageViewType(type);
   71.99 +            }
  71.100 +        });
  71.101 +        return item;
  71.102 +    }
  71.103 +    
  71.104 +}
    72.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    72.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/MainModuleChooser.form	Tue Feb 24 01:58:36 2015 -0800
    72.3 @@ -0,0 +1,60 @@
    72.4 +<?xml version="1.0" encoding="UTF-8" ?>
    72.5 +
    72.6 +<Form version="1.5" maxVersion="1.6" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
    72.7 +  <Properties>
    72.8 +    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
    72.9 +      <Dimension value="[300, 300]"/>
   72.10 +    </Property>
   72.11 +  </Properties>
   72.12 +  <AuxValues>
   72.13 +    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
   72.14 +    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
   72.15 +    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
   72.16 +    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
   72.17 +    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
   72.18 +    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="2"/>
   72.19 +    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
   72.20 +    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
   72.21 +    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
   72.22 +  </AuxValues>
   72.23 +
   72.24 +  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
   72.25 +  <SubComponents>
   72.26 +    <Component class="javax.swing.JLabel" name="jLabel1">
   72.27 +      <Properties>
   72.28 +        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
   72.29 +          <ComponentRef name="mainModules"/>
   72.30 +        </Property>
   72.31 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   72.32 +          <ResourceString bundle="org/netbeans/modules/python/project2/ui/Bundle.properties" key="MainModuleChooser.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   72.33 +        </Property>
   72.34 +      </Properties>
   72.35 +      <AuxValues>
   72.36 +        <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
   72.37 +      </AuxValues>
   72.38 +      <Constraints>
   72.39 +        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
   72.40 +          <GridBagConstraints gridX="-1" gridY="-1" gridWidth="0" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="12" insetsLeft="12" insetsBottom="0" insetsRight="12" anchor="18" weightX="1.0" weightY="0.0"/>
   72.41 +        </Constraint>
   72.42 +      </Constraints>
   72.43 +    </Component>
   72.44 +    <Container class="javax.swing.JScrollPane" name="jScrollPane1">
   72.45 +      <Constraints>
   72.46 +        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
   72.47 +          <GridBagConstraints gridX="-1" gridY="-1" gridWidth="0" gridHeight="0" fill="1" ipadX="0" ipadY="0" insetsTop="12" insetsLeft="12" insetsBottom="12" insetsRight="12" anchor="10" weightX="1.0" weightY="1.0"/>
   72.48 +        </Constraint>
   72.49 +      </Constraints>
   72.50 +
   72.51 +      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
   72.52 +      <SubComponents>
   72.53 +        <Component class="javax.swing.JList" name="mainModules">
   72.54 +          <Properties>
   72.55 +            <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
   72.56 +              <Connection code="new DefaultListModel()" type="code"/>
   72.57 +            </Property>
   72.58 +          </Properties>
   72.59 +        </Component>
   72.60 +      </SubComponents>
   72.61 +    </Container>
   72.62 +  </SubComponents>
   72.63 +</Form>
    73.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    73.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/MainModuleChooser.java	Tue Feb 24 01:58:36 2015 -0800
    73.3 @@ -0,0 +1,137 @@
    73.4 +/*
    73.5 + * MainClassChooser.java
    73.6 + *
    73.7 + * Created on August 22, 2008, 6:07 PM
    73.8 + */
    73.9 +
   73.10 +package org.netbeans.modules.python.project2.ui;
   73.11 +
   73.12 +import java.util.Enumeration;
   73.13 +import java.util.LinkedList;
   73.14 +import java.util.List;
   73.15 +import javax.swing.DefaultListModel;
   73.16 +import javax.swing.JButton;
   73.17 +import javax.swing.SwingUtilities;
   73.18 +import org.netbeans.api.project.ProjectUtils;
   73.19 +import org.netbeans.api.project.SourceGroup;
   73.20 +import org.netbeans.api.project.Sources;
   73.21 +import org.netbeans.modules.python.api.PythonMIMEResolver;
   73.22 +import org.netbeans.modules.python.project2.PythonProject2;
   73.23 +import org.openide.filesystems.FileObject;
   73.24 +import org.openide.filesystems.FileUtil;
   73.25 +import org.openide.util.NbBundle;
   73.26 +import org.openide.util.RequestProcessor;
   73.27 +
   73.28 +/**
   73.29 + *
   73.30 + * @author  Tomas Zezula
   73.31 + */
   73.32 +@NbBundle.Messages({"TXT_PleaseWait=Please Wait..."})
   73.33 +final class MainModuleChooser extends javax.swing.JPanel {
   73.34 +
   73.35 +    private final PythonProject2 project;
   73.36 +    private final JButton okButton;
   73.37 +    private final static RequestProcessor RP = new RequestProcessor("MainModuleChooser");   //NOI18N
   73.38 +    
   73.39 +    /** Creates new form MainClassChooser */
   73.40 +    MainModuleChooser (final PythonProject2 project, final JButton okButton) {
   73.41 +        assert project != null;
   73.42 +        assert okButton != null;
   73.43 +        initComponents();
   73.44 +        this.project = project;
   73.45 +        this.okButton = okButton;
   73.46 +        this.okButton.setEnabled(false);
   73.47 +        ((DefaultListModel)this.mainModules.getModel()).addElement(NbBundle.getMessage(MainModuleChooser.class, "TXT_PleaseWait"));
   73.48 +        RP.post(new Runnable() {
   73.49 +            @Override
   73.50 +            public void run() {
   73.51 +                initData();
   73.52 +            }
   73.53 +        });        
   73.54 +    }
   73.55 +    
   73.56 +    public String getMainModule () {
   73.57 +        return (String) mainModules.getSelectedValue();
   73.58 +    }
   73.59 +    
   73.60 +    
   73.61 +    private void initData () {
   73.62 +        final List<String> data = new LinkedList<>();
   73.63 +        Sources sources = ProjectUtils.getSources(project);
   73.64 +        for (SourceGroup sourceGroup : sources.getSourceGroups(PythonProject2.SOURCES_TYPE_PYTHON)) {
   73.65 +            final Enumeration<? extends FileObject> fos = sourceGroup.getRootFolder().getChildren(true);
   73.66 +            while (fos.hasMoreElements()) {
   73.67 +                final FileObject fo = fos.nextElement();
   73.68 +                if (isPython (fo)) {
   73.69 +                    data.add(FileUtil.getRelativePath(sourceGroup.getRootFolder(), fo));
   73.70 +                }
   73.71 +            }
   73.72 +        }
   73.73 +        SwingUtilities.invokeLater(new Runnable() {
   73.74 +            @Override
   73.75 +            public void run() {
   73.76 +                DefaultListModel lm = (DefaultListModel)mainModules.getModel();
   73.77 +                lm.clear();
   73.78 +                for (String s : data) {
   73.79 +                    lm.addElement(s);
   73.80 +                }
   73.81 +                okButton.setEnabled(true);
   73.82 +            }
   73.83 +        });        
   73.84 +    }
   73.85 +    
   73.86 +    private static boolean isPython (final FileObject fo) {
   73.87 +        if (fo.isFolder() || !fo.isValid() || fo.isVirtual()) {
   73.88 +            return false;
   73.89 +        }
   73.90 +        return PythonMIMEResolver.PYTHON_MIME_TYPE.equals(FileUtil.getMIMEType(fo));
   73.91 +    }
   73.92 +
   73.93 +    /** This method is called from within the constructor to
   73.94 +     * initialize the form.
   73.95 +     * WARNING: Do NOT modify this code. The content of this method is
   73.96 +     * always regenerated by the Form Editor.
   73.97 +     */
   73.98 +    @SuppressWarnings("unchecked")
   73.99 +    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  73.100 +    private void initComponents() {
  73.101 +        java.awt.GridBagConstraints gridBagConstraints;
  73.102 +
  73.103 +        jLabel1 = new javax.swing.JLabel();
  73.104 +        jScrollPane1 = new javax.swing.JScrollPane();
  73.105 +        mainModules = new javax.swing.JList();
  73.106 +
  73.107 +        setPreferredSize(new java.awt.Dimension(300, 300));
  73.108 +        setLayout(new java.awt.GridBagLayout());
  73.109 +
  73.110 +        jLabel1.setLabelFor(mainModules);
  73.111 +        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(MainModuleChooser.class, "MainModuleChooser.jLabel1.text")); // NOI18N
  73.112 +        gridBagConstraints = new java.awt.GridBagConstraints();
  73.113 +        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
  73.114 +        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
  73.115 +        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
  73.116 +        gridBagConstraints.weightx = 1.0;
  73.117 +        gridBagConstraints.insets = new java.awt.Insets(12, 12, 0, 12);
  73.118 +        add(jLabel1, gridBagConstraints);
  73.119 +
  73.120 +        mainModules.setModel(new DefaultListModel());
  73.121 +        jScrollPane1.setViewportView(mainModules);
  73.122 +
  73.123 +        gridBagConstraints = new java.awt.GridBagConstraints();
  73.124 +        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
  73.125 +        gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER;
  73.126 +        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
  73.127 +        gridBagConstraints.weightx = 1.0;
  73.128 +        gridBagConstraints.weighty = 1.0;
  73.129 +        gridBagConstraints.insets = new java.awt.Insets(12, 12, 12, 12);
  73.130 +        add(jScrollPane1, gridBagConstraints);
  73.131 +    }// </editor-fold>//GEN-END:initComponents
  73.132 +
  73.133 +
  73.134 +    // Variables declaration - do not modify//GEN-BEGIN:variables
  73.135 +    private javax.swing.JLabel jLabel1;
  73.136 +    private javax.swing.JScrollPane jScrollPane1;
  73.137 +    private javax.swing.JList mainModules;
  73.138 +    // End of variables declaration//GEN-END:variables
  73.139 +
  73.140 +}
    74.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    74.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/PackageDisplayUtils.java	Tue Feb 24 01:58:36 2015 -0800
    74.3 @@ -0,0 +1,222 @@
    74.4 +/*
    74.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    74.6 + *
    74.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    74.8 + *
    74.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   74.10 + * Other names may be trademarks of their respective owners.
   74.11 + *
   74.12 + * The contents of this file are subject to the terms of either the GNU
   74.13 + * General Public License Version 2 only ("GPL") or the Common
   74.14 + * Development and Distribution License("CDDL") (collectively, the
   74.15 + * "License"). You may not use this file except in compliance with the
   74.16 + * License. You can obtain a copy of the License at
   74.17 + * http://www.netbeans.org/cddl-gplv2.html
   74.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   74.19 + * specific language governing permissions and limitations under the
   74.20 + * License.  When distributing the software, include this License Header
   74.21 + * Notice in each file and include the License file at
   74.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   74.23 + * particular file as subject to the "Classpath" exception as provided
   74.24 + * by Oracle in the GPL Version 2 section of the License file that
   74.25 + * accompanied this code. If applicable, add the following below the
   74.26 + * License Header, with the fields enclosed by brackets [] replaced by
   74.27 + * your own identifying information:
   74.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   74.29 + *
   74.30 + * Contributor(s):
   74.31 + *
   74.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   74.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   74.34 + * Microsystems, Inc. All Rights Reserved.
   74.35 + *
   74.36 + * If you wish your version of this file to be governed by only the CDDL
   74.37 + * or only the GPL Version 2, indicate your decision by adding
   74.38 + * "[Contributor] elects to include this software in this distribution
   74.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   74.40 + * single choice of license, a recipient has the option to distribute
   74.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   74.42 + * to extend the choice of license to its licensees as provided above.
   74.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   74.44 + * Version 2 license, then the option applies only if the new code is
   74.45 + * made subject to such option by the copyright holder.
   74.46 + */
   74.47 +
   74.48 +package org.netbeans.modules.python.project2.ui;
   74.49 +
   74.50 +import java.awt.Image;
   74.51 +//import org.netbeans.api.java.queries.AccessibilityQuery;
   74.52 +import org.netbeans.api.queries.VisibilityQuery;
   74.53 +import org.openide.filesystems.FileObject;
   74.54 +import org.openide.util.ImageUtilities;
   74.55 +import org.openide.util.NbBundle;
   74.56 +
   74.57 +// XXX needs unit test
   74.58 +
   74.59 +/**
   74.60 + * Provides display name and icon utilities for
   74.61 + * {@link PackageViewChildren.PackageNode} and {@link PackageListView.PackageItem}.
   74.62 + *
   74.63 + * <p>
   74.64 + * <b>This is copied from the corresponding Java action in java.projects</b>
   74.65 + * </p>
   74.66 + *
   74.67 + *
   74.68 + * @author Jesse Glick
   74.69 + */
   74.70 +@NbBundle.Messages({"#LBL_DefaultPackage=<default package>",
   74.71 +    "LBL_DefaultPackage=<Top Level>",
   74.72 +    "# {0} - full package name",
   74.73 +    "LBL_package= PythonPackage ({0})",
   74.74 +    "# {0} - full package name",
   74.75 +    "LBL_public_package=Exported Python Source Package ({0})",
   74.76 +    "# {0} - full package name",
   74.77 +    "LBL_private_package=Private Python Source Package ({0})"})
   74.78 +public final class PackageDisplayUtils {
   74.79 +
   74.80 +    private PackageDisplayUtils() {}
   74.81 +
   74.82 +    private static final Image PACKAGE = ImageUtilities.loadImage("org/netbeans/modules/python/project2/resources/package.gif"); // NOI18N
   74.83 +    private static final Image PACKAGE_EMPTY = ImageUtilities.loadImage("org/netbeans/modules/python/project2/resources/packageEmpty.gif"); // NOI18N
   74.84 +    private static final Image PACKAGE_PRIVATE = ImageUtilities.loadImage("org/netbeans/modules/python/project2/resources/packagePrivate.gif"); // NOI18N
   74.85 +    private static final Image PACKAGE_PUBLIC = ImageUtilities.loadImage("org/netbeans/modules/python/project2/resources/packagePublic.gif"); // NOI18N
   74.86 +
   74.87 +    /**
   74.88 +     * Find the proper display label for a package.
   74.89 +     * @param pkg the actual folder
   74.90 +     * @param pkgname the dot-separated package name (<code>""</code> for default package)
   74.91 +     * @return an appropriate display label for it
   74.92 +     */
   74.93 +    public static String getDisplayLabel(String pkgname) {
   74.94 +        return computePackageName(pkgname);
   74.95 +    }
   74.96 +    
   74.97 +    /**
   74.98 +     * Find the proper tool tip for a package.
   74.99 +     * May have more info than the display label.
  74.100 +     * @param pkg the actual folder
  74.101 +     * @param pkgname the dot-separated package name (<code>""</code> for default package)
  74.102 +     * @return an appropriate display label for it
  74.103 +     */
  74.104 +    public static String getToolTip(FileObject pkg, String pkgname) {
  74.105 +        String pkglabel = computePackageName(pkgname);
  74.106 +//        Boolean b = AccessibilityQuery.isPubliclyAccessible(pkg);
  74.107 +//        if (b != null) {
  74.108 +//            if (b.booleanValue()) {
  74.109 +//                return NbBundle.getMessage(PackageDisplayUtils.class, "LBL_public_package", pkglabel);
  74.110 +//            } else {
  74.111 +//                return NbBundle.getMessage(PackageDisplayUtils.class, "LBL_private_package", pkglabel);
  74.112 +//            }
  74.113 +//        } else {
  74.114 +            return NbBundle.getMessage(PackageDisplayUtils.class, "LBL_package", pkglabel);
  74.115 +//        }
  74.116 +    }
  74.117 +    
  74.118 +    /**
  74.119 +     * Get package name.
  74.120 +     * Handles default package specially.
  74.121 +     */
  74.122 +    private static String computePackageName(String pkgname) {
  74.123 +        if (pkgname.length() == 0) {
  74.124 +            return NbBundle.getMessage(PackageDisplayUtils.class, "LBL_DefaultPackage"); // NOI18N
  74.125 +        } else {
  74.126 +            return pkgname;
  74.127 +        }
  74.128 +    }
  74.129 +
  74.130 +     
  74.131 +    
  74.132 +    /**
  74.133 +     * Find the proper display icon for a package.
  74.134 +     * @param pkg the actual folder
  74.135 +     * @param pkgname the dot-separated package name (<code>""</code> for default package)
  74.136 +     * @return an appropriate display icon for it
  74.137 +     */
  74.138 +    public static Image getIcon(FileObject pkg, String pkgname) {
  74.139 +        return getIcon( pkg, pkgname, isEmpty(pkg) );
  74.140 +    }
  74.141 +    
  74.142 +    /** Performance optimization if the the isEmpty status is already known.
  74.143 +     * 
  74.144 +     */
  74.145 +    public static Image getIcon(FileObject pkg, String pkgname, boolean empty ) {
  74.146 +        if ( empty ) {
  74.147 +            return PACKAGE_EMPTY;
  74.148 +        } else {
  74.149 +//            Boolean b = pkg.isValid() ? AccessibilityQuery.isPubliclyAccessible(pkg) : null;
  74.150 +//            if (b != null) {
  74.151 +//                if (b.booleanValue()) {
  74.152 +//                    return PACKAGE_PUBLIC;
  74.153 +//                } else {
  74.154 +//                    return PACKAGE_PRIVATE;
  74.155 +//                }
  74.156 +//            } else {
  74.157 +                return PACKAGE;
  74.158 +//            }
  74.159 +        }
  74.160 +    }
  74.161 +    
  74.162 +    
  74.163 +    /**
  74.164 +     * Check whether a package is empty (devoid of files except for subpackages).
  74.165 +     */
  74.166 +    public static boolean isEmpty( FileObject fo ) {    
  74.167 +        return isEmpty (fo, true, false );
  74.168 +    }
  74.169 +
  74.170 +    /**
  74.171 +     * Check whether a package is empty (devoid of files except for subpackages).
  74.172 +     * @param recurse specifies whether to check if subpackages are empty too.
  74.173 +     * @param initIsEmpty If true, don't consider __init__.py presence
  74.174 +     */
  74.175 +    public static boolean isEmpty( FileObject fo, boolean recurse, boolean initIsEmpty ) {
  74.176 +        FileObject[] kids = fo.getChildren();
  74.177 +        for( int i = 0; i < kids.length; i++ ) {
  74.178 +            final FileObject kid = kids[i];
  74.179 +            // Package init files don't count unless they have contents (or are pyc files)
  74.180 +            if (initIsEmpty && kid.getName().equals("__init__")) { // NOI18N
  74.181 +                if ("pyc".equals(kid.getExt()) || "pyo".equals(kid.getExt()) || kid.getSize() == 0) { // NOI18N
  74.182 +                    continue;
  74.183 +                }
  74.184 +            }
  74.185 +            // XXX consider using group.contains() here
  74.186 +            if ( !kid.isFolder() && VisibilityQuery.getDefault().isVisible( kid) ) {
  74.187 +                return false;
  74.188 +            }  
  74.189 +            else if (recurse && VisibilityQuery.getDefault().isVisible( kid) && !isEmpty(kid)) {
  74.190 +                    return false;
  74.191 +            }
  74.192 +        }
  74.193 +        return true;
  74.194 +    }
  74.195 +    
  74.196 +    /**
  74.197 +     * Check whether a package should be displayed.
  74.198 +     * It should be displayed if {@link VisibilityQuery} says it should be,
  74.199 +     * and it is either completely empty, or contains files (as opposed to
  74.200 +     * containing some subpackages but no files).
  74.201 +     */
  74.202 +//    public static boolean isSignificant(FileObject pkg) throws IllegalArgumentException {
  74.203 +//        if (!pkg.isFolder()) {
  74.204 +//            throw new IllegalArgumentException("Not a folder"); // NOI18N
  74.205 +//        }
  74.206 +//        // XXX consider using group.contains() here
  74.207 +//        if (!VisibilityQuery.getDefault().isVisible(pkg)) {
  74.208 +//            return false;
  74.209 +//        }
  74.210 +//        FileObject[] kids = pkg.getChildren();
  74.211 +//        boolean subpackages = false;
  74.212 +//        for (int i = 0; i < kids.length; i++) {
  74.213 +//            if (!VisibilityQuery.getDefault().isVisible(kids[i])) {
  74.214 +//                continue;
  74.215 +//            }
  74.216 +//            if (kids[i].isData()) {
  74.217 +//                return true;
  74.218 +//            } else {
  74.219 +//                subpackages = true;
  74.220 +//            }
  74.221 +//        }
  74.222 +//        return !subpackages;
  74.223 +//    }
  74.224 +    
  74.225 +}
    75.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    75.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/PackageRootNode.java	Tue Feb 24 01:58:36 2015 -0800
    75.3 @@ -0,0 +1,449 @@
    75.4 +/*
    75.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    75.6 + *
    75.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    75.8 + *
    75.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   75.10 + * Other names may be trademarks of their respective owners.
   75.11 + *
   75.12 + * The contents of this file are subject to the terms of either the GNU
   75.13 + * General Public License Version 2 only ("GPL") or the Common
   75.14 + * Development and Distribution License("CDDL") (collectively, the
   75.15 + * "License"). You may not use this file except in compliance with the
   75.16 + * License. You can obtain a copy of the License at
   75.17 + * http://www.netbeans.org/cddl-gplv2.html
   75.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   75.19 + * specific language governing permissions and limitations under the
   75.20 + * License.  When distributing the software, include this License Header
   75.21 + * Notice in each file and include the License file at
   75.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   75.23 + * particular file as subject to the "Classpath" exception as provided
   75.24 + * by Oracle in the GPL Version 2 section of the License file that
   75.25 + * accompanied this code. If applicable, add the following below the
   75.26 + * License Header, with the fields enclosed by brackets [] replaced by
   75.27 + * your own identifying information:
   75.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   75.29 + *
   75.30 + * Contributor(s):
   75.31 + *
   75.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   75.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   75.34 + * Microsystems, Inc. All Rights Reserved.
   75.35 + *
   75.36 + * If you wish your version of this file to be governed by only the CDDL
   75.37 + * or only the GPL Version 2, indicate your decision by adding
   75.38 + * "[Contributor] elects to include this software in this distribution
   75.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   75.40 + * single choice of license, a recipient has the option to distribute
   75.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   75.42 + * to extend the choice of license to its licensees as provided above.
   75.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   75.44 + * Version 2 license, then the option applies only if the new code is
   75.45 + * made subject to such option by the copyright holder.
   75.46 + */
   75.47 +
   75.48 +package org.netbeans.modules.python.project2.ui;
   75.49 +
   75.50 +import java.awt.Image;
   75.51 +import java.awt.datatransfer.DataFlavor;
   75.52 +import java.awt.datatransfer.Transferable;
   75.53 +import java.awt.datatransfer.UnsupportedFlavorException;
   75.54 +import java.io.IOException;
   75.55 +import java.util.ArrayList;
   75.56 +import java.util.Arrays;
   75.57 +import java.util.Collections;
   75.58 +import java.util.Iterator;
   75.59 +import java.util.List;
   75.60 +import java.util.Set;
   75.61 +import javax.swing.Action;
   75.62 +import javax.swing.Icon;
   75.63 +import org.netbeans.api.project.SourceGroup;
   75.64 +import org.netbeans.spi.project.ui.support.CommonProjectActions;
   75.65 +import org.openide.ErrorManager;
   75.66 +import org.openide.actions.FileSystemAction;
   75.67 +import org.openide.actions.FindAction;
   75.68 +import org.openide.actions.PasteAction;
   75.69 +import org.openide.actions.ToolsAction;
   75.70 +import org.openide.filesystems.FileObject;
   75.71 +import org.openide.filesystems.FileStateInvalidException;
   75.72 +import org.openide.filesystems.FileStatusEvent;
   75.73 +import org.openide.filesystems.FileStatusListener;
   75.74 +import org.openide.filesystems.FileSystem;
   75.75 +import org.openide.filesystems.FileUtil;
   75.76 +import org.openide.loaders.DataFolder;
   75.77 +import org.openide.loaders.DataObject;
   75.78 +import org.openide.nodes.AbstractNode;
   75.79 +import org.openide.nodes.Children;
   75.80 +import org.openide.nodes.Node;
   75.81 +import org.openide.nodes.Node.PropertySet;
   75.82 +import org.openide.nodes.NodeNotFoundException;
   75.83 +import org.openide.nodes.NodeOp;
   75.84 +import org.openide.nodes.PropertySupport;
   75.85 +import org.openide.nodes.Sheet;
   75.86 +import org.openide.util.ImageUtilities;
   75.87 +import org.openide.util.Lookup;
   75.88 +import org.openide.util.NbBundle;
   75.89 +import org.openide.util.RequestProcessor;
   75.90 +import org.openide.util.actions.SystemAction;
   75.91 +import org.openide.util.datatransfer.ExTransferable;
   75.92 +import org.openide.util.datatransfer.MultiTransferObject;
   75.93 +import org.openide.util.datatransfer.PasteType;
   75.94 +import org.openide.util.lookup.AbstractLookup;
   75.95 +import org.openide.util.lookup.InstanceContent;
   75.96 +import org.openide.util.lookup.Lookups;
   75.97 +import org.openide.util.lookup.ProxyLookup;
   75.98 +//import org.openidex.search.SearchInfo;
   75.99 +//import org.openidex.search.SearchInfoFactory;
  75.100 +
  75.101 +/** Node displaying a packages in given SourceGroup
  75.102 + * 
  75.103 + * <p>
  75.104 + * <b>This is copied from the corresponding Java action in java.projects</b>
  75.105 + * </p>
  75.106 + *
  75.107 + * @author Petr Hrebejk
  75.108 + */
  75.109 +final class PackageRootNode extends AbstractNode implements Runnable, FileStatusListener {
  75.110 +
  75.111 +    static Image PACKAGE_BADGE = ImageUtilities.loadImage("org/netbeans/modules/python/project2/resources/packageBadge.gif"); // NOI18N
  75.112 +        
  75.113 +    private static Action actions[]; 
  75.114 +
  75.115 +    private SourceGroup group;
  75.116 +
  75.117 +    private final FileObject file;
  75.118 +    private final Set<FileObject> files;
  75.119 +    private FileStatusListener fileSystemListener;
  75.120 +    private static final RequestProcessor RP = new RequestProcessor(PackageRootNode.class);
  75.121 +    private RequestProcessor.Task task;
  75.122 +    private volatile boolean iconChange;
  75.123 +    private volatile boolean nameChange;
  75.124 +    
  75.125 +    PackageRootNode( SourceGroup group ) {
  75.126 +        this( group, new InstanceContent() );
  75.127 +    }
  75.128 +    
  75.129 +    private PackageRootNode( SourceGroup group, InstanceContent ic ) {
  75.130 +        super( new PackageViewChildren(group),
  75.131 +                new ProxyLookup(createLookup(group), new AbstractLookup(ic)));
  75.132 +//        ic.add(alwaysSearchableSearchInfo(SearchInfoFactory.createSearchInfoBySubnodes(this)));
  75.133 +        this.group = group;
  75.134 +        file = group.getRootFolder();
  75.135 +        files = Collections.singleton(file);
  75.136 +        try {
  75.137 +            FileSystem fs = file.getFileSystem();
  75.138 +            fileSystemListener = FileUtil.weakFileStatusListener(this, fs);
  75.139 +            fs.addFileStatusListener(fileSystemListener);
  75.140 +        } catch (FileStateInvalidException e) {
  75.141 +            ErrorManager err = ErrorManager.getDefault();
  75.142 +            err.annotate(e, "Can not get " + file + " filesystem, ignoring...");  // NO18N
  75.143 +            err.notify(ErrorManager.INFORMATIONAL, e);
  75.144 +        }
  75.145 +        setName( group.getName() );
  75.146 +        setDisplayName( group.getDisplayName() );        
  75.147 +        // setIconBase("org/netbeans/modules/java/j2seproject/ui/resources/packageRoot");
  75.148 +    }
  75.149 +
  75.150 +    public @Override Image getIcon(int type) {
  75.151 +        return computeIcon( false, type );
  75.152 +    }
  75.153 +        
  75.154 +    public @Override Image getOpenedIcon(int type) {
  75.155 +        return computeIcon( true, type );
  75.156 +    }
  75.157 +    
  75.158 +    public @Override String getDisplayName() {
  75.159 +        String s = super.getDisplayName ();
  75.160 +
  75.161 +        try {            
  75.162 +            s = file.getFileSystem ().getStatus ().annotateName (s, files);
  75.163 +        } catch (FileStateInvalidException e) {
  75.164 +            ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
  75.165 +        }
  75.166 +
  75.167 +        return s;
  75.168 +    }
  75.169 +
  75.170 +    public @Override String getHtmlDisplayName() {
  75.171 +         try {
  75.172 +             FileSystem.Status stat = file.getFileSystem().getStatus();
  75.173 +             if (stat instanceof FileSystem.HtmlStatus) {
  75.174 +                 FileSystem.HtmlStatus hstat = (FileSystem.HtmlStatus) stat;
  75.175 +
  75.176 +                 String result = hstat.annotateNameHtml (
  75.177 +                     super.getDisplayName(), files);
  75.178 +
  75.179 +                 //Make sure the super string was really modified
  75.180 +                 if (!super.getDisplayName().equals(result)) {
  75.181 +                     return result;
  75.182 +                 }
  75.183 +             }
  75.184 +         } catch (FileStateInvalidException e) {
  75.185 +             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
  75.186 +         }
  75.187 +         return super.getHtmlDisplayName();
  75.188 +    }
  75.189 +
  75.190 +    @Override
  75.191 +    public void run() {
  75.192 +        if (iconChange) {
  75.193 +            fireIconChange();
  75.194 +            fireOpenedIconChange();
  75.195 +            iconChange = false;
  75.196 +        }
  75.197 +        if (nameChange) {
  75.198 +            fireDisplayNameChange(null, null);
  75.199 +            nameChange = false;
  75.200 +        }
  75.201 +    }
  75.202 +
  75.203 +    @Override
  75.204 +    public void annotationChanged(FileStatusEvent event) {
  75.205 +        if (task == null) {
  75.206 +            task = RP.create(this);
  75.207 +        }
  75.208 +
  75.209 +        if ((iconChange == false && event.isIconChange())  || (nameChange == false && event.isNameChange())) {
  75.210 +            if (event.hasChanged(file)) {
  75.211 +                iconChange |= event.isIconChange();
  75.212 +                nameChange |= event.isNameChange();
  75.213 +            }
  75.214 +        }
  75.215 +
  75.216 +        task.schedule(50);  // batch by 50 ms
  75.217 +    }    
  75.218 +    
  75.219 +    public @Override Action[] getActions(boolean context) {
  75.220 +        if ( actions == null ) {
  75.221 +            actions = new Action[] {
  75.222 +                CommonProjectActions.newFileAction(),
  75.223 +                null,
  75.224 +                SystemAction.get( FileSystemAction.class ),
  75.225 +                null,
  75.226 +                SystemAction.get( FindAction.class ),
  75.227 +                null,
  75.228 +                SystemAction.get( PasteAction.class ),
  75.229 +                null,
  75.230 +                SystemAction.get( ToolsAction.class ),
  75.231 +            };
  75.232 +        }
  75.233 +        return actions;            
  75.234 +    }
  75.235 +
  75.236 +    // Show reasonable properties of the DataFolder,
  75.237 +    //it shows the sorting names as rw property, the name as ro property and the path to root as ro property
  75.238 +    public @Override PropertySet[] getPropertySets() {            
  75.239 +        PropertySet[] properties =  getDataFolderNodeDelegate().getPropertySets();
  75.240 +        for (int i=0; i< properties.length; i++) {
  75.241 +            if (Sheet.PROPERTIES.equals(properties[i].getName())) {
  75.242 +                //Replace the Sheet.PROPERTIES by the new one
  75.243 +                //having the ro name property and ro path property
  75.244 +                properties[i] = Sheet.createPropertiesSet();
  75.245 +                ((Sheet.Set) properties[i]).put(new PropertySupport.ReadOnly<String>(DataObject.PROP_NAME, String.class,
  75.246 +                        NbBundle.getMessage(PackageRootNode.class,"PROP_name"), NbBundle.getMessage(PackageRootNode.class,"HINT_name")) {
  75.247 +                    @Override
  75.248 +                    public String getValue() {
  75.249 +                        return PackageRootNode.this.getDisplayName();
  75.250 +                    }
  75.251 +                });
  75.252 +                ((Sheet.Set) properties[i]).put(new PropertySupport.ReadOnly<String>("ROOT_PATH", String.class,    //NOI18N
  75.253 +NbBundle.getMessage(PackageRootNode.class,"PROP_rootpath"), NbBundle.getMessage(PackageRootNode.class,"HINT_rootpath")) {
  75.254 +                    @Override
  75.255 +                    public String getValue() {
  75.256 +                        return FileUtil.getFileDisplayName(PackageRootNode.this.file);
  75.257 +                    }
  75.258 +                });
  75.259 +            }
  75.260 +        }
  75.261 +        return properties;
  75.262 +    }
  75.263 +
  75.264 +    // XXX Paste types - probably not very nice 
  75.265 +    public @Override void createPasteTypes(Transferable t, List<PasteType> list) {
  75.266 +        if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
  75.267 +            try {
  75.268 +                MultiTransferObject mto = (MultiTransferObject) t.getTransferData(ExTransferable.multiFlavor);
  75.269 +                List<PackageViewChildren.PackageNode> l = new ArrayList<>();
  75.270 +                boolean isPackageFlavor = false;
  75.271 +                boolean hasTheSameRoot = false;
  75.272 +                int op = -1;
  75.273 +                for (int i=0; i < mto.getCount(); i++) {
  75.274 +                    Transferable pt = mto.getTransferableAt(i);
  75.275 +                    DataFlavor[] flavors = mto.getTransferDataFlavors(i);
  75.276 +                    for (int j=0; j< flavors.length; j++) {
  75.277 +                        if (PackageViewChildren.SUBTYPE.equals(flavors[j].getSubType ()) &&
  75.278 +                                PackageViewChildren.PRIMARY_TYPE.equals(flavors[j].getPrimaryType ())) {
  75.279 +                            if (op == -1) {
  75.280 +                                op = Integer.valueOf (flavors[j].getParameter (PackageViewChildren.MASK)).intValue ();
  75.281 +                            }
  75.282 +                            PackageViewChildren.PackageNode pkgNode = (PackageViewChildren.PackageNode) pt.getTransferData(flavors[j]);
  75.283 +                            if ( !((PackageViewChildren)getChildren()).getRoot().equals( pkgNode.getRoot() ) ) {
  75.284 +                                l.add(pkgNode);
  75.285 +                            }
  75.286 +                            else {
  75.287 +                                hasTheSameRoot = true;
  75.288 +                            }
  75.289 +                            isPackageFlavor = true;
  75.290 +                        }
  75.291 +                    }
  75.292 +                }
  75.293 +                if (isPackageFlavor && !hasTheSameRoot) {
  75.294 +                    list.add(new PackageViewChildren.PackagePasteType(this.group.getRootFolder(),
  75.295 +                            l.toArray(new PackageViewChildren.PackageNode[l.size()]),
  75.296 +                            op));
  75.297 +                }
  75.298 +                else if (!isPackageFlavor) {
  75.299 +                    list.addAll( Arrays.asList( getDataFolderNodeDelegate().getPasteTypes( t ) ) );
  75.300 +                }
  75.301 +            } catch (UnsupportedFlavorException | IOException e) {
  75.302 +                ErrorManager.getDefault().notify(e);
  75.303 +            }
  75.304 +        }
  75.305 +        else {
  75.306 +            DataFlavor[] flavors = t.getTransferDataFlavors();
  75.307 +            FileObject root = this.group.getRootFolder();
  75.308 +            boolean isPackageFlavor = false;
  75.309 +            if (root!= null  && root.canWrite()) {
  75.310 +                for (DataFlavor flavor : flavors) {
  75.311 +                    if (PackageViewChildren.SUBTYPE.equals(flavor.getSubType ()) &&
  75.312 +                            PackageViewChildren.PRIMARY_TYPE.equals(flavor.getPrimaryType ())) {
  75.313 +                        isPackageFlavor = true;
  75.314 +                        try {
  75.315 +                            int op = Integer.parseInt(flavor.getParameter(PackageViewChildren.MASK));
  75.316 +                            PackageViewChildren.PackageNode pkgNode = (PackageViewChildren.PackageNode) t.getTransferData(flavor);
  75.317 +                            if ( !((PackageViewChildren)getChildren()).getRoot().equals( pkgNode.getRoot() ) ) {
  75.318 +                                list.add(new PackageViewChildren.PackagePasteType (root, new PackageViewChildren.PackageNode[] {pkgNode}, op));
  75.319 +                            }
  75.320 +                        } catch (IOException | UnsupportedFlavorException ioe) {
  75.321 +                            ErrorManager.getDefault().notify(ioe);
  75.322 +                        }
  75.323 +                    }
  75.324 +                }
  75.325 +            }
  75.326 +            if (!isPackageFlavor) {
  75.327 +                list.addAll( Arrays.asList( getDataFolderNodeDelegate().getPasteTypes( t ) ) );
  75.328 +            }
  75.329 +        }
  75.330 +    }
  75.331 +    
  75.332 +    @Override
  75.333 +    public PasteType getDropType(Transferable t, int action, int index) {
  75.334 +        PasteType pasteType = super.getDropType(t, action, index);
  75.335 +        //The pasteType can be:
  75.336 +        // 1) PackagePasteType - the t.flavor is package flavor
  75.337 +        // 2) null or DataPasteType - the t.flavor in not package flavor
  75.338 +        if (pasteType instanceof PackageViewChildren.PackagePasteType) {
  75.339 +            ((PackageViewChildren.PackagePasteType)pasteType).setOperation (action);
  75.340 +        }
  75.341 +        return pasteType;
  75.342 +    }
  75.343 +
  75.344 +    // Private methods ---------------------------------------------------------
  75.345 +    
  75.346 +    private Node getDataFolderNodeDelegate() {
  75.347 +        DataFolder df = getLookup().lookup(DataFolder.class);
  75.348 +        try {
  75.349 +            if (df.isValid()) {
  75.350 +                return df.getNodeDelegate();
  75.351 +            } 
  75.352 +        } catch (IllegalStateException e) {
  75.353 +            //The data systems API is not thread save,
  75.354 +            //the DataObject may become invalid after isValid call and before
  75.355 +            //getNodeDelegate call, we have to catch the ISE. When the DataObject
  75.356 +            //is valid - other cause rethrow it otherwise return leaf node.
  75.357 +            //todo: The DataObject.getNodedelegate should throw specialized exception type.
  75.358 +            if (df.isValid()) {
  75.359 +                throw e;
  75.360 +            }
  75.361 +        }
  75.362 +        return new AbstractNode(Children.LEAF);
  75.363 +    }
  75.364 +    
  75.365 +    private Image computeIcon( boolean opened, int type ) {
  75.366 +        Image image;
  75.367 +        Icon icon = group.getIcon( opened );
  75.368 +        
  75.369 +        if ( icon == null ) {
  75.370 +            image = opened ? getDataFolderNodeDelegate().getOpenedIcon( type ) : 
  75.371 +                             getDataFolderNodeDelegate().getIcon( type );
  75.372 +            image = ImageUtilities.mergeImages(image, PACKAGE_BADGE, 7, 7);
  75.373 +        }
  75.374 +        else {
  75.375 +            image = ImageUtilities.icon2Image(icon);
  75.376 +        }
  75.377 +        
  75.378 +        return image;        
  75.379 +    }
  75.380 +    
  75.381 +    private static Lookup createLookup( SourceGroup group ) {
  75.382 +        // XXX Remove DataFolder when paste, find and refresh are reimplemented
  75.383 +        FileObject rootFolder = group.getRootFolder();
  75.384 +        DataFolder dataFolder = DataFolder.findFolder( rootFolder );        
  75.385 +        return Lookups.fixed(dataFolder, new PathFinder(group));
  75.386 +    }
  75.387 +    
  75.388 +    /** If contained in the lookup can perform the search for a node
  75.389 +     */    
  75.390 +    public static class PathFinder {
  75.391 +        
  75.392 +        private SourceGroup group;
  75.393 +        
  75.394 +        public PathFinder( SourceGroup group ) {
  75.395 +            this.group = group;
  75.396 +        }
  75.397 +        
  75.398 +        public Node findPath( Node root, Object object ) {
  75.399 +            FileObject fo;
  75.400 +            if (object instanceof FileObject) {
  75.401 +                fo = (FileObject) object;
  75.402 +            } else if (object instanceof DataObject) {
  75.403 +                fo = ((DataObject) object).getPrimaryFile();
  75.404 +            } else {
  75.405 +                return null;
  75.406 +            }
  75.407 +            
  75.408 +            FileObject groupRoot = group.getRootFolder();
  75.409 +            if ( FileUtil.isParentOf( groupRoot, fo ) /* && group.contains( fo ) */ ) {
  75.410 +                // The group contains the object
  75.411 +
  75.412 +                String relPath = FileUtil.getRelativePath( groupRoot, fo.isFolder() ? fo : fo.getParent() );
  75.413 +
  75.414 +                String[] path = new String[] { relPath.replace( '/', '.' ) };
  75.415 +                try {
  75.416 +                    Node packageNode = NodeOp.findPath( root, path );
  75.417 +                    if (fo.isFolder()) {
  75.418 +                        return packageNode;
  75.419 +                    } else {
  75.420 +                        for (Node child : packageNode.getChildren().getNodes(true)) {
  75.421 +                           DataObject dobj = child.getLookup().lookup(DataObject.class);
  75.422 +                           if (dobj != null && dobj.getPrimaryFile().getNameExt().equals(fo.getNameExt())) {
  75.423 +                               return child;
  75.424 +                           }
  75.425 +                        }
  75.426 +                    }
  75.427 +                }
  75.428 +                catch ( NodeNotFoundException e ) {
  75.429 +                    // did not manage to find it after all... why?
  75.430 +                    return null;
  75.431 +                }
  75.432 +            }   
  75.433 +            else if ( groupRoot.equals( fo ) ) {
  75.434 +                // First try to find default package
  75.435 +                try {
  75.436 +                    return NodeOp.findPath( root, new String[] { "" } ); // NOI18N
  75.437 +                }
  75.438 +                catch ( NodeNotFoundException e ) {
  75.439 +                    // If it does not exists return this node
  75.440 +                }                        
  75.441 +                return root;
  75.442 +            }
  75.443 +
  75.444 +            return null;
  75.445 +        }
  75.446 +        
  75.447 +        public @Override String toString() {
  75.448 +            return "PathFinder[" + group + "]"; // NOI18N
  75.449 +        }
  75.450 +                    
  75.451 +    }
  75.452 +}
    76.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    76.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/PackageView.java	Tue Feb 24 01:58:36 2015 -0800
    76.3 @@ -0,0 +1,403 @@
    76.4 +/*
    76.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    76.6 + *
    76.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    76.8 + *
    76.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   76.10 + * Other names may be trademarks of their respective owners.
   76.11 + *
   76.12 + * The contents of this file are subject to the terms of either the GNU
   76.13 + * General Public License Version 2 only ("GPL") or the Common
   76.14 + * Development and Distribution License("CDDL") (collectively, the
   76.15 + * "License"). You may not use this file except in compliance with the
   76.16 + * License. You can obtain a copy of the License at
   76.17 + * http://www.netbeans.org/cddl-gplv2.html
   76.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   76.19 + * specific language governing permissions and limitations under the
   76.20 + * License.  When distributing the software, include this License Header
   76.21 + * Notice in each file and include the License file at
   76.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   76.23 + * particular file as subject to the "Classpath" exception as provided
   76.24 + * by Oracle in the GPL Version 2 section of the License file that
   76.25 + * accompanied this code. If applicable, add the following below the
   76.26 + * License Header, with the fields enclosed by brackets [] replaced by
   76.27 + * your own identifying information:
   76.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   76.29 + *
   76.30 + * Contributor(s):
   76.31 + *
   76.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   76.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   76.34 + * Microsystems, Inc. All Rights Reserved.
   76.35 + *
   76.36 + * If you wish your version of this file to be governed by only the CDDL
   76.37 + * or only the GPL Version 2, indicate your decision by adding
   76.38 + * "[Contributor] elects to include this software in this distribution
   76.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   76.40 + * single choice of license, a recipient has the option to distribute
   76.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   76.42 + * to extend the choice of license to its licensees as provided above.
   76.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   76.44 + * Version 2 license, then the option applies only if the new code is
   76.45 + * made subject to such option by the copyright holder.
   76.46 + */
   76.47 +
   76.48 +package org.netbeans.modules.python.project2.ui;
   76.49 +
   76.50 +import java.awt.Component;
   76.51 +import java.awt.EventQueue;
   76.52 +import java.awt.Image;
   76.53 +import java.beans.PropertyChangeEvent;
   76.54 +import java.beans.PropertyChangeListener;
   76.55 +import java.util.ArrayList;
   76.56 +import java.util.Collection;
   76.57 +import java.util.IdentityHashMap;
   76.58 +import java.util.List;
   76.59 +import java.util.Map;
   76.60 +import java.util.SortedSet;
   76.61 +import java.util.TreeSet;
   76.62 +import javax.swing.ComboBoxModel;
   76.63 +import javax.swing.DefaultComboBoxModel;
   76.64 +import javax.swing.Icon;
   76.65 +import javax.swing.ImageIcon;
   76.66 +import javax.swing.JLabel;
   76.67 +import javax.swing.JList;
   76.68 +import javax.swing.ListCellRenderer;
   76.69 +import javax.swing.plaf.UIResource;
   76.70 +import org.netbeans.api.progress.ProgressHandle;
   76.71 +import org.netbeans.api.progress.ProgressHandleFactory;
   76.72 +import org.netbeans.api.project.SourceGroup;
   76.73 +import org.netbeans.api.queries.VisibilityQuery;
   76.74 +import org.openide.filesystems.FileObject;
   76.75 +import org.openide.filesystems.FileUtil;
   76.76 +import org.openide.nodes.AbstractNode;
   76.77 +import org.openide.nodes.FilterNode;
   76.78 +import org.openide.nodes.Node;
   76.79 +import org.openide.util.NbBundle;
   76.80 +import org.openide.util.Parameters;
   76.81 +import org.openide.util.WeakListeners;
   76.82 +
   76.83 +/**
   76.84 + * Factory for package views.
   76.85 + * @see org.netbeans.spi.project.ui.LogicalViewProvider
   76.86 + * <p>
   76.87 + * <b>This is copied from the corresponding Java action in java.projects</b>
   76.88 + * </p>
   76.89 + *
   76.90 + * @author Jesse Glick
   76.91 + */
   76.92 +@NbBundle.Messages({"# {0} - Filename", "PackageView.find_packages_progress=Finding packages in {0}"})
   76.93 +public class PackageView {
   76.94 +        
   76.95 +    private PackageView() {}
   76.96 +    
   76.97 +    /**
   76.98 +     * Create a node which will contain package-oriented view of a source group.
   76.99 +     * <p>
  76.100 +     * The precise structure of this node is <em>not</em> specified by the API
  76.101 +     * and is subject to arbitrary change (perhaps at user option).
  76.102 +     * Callers should not make assumptions about the nature of subnodes, the
  76.103 +     * code or display names of certain nodes, and so on. You may use cookies/lookup
  76.104 +     * to find if particular subnodes correspond to folders or files.
  76.105 +     * </p>
  76.106 +     * @param group a source group which should be represented
  76.107 +     * @return node which will display packages in given group
  76.108 +     */
  76.109 +    public static Node createPackageView( SourceGroup group ) {
  76.110 +        return new RootNode (group);                
  76.111 +    }
  76.112 +    
  76.113 +    /**
  76.114 +     * Finds the node representing given object, if any.
  76.115 +     * The current implementation works only for {@link org.openide.filesystems.FileObject}s
  76.116 +     * and {@link org.openide.loaders.DataObject}s.
  76.117 +     * @param rootNode a node some descendant of which should contain the object
  76.118 +     * @param object object to find
  76.119 +     * @return a node representing the given object, or null if no such node was found
  76.120 +     */
  76.121 +    public static Node findPath(Node rootNode, Object object) {
  76.122 +        
  76.123 +        PackageRootNode.PathFinder pf = rootNode.getLookup().lookup(PackageRootNode.PathFinder.class);
  76.124 +        
  76.125 +        if ( pf != null ) {
  76.126 +            return pf.findPath( rootNode, object );
  76.127 +        } else {
  76.128 +            TreeRootNode.PathFinder pf2 = rootNode.getLookup().lookup(TreeRootNode.PathFinder.class);
  76.129 +            if (pf2 != null) {
  76.130 +                return pf2.findPath(rootNode, object);
  76.131 +            } else {
  76.132 +                return null;
  76.133 +            }
  76.134 +        }
  76.135 +    }
  76.136 +    
  76.137 +    /**
  76.138 +     * Create a list or combo box model suitable for {@link javax.swing.JList} from a source group
  76.139 +     * showing all Java packages in the source group.
  76.140 +     * To display it you will also need {@link #listRenderer}.
  76.141 +     * <p>No particular guarantees are made as to the nature of the model objects themselves,
  76.142 +     * except that {@link Object#toString} will give the fully-qualified package name
  76.143 +     * (or <code>""</code> for the default package), regardless of what the renderer
  76.144 +     * actually displays.</p>
  76.145 +     * @param group a Java-like source group
  76.146 +     * @return a model of its packages
  76.147 +     * @since org.netbeans.modules.java.project/1 1.3 
  76.148 +     */
  76.149 +    
  76.150 +    public static ComboBoxModel createListView(SourceGroup group) {
  76.151 +        Parameters.notNull("group", group); //NOI18N
  76.152 +        SortedSet<PackageItem> data = new TreeSet<>();
  76.153 +        findNonExcludedPackages(null, data, group.getRootFolder(), group, false);
  76.154 +        return new DefaultComboBoxModel(data.toArray(new PackageItem[data.size()]));
  76.155 +    }
  76.156 +    
  76.157 +    /** Fills given collection with flattened packages under given folder
  76.158 +     *@param target The collection to be filled
  76.159 +     *@param fo The folder to be scanned
  76.160 +     * @param group the group to scan
  76.161 +     * @param createPackageItems if false the collection will be filled with file objects; if
  76.162 +     *       true PackageItems will be created.
  76.163 +     * @param showProgress whether to show a progress handle or not
  76.164 +     */
  76.165 +    static void findNonExcludedPackages(PackageViewChildren children, Collection<PackageItem> target, FileObject fo, SourceGroup group, boolean showProgress) {
  76.166 +        if (showProgress) {
  76.167 +            ProgressHandle progress = ProgressHandleFactory.createHandle(NbBundle.getMessage(PackageView.class, "PackageView.find_packages_progress", FileUtil.getFileDisplayName(fo)));
  76.168 +            progress.start(1000);
  76.169 +            findNonExcludedPackages(children, target, fo, group, progress, 0, 1000);
  76.170 +            progress.finish();
  76.171 +        } else {
  76.172 +            findNonExcludedPackages(children, target, fo, group, null, 0, 0);
  76.173 +        }
  76.174 +    }
  76.175 +
  76.176 +    private static void findNonExcludedPackages(PackageViewChildren children, Collection<PackageItem> target, FileObject fo, SourceGroup group, ProgressHandle progress, int start, int end) {
  76.177 +        
  76.178 +        assert fo.isFolder() : "Package view only accepts folders"; // NOI18N
  76.179 +        
  76.180 +        if (progress != null) {
  76.181 +            String path = FileUtil.getRelativePath(children.getRoot(), fo);
  76.182 +            assert path != null : fo + " in " + children.getRoot();
  76.183 +            progress.progress(path.replace('/', '.'), start);
  76.184 +        }
  76.185 +        
  76.186 +        if (!fo.isValid()) {
  76.187 +            return;
  76.188 +        }
  76.189 +               
  76.190 +        if ( !VisibilityQuery.getDefault().isVisible( fo ) ) {
  76.191 +            return; // Don't show hidden packages
  76.192 +        }
  76.193 +        
  76.194 +        boolean hasSubfolders = false;
  76.195 +        boolean hasFiles = false;
  76.196 +        List<FileObject> folders = new ArrayList<>();
  76.197 +        for (FileObject kid : fo.getChildren()) {
  76.198 +            if (kid.isValid() && VisibilityQuery.getDefault().isVisible(kid) && group.contains(kid)) {
  76.199 +                if (kid.isFolder()) {
  76.200 +                    FileObject init = kid.getFileObject("__init__", "py"); //NOI18N
  76.201 +                    if(init != null) {
  76.202 +                        folders.add(kid);
  76.203 +                        hasSubfolders = true;
  76.204 +                    }
  76.205 +                } 
  76.206 +                else {
  76.207 +                    hasFiles = true;
  76.208 +                }
  76.209 +            }
  76.210 +        }
  76.211 +        if (hasFiles || !hasSubfolders) {
  76.212 +            if (target != null) {
  76.213 +                target.add( new PackageItem(group, fo, !hasFiles ) );
  76.214 +            }
  76.215 +            else {
  76.216 +                if (fo.isValid()) {
  76.217 +                    children.add(fo, !hasFiles, false);
  76.218 +                }
  76.219 +            }
  76.220 +        }
  76.221 +        if (!folders.isEmpty()) {
  76.222 +            int diff = (end - start) / folders.size();
  76.223 +            int c = 0;
  76.224 +            for (FileObject kid : folders) {
  76.225 +                // Do this after adding the parent, so we get a pre-order traversal.
  76.226 +                // Also see PackageViewChildren.findChild: prefer to get root first.
  76.227 +                findNonExcludedPackages(children, target, kid, group, progress, start + c * diff, start + (c + 1) * diff);
  76.228 +                c++;
  76.229 +            }
  76.230 +        }
  76.231 +    }
  76.232 +         
  76.233 +//    public static ComboBoxModel createListView(SourceGroup group) {
  76.234 +//        DefaultListModel model = new DefaultListModel();
  76.235 +//        SortedSet/*<PackageItem>*/ items = new TreeSet();
  76.236 +//        FileObject root = group.getRootFolder();
  76.237 +//        if (PackageDisplayUtils.isSignificant(root)) {
  76.238 +//            items.add(new PackageItem(group, root));
  76.239 +//        }
  76.240 +//        Enumeration/*<FileObject>*/ files = root.getChildren(true);
  76.241 +//        while (files.hasMoreElements()) {
  76.242 +//            FileObject f = (FileObject) files.nextElement();
  76.243 +//            if (f.isFolder() && PackageDisplayUtils.isSignificant(f)) {
  76.244 +//                items.add(new PackageItem(group, f));
  76.245 +//            }
  76.246 +//        }
  76.247 +//        return new DefaultComboBoxModel(items.toArray(new PackageItem[items.size()]));
  76.248 +//    }
  76.249 +    
  76.250 +    
  76.251 +    /**
  76.252 +     * Create a renderer suited to rendering models created using {@link #createListView}.
  76.253 +     * The exact nature of the display is not specified.
  76.254 +     * Instances of String can also be rendered.
  76.255 +     * @return a suitable package renderer
  76.256 +     * @since org.netbeans.modules.java.project/1 1.3 
  76.257 +     */
  76.258 +    public static ListCellRenderer listRenderer() {
  76.259 +        return new PackageListCellRenderer();
  76.260 +    }
  76.261 +    
  76.262 +    /**
  76.263 +     * FilterNode which listens on the PackageViewSettings and changes the view to 
  76.264 +     * the package view or tree view
  76.265 +     *
  76.266 +     */
  76.267 +    private static final class RootNode extends FilterNode implements PropertyChangeListener {
  76.268 +        
  76.269 +        private final SourceGroup sourceGroup;
  76.270 +        
  76.271 +        @SuppressWarnings("LeakingThisInConstructor")
  76.272 +        private RootNode (SourceGroup group) {
  76.273 +            super(getOriginalNode(group));
  76.274 +            this.sourceGroup = group;
  76.275 +            PythonProjectSettings.addPropertyChangeListener(WeakListeners.propertyChange(this, PythonProjectSettings.class));
  76.276 +            group.addPropertyChangeListener(WeakListeners.propertyChange(this, group));
  76.277 +        }
  76.278 +
  76.279 +        @Override
  76.280 +        public void propertyChange (PropertyChangeEvent event) {
  76.281 +            String prop = event.getPropertyName();
  76.282 +            if (PythonProjectSettings.PROP_PACKAGE_VIEW_TYPE.equals(prop) || SourceGroup.PROP_CONTAINERSHIP.equals(prop)) {
  76.283 +                EventQueue.invokeLater(new Runnable() {
  76.284 +                    @Override
  76.285 +                    public void run() {
  76.286 +                        changeOriginal(getOriginalNode(sourceGroup), true);
  76.287 +                    }
  76.288 +                });
  76.289 +            }
  76.290 +        }
  76.291 +        
  76.292 +        private static Node getOriginalNode(SourceGroup group) {
  76.293 +            FileObject root = group.getRootFolder();
  76.294 +            //Guard condition, if the project is (closed) and deleted but not yet gced
  76.295 +            // and the view is switched, the source group is not valid.
  76.296 +            if ( root == null || !root.isValid()) {
  76.297 +                return new AbstractNode (Children.LEAF);
  76.298 +            }
  76.299 +            switch (PythonProjectSettings.getPackageViewType()) {
  76.300 +                case PythonProjectSettings.TYPE_PACKAGE_VIEW:
  76.301 +                    return new PackageRootNode(group);
  76.302 +                case PythonProjectSettings.TYPE_TREE:
  76.303 +                    return new TreeRootNode(group);
  76.304 +                default:
  76.305 +                    assert false : "Unknown PackageView Type"; //NOI18N
  76.306 +                    return new PackageRootNode(group);
  76.307 +            }
  76.308 +        }        
  76.309 +    }
  76.310 +    
  76.311 +    /**
  76.312 +     * Model item representing one package.
  76.313 +     */
  76.314 +    static final class PackageItem implements Comparable<PackageItem> {
  76.315 +        
  76.316 +        private static final Map<Image,Icon> image2icon = new IdentityHashMap<>();
  76.317 +        
  76.318 +        private final boolean empty;
  76.319 +        private final FileObject pkg;
  76.320 +        private final String pkgname;
  76.321 +        private Icon icon;
  76.322 +        
  76.323 +        public PackageItem(SourceGroup group, FileObject pkg, boolean empty) {
  76.324 +            this.pkg = pkg;
  76.325 +            this.empty = empty;
  76.326 +            String path = FileUtil.getRelativePath(group.getRootFolder(), pkg);
  76.327 +            assert path != null : "No " + pkg + " in " + group;
  76.328 +            pkgname = path.replace('/', '.');
  76.329 +        }
  76.330 +        
  76.331 +        @Override
  76.332 +        public String toString() {
  76.333 +            return pkgname;
  76.334 +        }
  76.335 +        
  76.336 +        public String getLabel() {
  76.337 +            return PackageDisplayUtils.getDisplayLabel(pkgname);
  76.338 +        }
  76.339 +        
  76.340 +        public Icon getIcon() {
  76.341 +            if ( icon == null ) {
  76.342 +                Image image = PackageDisplayUtils.getIcon(pkg, pkgname, empty);
  76.343 +                icon = image2icon.get(image);
  76.344 +                if ( icon == null ) {            
  76.345 +                    icon = new ImageIcon( image );
  76.346 +                    image2icon.put( image, icon );
  76.347 +                }
  76.348 +            }
  76.349 +            return icon;
  76.350 +        }
  76.351 +
  76.352 +        @Override
  76.353 +        public int compareTo(PackageItem p) {
  76.354 +            return pkgname.compareTo(p.pkgname);
  76.355 +        }
  76.356 +        
  76.357 +    }
  76.358 +    
  76.359 +    /**
  76.360 +     * The renderer which just displays {@link PackageItem#getLabel} and {@link PackageItem#getIcon}.
  76.361 +     */
  76.362 +    private static final class PackageListCellRenderer extends JLabel implements ListCellRenderer, UIResource {
  76.363 +        
  76.364 +        public PackageListCellRenderer() {
  76.365 +            setOpaque(true);
  76.366 +        }
  76.367 +
  76.368 +        @Override
  76.369 +        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
  76.370 +            // #93658: GTK needs name to render cell renderer "natively"
  76.371 +            setName("ComboBox.listRenderer"); // NOI18N
  76.372 +            
  76.373 +            if (value instanceof PackageItem) {
  76.374 +                PackageItem pkgitem = (PackageItem) value;
  76.375 +                setText(pkgitem.getLabel());
  76.376 +                setIcon(pkgitem.getIcon());
  76.377 +            } else {
  76.378 +                // #49954: render a specially inserted package somehow.
  76.379 +                String pkgitem = (String) value;
  76.380 +                setText(pkgitem);
  76.381 +                setIcon(null);
  76.382 +            }
  76.383 +            
  76.384 +            if ( isSelected ) {
  76.385 +                setBackground(list.getSelectionBackground());
  76.386 +                setForeground(list.getSelectionForeground());             
  76.387 +            }
  76.388 +            else {
  76.389 +                setBackground(list.getBackground());
  76.390 +                setForeground(list.getForeground());
  76.391 +            }
  76.392 +            
  76.393 +            return this;
  76.394 +        }
  76.395 +        
  76.396 +        // #93658: GTK needs name to render cell renderer "natively"
  76.397 +        @Override
  76.398 +        public String getName() {
  76.399 +            String name = super.getName();
  76.400 +            return name == null ? "ComboBox.renderer" : name;  // NOI18N
  76.401 +    }
  76.402 +    
  76.403 +    }
  76.404 +    
  76.405 +    
  76.406 +}
    77.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    77.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/PackageViewChildren.java	Tue Feb 24 01:58:36 2015 -0800
    77.3 @@ -0,0 +1,1332 @@
    77.4 +/*
    77.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    77.6 + *
    77.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    77.8 + *
    77.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   77.10 + * Other names may be trademarks of their respective owners.
   77.11 + *
   77.12 + * The contents of this file are subject to the terms of either the GNU
   77.13 + * General Public License Version 2 only ("GPL") or the Common
   77.14 + * Development and Distribution License("CDDL") (collectively, the
   77.15 + * "License"). You may not use this file except in compliance with the
   77.16 + * License. You can obtain a copy of the License at
   77.17 + * http://www.netbeans.org/cddl-gplv2.html
   77.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   77.19 + * specific language governing permissions and limitations under the
   77.20 + * License.  When distributing the software, include this License Header
   77.21 + * Notice in each file and include the License file at
   77.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   77.23 + * particular file as subject to the "Classpath" exception as provided
   77.24 + * by Oracle in the GPL Version 2 section of the License file that
   77.25 + * accompanied this code. If applicable, add the following below the
   77.26 + * License Header, with the fields enclosed by brackets [] replaced by
   77.27 + * your own identifying information:
   77.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   77.29 + *
   77.30 + * Contributor(s):
   77.31 + *
   77.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   77.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   77.34 + * Microsystems, Inc. All Rights Reserved.
   77.35 + *
   77.36 + * If you wish your version of this file to be governed by only the CDDL
   77.37 + * or only the GPL Version 2, indicate your decision by adding
   77.38 + * "[Contributor] elects to include this software in this distribution
   77.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   77.40 + * single choice of license, a recipient has the option to distribute
   77.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   77.42 + * to extend the choice of license to its licensees as provided above.
   77.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   77.44 + * Version 2 license, then the option applies only if the new code is
   77.45 + * made subject to such option by the copyright holder.
   77.46 + */
   77.47 +
   77.48 +package org.netbeans.modules.python.project2.ui;
   77.49 +
   77.50 +import java.awt.EventQueue;
   77.51 +import java.awt.Image;
   77.52 +import java.awt.datatransfer.DataFlavor;
   77.53 +import java.awt.datatransfer.Transferable;
   77.54 +import java.awt.datatransfer.UnsupportedFlavorException;
   77.55 +import java.awt.dnd.DnDConstants;
   77.56 +import java.beans.PropertyChangeEvent;
   77.57 +import java.beans.PropertyChangeListener;
   77.58 +import java.beans.PropertyChangeSupport;
   77.59 +import java.io.IOException;
   77.60 +import java.lang.reflect.InvocationTargetException;
   77.61 +import java.text.MessageFormat;
   77.62 +import java.util.ArrayList;
   77.63 +import java.util.Collections;
   77.64 +import java.util.HashSet;
   77.65 +import java.util.Iterator;
   77.66 +import java.util.List;
   77.67 +import java.util.Set;
   77.68 +import java.util.StringTokenizer;
   77.69 +import java.util.TreeMap;
   77.70 +import java.util.TreeSet;
   77.71 +import javax.swing.Action;
   77.72 +import javax.swing.SwingUtilities;
   77.73 +import javax.swing.event.ChangeEvent;
   77.74 +import javax.swing.event.ChangeListener;
   77.75 +import org.netbeans.api.fileinfo.NonRecursiveFolder;
   77.76 +import org.netbeans.api.project.SourceGroup;
   77.77 +import org.netbeans.api.queries.VisibilityQuery;
   77.78 +import org.netbeans.spi.project.ActionProvider;
   77.79 +import org.netbeans.spi.project.ui.support.FileSensitiveActions;
   77.80 +import org.openide.DialogDisplayer;
   77.81 +import org.openide.ErrorManager;
   77.82 +import org.openide.NotifyDescriptor;
   77.83 +import org.openide.actions.FileSystemAction;
   77.84 +import org.openide.actions.PropertiesAction;
   77.85 +import org.openide.filesystems.FileAttributeEvent;
   77.86 +import org.openide.filesystems.FileChangeListener;
   77.87 +import org.openide.filesystems.FileEvent;
   77.88 +import org.openide.filesystems.FileObject;
   77.89 +import org.openide.filesystems.FileRenameEvent;
   77.90 +import org.openide.filesystems.FileStateInvalidException;
   77.91 +import org.openide.filesystems.FileSystem;
   77.92 +import org.openide.filesystems.FileUtil;
   77.93 +import org.openide.loaders.ChangeableDataFilter;
   77.94 +import org.openide.loaders.DataFilter;
   77.95 +import org.openide.loaders.DataFolder;
   77.96 +import org.openide.loaders.DataObject;
   77.97 +import org.openide.nodes.Children;
   77.98 +import org.openide.nodes.FilterNode;
   77.99 +import org.openide.nodes.Node;
  77.100 +import org.openide.nodes.PropertySupport;
  77.101 +import org.openide.nodes.Sheet;
  77.102 +import org.openide.util.ChangeSupport;
  77.103 +import org.openide.util.Exceptions;
  77.104 +import org.openide.util.ImageUtilities;
  77.105 +import org.openide.util.NbBundle;
  77.106 +import org.openide.util.RequestProcessor;
  77.107 +import org.openide.util.WeakListeners;
  77.108 +import org.openide.util.datatransfer.ExTransferable;
  77.109 +import org.openide.util.datatransfer.MultiTransferObject;
  77.110 +import org.openide.util.datatransfer.PasteType;
  77.111 +import org.openide.util.lookup.Lookups;
  77.112 +import org.openide.util.lookup.ProxyLookup;
  77.113 +
  77.114 +/**
  77.115 + * Display of Java sources in a package structure rather than folder structure.
  77.116 + *
  77.117 + * <p>
  77.118 + * <b>This is copied from the corresponding Java action in java.projects</b>
  77.119 + * </p>
  77.120 + *
  77.121 + * @author Adam Sotona, Jesse Glick, Petr Hrebejk, Tomas Zezula
  77.122 + */
  77.123 +@NbBundle.Messages({"TXT_PastePackage=Paste Package",
  77.124 +    "MSG_InvalidPackageName=Name is not a valid Java package.",
  77.125 +    "LBL_CompilePackage_Action=Compile Package",
  77.126 +    "PROP_name=Name",
  77.127 +    "HINT_name=Package Name"})
  77.128 +final class PackageViewChildren extends Children.Keys<String> implements FileChangeListener, ChangeListener, Runnable {
  77.129 +    
  77.130 +    private static final String NODE_NOT_CREATED = "NNC"; // NOI18N
  77.131 +    private static final String NODE_NOT_CREATED_EMPTY = "NNC_E"; //NOI18N
  77.132 +    
  77.133 +    private static final MessageFormat PACKAGE_FLAVOR = new MessageFormat("application/x-python-org-netbeans-modules-python-project-packagenodednd; class=org.netbeans.spi.java.project.support.ui.PackageViewChildren$PackageNode; mask={0}"); //NOI18N
  77.134 +        
  77.135 +    static final String PRIMARY_TYPE = "application";   //NOI18N
  77.136 +    static final String SUBTYPE = "x-python-org-netbeans-modules-python-project-packagenodednd";    //NOI18N
  77.137 +    static final String MASK = "mask";  //NOI18N
  77.138 +
  77.139 +    private java.util.Map<String,Object/*NODE_NOT_CREATED|NODE_NOT_CREATED_EMPTY|PackageNode*/> names2nodes;
  77.140 +    private final FileObject root;
  77.141 +    private final SourceGroup group;
  77.142 +    private FileChangeListener wfcl;    // Weak listener on the system filesystem
  77.143 +    private ChangeListener wvqcl;       // Weak listener on the VisibilityQuery
  77.144 +
  77.145 +    /**
  77.146 +     * Creates children based on a single source root.
  77.147 +     * @param root the folder where sources start (must be a package root)
  77.148 +     */    
  77.149 +    public PackageViewChildren(SourceGroup group) {
  77.150 +        
  77.151 +        // Sem mas dat cache a bude to uplne nejrychlejsi na svete
  77.152 +        
  77.153 +        this.root = group.getRootFolder();
  77.154 +        this.group = group;
  77.155 +    }
  77.156 +
  77.157 +    FileObject getRoot() {
  77.158 +        return root; // Used from PackageRootNode
  77.159 +    }
  77.160 +    
  77.161 +    @Override
  77.162 +    protected Node[] createNodes(String path) {
  77.163 +        FileObject fo = root.getFileObject(path);
  77.164 +        if ( fo != null && fo.isValid()) {
  77.165 +            Object o = names2nodes.get(path);
  77.166 +            PackageNode n;
  77.167 +            DataFolder folder = DataFolder.findFolder(fo);
  77.168 +            if (folder.isValid()) {
  77.169 +                if ( o == NODE_NOT_CREATED ) {
  77.170 +                    n = new PackageNode(root, folder, false);
  77.171 +                } else { // NODE_NOT_CREATED_EMPTY, PackageNode
  77.172 +                    n = new PackageNode(root, folder);
  77.173 +                }
  77.174 +                names2nodes.put(path, n);
  77.175 +                return new Node[] {n};
  77.176 +            }
  77.177 +        }
  77.178 +        return new Node[0];
  77.179 +    }
  77.180 +    
  77.181 +    RequestProcessor.Task task = RequestProcessor.getDefault().create( this );
  77.182 +        
  77.183 +    @Override
  77.184 +    protected void addNotify() {
  77.185 +        // System.out.println("ADD NOTIFY" + root + " : " + this );
  77.186 +        super.addNotify();
  77.187 +        task.schedule( 0 );
  77.188 +    }
  77.189 +    
  77.190 +    @Override
  77.191 +    public Node[] getNodes( boolean optimal ) {
  77.192 +        if ( optimal ) {
  77.193 +            Node[] garbage = super.getNodes( false );
  77.194 +            task.waitFinished();
  77.195 +        }
  77.196 +        return super.getNodes( false );
  77.197 +    }
  77.198 +    
  77.199 +    @Override
  77.200 +    public Node findChild (String name) {
  77.201 +        while (true) {
  77.202 +            Node n = super.findChild(name);
  77.203 +            if (n != null) {
  77.204 +                // If already there, get it quickly.
  77.205 +                return n;
  77.206 +            }
  77.207 +            // In case a project is made on a large existing source root,
  77.208 +            // which happens to have a file in the root dir (so package node
  77.209 +            // should exist), try to select the root package node soon; no need
  77.210 +            // to wait for whole tree.
  77.211 +            try {
  77.212 +                if (task.waitFinished(5000)) {
  77.213 +                    return super.findChild(name);
  77.214 +                }
  77.215 +                // refreshKeysAsync won't run since we are blocking EQ!
  77.216 +                refreshKeys();
  77.217 +            } catch (InterruptedException x) {
  77.218 +                Exceptions.printStackTrace(x);
  77.219 +            }
  77.220 +        }
  77.221 +    }
  77.222 +    
  77.223 +    @Override
  77.224 +    public void run() {
  77.225 +        computeKeys();
  77.226 +        refreshKeys();
  77.227 +        try { 
  77.228 +            FileSystem fs = root.getFileSystem();
  77.229 +            wfcl = FileUtil.weakFileChangeListener(this, fs);
  77.230 +            fs.addFileChangeListener( wfcl );
  77.231 +        }
  77.232 +        catch ( FileStateInvalidException e ) {
  77.233 +            ErrorManager.getDefault().notify( ErrorManager.INFORMATIONAL, e );
  77.234 +        }
  77.235 +        wvqcl = WeakListeners.change( this, VisibilityQuery.getDefault() );
  77.236 +        VisibilityQuery.getDefault().addChangeListener( wvqcl );
  77.237 +    }
  77.238 +
  77.239 +    @Override
  77.240 +    protected void removeNotify() {
  77.241 +        // System.out.println("REMOVE NOTIFY" + root + " : " + this );        
  77.242 +        VisibilityQuery.getDefault().removeChangeListener( wvqcl );
  77.243 +        try {
  77.244 +            root.getFileSystem().removeFileChangeListener( wfcl );
  77.245 +        }
  77.246 +        catch ( FileStateInvalidException e ) {
  77.247 +            ErrorManager.getDefault().notify( ErrorManager.INFORMATIONAL, e );
  77.248 +        }
  77.249 +        setKeys(new String[0]);
  77.250 +        names2nodes.clear();
  77.251 +        super.removeNotify();
  77.252 +    }
  77.253 +    
  77.254 +    // Private methods ---------------------------------------------------------
  77.255 +        
  77.256 +    private void refreshKeys() {
  77.257 +        Set<String> keys;
  77.258 +        synchronized (names2nodes) {
  77.259 +            keys = new TreeSet<>(names2nodes.keySet());
  77.260 +        }
  77.261 +        setKeys(keys);
  77.262 +    }
  77.263 +    
  77.264 +    /* #70097: workaround of a javacore deadlock
  77.265 +     * See related issue: #61027
  77.266 +     */
  77.267 +    private void refreshKeysAsync () {
  77.268 +        EventQueue.invokeLater(new Runnable() {
  77.269 +            @Override
  77.270 +            public void run () {
  77.271 +                refreshKeys();
  77.272 +            }
  77.273 +         });
  77.274 +    }
  77.275 +
  77.276 +    private void computeKeys() {
  77.277 +        // XXX this is not going to perform too well for a huge source root...
  77.278 +        // However we have to go through the whole hierarchy in order to find
  77.279 +        // all packages (Hrebejk)
  77.280 +        names2nodes = Collections.synchronizedMap(new TreeMap<String,Object>());
  77.281 +        findNonExcludedPackages( root );
  77.282 +    }
  77.283 +    
  77.284 +    /**
  77.285 +     * Collect all recursive subfolders, except those which have subfolders
  77.286 +     * but no files.
  77.287 +     */    
  77.288 +    private void findNonExcludedPackages( FileObject fo ) {
  77.289 +        PackageView.findNonExcludedPackages(this, null, fo, group, true);
  77.290 +    }
  77.291 +    
  77.292 +    
  77.293 +    /** Finds all empty parents of given package and deletes them
  77.294 +     */
  77.295 +    private void cleanEmptyKeys( FileObject fo ) {
  77.296 +        FileObject parent = fo.getParent(); 
  77.297 +        
  77.298 +        // Special case for default package
  77.299 +        if ( root.equals( parent ) ) {
  77.300 +            PackageNode n = get( parent );
  77.301 +            // the default package is considered empty if it only contains folders,
  77.302 +            // regardless of the contents of these folders (empty or not)
  77.303 +            if ( n != null && PackageDisplayUtils.isEmpty( root, false, false ) ) {
  77.304 +                remove( root );
  77.305 +            }
  77.306 +            return;
  77.307 +        }
  77.308 +        
  77.309 +        while ( FileUtil.isParentOf( root, parent ) ) {
  77.310 +            PackageNode n = get( parent );
  77.311 +            if ( n != null && n.isLeaf() ) {
  77.312 +                // System.out.println("Cleaning " + parent);
  77.313 +                remove( parent );
  77.314 +            }
  77.315 +            parent = parent.getParent();
  77.316 +        }
  77.317 +    }
  77.318 +    
  77.319 +    // Non private only to be able to have the findNonExcludedPackages impl
  77.320 +    // in on place (PackageView) 
  77.321 +    void add(FileObject fo, boolean empty, boolean refreshImmediately) {
  77.322 +        String path = FileUtil.getRelativePath( root, fo );
  77.323 +        assert path != null : "Adding wrong folder " + fo +"(valid="+fo.isValid()+")"+ "under root" + this.root + "(valid="+this.root.isValid()+")";
  77.324 +        if ( get( fo ) == null ) { 
  77.325 +            names2nodes.put( path, empty ? NODE_NOT_CREATED_EMPTY : NODE_NOT_CREATED );
  77.326 +            if (refreshImmediately) {
  77.327 +                refreshKeysAsync();
  77.328 +            } else {
  77.329 +                synchronized (this) {
  77.330 +                    if (refreshLazilyTask == null) {
  77.331 +                        refreshLazilyTask = RequestProcessor.getDefault().post(new Runnable() {
  77.332 +                            @Override
  77.333 +                            public void run() {
  77.334 +                                synchronized (PackageViewChildren.this) {
  77.335 +                                    refreshLazilyTask = null;
  77.336 +                                    refreshKeysAsync();
  77.337 +                                }
  77.338 +                            }
  77.339 +                        }, 2500);
  77.340 +                    }
  77.341 +                }
  77.342 +            }
  77.343 +        }
  77.344 +    }
  77.345 +    private RequestProcessor.Task refreshLazilyTask;
  77.346 +
  77.347 +    private void remove( FileObject fo ) {
  77.348 +        String path = FileUtil.getRelativePath( root, fo );        
  77.349 +        assert path != null : "Removing wrong folder" + fo;
  77.350 +        names2nodes.remove( path );
  77.351 +    }
  77.352 +
  77.353 +    private void removeSubTree (FileObject fo) {
  77.354 +        String path = FileUtil.getRelativePath( root, fo );
  77.355 +        assert path != null : "Removing wrong folder" + fo;
  77.356 +        synchronized (names2nodes) {
  77.357 +            Set<String> keys = names2nodes.keySet();
  77.358 +            keys.remove(path);
  77.359 +            path = path + '/';  //NOI18N
  77.360 +            Iterator<String> it = keys.iterator();
  77.361 +            while (it.hasNext()) {
  77.362 +                if (it.next().startsWith(path)) {
  77.363 +                    it.remove();
  77.364 +                }
  77.365 +            }
  77.366 +        }
  77.367 +    }
  77.368 +
  77.369 +    private PackageNode get( FileObject fo ) {
  77.370 +        String path = FileUtil.getRelativePath( root, fo );        
  77.371 +        assert path != null : "Asking for wrong folder" + fo;
  77.372 +        Object o = names2nodes.get( path );
  77.373 +        return !isNodeCreated( o ) ? null : (PackageNode)o;
  77.374 +    }
  77.375 +    
  77.376 +    private boolean contains( FileObject fo ) {
  77.377 +        String path = FileUtil.getRelativePath( root, fo );        
  77.378 +        assert path != null : "Asking for wrong folder" + fo;
  77.379 +        Object o = names2nodes.get( path );
  77.380 +        return o != null;
  77.381 +    }
  77.382 +    
  77.383 +    private boolean exists( FileObject fo ) {
  77.384 +        String path = FileUtil.getRelativePath( root, fo );
  77.385 +        return names2nodes.get( path ) != null;
  77.386 +    }
  77.387 +    
  77.388 +    private boolean isNodeCreated( Object o ) {
  77.389 +        return o instanceof Node;
  77.390 +    }
  77.391 +    
  77.392 +    private PackageNode updatePath( String oldPath, String newPath ) {
  77.393 +        assert newPath != null;
  77.394 +        Object o = names2nodes.get( oldPath );
  77.395 +        if ( o == null ) {
  77.396 +            return null;
  77.397 +        }        
  77.398 +        names2nodes.remove( oldPath );
  77.399 +        names2nodes.put( newPath, o );
  77.400 +        return !isNodeCreated( o ) ? null : (PackageNode)o;
  77.401 +    }
  77.402 +    
  77.403 +    // Implementation of FileChangeListener ------------------------------------
  77.404 +    
  77.405 +    @Override
  77.406 +    public void fileAttributeChanged( FileAttributeEvent fe ) {}
  77.407 +
  77.408 +    @Override
  77.409 +    public void fileChanged( FileEvent fe ) {} 
  77.410 +
  77.411 +    @Override
  77.412 +    public void fileFolderCreated( FileEvent fe ) {
  77.413 +        FileObject fo = fe.getFile();        
  77.414 +        if ( FileUtil.isParentOf( root, fo ) && isVisible( root, fo ) ) {
  77.415 +            cleanEmptyKeys( fo );                
  77.416 +//            add( fo, false);
  77.417 +            findNonExcludedPackages( fo );
  77.418 +            refreshKeys();
  77.419 +        }
  77.420 +    }
  77.421 +    
  77.422 +    @Override
  77.423 +    public void fileDataCreated( FileEvent fe ) {
  77.424 +        FileObject fo = fe.getFile();
  77.425 +        if ( FileUtil.isParentOf( root, fo ) && isVisible( root, fo ) ) {
  77.426 +            FileObject parent = fo.getParent();
  77.427 +            // XXX consider using group.contains() here
  77.428 +            if ( !VisibilityQuery.getDefault().isVisible( parent ) ) {
  77.429 +                return; // Adding file into ignored directory
  77.430 +            }
  77.431 +            PackageNode n = get( parent );
  77.432 +            if ( n == null && !contains( parent ) ) {                
  77.433 +                add(parent, false, true);
  77.434 +                refreshKeys();
  77.435 +            }
  77.436 +            else if ( n != null ) {
  77.437 +                n.updateChildren();
  77.438 +            }
  77.439 +        }
  77.440 +    }
  77.441 +
  77.442 +    @Override
  77.443 +    public void fileDeleted( FileEvent fe ) {
  77.444 +        FileObject fo = fe.getFile();       
  77.445 +        
  77.446 +        // System.out.println("FILE DELETED " + FileUtil.getRelativePath( root, fo ) );
  77.447 +        
  77.448 +        if ( FileUtil.isParentOf( root, fo ) && isVisible( root, fo ) ) {
  77.449 +            
  77.450 +            // System.out.println("IS FOLDER? " + fo + " : " + fo.isFolder() );
  77.451 +                                  /* Hack for MasterFS see #42464 */
  77.452 +            if ( fo.isFolder() || get( fo ) != null ) {
  77.453 +                // System.out.println("REMOVING FODER " + fo );                
  77.454 +                removeSubTree( fo );
  77.455 +                // Now add the parent if necessary 
  77.456 +                FileObject parent = fo.getParent();
  77.457 +                if ( ( FileUtil.isParentOf( root, parent ) || root.equals( parent ) ) && get( parent ) == null && parent.isValid() ) {
  77.458 +                    // Candidate for adding
  77.459 +                    if ( !toBeRemoved( parent ) ) {
  77.460 +                        // System.out.println("ADDING PARENT " + parent );
  77.461 +                        add(parent, true, true);
  77.462 +                    }
  77.463 +                }
  77.464 +                refreshKeysAsync();
  77.465 +            }
  77.466 +            else {
  77.467 +                FileObject parent = fo.getParent();
  77.468 +                final PackageNode n = get( parent );
  77.469 +                if ( n != null ) {
  77.470 +                    //#61027: workaround to a deadlock when the package is being changed from non-leaf to leaf:
  77.471 +                    boolean leaf = n.isLeaf();
  77.472 +                    DataFolder df = n.getDataFolder();
  77.473 +                    boolean empty = isEmpty(df);
  77.474 +                    
  77.475 +                    if (leaf != empty) {
  77.476 +                        SwingUtilities.invokeLater(new Runnable() {
  77.477 +                            @Override
  77.478 +                            public void run() {
  77.479 +                                n.updateChildren();
  77.480 +                            }
  77.481 +                        });
  77.482 +                    } else {
  77.483 +                        n.updateChildren();
  77.484 +                    }
  77.485 +                }
  77.486 +                // If the parent folder only contains folders remove it
  77.487 +                if ( toBeRemoved( parent ) ) {
  77.488 +                    remove( parent );
  77.489 +                    refreshKeysAsync();
  77.490 +                }
  77.491 +                 
  77.492 +            }
  77.493 +        }
  77.494 +        // else {
  77.495 +        //    System.out.println("NOT A PARENT " + fo );
  77.496 +        // }
  77.497 +    }
  77.498 +    
  77.499 +    /** Returns true if the folder should be removed from the view
  77.500 +     * i.e. it has some unignored children and the children are folders only
  77.501 +     */
  77.502 +    private boolean toBeRemoved( FileObject folder ) {
  77.503 +        boolean ignoredOnly = true;
  77.504 +        boolean foldersOnly = true;
  77.505 +        for (FileObject kid : folder.getChildren()) {
  77.506 +            // XXX consider using group.contains() here
  77.507 +            if (VisibilityQuery.getDefault().isVisible(kid)) {
  77.508 +                ignoredOnly = false;
  77.509 +                if (!kid.isFolder()) {
  77.510 +                    foldersOnly = false;
  77.511 +                    break;
  77.512 +                }
  77.513 +            }                                  
  77.514 +        }
  77.515 +        if ( ignoredOnly ) {
  77.516 +            return false; // It is either empty or it only contains ignored files
  77.517 +                          // thus is leaf and it means package
  77.518 +        }
  77.519 +        else {
  77.520 +            return foldersOnly;
  77.521 +        }
  77.522 +    }
  77.523 +    
  77.524 +    
  77.525 +    @Override
  77.526 +    public void fileRenamed( FileRenameEvent fe ) {
  77.527 +        FileObject fo = fe.getFile();        
  77.528 +        if ( FileUtil.isParentOf( root, fo ) && fo.isFolder() ) {
  77.529 +            String rp = FileUtil.getRelativePath( root, fo.getParent() );
  77.530 +            String oldPath = rp + ( rp.length() == 0 ? "" : "/" ) + fe.getName() + fe.getExt(); // NOI18N
  77.531 +
  77.532 +            // XXX consider using group.contains() here
  77.533 +            boolean visible = VisibilityQuery.getDefault().isVisible( fo );
  77.534 +            boolean doUpdate = false;
  77.535 +            
  77.536 +            // Find all entries which have to be updated
  77.537 +            List<String> needsUpdate = new ArrayList<>();
  77.538 +            synchronized (names2nodes) {
  77.539 +                for (Iterator<String> it = names2nodes.keySet().iterator(); it.hasNext(); ) {
  77.540 +                    String p = it.next();
  77.541 +                    if ( p.startsWith( oldPath ) ) {
  77.542 +                        if ( visible ) {
  77.543 +                            needsUpdate.add( p );
  77.544 +                        } else {
  77.545 +                            it.remove();
  77.546 +                            doUpdate = true;
  77.547 +                        }
  77.548 +                    }
  77.549 +                }
  77.550 +            }   
  77.551 +                        
  77.552 +            // If the node does not exists then there might have been update
  77.553 +            // from ignored to non ignored
  77.554 +            if ( get( fo ) == null && visible ) {
  77.555 +                cleanEmptyKeys( fo );                
  77.556 +                findNonExcludedPackages( fo );
  77.557 +                doUpdate = true;  // force refresh
  77.558 +            }
  77.559 +            
  77.560 +            int oldPathLen = oldPath.length();
  77.561 +            String newPath = FileUtil.getRelativePath( root, fo );
  77.562 +            for (String p : needsUpdate) {
  77.563 +                StringBuilder np = new StringBuilder(p);
  77.564 +                np.replace( 0, oldPathLen, newPath );                    
  77.565 +                PackageNode n = updatePath( p, np.toString() ); // Replace entries in cache
  77.566 +                if ( n != null ) {
  77.567 +                    n.updateDisplayName(); // Update nodes
  77.568 +                }
  77.569 +            }
  77.570 +            
  77.571 +            if ( needsUpdate.size() > 1 || doUpdate ) {
  77.572 +                // Sorting might change
  77.573 +                refreshKeys();
  77.574 +            }
  77.575 +        }
  77.576 +        /*
  77.577 +        else if ( FileUtil.isParentOf( root, fo ) && fo.isFolder() ) {
  77.578 +            FileObject parent = fo.getParent();
  77.579 +            PackageNode n = get( parent );
  77.580 +            if ( n != null && VisibilityQuery.getDefault().isVisible( parent ) ) {
  77.581 +                n.updateChildren();
  77.582 +            }
  77.583 +            
  77.584 +        }
  77.585 +        */
  77.586 +        
  77.587 +    }
  77.588 +    
  77.589 +    /** Test whether file and all it's parent up to parent paremeter
  77.590 +     * are visible
  77.591 +     */    
  77.592 +    private boolean isVisible( FileObject parent, FileObject file ) {
  77.593 +        
  77.594 +        do {    
  77.595 +            // XXX consider using group.contains() here
  77.596 +            if ( !VisibilityQuery.getDefault().isVisible( file ) )  {
  77.597 +                return false;
  77.598 +            }
  77.599 +            file = file.getParent();
  77.600 +        }
  77.601 +        while ( file != null && file != parent );    
  77.602 +                
  77.603 +        return true;        
  77.604 +    }
  77.605 +    
  77.606 +
  77.607 +    // Implementation of ChangeListener ------------------------------------
  77.608 +        
  77.609 +    @Override
  77.610 +    public void stateChanged( ChangeEvent e ) {
  77.611 +        computeKeys();
  77.612 +        refreshKeys();
  77.613 +    }
  77.614 +    
  77.615 +
  77.616 +    /*
  77.617 +    private void debugKeySet() {
  77.618 +        for( Iterator it = names2nodes.keySet().iterator(); it.hasNext(); ) {
  77.619 +            String k = (String)it.next();
  77.620 +            System.out.println( "    " + k + " -> " +  names2nodes.get( k ) );
  77.621 +        }
  77.622 +    }
  77.623 +     */
  77.624 +    
  77.625 +    private final DataFilter NO_FOLDERS_FILTER = new NoFoldersDataFilter();
  77.626 +        
  77.627 +    private static boolean isEmpty(DataFolder dataFolder) {
  77.628 +        if ( dataFolder == null ) {
  77.629 +            return true;
  77.630 +        }
  77.631 +        return PackageDisplayUtils.isEmpty( dataFolder.getPrimaryFile() );
  77.632 +    }
  77.633 +    
  77.634 +    final class PackageNode extends FilterNode {
  77.635 +        
  77.636 +        private Action actions[];
  77.637 +        
  77.638 +        private final FileObject root;
  77.639 +        private DataFolder dataFolder;
  77.640 +        private boolean isDefaultPackage;
  77.641 +        
  77.642 +        public PackageNode( FileObject root, DataFolder dataFolder ) {
  77.643 +            this( root, dataFolder, isEmpty( dataFolder ) );
  77.644 +        }
  77.645 +        
  77.646 +        public PackageNode( FileObject root, DataFolder dataFolder, boolean empty ) {    
  77.647 +            super( dataFolder.getNodeDelegate(), 
  77.648 +                   empty ? Children.LEAF : dataFolder.createNodeChildren( NO_FOLDERS_FILTER ),
  77.649 +                   new ProxyLookup(
  77.650 +                        Lookups.singleton(new NoFoldersContainer (dataFolder)),
  77.651 +                        dataFolder.getNodeDelegate().getLookup()
  77.652 +//                        Lookups.singleton(PackageRootNode.alwaysSearchableSearchInfo(SearchInfoFactory.createSearchInfo(
  77.653 +//                                                  dataFolder.getPrimaryFile(),
  77.654 +//                                                  false,      //not recursive
  77.655 +//                                                  new FileObjectFilter[] {
  77.656 +//                                                          SearchInfoFactory.VISIBILITY_FILTER})
  77.657 +                   ));
  77.658 +            this.root = root;
  77.659 +            this.dataFolder = dataFolder;
  77.660 +            this.isDefaultPackage = root.equals( dataFolder.getPrimaryFile() );
  77.661 +        }
  77.662 +    
  77.663 +        FileObject getRoot() {
  77.664 +            return root; // Used from PackageRootNode
  77.665 +        }
  77.666 +    
  77.667 +        
  77.668 +        @Override
  77.669 +        public String getName() {
  77.670 +            String relativePath = FileUtil.getRelativePath(root, dataFolder.getPrimaryFile());
  77.671 +            return relativePath == null ?  null : relativePath.replace('/', '.'); // NOI18N
  77.672 +        }
  77.673 +        
  77.674 +        @Override
  77.675 +        public Action[] getActions( boolean context ) {
  77.676 +            
  77.677 +            if ( !context ) {
  77.678 +                if ( actions == null ) {                
  77.679 +                    // Copy actions and leave out the PropertiesAction and FileSystemAction.
  77.680 +                    Action superActions[] = super.getActions( context );            
  77.681 +                    List<Action> actionList = new ArrayList<>(superActions.length);
  77.682 +                    
  77.683 +                    for( int i = 0; i < superActions.length; i++ ) {
  77.684 +
  77.685 +                        if ( (i <= superActions.length - 2) && superActions[ i ] == null && superActions[i + 1] instanceof PropertiesAction ) {
  77.686 +                            i ++;
  77.687 +                            continue;
  77.688 +                        }
  77.689 +                        else if ( superActions[i] instanceof PropertiesAction ) {
  77.690 +                            continue;
  77.691 +                        }
  77.692 +                        else if ( superActions[i] instanceof FileSystemAction ) {
  77.693 +                            actionList.add (null); // insert separator and new action
  77.694 +                            actionList.add (FileSensitiveActions.fileCommandAction(ActionProvider.COMMAND_COMPILE_SINGLE, 
  77.695 +                                NbBundle.getMessage( PackageViewChildren.class, "LBL_CompilePackage_Action" ), // NOI18N
  77.696 +                                null ));                            
  77.697 +                        }
  77.698 +                        
  77.699 +                        actionList.add( superActions[i] );                                                  
  77.700 +                    }
  77.701 +
  77.702 +                    actions = new Action[ actionList.size() ];
  77.703 +                    actionList.toArray( actions );
  77.704 +                }
  77.705 +                return actions;
  77.706 +            }
  77.707 +            else {
  77.708 +                return super.getActions( context );
  77.709 +            }
  77.710 +        }
  77.711 +        
  77.712 +        @Override
  77.713 +        public boolean canRename() {
  77.714 +            return !isDefaultPackage;
  77.715 +        }
  77.716 +
  77.717 +        @Override
  77.718 +        public boolean canCut () {
  77.719 +            return !isDefaultPackage;    
  77.720 +        }
  77.721 +
  77.722 +        /**
  77.723 +         * Copy handling
  77.724 +         */
  77.725 +        @Override
  77.726 +        public Transferable clipboardCopy () throws IOException {
  77.727 +            try {
  77.728 +                return new PackageTransferable (this, DnDConstants.ACTION_COPY);
  77.729 +            } catch (ClassNotFoundException e) {
  77.730 +                throw new AssertionError(e);
  77.731 +            }
  77.732 +        }
  77.733 +        
  77.734 +        @Override
  77.735 +        public Transferable clipboardCut () throws IOException {
  77.736 +            try {
  77.737 +                return new PackageTransferable (this, DnDConstants.ACTION_MOVE);
  77.738 +            } catch (ClassNotFoundException e) {
  77.739 +                throw new AssertionError(e);
  77.740 +            }
  77.741 +        }
  77.742 +        
  77.743 +        @Override
  77.744 +        public /*@Override*/ Transferable drag () throws IOException {
  77.745 +            try {
  77.746 +                return new PackageTransferable (this, DnDConstants.ACTION_NONE);
  77.747 +            } catch (ClassNotFoundException e) {
  77.748 +                throw new AssertionError(e);
  77.749 +            }
  77.750 +        }
  77.751 +
  77.752 +        @Override
  77.753 +        public PasteType[] getPasteTypes(Transferable t) {
  77.754 +            if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
  77.755 +                try {
  77.756 +                    MultiTransferObject mto = (MultiTransferObject) t.getTransferData (ExTransferable.multiFlavor);
  77.757 +                    boolean hasPackageFlavor = false;
  77.758 +                    for (int i=0; i < mto.getCount(); i++) {
  77.759 +                        DataFlavor[] flavors = mto.getTransferDataFlavors(i);
  77.760 +                        if (isPackageFlavor(flavors)) {
  77.761 +                            hasPackageFlavor = true;
  77.762 +                        }
  77.763 +                    }
  77.764 +                    return hasPackageFlavor ? new PasteType[0] : super.getPasteTypes (t);
  77.765 +                } catch (UnsupportedFlavorException | IOException e) {
  77.766 +                    ErrorManager.getDefault().notify(e);
  77.767 +                    return new PasteType[0];
  77.768 +                }
  77.769 +            }
  77.770 +            else {
  77.771 +                DataFlavor[] flavors = t.getTransferDataFlavors();
  77.772 +                if (isPackageFlavor(flavors)) {
  77.773 +                    return new PasteType[0];
  77.774 +                }
  77.775 +                else {
  77.776 +                    return super.getPasteTypes(t);
  77.777 +                }
  77.778 +            }
  77.779 +        }
  77.780 +        
  77.781 +        @Override
  77.782 +        public /*@Override*/ PasteType getDropType (Transferable t, int action, int index) {
  77.783 +            if (t.isDataFlavorSupported(ExTransferable.multiFlavor)) {
  77.784 +                try {
  77.785 +                    MultiTransferObject mto = (MultiTransferObject) t.getTransferData (ExTransferable.multiFlavor);
  77.786 +                    boolean hasPackageFlavor = false;
  77.787 +                    for (int i=0; i < mto.getCount(); i++) {
  77.788 +                        DataFlavor[] flavors = mto.getTransferDataFlavors(i);
  77.789 +                        if (isPackageFlavor(flavors)) {
  77.790 +                            hasPackageFlavor = true;
  77.791 +                        }
  77.792 +                    }
  77.793 +                    return hasPackageFlavor ? null : super.getDropType (t, action, index);
  77.794 +                } catch (UnsupportedFlavorException | IOException e) {
  77.795 +                    ErrorManager.getDefault().notify(e);
  77.796 +                    return null;
  77.797 +                }
  77.798 +            }
  77.799 +            else {
  77.800 +                DataFlavor[] flavors = t.getTransferDataFlavors();
  77.801 +                if (isPackageFlavor(flavors)) {
  77.802 +                    return null;
  77.803 +                }
  77.804 +                else {
  77.805 +                    return super.getDropType (t, action, index);
  77.806 +                }
  77.807 +            }
  77.808 +        }
  77.809 +
  77.810 +
  77.811 +        private boolean isPackageFlavor (DataFlavor[] flavors) {
  77.812 +            for (int i=0; i<flavors.length; i++) {
  77.813 +                if (SUBTYPE.equals(flavors[i].getSubType ()) && PRIMARY_TYPE.equals(flavors[i].getPrimaryType ())) {
  77.814 +                    //Disable pasting into package, only paste into root is allowed
  77.815 +                    return true;
  77.816 +                }
  77.817 +            }
  77.818 +            return false;
  77.819 +        }
  77.820 +
  77.821 +// TOR
  77.822 +//        private synchronized PackageRenameHandler getRenameHandler() {
  77.823 +//            Collection<? extends PackageRenameHandler> handlers = Lookup.getDefault().lookupAll(PackageRenameHandler.class);
  77.824 +//            if (handlers.size()==0)
  77.825 +//                return null;
  77.826 +//            if (handlers.size()>1)
  77.827 +//                ErrorManager.getDefault().log(ErrorManager.WARNING, "Multiple instances of PackageRenameHandler found in Lookup; only using first one: " + handlers); //NOI18N
  77.828 +//            return handlers.iterator().next();
  77.829 +//        }
  77.830 +        
  77.831 +        @Override
  77.832 +        public void setName(String name) {
  77.833 +//            PackageRenameHandler handler = getRenameHandler();
  77.834 +//            if (handler!=null) {
  77.835 +//                handler.handleRename(this, name);
  77.836 +//                return;
  77.837 +//            }
  77.838 +            
  77.839 +            if (isDefaultPackage) {
  77.840 +                return;
  77.841 +            }
  77.842 +            String oldName = getName();
  77.843 +            if (oldName.equals(name)) {
  77.844 +                return;
  77.845 +            }
  77.846 +            if (!isValidPackageName (name)) {
  77.847 +                DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message (
  77.848 +                        NbBundle.getMessage(PackageViewChildren.class,"MSG_InvalidPackageName"), NotifyDescriptor.INFORMATION_MESSAGE));
  77.849 +                return;
  77.850 +            }
  77.851 +            name = name.replace('.','/')+'/';           //NOI18N
  77.852 +            oldName = oldName.replace('.','/')+'/';     //NOI18N
  77.853 +            int i;
  77.854 +            for (i=0; i<oldName.length() && i< name.length(); i++) {
  77.855 +                if (oldName.charAt(i) != name.charAt(i)) {
  77.856 +                    break;
  77.857 +                }
  77.858 +            }
  77.859 +            i--;
  77.860 +            int index = oldName.lastIndexOf('/',i);     //NOI18N
  77.861 +            String commonPrefix = index == -1 ? null : oldName.substring(0,index);
  77.862 +            String toCreate = (index+1 == name.length()) ? "" : name.substring(index+1);    //NOI18N
  77.863 +            try {
  77.864 +                FileObject commonFolder = commonPrefix == null ? this.root : this.root.getFileObject(commonPrefix);
  77.865 +                FileObject destination = commonFolder;
  77.866 +                StringTokenizer dtk = new StringTokenizer(toCreate,"/");    //NOI18N
  77.867 +                while (dtk.hasMoreTokens()) {
  77.868 +                    String pathElement = dtk.nextToken();
  77.869 +                    FileObject tmp = destination.getFileObject(pathElement);
  77.870 +                    if (tmp == null) {
  77.871 +                        tmp = destination.createFolder (pathElement);
  77.872 +                    }
  77.873 +                    destination = tmp;
  77.874 +                }
  77.875 +                FileObject source = this.dataFolder.getPrimaryFile();                
  77.876 +                DataFolder sourceFolder = DataFolder.findFolder (source);
  77.877 +                DataFolder destinationFolder = DataFolder.findFolder (destination);
  77.878 +                DataObject[] children = sourceFolder.getChildren();
  77.879 +                for (int j=0; j<children.length; j++) {
  77.880 +                    if (children[j].getPrimaryFile().isData()) {
  77.881 +                        children[j].move(destinationFolder);
  77.882 +                    }
  77.883 +                }
  77.884 +                while (!commonFolder.equals(source)) {
  77.885 +                    if (source.getChildren().length==0) {
  77.886 +                        FileObject tmp = source;
  77.887 +                        source = source.getParent();
  77.888 +                        tmp.delete();
  77.889 +                    }
  77.890 +                    else {
  77.891 +                        break;
  77.892 +                    }
  77.893 +                }
  77.894 +            } catch (IOException ioe) {
  77.895 +                ErrorManager.getDefault().notify (ioe);
  77.896 +            }
  77.897 +        }
  77.898 +        
  77.899 +        
  77.900 +        
  77.901 +        @Override
  77.902 +        public boolean canDestroy() {
  77.903 +            if ( isDefaultPackage ) {
  77.904 +                return false;
  77.905 +            }
  77.906 +            else {
  77.907 +                return true;
  77.908 +            }
  77.909 +        }
  77.910 +        
  77.911 +        @Override
  77.912 +        public void destroy() throws IOException {
  77.913 +            FileObject parent = dataFolder.getPrimaryFile().getParent();
  77.914 +            // First; delete all files except packages
  77.915 +            DataObject ch[] = dataFolder.getChildren();
  77.916 +            boolean empty = true;
  77.917 +            for( int i = 0; ch != null && i < ch.length; i++ ) {
  77.918 +                if ( !ch[i].getPrimaryFile().isFolder() ) {
  77.919 +                    ch[i].delete();
  77.920 +                }
  77.921 +                else {
  77.922 +                    empty = false;
  77.923 +                }
  77.924 +            }
  77.925 +            
  77.926 +            // If empty delete itself
  77.927 +            if ( empty ) {
  77.928 +                super.destroy();
  77.929 +            }
  77.930 +            
  77.931 +            
  77.932 +            // Second; delete empty super packages
  77.933 +            while( !parent.equals( root ) && parent.getChildren().length == 0  ) {
  77.934 +                FileObject newParent = parent.getParent();
  77.935 +                parent.delete();
  77.936 +                parent = newParent;
  77.937 +            }
  77.938 +        }
  77.939 +        
  77.940 +        /**
  77.941 +         * Initially overridden to support CVS status labels in package nodes.
  77.942 +         *  
  77.943 +         * @return annotated display name
  77.944 +         */ 
  77.945 +        @Override
  77.946 +        public String getHtmlDisplayName() {
  77.947 +            String name = getDisplayName();
  77.948 +            try {
  77.949 +                FileObject fo = dataFolder.getPrimaryFile();
  77.950 +                Set<FileObject> set = new NonRecursiveFolderSet(fo);
  77.951 +                FileSystem.Status status = fo.getFileSystem().getStatus();
  77.952 +                if (status instanceof FileSystem.HtmlStatus) {
  77.953 +                    name = ((FileSystem.HtmlStatus) status).annotateNameHtml(name, set);
  77.954 +                } else {
  77.955 +                    // #89138: return null if the name starts with '<' and status is not HtmlStatus
  77.956 +                    if (name.startsWith("<")) {
  77.957 +                        name = null;
  77.958 +                    } else {
  77.959 +                        name = status.annotateName(name, set);
  77.960 +                    }
  77.961 +                }
  77.962 +            } catch (FileStateInvalidException e) {
  77.963 +                // no fs, do nothing
  77.964 +            }
  77.965 +            return name;
  77.966 +        }
  77.967 +        
  77.968 +        @Override
  77.969 +        public String getDisplayName() {
  77.970 +            FileObject folder = dataFolder.getPrimaryFile();
  77.971 +            String path = FileUtil.getRelativePath(root, folder);
  77.972 +            if (path == null) {
  77.973 +                // ???
  77.974 +                return "";
  77.975 +            }
  77.976 +            return PackageDisplayUtils.getDisplayLabel( path.replace('/', '.'));
  77.977 +        }
  77.978 +        
  77.979 +        @Override
  77.980 +        public String getShortDescription() {
  77.981 +            FileObject folder = dataFolder.getPrimaryFile();
  77.982 +            String path = FileUtil.getRelativePath(root, folder);
  77.983 +            if (path == null) {
  77.984 +                // ???
  77.985 +                return "";
  77.986 +            }
  77.987 +            return PackageDisplayUtils.getToolTip(folder, path.replace('/', '.'));
  77.988 +        }
  77.989 +
  77.990 +        @Override
  77.991 +        public Image getIcon (int type) {
  77.992 +            Image img = getMyIcon (type);
  77.993 +
  77.994 +            try {
  77.995 +                FileObject fo = dataFolder.getPrimaryFile();
  77.996 +                Set<FileObject> set = new NonRecursiveFolderSet(fo);
  77.997 +                img = fo.getFileSystem ().getStatus ().annotateIcon (img, type, set);
  77.998 +            } catch (FileStateInvalidException e) {
  77.999 +                // no fs, do nothing
 77.1000 +            }
 77.1001 +
 77.1002 +            return img;
 77.1003 +        }
 77.1004 +
 77.1005 +        @Override
 77.1006 +        public Image getOpenedIcon (int type) {
 77.1007 +            Image img = getMyOpenedIcon(type);
 77.1008 +
 77.1009 +            try {
 77.1010 +                FileObject fo = dataFolder.getPrimaryFile();
 77.1011 +                Set<FileObject> set = new NonRecursiveFolderSet(fo);
 77.1012 +                img = fo.getFileSystem ().getStatus ().annotateIcon (img, type, set);
 77.1013 +            } catch (FileStateInvalidException e) {
 77.1014 +                // no fs, do nothing
 77.1015 +            }
 77.1016 +
 77.1017 +            return img;
 77.1018 +        }
 77.1019 +        
 77.1020 +        
 77.1021 +        private Image getMyIcon(int type) {
 77.1022 +            FileObject folder = dataFolder.getPrimaryFile();
 77.1023 +            String path = FileUtil.getRelativePath(root, folder);
 77.1024 +            if (path == null) {
 77.1025 +                // ??? - #103711: null cannot be returned because the icon 
 77.1026 +                // must be annotated; general package icon is returned instead
 77.1027 +                return ImageUtilities.loadImage("org/netbeans/modules/python/project/resources/package.gif"); // NOI18N
 77.1028 +            }
 77.1029 +            //return PackageDisplayUtils.getIcon(folder, path.replace('/', '.'), isLeaf() );
 77.1030 +            return PackageDisplayUtils.getIcon(folder, path.replace('/', '.'),
 77.1031 +                    PackageDisplayUtils.isEmpty(dataFolder.getPrimaryFile(), false, true));
 77.1032 +        }
 77.1033 +        
 77.1034 +        private Image getMyOpenedIcon(int type) {
 77.1035 +            return getMyIcon(type);
 77.1036 +        }
 77.1037 +        
 77.1038 +        public void update() {
 77.1039 +            fireIconChange();
 77.1040 +            fireOpenedIconChange();            
 77.1041 +        }
 77.1042 +        
 77.1043 +        public void updateDisplayName() {
 77.1044 +            fireNameChange(null, null);
 77.1045 +            fireDisplayNameChange(null, null);
 77.1046 +            fireShortDescriptionChange(null, null);
 77.1047 +        }
 77.1048 +        
 77.1049 +        public void updateChildren() {            
 77.1050 +            boolean leaf = isLeaf();
 77.1051 +            DataFolder df = getDataFolder();
 77.1052 +            boolean empty = isEmpty( df ); 
 77.1053 +            if ( leaf != empty ) {
 77.1054 +                setChildren( empty ? Children.LEAF: df.createNodeChildren( NO_FOLDERS_FILTER ) );                
 77.1055 +                update();
 77.1056 +            }
 77.1057 +        }
 77.1058 +        
 77.1059 +        @Override
 77.1060 +        public Node.PropertySet[] getPropertySets () {
 77.1061 +            Node.PropertySet[] properties = super.getPropertySets ();
 77.1062 +            for (int i=0; i< properties.length; i++) {
 77.1063 +                if (Sheet.PROPERTIES.equals(properties[i].getName())) {
 77.1064 +                    //Replace the Sheet.PROPERTIES by the new one
 77.1065 +                    //having only the name property which does refactoring
 77.1066 +                    properties[i] = Sheet.createPropertiesSet();
 77.1067 +                    ((Sheet.Set) properties[i]).put(new PropertySupport.ReadWrite<String>(DataObject.PROP_NAME, String.class,
 77.1068 +                            NbBundle.getMessage(PackageViewChildren.class,"PROP_name"), NbBundle.getMessage(PackageViewChildren.class,"HINT_name")) {
 77.1069 +                        @Override
 77.1070 +                        public String getValue() {
 77.1071 +                            return PackageViewChildren.PackageNode.this.getName();
 77.1072 +                        }
 77.1073 +                        @Override
 77.1074 +                        public void setValue(String n) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 77.1075 +                            if (!canRename()) {
 77.1076 +                                throw new IllegalAccessException();
 77.1077 +                            }
 77.1078 +                            PackageViewChildren.PackageNode.this.setName(n);
 77.1079 +                        }
 77.1080 +                        @Override
 77.1081 +                        public boolean canWrite() {
 77.1082 +                            return PackageViewChildren.PackageNode.this.canRename();
 77.1083 +                        }
 77.1084 +                    });
 77.1085 +                }
 77.1086 +            }
 77.1087 +            return properties;
 77.1088 +        }
 77.1089 +        
 77.1090 +        private DataFolder getDataFolder() {
 77.1091 +            return getCookie(DataFolder.class);
 77.1092 +        }
 77.1093 +        
 77.1094 +        private boolean isValidPackageName(String name) {
 77.1095 +            if (name.length() == 0) {
 77.1096 +                //Fast check of default pkg
 77.1097 +                return true;
 77.1098 +            }
 77.1099 +            StringTokenizer tk = new StringTokenizer(name,".",true); //NOI18N
 77.1100 +            boolean delimExpected = false;
 77.1101 +            while (tk.hasMoreTokens()) {
 77.1102 +                String namePart = tk.nextToken();
 77.1103 +                if (!delimExpected) {
 77.1104 +                    if (namePart.equals(".")) { //NOI18N
 77.1105 +                        return false;
 77.1106 +                    }
 77.1107 +                    for (int i=0; i< namePart.length(); i++) {
 77.1108 +                        char c = namePart.charAt(i);
 77.1109 +                        if (i == 0) {
 77.1110 +                            if (!Character.isJavaIdentifierStart (c)) {
 77.1111 +                                return false;
 77.1112 +                            }
 77.1113 +                        }
 77.1114 +                        else {
 77.1115 +                            if (!Character.isJavaIdentifierPart(c)) {
 77.1116 +                                return false;
 77.1117 +                            }
 77.1118 +                        }
 77.1119 +                    }
 77.1120 +                }
 77.1121 +                else {
 77.1122 +                    if (!namePart.equals(".")) { //NOI18N
 77.1123 +                        return false;
 77.1124 +                    }
 77.1125 +                }
 77.1126 +                delimExpected = !delimExpected;
 77.1127 +            }
 77.1128 +            return delimExpected;
 77.1129 +        }
 77.1130 +    }
 77.1131 +    
 77.1132 +    private static final class NoFoldersContainer 
 77.1133 +    implements DataObject.Container, PropertyChangeListener,
 77.1134 +               NonRecursiveFolder {
 77.1135 +        private DataFolder folder;
 77.1136 +        private PropertyChangeSupport prop = new PropertyChangeSupport (this);
 77.1137 +        
 77.1138 +        public NoFoldersContainer (DataFolder folder) {
 77.1139 +            this.folder = folder;
 77.1140 +        }
 77.1141 +        
 77.1142 +        @Override
 77.1143 +        public FileObject getFolder() {
 77.1144 +            return folder.getPrimaryFile();
 77.1145 +        }
 77.1146 +        
 77.1147 +        @Override
 77.1148 +        public DataObject[] getChildren () {
 77.1149 +            DataObject[] arr = folder.getChildren ();
 77.1150 +            List<DataObject> list = new ArrayList<>(arr.length);
 77.1151 +            for (int i = 0; i < arr.length; i++) {
 77.1152 +                if (arr[i] instanceof DataFolder) continue;
 77.1153 +                
 77.1154 +                list.add (arr[i]);
 77.1155 +            }
 77.1156 +            return list.size() == arr.length ? arr : list.toArray(new DataObject[0]);
 77.1157 +        }
 77.1158 +
 77.1159 +        @Override
 77.1160 +        public void addPropertyChangeListener(PropertyChangeListener l) {
 77.1161 +            prop.addPropertyChangeListener (l);
 77.1162 +        }
 77.1163 +
 77.1164 +        @Override
 77.1165 +        public void removePropertyChangeListener(PropertyChangeListener l) {
 77.1166 +            prop.removePropertyChangeListener (l);
 77.1167 +        }
 77.1168 +
 77.1169 +        @Override
 77.1170 +        public void propertyChange(PropertyChangeEvent evt) {
 77.1171 +            if (DataObject.Container.PROP_CHILDREN.equals (evt.getPropertyName ())) {
 77.1172 +                prop.firePropertyChange (PROP_CHILDREN, null, null);
 77.1173 +            }
 77.1174 +        }
 77.1175 +    }
 77.1176 +    
 77.1177 +    private final class NoFoldersDataFilter implements ChangeListener, ChangeableDataFilter, DataFilter.FileBased {
 77.1178 +        
 77.1179 +        private final ChangeSupport cs = new ChangeSupport(this);
 77.1180 +        
 77.1181 +        public NoFoldersDataFilter() {
 77.1182 +            VisibilityQuery.getDefault().addChangeListener(WeakListeners.change(this, VisibilityQuery.getDefault()));
 77.1183 +        }
 77.1184 +                
 77.1185 +        @Override
 77.1186 +        public boolean acceptDataObject(DataObject obj) {  
 77.1187 +            // Filter out .pyc or .pyo files!
 77.1188 +            String ext = obj.getPrimaryFile().getExt();
 77.1189 +            if ("pyc".equals(ext) || "pyo".equals(ext)) { // NOI18N
 77.1190 +                return false;
 77.1191 +            }
 77.1192 +            return acceptFileObject(obj.getPrimaryFile());
 77.1193 +        }
 77.1194 +        
 77.1195 +        @Override
 77.1196 +        public void stateChanged( ChangeEvent e) {            
 77.1197 +            cs.fireChange();
 77.1198 +        }        
 77.1199 +    
 77.1200 +        @Override
 77.1201 +        public void addChangeListener( ChangeListener listener ) {
 77.1202 +            cs.addChangeListener(listener);
 77.1203 +        }        
 77.1204 +                        
 77.1205 +        @Override
 77.1206 +        public void removeChangeListener( ChangeListener listener ) {
 77.1207 +            cs.removeChangeListener(listener);
 77.1208 +        }
 77.1209 +
 77.1210 +        @Override
 77.1211 +        public boolean acceptFileObject(FileObject fo) {
 77.1212 +            return  fo.isValid() && VisibilityQuery.getDefault().isVisible(fo) && fo.isData() && group.contains(fo);
 77.1213 +        }
 77.1214 +        
 77.1215 +    }
 77.1216 +
 77.1217 +    static class PackageTransferable extends ExTransferable.Single {
 77.1218 +
 77.1219 +        private PackageNode node;
 77.1220 +
 77.1221 +        public PackageTransferable (PackageNode node, int operation) throws ClassNotFoundException {
 77.1222 +            super(new DataFlavor(PACKAGE_FLAVOR.format(new Object[] {new Integer(operation)}), null, PackageNode.class.getClassLoader()));
 77.1223 +            this.node = node;
 77.1224 +        }
 77.1225 +
 77.1226 +        @Override
 77.1227 +        protected Object getData() throws IOException, UnsupportedFlavorException {
 77.1228 +            return this.node;
 77.1229 +        }
 77.1230 +    }
 77.1231 +
 77.1232 +
 77.1233 +    static class PackagePasteType extends PasteType {
 77.1234 +        
 77.1235 +        private int op;
 77.1236 +        private PackageNode[] nodes;
 77.1237 +        private FileObject srcRoot;
 77.1238 +
 77.1239 +        public PackagePasteType (FileObject srcRoot, PackageNode[] node, int op) {
 77.1240 +            assert op == DnDConstants.ACTION_COPY || op == DnDConstants.ACTION_MOVE  || op == DnDConstants.ACTION_NONE : "Invalid DnD operation";  //NOI18N
 77.1241 +            this.nodes = node;
 77.1242 +            this.op = op;
 77.1243 +            this.srcRoot = srcRoot;
 77.1244 +        }
 77.1245 +        
 77.1246 +        public void setOperation (int op) {
 77.1247 +            this.op = op;
 77.1248 +        }
 77.1249 +
 77.1250 +        @Override
 77.1251 +        public Transferable paste() throws IOException {
 77.1252 +            assert this.op != DnDConstants.ACTION_NONE;
 77.1253 +            for (int ni=0; ni< nodes.length; ni++) {
 77.1254 +                FileObject fo = srcRoot;
 77.1255 +                if (!nodes[ni].isDefaultPackage) {
 77.1256 +                    String pkgName = nodes[ni].getName();
 77.1257 +                    StringTokenizer tk = new StringTokenizer(pkgName,".");  //NOI18N
 77.1258 +                    while (tk.hasMoreTokens()) {
 77.1259 +                        String name = tk.nextToken();
 77.1260 +                        FileObject tmp = fo.getFileObject(name,null);
 77.1261 +                        if (tmp == null) {
 77.1262 +                            tmp = fo.createFolder(name);
 77.1263 +                        }
 77.1264 +                        fo = tmp;
 77.1265 +                    }
 77.1266 +                }
 77.1267 +                DataFolder dest = DataFolder.findFolder(fo);
 77.1268 +                DataObject[] children = nodes[ni].dataFolder.getChildren();
 77.1269 +                boolean cantDelete = false;
 77.1270 +                for (int i=0; i< children.length; i++) {
 77.1271 +                    if (children[i].getPrimaryFile().isData() 
 77.1272 +                    && VisibilityQuery.getDefault().isVisible (children[i].getPrimaryFile())) {
 77.1273 +                        //Copy only the package level
 77.1274 +                        if (this.op == DnDConstants.ACTION_MOVE) {
 77.1275 +                            children[i].move(dest);
 77.1276 +                        }
 77.1277 +                        else {
 77.1278 +                            children[i].copy (dest);
 77.1279 +                        }                                                
 77.1280 +                    }
 77.1281 +                    else {
 77.1282 +                        cantDelete = true;
 77.1283 +                    }
 77.1284 +                }
 77.1285 +                if (this.op == DnDConstants.ACTION_MOVE && !cantDelete) {
 77.1286 +                    try {
 77.1287 +                        FileObject tmpFo = nodes[ni].dataFolder.getPrimaryFile();
 77.1288 +                        FileObject originalRoot = nodes[ni].root;
 77.1289 +                        assert tmpFo != null && originalRoot != null;
 77.1290 +                        while (!tmpFo.equals(originalRoot)) {
 77.1291 +                            if (tmpFo.getChildren().length == 0) {
 77.1292 +                                FileObject tmpFoParent = tmpFo.getParent();
 77.1293 +                                tmpFo.delete ();
 77.1294 +                                tmpFo = tmpFoParent;
 77.1295 +                            }
 77.1296 +                            else {
 77.1297 +                                break;
 77.1298 +                            }
 77.1299 +                        }
 77.1300 +                    } catch (IOException ioe) {
 77.1301 +                        //Not important
 77.1302 +                    }
 77.1303 +                }
 77.1304 +            }
 77.1305 +            return ExTransferable.EMPTY;
 77.1306 +        }
 77.1307 +
 77.1308 +        @Override
 77.1309 +        public String getName() {
 77.1310 +            return NbBundle.getMessage(PackageViewChildren.class,"TXT_PastePackage");
 77.1311 +        }
 77.1312 +    }
 77.1313 +
 77.1314 +    /**
 77.1315 +     * FileObject set that represents package. It means
 77.1316 +     * that it's content must not be processed recursively.
 77.1317 +     */
 77.1318 +    private static class NonRecursiveFolderSet extends HashSet<FileObject> implements NonRecursiveFolder {
 77.1319 +        
 77.1320 +        private final FileObject folder;
 77.1321 +        
 77.1322 +        /**
 77.1323 +         * Creates set with one element, the folder.
 77.1324 +         */
 77.1325 +        public NonRecursiveFolderSet(FileObject folder) {
 77.1326 +            this.folder = folder;
 77.1327 +            add(folder);
 77.1328 +        }
 77.1329 +        
 77.1330 +        @Override
 77.1331 +        public FileObject getFolder() {
 77.1332 +            return folder;
 77.1333 +        }        
 77.1334 +    }
 77.1335 +}
    78.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    78.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/PythonProjectSettings.java	Tue Feb 24 01:58:36 2015 -0800
    78.3 @@ -0,0 +1,119 @@
    78.4 +/*
    78.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    78.6 + *
    78.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    78.8 + *
    78.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   78.10 + * Other names may be trademarks of their respective owners.
   78.11 + *
   78.12 + * The contents of this file are subject to the terms of either the GNU
   78.13 + * General Public License Version 2 only ("GPL") or the Common
   78.14 + * Development and Distribution License("CDDL") (collectively, the
   78.15 + * "License"). You may not use this file except in compliance with the
   78.16 + * License. You can obtain a copy of the License at
   78.17 + * http://www.netbeans.org/cddl-gplv2.html
   78.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   78.19 + * specific language governing permissions and limitations under the
   78.20 + * License.  When distributing the software, include this License Header
   78.21 + * Notice in each file and include the License file at
   78.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   78.23 + * particular file as subject to the "Classpath" exception as provided
   78.24 + * by Oracle in the GPL Version 2 section of the License file that
   78.25 + * accompanied this code. If applicable, add the following below the
   78.26 + * License Header, with the fields enclosed by brackets [] replaced by
   78.27 + * your own identifying information:
   78.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   78.29 + *
   78.30 + * Contributor(s):
   78.31 + *
   78.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   78.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   78.34 + * Microsystems, Inc. All Rights Reserved.
   78.35 + *
   78.36 + * If you wish your version of this file to be governed by only the CDDL
   78.37 + * or only the GPL Version 2, indicate your decision by adding
   78.38 + * "[Contributor] elects to include this software in this distribution
   78.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   78.40 + * single choice of license, a recipient has the option to distribute
   78.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   78.42 + * to extend the choice of license to its licensees as provided above.
   78.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   78.44 + * Version 2 license, then the option applies only if the new code is
   78.45 + * made subject to such option by the copyright holder.
   78.46 + */
   78.47 +
   78.48 +package org.netbeans.modules.python.project2.ui;
   78.49 +
   78.50 +import java.beans.PropertyChangeListener;
   78.51 +import java.beans.PropertyChangeSupport;
   78.52 +import java.util.prefs.Preferences;
   78.53 +import org.openide.util.NbPreferences;
   78.54 +
   78.55 +/**
   78.56 + * Preferences for the module.
   78.57 + * <p>
   78.58 + * <b>This is copied from the corresponding Java action in java.projects (JavaProjectSettings)</b>
   78.59 + * </p>
   78.60 + *
   78.61 + * @author Tomas Zezula, Jesse Glick
   78.62 + */
   78.63 +public class PythonProjectSettings {
   78.64 +
   78.65 +    private PythonProjectSettings() {}
   78.66 +
   78.67 +    private static final PropertyChangeSupport pcs = new PropertyChangeSupport(PythonProjectSettings.class);
   78.68 +
   78.69 +    /**
   78.70 +     * The package view should be displayed as a list of packages.
   78.71 +     */
   78.72 +    public static final int TYPE_PACKAGE_VIEW = 0;
   78.73 +
   78.74 +    /**
   78.75 +     * The package view should be displayed as a tree of folders.
   78.76 +     */
   78.77 +    public static final int TYPE_TREE = 1;
   78.78 +
   78.79 +    public static final String PROP_PACKAGE_VIEW_TYPE = "packageViewType"; //NOI18N
   78.80 +//    private static final String PROP_SHOW_AGAIN_BROKEN_REF_ALERT = "showAgainBrokenRefAlert"; //NOI18N
   78.81 +
   78.82 +    private static Preferences prefs() {
   78.83 +        return NbPreferences.forModule(PythonProjectSettings.class);
   78.84 +    }
   78.85 +
   78.86 +    /**
   78.87 +     * Returns how the package view should be displayed.
   78.88 +     * @return {@link #TYPE_PACKAGE_VIEW} or {@link #TYPE_TREE}
   78.89 +     */
   78.90 +    public static int getPackageViewType() {
   78.91 +        return prefs().getInt(PROP_PACKAGE_VIEW_TYPE, TYPE_PACKAGE_VIEW);
   78.92 +    }
   78.93 +
   78.94 +    /**
   78.95 +     * Sets how the package view should be displayed.
   78.96 +     * @param type either {@link #TYPE_PACKAGE_VIEW} or {@link #TYPE_TREE}
   78.97 +     */
   78.98 +    public static void setPackageViewType(int type) {
   78.99 +        int currentType = getPackageViewType();
  78.100 +        if (currentType != type) {
  78.101 +            prefs().putInt(PROP_PACKAGE_VIEW_TYPE, type);
  78.102 +            pcs.firePropertyChange(PROP_PACKAGE_VIEW_TYPE, currentType, type);
  78.103 +        }
  78.104 +    }
  78.105 +
  78.106 +//    public static boolean isShowAgainBrokenRefAlert() {
  78.107 +//        return prefs().getBoolean(PROP_SHOW_AGAIN_BROKEN_REF_ALERT, true);
  78.108 +//    }
  78.109 +//
  78.110 +//    public static void setShowAgainBrokenRefAlert(boolean again) {
  78.111 +//        prefs().putBoolean(PROP_SHOW_AGAIN_BROKEN_REF_ALERT, again);
  78.112 +//    }
  78.113 +
  78.114 +    public static void addPropertyChangeListener(PropertyChangeListener l) {
  78.115 +        pcs.addPropertyChangeListener(l);
  78.116 +    }
  78.117 +
  78.118 +    public static void removePropertyChangeListener(PropertyChangeListener l) {
  78.119 +        pcs.removePropertyChangeListener(l);
  78.120 +    }
  78.121 +
  78.122 +}
    79.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    79.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/SourceNodeFactory.java	Tue Feb 24 01:58:36 2015 -0800
    79.3 @@ -0,0 +1,257 @@
    79.4 +/*
    79.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    79.6 + *
    79.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    79.8 + *
    79.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   79.10 + * Other names may be trademarks of their respective owners.
   79.11 + *
   79.12 + * The contents of this file are subject to the terms of either the GNU
   79.13 + * General Public License Version 2 only ("GPL") or the Common
   79.14 + * Development and Distribution License("CDDL") (collectively, the
   79.15 + * "License"). You may not use this file except in compliance with the
   79.16 + * License. You can obtain a copy of the License at
   79.17 + * http://www.netbeans.org/cddl-gplv2.html
   79.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   79.19 + * specific language governing permissions and limitations under the
   79.20 + * License.  When distributing the software, include this License Header
   79.21 + * Notice in each file and include the License file at
   79.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   79.23 + * particular file as subject to the "Classpath" exception as provided
   79.24 + * by Oracle in the GPL Version 2 section of the License file that
   79.25 + * accompanied this code. If applicable, add the following below the
   79.26 + * License Header, with the fields enclosed by brackets [] replaced by
   79.27 + * your own identifying information:
   79.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   79.29 + *
   79.30 + * Contributor(s):
   79.31 + *
   79.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   79.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   79.34 + * Microsystems, Inc. All Rights Reserved.
   79.35 + *
   79.36 + * If you wish your version of this file to be governed by only the CDDL
   79.37 + * or only the GPL Version 2, indicate your decision by adding
   79.38 + * "[Contributor] elects to include this software in this distribution
   79.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   79.40 + * single choice of license, a recipient has the option to distribute
   79.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   79.42 + * to extend the choice of license to its licensees as provided above.
   79.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   79.44 + * Version 2 license, then the option applies only if the new code is
   79.45 + * made subject to such option by the copyright holder.
   79.46 + */
   79.47 +
   79.48 +package org.netbeans.modules.python.project2.ui;
   79.49 +
   79.50 +import java.awt.event.ActionEvent;
   79.51 +import java.util.ArrayList;
   79.52 +import java.util.Collections;
   79.53 +import java.util.List;
   79.54 +import javax.swing.AbstractAction;
   79.55 +import javax.swing.Action;
   79.56 +import javax.swing.SwingUtilities;
   79.57 +import javax.swing.event.ChangeEvent;
   79.58 +import javax.swing.event.ChangeListener;
   79.59 +import org.netbeans.modules.python.project2.PythonProject2;
   79.60 +import org.netbeans.api.project.Project;
   79.61 +import org.netbeans.api.project.ProjectUtils;
   79.62 +import org.netbeans.api.project.SourceGroup;
   79.63 +import org.netbeans.api.project.Sources;
   79.64 +import org.netbeans.spi.project.ui.support.NodeFactory;
   79.65 +import org.netbeans.spi.project.ui.support.NodeList;
   79.66 +import org.openide.filesystems.FileObject;
   79.67 +import org.openide.nodes.FilterNode;
   79.68 +import org.openide.nodes.Node;
   79.69 +import org.openide.util.ChangeSupport;
   79.70 +import org.openide.util.NbBundle;
   79.71 +
   79.72 +/**
   79.73 + * Source roots view
   79.74 + * @author Tomas Zezula
   79.75 + */
   79.76 +@NbBundle.Messages({"LBL_Properties_Action=Properties"})
   79.77 +public final class SourceNodeFactory implements NodeFactory {
   79.78 +    public SourceNodeFactory() {
   79.79 +    }
   79.80 +    
   79.81 +    @Override
   79.82 +    public NodeList createNodes(Project p) {
   79.83 +        PythonProject2 project = (PythonProject2)p.getLookup().lookup(PythonProject2.class);
   79.84 +        assert project != null;
   79.85 +        return new SourcesNodeList(project);
   79.86 +    }
   79.87 +    
   79.88 +    private static class SourcesNodeList implements NodeList<SourceGroupKey>, ChangeListener {
   79.89 +        
   79.90 +        private PythonProject2 project;
   79.91 +        
   79.92 +        private final ChangeSupport changeSupport = new ChangeSupport(this);
   79.93 +        
   79.94 +        public SourcesNodeList(PythonProject2 proj) {
   79.95 +            project = proj;
   79.96 +        }
   79.97 +        
   79.98 +        @Override
   79.99 +        public List<SourceGroupKey> keys() {
  79.100 +            if (this.project.getProjectDirectory() == null || !this.project.getProjectDirectory().isValid()) {
  79.101 +                return Collections.EMPTY_LIST;
  79.102 +            }
  79.103 +            Sources sources = getSources();
  79.104 +            SourceGroup[] groups = sources.getSourceGroups(PythonProject2.SOURCES_TYPE_PYTHON);
  79.105 +            
  79.106 +            List result =  new ArrayList(groups.length);
  79.107 +            for (SourceGroup group : groups) {
  79.108 +                result.add(new SourceGroupKey(group));
  79.109 +            }
  79.110 +            return result;
  79.111 +        }
  79.112 +        
  79.113 +        @Override
  79.114 +        public void addChangeListener(ChangeListener l) {
  79.115 +            changeSupport.addChangeListener(l);
  79.116 +        }
  79.117 +        
  79.118 +        @Override
  79.119 +        public void removeChangeListener(ChangeListener l) {
  79.120 +            changeSupport.removeChangeListener(l);
  79.121 +        }
  79.122 +        
  79.123 +        @Override
  79.124 +        public Node node(SourceGroupKey key) {
  79.125 +            return new PackageViewFilterNode(key.group, project);
  79.126 +        }
  79.127 +        
  79.128 +        @Override
  79.129 +        public void addNotify() {
  79.130 +            getSources().addChangeListener(this);
  79.131 +        }
  79.132 +        
  79.133 +        @Override
  79.134 +        public void removeNotify() {
  79.135 +            getSources().removeChangeListener(this);
  79.136 +        }
  79.137 +        
  79.138 +        @Override
  79.139 +        public void stateChanged(ChangeEvent e) {
  79.140 +            // setKeys(getKeys());
  79.141 +            // The caller holds ProjectManager.mutex() read lock
  79.142 +            SwingUtilities.invokeLater(new Runnable() {
  79.143 +                @Override
  79.144 +                public void run() {
  79.145 +                    changeSupport.fireChange();
  79.146 +                }
  79.147 +            });
  79.148 +        }
  79.149 +        
  79.150 +        private Sources getSources() {
  79.151 +            return ProjectUtils.getSources(project);
  79.152 +        }
  79.153 +        
  79.154 +    }
  79.155 +    
  79.156 +    private static class SourceGroupKey {
  79.157 +        
  79.158 +        public final SourceGroup group;
  79.159 +        public final FileObject fileObject;
  79.160 +        
  79.161 +        SourceGroupKey(SourceGroup group) {
  79.162 +            this.group = group;
  79.163 +            this.fileObject = group.getRootFolder();
  79.164 +        }
  79.165 +        
  79.166 +        @Override
  79.167 +        public int hashCode() {
  79.168 +            int hash = 5;
  79.169 +            String disp = this.group.getDisplayName();
  79.170 +            hash = 79 * hash + (fileObject != null ? fileObject.hashCode() : 0);
  79.171 +            hash = 79 * hash + (disp != null ? disp.hashCode() : 0);
  79.172 +            return hash;
  79.173 +        }
  79.174 +        
  79.175 +        @Override
  79.176 +        public boolean equals(Object obj) {
  79.177 +            if (!(obj instanceof SourceGroupKey)) {
  79.178 +                return false;
  79.179 +            } else {
  79.180 +                SourceGroupKey otherKey = (SourceGroupKey) obj;
  79.181 +                
  79.182 +                if (fileObject != otherKey.fileObject && (fileObject == null || !fileObject.equals(otherKey.fileObject))) {
  79.183 +                    return false;
  79.184 +                }
  79.185 +                String thisDisplayName = this.group.getDisplayName();
  79.186 +                String otherDisplayName = otherKey.group.getDisplayName();
  79.187 +                boolean oneNull = thisDisplayName == null;
  79.188 +                boolean twoNull = otherDisplayName == null;
  79.189 +                return !(oneNull != twoNull || !thisDisplayName.equals(otherDisplayName));
  79.190 +            }
  79.191 +        }
  79.192 +
  79.193 +        
  79.194 +    }
  79.195 +    
  79.196 +    /** Yet another cool filter node just to add properties action
  79.197 +     */
  79.198 +    private static class PackageViewFilterNode extends FilterNode {
  79.199 +        
  79.200 +        private String nodeName;
  79.201 +        private Project project;
  79.202 +        
  79.203 +        Action[] actions;
  79.204 +        
  79.205 +        public PackageViewFilterNode(SourceGroup sourceGroup, Project project) {
  79.206 +            super(PackageView.createPackageView(sourceGroup));
  79.207 +            this.project = project;
  79.208 +            this.nodeName = "Sources";  //NOI18N
  79.209 +        }
  79.210 +        
  79.211 +        @Override
  79.212 +        public Action[] getActions(boolean context) {
  79.213 +            if (!context) {
  79.214 +                if (actions == null) {
  79.215 +                    Action superActions[] = super.getActions(context);
  79.216 +                    actions = new Action[superActions.length + 2];
  79.217 +                    System.arraycopy(superActions, 0, actions, 0, superActions.length);
  79.218 +                    actions[superActions.length] = null;
  79.219 +                    actions[superActions.length + 1] = new PreselectPropertiesAction(project, nodeName);
  79.220 +                }
  79.221 +                return actions;
  79.222 +            } else {
  79.223 +                return super.getActions(context);
  79.224 +            }
  79.225 +        }
  79.226 +        
  79.227 +    }
  79.228 +    
  79.229 +    
  79.230 +    /** The special properties action
  79.231 +     */
  79.232 +    static class PreselectPropertiesAction extends AbstractAction {
  79.233 +        
  79.234 +        private final Project project;
  79.235 +        private final String nodeName;
  79.236 +        private final String panelName;
  79.237 +        
  79.238 +        public PreselectPropertiesAction(Project project, String nodeName) {
  79.239 +            this(project, nodeName, null);
  79.240 +        }
  79.241 +        
  79.242 +        public PreselectPropertiesAction(Project project, String nodeName, String panelName) {
  79.243 +            super(NbBundle.getMessage(SourceNodeFactory.class, "LBL_Properties_Action"));
  79.244 +            this.project = project;
  79.245 +            this.nodeName = nodeName;
  79.246 +            this.panelName = panelName;
  79.247 +        }
  79.248 +        
  79.249 +        @Override
  79.250 +        public void actionPerformed(ActionEvent e) {
  79.251 +//todo: Add customizer            
  79.252 +//            CustomizerProviderImpl cp = (CustomizerProviderImpl) project.getLookup().lookup(CustomizerProviderImpl.class);
  79.253 +//            if (cp != null) {
  79.254 +//                cp.showCustomizer(nodeName, panelName);
  79.255 +//            }
  79.256 +            
  79.257 +        }
  79.258 +    }
  79.259 +    
  79.260 +}
    80.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    80.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/TreeRootNode.java	Tue Feb 24 01:58:36 2015 -0800
    80.3 @@ -0,0 +1,312 @@
    80.4 +/*
    80.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    80.6 + *
    80.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    80.8 + *
    80.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   80.10 + * Other names may be trademarks of their respective owners.
   80.11 + *
   80.12 + * The contents of this file are subject to the terms of either the GNU
   80.13 + * General Public License Version 2 only ("GPL") or the Common
   80.14 + * Development and Distribution License("CDDL") (collectively, the
   80.15 + * "License"). You may not use this file except in compliance with the
   80.16 + * License. You can obtain a copy of the License at
   80.17 + * http://www.netbeans.org/cddl-gplv2.html
   80.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   80.19 + * specific language governing permissions and limitations under the
   80.20 + * License.  When distributing the software, include this License Header
   80.21 + * Notice in each file and include the License file at
   80.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   80.23 + * particular file as subject to the "Classpath" exception as provided
   80.24 + * by Oracle in the GPL Version 2 section of the License file that
   80.25 + * accompanied this code. If applicable, add the following below the
   80.26 + * License Header, with the fields enclosed by brackets [] replaced by
   80.27 + * your own identifying information:
   80.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   80.29 + *
   80.30 + * Contributor(s):
   80.31 + *
   80.32 + * The Original Software is NetBeans. The Initial Developer of the Original
   80.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
   80.34 + * Microsystems, Inc. All Rights Reserved.
   80.35 + *
   80.36 + * If you wish your version of this file to be governed by only the CDDL
   80.37 + * or only the GPL Version 2, indicate your decision by adding
   80.38 + * "[Contributor] elects to include this software in this distribution
   80.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   80.40 + * single choice of license, a recipient has the option to distribute
   80.41 + * your version of this file under either the CDDL, the GPL Version 2 or
   80.42 + * to extend the choice of license to its licensees as provided above.
   80.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   80.44 + * Version 2 license, then the option applies only if the new code is
   80.45 + * made subject to such option by the copyright holder.
   80.46 + */
   80.47 +package org.netbeans.modules.python.project2.ui;
   80.48 +
   80.49 +import java.awt.EventQueue;
   80.50 +import java.awt.Image;
   80.51 +import java.beans.PropertyChangeEvent;
   80.52 +import java.beans.PropertyChangeListener;
   80.53 +import java.util.ArrayList;
   80.54 +import java.util.Collections;
   80.55 +import java.util.List;
   80.56 +import java.util.StringTokenizer;
   80.57 +import java.util.logging.Level;
   80.58 +import java.util.logging.Logger;
   80.59 +import javax.swing.Icon;
   80.60 +import javax.swing.event.ChangeEvent;
   80.61 +import javax.swing.event.ChangeListener;
   80.62 +import javax.swing.event.EventListenerList;
   80.63 +import org.netbeans.api.project.SourceGroup;
   80.64 +import org.netbeans.api.queries.VisibilityQuery;
   80.65 +import org.openide.DialogDisplayer;
   80.66 +import org.openide.NotifyDescriptor;
   80.67 +import org.openide.filesystems.FileObject;
   80.68 +import org.openide.filesystems.FileUtil;
   80.69 +import org.openide.loaders.ChangeableDataFilter;
   80.70 +import org.openide.loaders.DataFolder;
   80.71 +import org.openide.loaders.DataObject;
   80.72 +import org.openide.nodes.FilterNode;
   80.73 +import org.openide.nodes.Node;
   80.74 +import org.openide.nodes.NodeNotFoundException;
   80.75 +import org.openide.nodes.NodeOp;
   80.76 +import org.openide.util.ImageUtilities;
   80.77 +import org.openide.util.NbBundle;
   80.78 +import org.openide.util.Utilities;
   80.79 +import org.openide.util.WeakListeners;
   80.80 +import org.openide.util.lookup.Lookups;
   80.81 +import org.openide.util.lookup.ProxyLookup;
   80.82 +
   80.83 +/**
   80.84 + * Copied from java.project API module, the same copy is in ruby project, rails project, etc.
   80.85 + * Unlike PackageViewChildren this class definitelly requires more generic API module, probably projectui.
   80.86 + * Displays a package root in a tree.
   80.87 + * @see "#42151"
   80.88 + * @author Jesse Glick
   80.89 + */
   80.90 +public final class TreeRootNode extends FilterNode implements PropertyChangeListener {
   80.91 +
   80.92 +    private final static Image SOURCE_ROOT_BADGE = ImageUtilities.loadImage("org/netbeans/modules/python/project2/resources/sourceBadge.gif"); // NOI18N
   80.93 +    private final SourceGroup g;
   80.94 +
   80.95 +    public TreeRootNode(SourceGroup g) {
   80.96 +        this(DataFolder.findFolder(g.getRootFolder()), g);
   80.97 +    }
   80.98 +
   80.99 +    private TreeRootNode(DataFolder folder, SourceGroup g) {
  80.100 +        this(new FilterNode(folder.getNodeDelegate(), folder.createNodeChildren(new VisibilityQueryDataFilter(g))), g);
  80.101 +    }
  80.102 +
  80.103 +    @SuppressWarnings("LeakingThisInConstructor")
  80.104 +    private TreeRootNode(Node originalNode, SourceGroup g) {
  80.105 +        super(originalNode, new PackageFilterChildren(originalNode),
  80.106 +                new ProxyLookup(
  80.107 +                originalNode.getLookup(),
  80.108 +                Lookups.singleton(new PathFinder(g)) // no need for explicit search info
  80.109 +                ));
  80.110 +        this.g = g;
  80.111 +        g.addPropertyChangeListener(WeakListeners.propertyChange(this, g));
  80.112 +    }
  80.113 +
  80.114 +    /** Copied from PackageRootNode with modifications. */
  80.115 +    private Image computeIcon(boolean opened, int type) {
  80.116 +        Icon icon = g.getIcon(opened);
  80.117 +        if (icon == null) {
  80.118 +            Image image = opened ? super.getOpenedIcon(type) : super.getIcon(type);
  80.119 +            return ImageUtilities.mergeImages(image, SOURCE_ROOT_BADGE, 7, 7);
  80.120 +        } else {
  80.121 +            return ImageUtilities.icon2Image(icon);
  80.122 +        }
  80.123 +    }
  80.124 +
  80.125 +    @Override
  80.126 +    public Image getIcon(int type) {
  80.127 +        return computeIcon(false, type);
  80.128 +    }
  80.129 +
  80.130 +    @Override
  80.131 +    public Image getOpenedIcon(int type) {
  80.132 +        return computeIcon(true, type);
  80.133 +    }
  80.134 +
  80.135 +    @Override
  80.136 +    public String getName() {
  80.137 +        return g.getName();
  80.138 +    }
  80.139 +
  80.140 +    @Override
  80.141 +    public String getDisplayName() {
  80.142 +        return g.getDisplayName();
  80.143 +    }
  80.144 +
  80.145 +    @Override
  80.146 +    public boolean canRename() {
  80.147 +        return false;
  80.148 +    }
  80.149 +
  80.150 +    @Override
  80.151 +    public boolean canDestroy() {
  80.152 +        return false;
  80.153 +    }
  80.154 +
  80.155 +    @Override
  80.156 +    public boolean canCut() {
  80.157 +        return false;
  80.158 +    }
  80.159 +
  80.160 +    @Override
  80.161 +    public void propertyChange(PropertyChangeEvent ev) {
  80.162 +        // XXX handle SourceGroup.rootFolder change too
  80.163 +        EventQueue.invokeLater(new Runnable() {
  80.164 +
  80.165 +            @Override
  80.166 +            public void run() {
  80.167 +                fireNameChange(null, null);
  80.168 +                fireDisplayNameChange(null, null);
  80.169 +                fireIconChange();
  80.170 +                fireOpenedIconChange();
  80.171 +            }
  80.172 +        });
  80.173 +    }
  80.174 +
  80.175 +    /** Copied from PhysicalView and PackageRootNode. */
  80.176 +    public static final class PathFinder {
  80.177 +
  80.178 +        private final SourceGroup g;
  80.179 +
  80.180 +        PathFinder(SourceGroup g) {
  80.181 +            this.g = g;
  80.182 +        }
  80.183 +
  80.184 +        public Node findPath(Node rootNode, Object o) {
  80.185 +            FileObject fo;
  80.186 +            if (o instanceof FileObject) {
  80.187 +                fo = (FileObject) o;
  80.188 +            } else if (o instanceof DataObject) {
  80.189 +                fo = ((DataObject) o).getPrimaryFile();
  80.190 +            } else {
  80.191 +                return null;
  80.192 +            }
  80.193 +            FileObject groupRoot = g.getRootFolder();
  80.194 +            if (FileUtil.isParentOf(groupRoot, fo) /* && group.contains(fo) */) {
  80.195 +                FileObject folder = fo.isFolder() ? fo : fo.getParent();
  80.196 +                String relPath = FileUtil.getRelativePath(groupRoot, folder);
  80.197 +                List<String> path = new ArrayList<>();
  80.198 +                StringTokenizer strtok = new StringTokenizer(relPath, "/"); // NOI18N
  80.199 +                while (strtok.hasMoreTokens()) {
  80.200 +                    String token = strtok.nextToken();
  80.201 +                    path.add(token);
  80.202 +                }
  80.203 +                try {
  80.204 +                    Node folderNode = folder.equals(groupRoot) ? rootNode : NodeOp.findPath(rootNode, Collections.enumeration(path));
  80.205 +                    if (fo.isFolder()) {
  80.206 +                        return folderNode;
  80.207 +                    } else {
  80.208 +                        Node[] childs = folderNode.getChildren().getNodes(true);
  80.209 +                        for (Node child : childs) {
  80.210 +                            DataObject dobj = child.getLookup().lookup(DataObject.class);
  80.211 +                            if (dobj != null && dobj.getPrimaryFile().getNameExt().equals(fo.getNameExt())) {
  80.212 +                                return child;
  80.213 +                            }
  80.214 +                        }
  80.215 +                    }
  80.216 +                } catch (NodeNotFoundException e) {
  80.217 +                    LOG.log(Level.WARNING, "TreeRootNode.findPath", e);
  80.218 +                }
  80.219 +            } else if (groupRoot.equals(fo)) {
  80.220 +                return rootNode;
  80.221 +            }
  80.222 +            return null;
  80.223 +        }
  80.224 +        private static final Logger LOG = Logger.getLogger(PathFinder.class.getName());
  80.225 +    }
  80.226 +
  80.227 +    private static final class VisibilityQueryDataFilter implements ChangeListener, PropertyChangeListener, ChangeableDataFilter {
  80.228 +
  80.229 +        private static final long serialVersionUID = 1L; // in case a DataFolder.ClonedFilterHandle saves me
  80.230 +        private final EventListenerList ell = new EventListenerList();
  80.231 +        private final SourceGroup g;
  80.232 +
  80.233 +        @SuppressWarnings("LeakingThisInConstructor")
  80.234 +        public VisibilityQueryDataFilter(SourceGroup g) {
  80.235 +            this.g = g;
  80.236 +            VisibilityQuery.getDefault().addChangeListener(WeakListeners.change(this, VisibilityQuery.getDefault()));
  80.237 +            g.addPropertyChangeListener(WeakListeners.propertyChange(this, g));
  80.238 +        }
  80.239 +
  80.240 +        @Override
  80.241 +        public boolean acceptDataObject(DataObject obj) {
  80.242 +            FileObject fo = obj.getPrimaryFile();
  80.243 +            if (fo.getExt().equalsIgnoreCase("pyc") || fo.getExt().equalsIgnoreCase("pyo") | fo.getExt().equalsIgnoreCase("egg-info") || fo.getName().equalsIgnoreCase("build") || fo.getName().equalsIgnoreCase("dist")) {
  80.244 +                return false;
  80.245 +            }
  80.246 +            return g.contains(fo) &&
  80.247 +                    VisibilityQuery.getDefault().isVisible(fo);
  80.248 +        }
  80.249 +
  80.250 +        @Override
  80.251 +        public void stateChanged(ChangeEvent e) {
  80.252 +            fireChange();
  80.253 +        }
  80.254 +
  80.255 +        @Override
  80.256 +        public void propertyChange(PropertyChangeEvent e) {
  80.257 +            if (SourceGroup.PROP_CONTAINERSHIP.equals(e.getPropertyName())) {
  80.258 +                fireChange();
  80.259 +            }
  80.260 +        }
  80.261 +
  80.262 +        private void fireChange() {
  80.263 +            Object[] listeners = ell.getListenerList();
  80.264 +            ChangeEvent event = null;
  80.265 +            for (int i = listeners.length - 2; i >= 0; i -= 2) {
  80.266 +                if (listeners[i] == ChangeListener.class) {
  80.267 +                    if (event == null) {
  80.268 +                        event = new ChangeEvent(this);
  80.269 +                    }
  80.270 +                    ((ChangeListener) listeners[i + 1]).stateChanged(event);
  80.271 +                }
  80.272 +            }
  80.273 +        }
  80.274 +
  80.275 +        @Override
  80.276 +        public void addChangeListener(ChangeListener listener) {
  80.277 +            ell.add(ChangeListener.class, listener);
  80.278 +        }
  80.279 +
  80.280 +        @Override
  80.281 +        public void removeChangeListener(ChangeListener listener) {
  80.282 +            ell.remove(ChangeListener.class, listener);
  80.283 +        }
  80.284 +    }
  80.285 +
  80.286 +    private static final class PackageFilterChildren extends FilterNode.Children {
  80.287 +
  80.288 +        public PackageFilterChildren(final Node originalNode) {
  80.289 +            super(originalNode);
  80.290 +        }
  80.291 +
  80.292 +        @Override
  80.293 +        protected Node copyNode(final Node originalNode) {
  80.294 +            DataObject dobj = originalNode.getLookup().lookup(DataObject.class);
  80.295 +            return (dobj instanceof DataFolder) ? new PackageFilterNode(originalNode) : super.copyNode(originalNode);
  80.296 +        }
  80.297 +    }
  80.298 +
  80.299 +    private static final class PackageFilterNode extends FilterNode {
  80.300 +
  80.301 +        public PackageFilterNode(final Node origNode) {
  80.302 +            super(origNode, new PackageFilterChildren(origNode));
  80.303 +        }
  80.304 +
  80.305 +        @Override
  80.306 +        public void setName(final String name) {
  80.307 +            if (Utilities.isJavaIdentifier(name)) {
  80.308 +                super.setName(name);
  80.309 +            } else {
  80.310 +                DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
  80.311 +                        NbBundle.getMessage(TreeRootNode.class, "MSG_InvalidPackageName"), NotifyDescriptor.INFORMATION_MESSAGE));
  80.312 +            }
  80.313 +        }
  80.314 +    }
  80.315 +}
    81.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    81.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/Utils.java	Tue Feb 24 01:58:36 2015 -0800
    81.3 @@ -0,0 +1,166 @@
    81.4 +package org.netbeans.modules.python.project2.ui;
    81.5 +
    81.6 +import java.awt.Component;
    81.7 +import java.io.File;
    81.8 +import java.util.Iterator;
    81.9 +import java.util.List;
   81.10 +import javax.swing.ComboBoxModel;
   81.11 +import javax.swing.DefaultComboBoxModel;
   81.12 +import javax.swing.JButton;
   81.13 +import javax.swing.JComponent;
   81.14 +import javax.swing.JFileChooser;
   81.15 +import javax.swing.JList;
   81.16 +import javax.swing.JTable;
   81.17 +import javax.swing.ListCellRenderer;
   81.18 +import javax.swing.table.DefaultTableCellRenderer;
   81.19 +import javax.swing.table.DefaultTableModel;
   81.20 +import javax.swing.table.TableModel;
   81.21 +import org.netbeans.modules.python.api.PythonPlatform;
   81.22 +import org.netbeans.modules.python.api.PythonPlatformManager;
   81.23 +import org.netbeans.modules.python.project2.PythonProject2;
   81.24 +import org.openide.DialogDescriptor;
   81.25 +import org.openide.DialogDisplayer;
   81.26 +import org.openide.awt.HtmlRenderer;
   81.27 +import org.openide.filesystems.FileObject;
   81.28 +import org.openide.filesystems.FileUtil;
   81.29 +import org.openide.util.HelpCtx;
   81.30 +import org.openide.util.NbBundle;
   81.31 +import org.openide.util.Pair;
   81.32 +
   81.33 +/**
   81.34 + *
   81.35 + * @author Tomas Zezula
   81.36 + */
   81.37 +public class Utils {
   81.38 +
   81.39 +    @NbBundle.Messages({"LBL_SelectMainModule=Select Main Module", "LBL_BrowseMainModules=Browse Main Modules"})
   81.40 +    public static String chooseMainModule (PythonProject2 project) {
   81.41 +        final JButton okButton = new JButton (NbBundle.getMessage(Utils.class, "LBL_SelectMainModule"));
   81.42 +        final MainModuleChooser mcc = new MainModuleChooser(project, okButton);
   81.43 +        final Object[] options = new Object[] {okButton, DialogDescriptor.CANCEL_OPTION};
   81.44 +        final DialogDescriptor dd = new DialogDescriptor (mcc, NbBundle.getMessage(Utils.class, "LBL_BrowseMainModules"), true, options,
   81.45 +        okButton,DialogDescriptor.RIGHT_ALIGN,HelpCtx.DEFAULT_HELP,null);
   81.46 +        dd.setClosingOptions(options);
   81.47 +        if (DialogDisplayer.getDefault().notify(dd) == okButton) {
   81.48 +            return mcc.getMainModule();
   81.49 +        }
   81.50 +        return null;
   81.51 +    }
   81.52 +
   81.53 +    public static interface SourceRootsMediator {
   81.54 +        public void setRelatedEditMediator(SourceRootsMediator rem);
   81.55 +    }
   81.56 +
   81.57 +    public static ComboBoxModel createPlatformModel () {
   81.58 +        return new PlatformModel ();
   81.59 +    }
   81.60 +
   81.61 +    public static ListCellRenderer createPlatformRenderer () {
   81.62 +        return new PlatformRenderer();
   81.63 +    }
   81.64 +
   81.65 +    private static class PlatformModel extends DefaultComboBoxModel {
   81.66 +
   81.67 +        private final PythonPlatformManager manager;
   81.68 +
   81.69 +        public PlatformModel () {
   81.70 +            manager = PythonPlatformManager.getInstance();
   81.71 +            init ();
   81.72 +        }
   81.73 +
   81.74 +        private void init () {
   81.75 +            this.removeAllElements();   //init will be used also in case of chnge of installed plaforms
   81.76 +            final List<String> ids = manager.getPlatformList();
   81.77 +            for (String id : ids) {
   81.78 +                PythonPlatform platform = manager.getPlatform(id);
   81.79 +                this.addElement(platform);
   81.80 +            }
   81.81 +        }
   81.82 +    }
   81.83 +
   81.84 +    private static class PlatformRenderer implements ListCellRenderer {
   81.85 +
   81.86 +        private final ListCellRenderer delegate;
   81.87 +
   81.88 +        public PlatformRenderer () {
   81.89 +            delegate = HtmlRenderer.createRenderer();
   81.90 +        }
   81.91 +
   81.92 +        @Override
   81.93 +        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
   81.94 +            String name;
   81.95 +            if (value instanceof PythonPlatform) {
   81.96 +                PythonPlatform key = (PythonPlatform) value;
   81.97 +                name = key.getName();
   81.98 +            }
   81.99 +            else if (value instanceof String) {
  81.100 +                //hndles broken platform for customizer
  81.101 +                name = "<html><font color=\"#A40000\">" //NOI18N
  81.102 +                            + NbBundle.getMessage(
  81.103 +                                    Utils.class, "TXT_BrokenPlatformFmt", (String)value);
  81.104 +            }
  81.105 +            else {
  81.106 +                name = "";
  81.107 +            }
  81.108 +            return delegate.getListCellRendererComponent(list, name, index, isSelected, cellHasFocus);
  81.109 +        }
  81.110 +
  81.111 +    }
  81.112 +
  81.113 +    private static class SourceRootsModel extends DefaultTableModel {
  81.114 +
  81.115 +        public SourceRootsModel (Object[][] data) {
  81.116 +            super (data,new Object[]{"location","label"});//NOI18N
  81.117 +        }
  81.118 +
  81.119 +        @Override
  81.120 +        public boolean isCellEditable(int row, int column) {
  81.121 +            return column == 1;
  81.122 +        }
  81.123 +
  81.124 +        @Override
  81.125 +        public Class getColumnClass(int columnIndex) {
  81.126 +            switch (columnIndex) {
  81.127 +                case 0:
  81.128 +                    return File.class;
  81.129 +                case 1:
  81.130 +                    return String.class;
  81.131 +                default:
  81.132 +                    return super.getColumnClass (columnIndex);
  81.133 +            }
  81.134 +        }
  81.135 +    }
  81.136 +
  81.137 +    private static class FileRenderer extends DefaultTableCellRenderer {
  81.138 +
  81.139 +        private File projectFolder;
  81.140 +
  81.141 +        public FileRenderer (File projectFolder) {
  81.142 +            this.projectFolder = projectFolder;
  81.143 +        }
  81.144 +
  81.145 +        @Override
  81.146 +        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,int row, int column) {
  81.147 +            String displayName;
  81.148 +            if (value instanceof File) {
  81.149 +                File root = (File) value;
  81.150 +                String pfPath = projectFolder.getAbsolutePath() + File.separatorChar;
  81.151 +                String srPath = root.getAbsolutePath();
  81.152 +                if (srPath.startsWith(pfPath)) {
  81.153 +                    displayName = srPath.substring(pfPath.length());
  81.154 +                }
  81.155 +                else {
  81.156 +                    displayName = srPath;
  81.157 +                }
  81.158 +            }
  81.159 +            else {
  81.160 +                displayName = null;
  81.161 +            }
  81.162 +            Component c = super.getTableCellRendererComponent(table, displayName, isSelected, hasFocus, row, column);
  81.163 +            if (c instanceof JComponent) {
  81.164 +                ((JComponent) c).setToolTipText (displayName);
  81.165 +            }
  81.166 +            return c;
  81.167 +        }
  81.168 +    }
  81.169 +}
    82.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    82.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/actions/Command.java	Tue Feb 24 01:58:36 2015 -0800
    82.3 @@ -0,0 +1,116 @@
    82.4 +/*
    82.5 + * To change this template, choose Tools | Templates
    82.6 + * and open the template in the editor.
    82.7 + */
    82.8 +package org.netbeans.modules.python.project2.ui.actions;
    82.9 +
   82.10 +import java.io.File;
   82.11 +import java.util.ArrayList;
   82.12 +import javax.swing.JOptionPane;
   82.13 +import org.netbeans.api.project.ProjectUtils;
   82.14 +import org.netbeans.api.project.SourceGroup;
   82.15 +import org.netbeans.api.project.Sources;
   82.16 +import org.netbeans.modules.python.api.PythonPlatform;
   82.17 +import org.netbeans.modules.python.api.PythonPlatformManager;
   82.18 +import org.netbeans.modules.python.project2.PythonProject2;
   82.19 +import org.openide.filesystems.FileUtil;
   82.20 +import org.openide.nodes.Node;
   82.21 +import org.openide.util.Lookup;
   82.22 +import org.openide.windows.TopComponent;
   82.23 +
   82.24 +/**
   82.25 + * @author Radek Matous
   82.26 + * @author Tomas Zezula
   82.27 + */
   82.28 +public abstract class Command {
   82.29 +
   82.30 +    private final PythonProject2 project;
   82.31 +
   82.32 +    public Command(PythonProject2 project) {
   82.33 +        this.project = project;
   82.34 +        assert project != null;
   82.35 +    }
   82.36 +
   82.37 +    public abstract String getCommandId();
   82.38 +
   82.39 +    public abstract void invokeAction(Lookup context) throws IllegalArgumentException;
   82.40 +
   82.41 +    public abstract boolean isActionEnabled(Lookup context) throws IllegalArgumentException;
   82.42 +
   82.43 +    public boolean asyncCallRequired() {
   82.44 +        return true;
   82.45 +    }
   82.46 +
   82.47 +    public boolean saveRequired() {
   82.48 +        return true;
   82.49 +    }
   82.50 +
   82.51 +    public final PythonProject2 getProject() {
   82.52 +        return project;
   82.53 +    }
   82.54 +
   82.55 +    public Node[] getSelectedNodes() {
   82.56 +        return TopComponent.getRegistry().getCurrentNodes();
   82.57 +    }
   82.58 +
   82.59 +    protected void showLaunchError(String message) {
   82.60 +        JOptionPane.showMessageDialog(null, message, "Python Launch Error", JOptionPane.ERROR_MESSAGE);
   82.61 +
   82.62 +    }
   82.63 +
   82.64 +    /**
   82.65 +     * used by children to handle sever launched errors
   82.66 +     *
   82.67 +     * @param errMessage
   82.68 +     */
   82.69 +    protected PythonPlatform checkProjectPythonPlatform(PythonProject2 pyProject) {
   82.70 +//       PythonPlatform platform = PythonProject2Util.getActivePlatform(pyProject);
   82.71 +//       if ( platform == null ) {
   82.72 +//         // Better to inform the user than try to use a default unsuited
   82.73 +//         String platformId = pyProject.getEvaluator().getProperty(PythonProject2Properties.ACTIVE_PLATFORM);
   82.74 +//         showLaunchError( "selected project has broken python platform : " +
   82.75 +//                           platformId +
   82.76 +//                           " => bind to an existing python platform in project's properties "
   82.77 +//                         );
   82.78 +//       }
   82.79 +        PythonPlatform platform = PythonPlatformManager.getInstance().getPlatforms().get(0);
   82.80 +        return platform;
   82.81 +    }
   82.82 +
   82.83 +    /**
   82.84 +     *
   82.85 +     * provide a reasonable common Build of PYTHONPATH for Run or Debug commands
   82.86 +     *
   82.87 +     * @param platform current platform
   82.88 +     * @param project current project
   82.89 +     * @return PythonPath FileList
   82.90 +     */
   82.91 +    protected ArrayList<String> buildPythonPath(PythonPlatform platform, PythonProject2 project) {
   82.92 +        final ArrayList<String> pythonPath = new ArrayList<String>();
   82.93 +        // start with platform
   82.94 +        pythonPath.addAll(platform.getPythonPath());
   82.95 +        Sources sources = ProjectUtils.getSources(project);
   82.96 +        for (SourceGroup fo : sources.getSourceGroups(PythonProject2.SOURCES_TYPE_PYTHON)) {
   82.97 +            File f = FileUtil.toFile(fo.getRootFolder());
   82.98 +            pythonPath.add(f.getAbsolutePath());
   82.99 +        }
  82.100 +        return pythonPath;
  82.101 +    }
  82.102 +
  82.103 +    /**
  82.104 +     *
  82.105 +     * provide a reasonable common Build of JAVAPATH for Run or Debug Jython
  82.106 +     * commands
  82.107 +     *
  82.108 +     * @param platform current platform
  82.109 +     * @param project current project
  82.110 +     * @return JavaPath fileList for jython CLASSPATH command
  82.111 +     */
  82.112 +//    protected ArrayList<String> buildJavaPath( PythonPlatform platform , PythonProject2 project ) {
  82.113 +//      final ArrayList<String> javaPath = new ArrayList<String>() ;
  82.114 +//      // start with platform
  82.115 +//      javaPath.addAll(platform.getJavaPath());
  82.116 +//      javaPath.addAll(getProperties().getJavaPath());
  82.117 +//      return javaPath ;
  82.118 +//    }
  82.119 +}
    83.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    83.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/actions/RunCommand.java	Tue Feb 24 01:58:36 2015 -0800
    83.3 @@ -0,0 +1,137 @@
    83.4 +package org.netbeans.modules.python.project2.ui.actions;
    83.5 +
    83.6 +import org.netbeans.modules.python.api.PythonExecution;
    83.7 +import org.netbeans.modules.python.api.PythonPlatform;
    83.8 +import org.netbeans.modules.python.project2.PythonProject2;
    83.9 +import org.netbeans.api.project.ProjectUtils;
   83.10 +import org.netbeans.api.project.SourceGroup;
   83.11 +import org.netbeans.api.project.Sources;
   83.12 +import org.netbeans.modules.python.project2.ui.Utils;
   83.13 +import org.netbeans.spi.project.ActionProvider;
   83.14 +import org.openide.filesystems.FileObject;
   83.15 +import org.openide.filesystems.FileUtil;
   83.16 +import org.openide.util.Lookup;
   83.17 +
   83.18 +/**
   83.19 + *
   83.20 + * @author alley
   83.21 + */
   83.22 +public class RunCommand extends Command {
   83.23 +
   83.24 +    protected final boolean isTest;
   83.25 +
   83.26 +    public RunCommand(PythonProject2 project, boolean isTest) {
   83.27 +        super(project);
   83.28 +        this.isTest = isTest;
   83.29 +    }
   83.30 +
   83.31 +    @Override
   83.32 +    public String getCommandId() {
   83.33 +        return isTest ? ActionProvider.COMMAND_TEST : ActionProvider.COMMAND_RUN;
   83.34 +    }
   83.35 +
   83.36 +    @Override
   83.37 +    public void invokeAction(Lookup context) throws IllegalArgumentException {
   83.38 +//        if (isTest) {
   83.39 +//            TestRunner testRunner = PythonActionProvider.getTestRunner(TestRunner.TestType.PY_UNIT);
   83.40 +//            //boolean testTaskExist = RakeSupport.getRakeTask(project, TEST_TASK_NAME) != null;
   83.41 +//            //if (testTaskExist) {
   83.42 +//            //    File pwd = FileUtil.toFile(project.getProjectDirectory());
   83.43 +//            //    RakeRunner runner = new RakeRunner(project);
   83.44 +//            //    runner.setPWD(pwd);
   83.45 +//            //    runner.setFileLocator(new RubyFileLocator(context, project));
   83.46 +//            //    runner.showWarnings(true);
   83.47 +//            //    runner.setDebug(COMMAND_DEBUG_SINGLE.equals(command));
   83.48 +//            //    runner.run(TEST_TASK_NAME);
   83.49 +//            //} else if (testRunner != null) {
   83.50 +//            testRunner.getInstance().runAllTests(getProject(), false);
   83.51 +//            //}
   83.52 +//            return;
   83.53 +//        }
   83.54 +
   83.55 +        final PythonProject2 pyProject = getProject();
   83.56 +        final PythonPlatform platform = checkProjectPythonPlatform(pyProject);
   83.57 +        if (platform == null) {
   83.58 +            return; // invalid platform user has been warn in check so safe to return
   83.59 +        }
   83.60 +
   83.61 +        String main = pyProject.getMainModule();
   83.62 +        if (main == null || main.isEmpty()) {
   83.63 +            main = Utils.chooseMainModule(pyProject);
   83.64 +            pyProject.setMainModule(main);
   83.65 +        }
   83.66 +        //System.out.println("main module " + getProperties().getMainModule());
   83.67 +        FileObject script = findMainFile(pyProject, main);
   83.68 +        //assert script != null;
   83.69 +        if (script == null) {
   83.70 +            main = Utils.chooseMainModule(pyProject);
   83.71 +            pyProject.setMainModule(main);
   83.72 +            script = findMainFile(pyProject, main);
   83.73 +        }
   83.74 +        if(script == null) {
   83.75 +            return;
   83.76 +        }
   83.77 +        final FileObject parent = script.getParent();
   83.78 +        PythonExecution pyexec = new PythonExecution();
   83.79 +        pyexec.setDisplayName(ProjectUtils.getInformation(pyProject).getDisplayName());
   83.80 +        //Set work dir - probably we need a property to store work dir
   83.81 +        String path = FileUtil.toFile(parent).getAbsolutePath();
   83.82 +        pyexec.setWorkingDirectory(path);
   83.83 +        pyexec.setCommand(platform.getInterpreterCommand());
   83.84 +        //Set python script
   83.85 +        path = FileUtil.toFile(script).getAbsolutePath();
   83.86 +        pyexec.setScript(path);
   83.87 +        pyexec.setCommandArgs(platform.getInterpreterArgs());
   83.88 +        pyexec.setScriptArgs(pyProject.getApplicationArgs());
   83.89 +        //build path & set
   83.90 +        //build path & set
   83.91 +        pyexec.setPath(PythonPlatform.buildPath(super.buildPythonPath(platform, pyProject)));
   83.92 +//        pyexec.setJavaPath(PythonPlatform.buildPath(super.buildJavaPath(platform, pyProject)));
   83.93 +        pyexec.setShowControls(true);
   83.94 +        pyexec.setShowInput(true);
   83.95 +        pyexec.setShowWindow(true);
   83.96 +        pyexec.addStandardRecognizers();
   83.97 +
   83.98 +//        PythonCoverageProvider coverageProvider = PythonCoverageProvider.get(pyProject);
   83.99 +//        if (coverageProvider != null && coverageProvider.isEnabled()) {
  83.100 +//            pyexec = coverageProvider.wrapWithCoverage(pyexec);
  83.101 +//        }
  83.102 +
  83.103 +        pyexec.run();
  83.104 +    }
  83.105 +
  83.106 +
  83.107 +    @Override
  83.108 +    public boolean isActionEnabled(Lookup context) throws IllegalArgumentException {
  83.109 +//        final PythonProject2 pyProject = getProject();
  83.110 +//        PythonPlatform platform = PythonProject2Util.getActivePlatform(pyProject);
  83.111 +//        if (platform == null) {
  83.112 +//            return false;
  83.113 +//        }
  83.114 +//        else{
  83.115 +//            return true;
  83.116 +//        }
  83.117 +//        final FileObject fo = findMainFile (pyProject);
  83.118 +//        if (fo == null) {
  83.119 +//            return false;
  83.120 +//        }
  83.121 +//        return PythonMIMEResolver.PYTHON_MIME_TYPE.equals(fo.getMIMEType());
  83.122 +        return true;
  83.123 +    }
  83.124 +
  83.125 +    protected static FileObject findMainFile(final PythonProject2 pyProject, final String mainFile) {
  83.126 +        if (mainFile == null) {
  83.127 +            return null;
  83.128 +        }
  83.129 +        final Sources sources = ProjectUtils.getSources(pyProject);
  83.130 +        FileObject fo = null;
  83.131 +        for (SourceGroup root : sources.getSourceGroups(PythonProject2.SOURCES_TYPE_PYTHON)) {
  83.132 +            fo = root.getRootFolder().getFileObject(mainFile);
  83.133 +            if (fo != null) {
  83.134 +                break;
  83.135 +            }
  83.136 +        }
  83.137 +        return fo;
  83.138 +    }
  83.139 +
  83.140 +}
    84.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    84.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/actions/RunSingleCommand.java	Tue Feb 24 01:58:36 2015 -0800
    84.3 @@ -0,0 +1,127 @@
    84.4 +package org.netbeans.modules.python.project2.ui.actions;
    84.5 +
    84.6 +import javax.swing.JOptionPane;
    84.7 +import org.netbeans.modules.python.api.PythonExecution;
    84.8 +import org.netbeans.modules.python.api.PythonMIMEResolver;
    84.9 +import org.netbeans.modules.python.api.PythonOptions;
   84.10 +import org.netbeans.modules.python.api.PythonPlatform;
   84.11 +import org.netbeans.modules.python.api.PythonPlatformManager;
   84.12 +import org.netbeans.modules.python.editor.codecoverage.PythonCoverageProvider;
   84.13 +import org.netbeans.modules.python.project2.PythonProject2;
   84.14 +import org.netbeans.spi.project.ActionProvider;
   84.15 +import org.openide.filesystems.FileObject;
   84.16 +import org.openide.filesystems.FileUtil;
   84.17 +import org.openide.loaders.DataObject;
   84.18 +import org.openide.nodes.Node;
   84.19 +import org.openide.util.Lookup;
   84.20 +
   84.21 +/**
   84.22 + *
   84.23 + * @author alley
   84.24 + */
   84.25 +public class RunSingleCommand extends Command {
   84.26 +
   84.27 +    PythonPlatformManager manager = PythonPlatformManager.getInstance();
   84.28 +    protected boolean isTest;
   84.29 +
   84.30 +    public RunSingleCommand(PythonProject2 project, boolean isTest) {
   84.31 +        super(project);
   84.32 +        this.isTest = isTest;
   84.33 +    }
   84.34 +
   84.35 +    @Override
   84.36 +    public String getCommandId() {
   84.37 +        return isTest ? ActionProvider.COMMAND_TEST_SINGLE : ActionProvider.COMMAND_RUN_SINGLE;
   84.38 +    }
   84.39 +
   84.40 +    @Override
   84.41 +    public void invokeAction(Lookup context) throws IllegalArgumentException {
   84.42 +        Node[] activatedNodes = getSelectedNodes();
   84.43 +        DataObject gdo = activatedNodes[0].getLookup().lookup(DataObject.class);
   84.44 +        FileObject file = gdo.getPrimaryFile();
   84.45 +        if (file.getMIMEType().equals(PythonMIMEResolver.PYTHON_MIME_TYPE)) {
   84.46 +            String path = FileUtil.toFile(file.getParent()).getAbsolutePath();
   84.47 +            // String workingdir = FileUtil.toFile(getProject().getSrcFolder()).getAbsolutePath();
   84.48 +            //int pos = path.lastIndexOf("/");
   84.49 +            //path = path.substring(0, pos);
   84.50 +            String script = FileUtil.toFile(file).getAbsolutePath();
   84.51 +            //System.out.println("Folder " + path);
   84.52 +            //System.out.println("File " + script);
   84.53 +
   84.54 +            final PythonProject2 pyProject = getProject();
   84.55 +
   84.56 +            //String target = FileUtil.getRelativePath(getRoot(project.getSourceRoots().getRoots(),file), file);
   84.57 +//            if (isTest || file.getName().endsWith("_test")) { // NOI18N
   84.58 +//
   84.59 +//                // See if this looks like a test file; if not, see if we can find its corresponding
   84.60 +//                // test
   84.61 +//                boolean isTestFile = (file.getName().endsWith("_test"));
   84.62 +//                if (!isTestFile) {
   84.63 +//                    for (FileObject testRoot : pyProject.getTestSourceRootFiles()) {
   84.64 +//                        if (FileUtil.isParentOf(testRoot, file)) {
   84.65 +//                            isTestFile = true;
   84.66 +//                            break;
   84.67 +//                        }
   84.68 +//                    }
   84.69 +//                }
   84.70 +//                if (!isTestFile) {
   84.71 +//                    // Try to find the matching test
   84.72 +//                    LocationResult result = new GotoTest().findTest(file, -1);
   84.73 +//                    if (result != null && result.getFileObject() != null) {
   84.74 +//                        file = result.getFileObject();
   84.75 +//                    }
   84.76 +//                }
   84.77 +//
   84.78 +//                // Run test normally - don't pop up browser
   84.79 +//                TestRunner testRunner = PythonActionProvider.getTestRunner(TestRunner.TestType.PY_UNIT);
   84.80 +//                if (testRunner != null) {
   84.81 +//                    testRunner.getInstance().runTest(file, false);
   84.82 +//                    return;
   84.83 +//                }
   84.84 +//            }
   84.85 +            PythonExecution pyexec = new PythonExecution();
   84.86 +            pyexec.setDisplayName(gdo.getName());
   84.87 +            pyexec.setWorkingDirectory(path);
   84.88 +            if (PythonOptions.getInstance().getPromptForArgs()) {
   84.89 +                String args = JOptionPane.showInputDialog("Enter the args for this script.", "");
   84.90 +                pyexec.setScriptArgs(args);
   84.91 +
   84.92 +            }
   84.93 +            final PythonPlatform platform = checkProjectPythonPlatform(pyProject);
   84.94 +            if (platform == null) {
   84.95 +                return; // invalid platform user has been warn in check so safe to return
   84.96 +            }
   84.97 +            pyexec.setCommand(platform.getInterpreterCommand());
   84.98 +            pyexec.setScript(script);
   84.99 +            pyexec.setCommandArgs(platform.getInterpreterArgs());
  84.100 +            pyexec.setPath(PythonPlatform.buildPath(super.buildPythonPath(platform, pyProject)));
  84.101 +//            pyexec.setJavaPath(PythonPlatform.buildPath(super.buildJavaPath(platform, pyProject)));
  84.102 +            pyexec.setShowControls(true);
  84.103 +            pyexec.setShowInput(true);
  84.104 +            pyexec.setShowWindow(true);
  84.105 +            pyexec.addStandardRecognizers();
  84.106 +
  84.107 +//            PythonCoverageProvider coverageProvider = PythonCoverageProvider.get(pyProject);
  84.108 +//            if (coverageProvider != null && coverageProvider.isEnabled()) {
  84.109 +//                pyexec = coverageProvider.wrapWithCoverage(pyexec);
  84.110 +//            }
  84.111 +
  84.112 +            pyexec.run();
  84.113 +        }
  84.114 +    }
  84.115 +
  84.116 +    @Override
  84.117 +    public boolean isActionEnabled(Lookup context) throws IllegalArgumentException {
  84.118 +        boolean results = false; //super.enable(activatedNodes);
  84.119 +        Node[] activatedNodes = getSelectedNodes();
  84.120 +        if (activatedNodes != null && activatedNodes.length > 0) {
  84.121 +            DataObject gdo = activatedNodes[0].getLookup().lookup(DataObject.class);
  84.122 +            if (gdo != null && gdo.getPrimaryFile() != null) {
  84.123 +                results = gdo.getPrimaryFile().getMIMEType().equals(
  84.124 +                        PythonMIMEResolver.PYTHON_MIME_TYPE);
  84.125 +            }
  84.126 +        }
  84.127 +        return results;
  84.128 +    }
  84.129 +
  84.130 +}
    85.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    85.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/customizer/Bundle.properties	Tue Feb 24 01:58:36 2015 -0800
    85.3 @@ -0,0 +1,64 @@
    85.4 +# To change this template, choose Tools | Templates
    85.5 +# and open the template in the editor.
    85.6 +
    85.7 +#PythonProjectProperties
    85.8 +LBL_Customizer_Title=Project Properties - {0}
    85.9 +
   85.10 +#CompositePanelProviderImpl
   85.11 +LBL_Config_RunConfig=Run
   85.12 +LBL_Config_PhpIncludePath=Python
   85.13 +
   85.14 +#Customizer Sources
   85.15 +CTL_ProjectFolder=Project Folder\:
   85.16 +MNE_ProjectFolder=F
   85.17 +CTL_SourceRoots=Source Root Folders\:
   85.18 +MNE_SourceRoots=S
   85.19 +CTL_TestRoots=Test Root Folders:
   85.20 +MNE_TestRoots=T
   85.21 +TXT_Encoding=&Encoding\:
   85.22 +CTL_AddSourceRoot=Add Folder...
   85.23 +MNE_AddSourceRoot=A
   85.24 +CTL_RemoveSourceRoot=Remove
   85.25 +MNE_RemoveSourceRoot=R
   85.26 +CTL_UpSourceRoot=Move Up
   85.27 +MNE_UpSourceRoot=U
   85.28 +CTL_DownSourceRoot=Move Down
   85.29 +MNE_DownSourceRoot=D
   85.30 +CTL_AddTestRoot=Add Folder...
   85.31 +MNE_AddTestRoot=o
   85.32 +CTL_RemoveTestRoot=Remove
   85.33 +MNE_RemoveTestRoot=m
   85.34 +CTL_UpTestRoot=Move Up
   85.35 +MNE_UpTestRoot=p
   85.36 +CTL_DownTestRoot=Move Down
   85.37 +MNE_DownTestRoot=w
   85.38 +AD_CustomizerSources_addSourceRoot=N/A
   85.39 +AD_CustomizerSources_testRoots=N/A
   85.40 +AD_CustomizerSources_sourceRoots=N/A
   85.41 +AD_CustomizerSources_downTestRoot=N/A
   85.42 +AD_CustomizerSources_upTestRoot=N/A
   85.43 +AD_CustomizerSources_removeTestRoot=N/A
   85.44 +AD_CustomizerSources_addTestRoot=N/A
   85.45 +AD_CustomizerSources_downSourceRoot=N/A
   85.46 +AD_CustomizerSources_upSourceRoot=N/A
   85.47 +AD_CustomizerSources_removeSourceRoot=N/A
   85.48 +AD_CustomizerSources_projectLocation=N/A
   85.49 +AD_CustomizerSources_Encoding=N/A
   85.50 +MSG_EncodingWarning=Changing project encoding might result in some characters in existing files not being read and written correctly.
   85.51 +CustomizerRun.mainModule.text=&Main Module:
   85.52 +CustomizerRun.mainModule.ad=N/A
   85.53 +CustomizerRun.appArgs.text=&Application Arguments:
   85.54 +CustomizerRun.appArgs.ad=N/A
   85.55 +CustomizerRun.browseMain.text=&Browse...
   85.56 +CustomizerRun.browseMain.ad=N/A
   85.57 +CustomizerPythonPath.addPythonPath.text=Add...
   85.58 +CustomizerPythonPath.moveDownPythonPath.text=Move Down
   85.59 +CustomizerPythonPath.moveUpPythonPath.text=Move Up
   85.60 +CustomizerPythonPath.removePythonPath.text=Remove
   85.61 +CustomizerPythonPath.addPythonPath.text_1=Add...
   85.62 +CustomizerPythonPath.moveDownPythonPath.text_1=Move Down
   85.63 +CustomizerPythonPath.moveUpPythonPath.text_1=Move Up
   85.64 +CustomizerPythonPath.removePythonPath.text_1=Remove
   85.65 +CustomizerPythonPath.manage.text=Manage...
   85.66 +CustomizerPythonPath.jLabel1.text=Python Platform:
   85.67 +CustomizerPythonPath.jLabel2.text=Python Path:
    86.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    86.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/customizer/CompositePanelProviderImpl.java	Tue Feb 24 01:58:36 2015 -0800
    86.3 @@ -0,0 +1,67 @@
    86.4 +package org.netbeans.modules.python.project2.ui.customizer;
    86.5 +
    86.6 +import javax.swing.JComponent;
    86.7 +import javax.swing.JPanel;
    86.8 +import org.netbeans.modules.python.project2.PythonProject2;
    86.9 +import org.netbeans.spi.project.ui.support.ProjectCustomizer;
   86.10 +import org.netbeans.spi.project.ui.support.ProjectCustomizer.CompositeCategoryProvider;
   86.11 +import org.openide.util.Lookup;
   86.12 +import org.openide.util.NbBundle;
   86.13 +
   86.14 +/**
   86.15 + *
   86.16 + * @author Tomas Zezula
   86.17 + */
   86.18 +public class CompositePanelProviderImpl implements CompositeCategoryProvider {
   86.19 +
   86.20 +    private static final String PYTHON_PATH = "PythonPath";  //NOI18N
   86.21 +    private static final String RUN = "Run"; // NOI18N
   86.22 +
   86.23 +    private final String name;
   86.24 +
   86.25 +    public CompositePanelProviderImpl(String name) {
   86.26 +        this.name = name;
   86.27 +    }
   86.28 +
   86.29 +    @Override
   86.30 +    public ProjectCustomizer.Category createCategory(Lookup context) {
   86.31 +        ProjectCustomizer.Category toReturn = null;
   86.32 +        final ProjectCustomizer.Category[] categories = null;
   86.33 +        if (RUN.equals(name)) {
   86.34 +            toReturn = ProjectCustomizer.Category.create(
   86.35 +                    RUN,
   86.36 +                    NbBundle.getMessage(CompositePanelProviderImpl.class, "LBL_Config_RunConfig"),
   86.37 +                    null,
   86.38 +                    categories);
   86.39 +        } else if (PYTHON_PATH.equals(name)) {
   86.40 +            toReturn = ProjectCustomizer.Category.create(
   86.41 +                    PYTHON_PATH,
   86.42 +                    NbBundle.getMessage(CompositePanelProviderImpl.class, "LBL_Config_PhpIncludePath"),
   86.43 +                    null,
   86.44 +                    categories);
   86.45 +        }
   86.46 +        assert toReturn != null : "No category for name: " + name;
   86.47 +        return toReturn;
   86.48 +    }
   86.49 +
   86.50 +    @Override
   86.51 +    public JComponent createComponent(ProjectCustomizer.Category category, Lookup context) {
   86.52 +        String nm = category.getName();
   86.53 +        PythonProject2 project = context.lookup(PythonProject2.class);
   86.54 +        if (RUN.equals(nm)) {
   86.55 +            return new CustomizerRun(project);
   86.56 +        } else if (PYTHON_PATH.equals(nm)) {
   86.57 +            return new CustomizerPythonPath(project);
   86.58 +        }
   86.59 +        return new JPanel();
   86.60 +    }
   86.61 +
   86.62 +    public static CompositePanelProviderImpl createRunConfig() {
   86.63 +        return new CompositePanelProviderImpl(RUN);
   86.64 +    }
   86.65 +
   86.66 +    public static CompositePanelProviderImpl createPythonPath() {
   86.67 +        return new CompositePanelProviderImpl(PYTHON_PATH);
   86.68 +    }
   86.69 +
   86.70 +}
    87.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    87.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/customizer/CustomizerPythonPath.form	Tue Feb 24 01:58:36 2015 -0800
    87.3 @@ -0,0 +1,168 @@
    87.4 +<?xml version="1.0" encoding="UTF-8" ?>
    87.5 +
    87.6 +<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
    87.7 +  <AuxValues>
    87.8 +    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
    87.9 +    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
   87.10 +    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
   87.11 +    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
   87.12 +    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
   87.13 +    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
   87.14 +    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
   87.15 +    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
   87.16 +    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
   87.17 +  </AuxValues>
   87.18 +
   87.19 +  <Layout>
   87.20 +    <DimensionLayout dim="0">
   87.21 +      <Group type="103" groupAlignment="0" attributes="0">
   87.22 +          <Group type="102" attributes="0">
   87.23 +              <EmptySpace max="-2" attributes="0"/>
   87.24 +              <Group type="103" groupAlignment="0" attributes="0">
   87.25 +                  <Group type="102" alignment="0" attributes="0">
   87.26 +                      <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
   87.27 +                      <EmptySpace max="-2" attributes="0"/>
   87.28 +                      <Component id="platforms" pref="205" max="32767" attributes="0"/>
   87.29 +                      <EmptySpace min="-2" max="-2" attributes="0"/>
   87.30 +                      <Component id="manage" min="-2" max="-2" attributes="0"/>
   87.31 +                      <EmptySpace min="30" pref="30" max="30" attributes="0"/>
   87.32 +                  </Group>
   87.33 +                  <Group type="102" alignment="1" attributes="0">
   87.34 +                      <Component id="jScrollPane2" pref="0" max="32767" attributes="0"/>
   87.35 +                      <EmptySpace max="-2" attributes="0"/>
   87.36 +                      <Group type="103" groupAlignment="0" attributes="0">
   87.37 +                          <Component id="moveDownPythonPath" linkSize="1" alignment="0" min="-2" max="-2" attributes="0"/>
   87.38 +                          <Component id="moveUpPythonPath" linkSize="1" alignment="0" min="-2" max="-2" attributes="1"/>
   87.39 +                          <Component id="removePythonPath" linkSize="1" alignment="0" min="-2" max="-2" attributes="1"/>
   87.40 +                          <Component id="addPythonPath" linkSize="1" alignment="0" min="-2" max="-2" attributes="1"/>
   87.41 +                      </Group>
   87.42 +                  </Group>
   87.43 +                  <Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
   87.44 +              </Group>
   87.45 +              <EmptySpace min="-2" max="-2" attributes="0"/>
   87.46 +          </Group>
   87.47 +      </Group>
   87.48 +    </DimensionLayout>
   87.49 +    <DimensionLayout dim="1">
   87.50 +      <Group type="103" groupAlignment="0" attributes="0">
   87.51 +          <Group type="102" alignment="0" attributes="0">
   87.52 +              <EmptySpace min="-2" max="-2" attributes="0"/>
   87.53 +              <Group type="103" groupAlignment="0" max="-2" attributes="0">
   87.54 +                  <Group type="103" alignment="0" groupAlignment="3" attributes="0">
   87.55 +                      <Component id="platforms" alignment="3" min="-2" max="-2" attributes="0"/>
   87.56 +                      <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
   87.57 +                  </Group>
   87.58 +                  <Component id="manage" alignment="0" min="-2" max="-2" attributes="0"/>
   87.59 +              </Group>
   87.60 +              <EmptySpace min="-2" max="-2" attributes="0"/>
   87.61 +              <Component id="jLabel2" min="-2" pref="16" max="-2" attributes="0"/>
   87.62 +              <EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
   87.63 +              <Group type="103" groupAlignment="0" attributes="0">
   87.64 +                  <Group type="102" attributes="0">
   87.65 +                      <Component id="addPythonPath" min="-2" max="-2" attributes="0"/>
   87.66 +                      <EmptySpace max="-2" attributes="0"/>
   87.67 +                      <Component id="removePythonPath" min="-2" max="-2" attributes="0"/>
   87.68 +                      <EmptySpace max="-2" attributes="0"/>
   87.69 +                      <Component id="moveUpPythonPath" min="-2" max="-2" attributes="0"/>
   87.70 +                      <EmptySpace max="-2" attributes="0"/>
   87.71 +                      <Component id="moveDownPythonPath" min="-2" max="-2" attributes="0"/>
   87.72 +                  </Group>
   87.73 +                  <Component id="jScrollPane2" pref="214" max="32767" attributes="0"/>
   87.74 +              </Group>
   87.75 +              <EmptySpace min="-2" pref="19" max="-2" attributes="0"/>
   87.76 +          </Group>
   87.77 +      </Group>
   87.78 +    </DimensionLayout>
   87.79 +  </Layout>
   87.80 +  <SubComponents>
   87.81 +    <Container class="javax.swing.JScrollPane" name="jScrollPane2">
   87.82 +      <AuxValues>
   87.83 +        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
   87.84 +      </AuxValues>
   87.85 +
   87.86 +      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
   87.87 +      <SubComponents>
   87.88 +        <Component class="javax.swing.JList" name="pythonPath">
   87.89 +          <Properties>
   87.90 +            <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
   87.91 +              <Connection code="pythonPathModel" type="code"/>
   87.92 +            </Property>
   87.93 +            <Property name="selectionMode" type="int" value="0"/>
   87.94 +          </Properties>
   87.95 +        </Component>
   87.96 +      </SubComponents>
   87.97 +    </Container>
   87.98 +    <Component class="javax.swing.JButton" name="addPythonPath">
   87.99 +      <Properties>
  87.100 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  87.101 +          <ResourceString bundle="org/netbeans/modules/python/project2/ui/customizer/Bundle.properties" key="CustomizerPythonPath.addPythonPath.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  87.102 +        </Property>
  87.103 +      </Properties>
  87.104 +      <Events>
  87.105 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="addPythonPathActionPerformed"/>
  87.106 +      </Events>
  87.107 +    </Component>
  87.108 +    <Component class="javax.swing.JButton" name="removePythonPath">
  87.109 +      <Properties>
  87.110 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  87.111 +          <ResourceString bundle="org/netbeans/modules/python/project2/ui/customizer/Bundle.properties" key="CustomizerPythonPath.removePythonPath.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  87.112 +        </Property>
  87.113 +      </Properties>
  87.114 +      <Events>
  87.115 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="removePythonPathActionPerformed"/>
  87.116 +      </Events>
  87.117 +    </Component>
  87.118 +    <Component class="javax.swing.JButton" name="moveUpPythonPath">
  87.119 +      <Properties>
  87.120 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  87.121 +          <ResourceString bundle="org/netbeans/modules/python/project2/ui/customizer/Bundle.properties" key="CustomizerPythonPath.moveUpPythonPath.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  87.122 +        </Property>
  87.123 +      </Properties>
  87.124 +      <Events>
  87.125 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="moveUpPythonPathActionPerformed"/>
  87.126 +      </Events>
  87.127 +    </Component>
  87.128 +    <Component class="javax.swing.JButton" name="moveDownPythonPath">
  87.129 +      <Properties>
  87.130 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  87.131 +          <ResourceString bundle="org/netbeans/modules/python/project2/ui/customizer/Bundle.properties" key="CustomizerPythonPath.moveDownPythonPath.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  87.132 +        </Property>
  87.133 +      </Properties>
  87.134 +      <Events>
  87.135 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="moveDownPythonPathActionPerformed"/>
  87.136 +      </Events>
  87.137 +    </Component>
  87.138 +    <Component class="javax.swing.JButton" name="manage">
  87.139 +      <Properties>
  87.140 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  87.141 +          <ResourceString bundle="org/netbeans/modules/python/project2/ui/customizer/Bundle.properties" key="CustomizerPythonPath.manage.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  87.142 +        </Property>
  87.143 +      </Properties>
  87.144 +      <Events>
  87.145 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="manageActionPerformed"/>
  87.146 +      </Events>
  87.147 +    </Component>
  87.148 +    <Component class="javax.swing.JComboBox" name="platforms">
  87.149 +      <Events>
  87.150 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="platformsActionPerformed"/>
  87.151 +      </Events>
  87.152 +      <AuxValues>
  87.153 +        <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="org.netbeans.modules.python.api.PlatformComponentFactory.getPythonPlatformsComboxBox()"/>
  87.154 +      </AuxValues>
  87.155 +    </Component>
  87.156 +    <Component class="javax.swing.JLabel" name="jLabel1">
  87.157 +      <Properties>
  87.158 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  87.159 +          <ResourceString bundle="org/netbeans/modules/python/project2/ui/customizer/Bundle.properties" key="CustomizerPythonPath.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  87.160 +        </Property>
  87.161 +      </Properties>
  87.162 +    </Component>
  87.163 +    <Component class="javax.swing.JLabel" name="jLabel2">
  87.164 +      <Properties>
  87.165 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  87.166 +          <ResourceString bundle="org/netbeans/modules/python/project2/ui/customizer/Bundle.properties" key="CustomizerPythonPath.jLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  87.167 +        </Property>
  87.168 +      </Properties>
  87.169 +    </Component>
  87.170 +  </SubComponents>
  87.171 +</Form>
    88.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    88.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/customizer/CustomizerPythonPath.java	Tue Feb 24 01:58:36 2015 -0800
    88.3 @@ -0,0 +1,271 @@
    88.4 +/*
    88.5 + * To change this template, choose Tools | Templates
    88.6 + * and open the template in the editor.
    88.7 + */
    88.8 +
    88.9 +/*
   88.10 + * CustomizerPythonPath.java
   88.11 + *
   88.12 + * Created on Sep 7, 2008, 6:56:16 PM
   88.13 + */
   88.14 +package org.netbeans.modules.python.project2.ui.customizer;
   88.15 +
   88.16 +import java.io.File;
   88.17 +import java.io.IOException;
   88.18 +import javax.swing.JFileChooser;
   88.19 +
   88.20 +import org.netbeans.modules.python.api.PlatformComponentFactory;
   88.21 +import org.netbeans.modules.python.api.PythonPlatform;
   88.22 +import org.netbeans.modules.python.core.ui.models.PathListModel;
   88.23 +import org.netbeans.modules.python.project2.PythonProject2;
   88.24 +import org.netbeans.modules.python.project2.ui.Utils;
   88.25 +
   88.26 +import org.openide.filesystems.FileObject;
   88.27 +import org.openide.filesystems.FileUtil;
   88.28 +import org.openide.loaders.DataObject;
   88.29 +import org.openide.loaders.InstanceDataObject;
   88.30 +import org.openide.util.Exceptions;
   88.31 +import org.openide.util.actions.CallableSystemAction;
   88.32 +
   88.33 +/**
   88.34 + *
   88.35 + * @author alley
   88.36 + */
   88.37 +public class CustomizerPythonPath extends javax.swing.JPanel {
   88.38 +
   88.39 +    private final PythonProject2 project;
   88.40 +    private PlatformComponentFactory.PlatformChangeListener platformListener;
   88.41 +    private final PathListModel pythonPathModel = new PathListModel();
   88.42 +
   88.43 +    /**
   88.44 +     * Creates new form CustomizerPythonPath
   88.45 +     */
   88.46 +    public CustomizerPythonPath(PythonProject2 properties) {
   88.47 +        this.project = properties;
   88.48 +        initComponents();
   88.49 +//        pythonPathModel.setModel(project.getPythonPath());
   88.50 +
   88.51 +        final PythonPlatform activePlatform = project.getActivePlatform();
   88.52 +        if (activePlatform != null) {
   88.53 +            platforms.setSelectedItem(activePlatform);
   88.54 +        }
   88.55 +    }
   88.56 +
   88.57 +    public @Override
   88.58 +    void addNotify() {
   88.59 +        super.addNotify();
   88.60 +        platformListener = new PlatformComponentFactory.PlatformChangeListener() {
   88.61 +            @Override
   88.62 +            public void platformChanged() {
   88.63 +                PythonPlatform platform = (PythonPlatform) platforms.getSelectedItem();
   88.64 +                if (platform != null) {
   88.65 +                    project.setActivePlatform(platform);
   88.66 +                    // When we support configurations:
   88.67 +                    //configs.get(getSelectedConfig()).put(PythonProjectProperties.ACTIVE_PLATFORM, platform.getName());
   88.68 +                }
   88.69 +            }
   88.70 +        };
   88.71 +        PlatformComponentFactory.addPlatformChangeListener(platforms, platformListener);
   88.72 +    }
   88.73 +
   88.74 +    public @Override
   88.75 +    void removeNotify() {
   88.76 +        PlatformComponentFactory.removePlatformChangeListener(platforms, platformListener);
   88.77 +        super.removeNotify();
   88.78 +    }
   88.79 +
   88.80 +    /**
   88.81 +     * This method is called from within the constructor to initialize the form.
   88.82 +     * WARNING: Do NOT modify this code. The content of this method is always
   88.83 +     * regenerated by the Form Editor.
   88.84 +     */
   88.85 +    @SuppressWarnings("unchecked")
   88.86 +    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
   88.87 +    private void initComponents() {
   88.88 +
   88.89 +        jScrollPane2 = new javax.swing.JScrollPane();
   88.90 +        pythonPath = new javax.swing.JList();
   88.91 +        addPythonPath = new javax.swing.JButton();
   88.92 +        removePythonPath = new javax.swing.JButton();
   88.93 +        moveUpPythonPath = new javax.swing.JButton();
   88.94 +        moveDownPythonPath = new javax.swing.JButton();
   88.95 +        manage = new javax.swing.JButton();
   88.96 +        platforms = org.netbeans.modules.python.api.PlatformComponentFactory.getPythonPlatformsComboxBox();
   88.97 +        jLabel1 = new javax.swing.JLabel();
   88.98 +        jLabel2 = new javax.swing.JLabel();
   88.99 +
  88.100 +        pythonPath.setModel(pythonPathModel);
  88.101 +        pythonPath.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
  88.102 +        jScrollPane2.setViewportView(pythonPath);
  88.103 +
  88.104 +        addPythonPath.setText(org.openide.util.NbBundle.getMessage(CustomizerPythonPath.class, "CustomizerPythonPath.addPythonPath.text_1")); // NOI18N
  88.105 +        addPythonPath.addActionListener(new java.awt.event.ActionListener() {
  88.106 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
  88.107 +                addPythonPathActionPerformed(evt);
  88.108 +            }
  88.109 +        });
  88.110 +
  88.111 +        removePythonPath.setText(org.openide.util.NbBundle.getMessage(CustomizerPythonPath.class, "CustomizerPythonPath.removePythonPath.text_1")); // NOI18N
  88.112 +        removePythonPath.addActionListener(new java.awt.event.ActionListener() {
  88.113 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
  88.114 +                removePythonPathActionPerformed(evt);
  88.115 +            }
  88.116 +        });
  88.117 +
  88.118 +        moveUpPythonPath.setText(org.openide.util.NbBundle.getMessage(CustomizerPythonPath.class, "CustomizerPythonPath.moveUpPythonPath.text_1")); // NOI18N
  88.119 +        moveUpPythonPath.addActionListener(new java.awt.event.ActionListener() {
  88.120 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
  88.121 +                moveUpPythonPathActionPerformed(evt);
  88.122 +            }
  88.123 +        });
  88.124 +
  88.125 +        moveDownPythonPath.setText(org.openide.util.NbBundle.getMessage(CustomizerPythonPath.class, "CustomizerPythonPath.moveDownPythonPath.text_1")); // NOI18N
  88.126 +        moveDownPythonPath.addActionListener(new java.awt.event.ActionListener() {
  88.127 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
  88.128 +                moveDownPythonPathActionPerformed(evt);
  88.129 +            }
  88.130 +        });
  88.131 +
  88.132 +        manage.setText(org.openide.util.NbBundle.getMessage(CustomizerPythonPath.class, "CustomizerPythonPath.manage.text")); // NOI18N
  88.133 +        manage.addActionListener(new java.awt.event.ActionListener() {
  88.134 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
  88.135 +                manageActionPerformed(evt);
  88.136 +            }
  88.137 +        });
  88.138 +
  88.139 +        platforms.addActionListener(new java.awt.event.ActionListener() {
  88.140 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
  88.141 +                platformsActionPerformed(evt);
  88.142 +            }
  88.143 +        });
  88.144 +
  88.145 +        jLabel1.setText(org.openide.util.NbBundle.getMessage(CustomizerPythonPath.class, "CustomizerPythonPath.jLabel1.text")); // NOI18N
  88.146 +
  88.147 +        jLabel2.setText(org.openide.util.NbBundle.getMessage(CustomizerPythonPath.class, "CustomizerPythonPath.jLabel2.text")); // NOI18N
  88.148 +
  88.149 +        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
  88.150 +        this.setLayout(layout);
  88.151 +        layout.setHorizontalGroup(
  88.152 +            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  88.153 +            .addGroup(layout.createSequentialGroup()
  88.154 +                .addContainerGap()
  88.155 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  88.156 +                    .addGroup(layout.createSequentialGroup()
  88.157 +                        .addComponent(jLabel1)
  88.158 +                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  88.159 +                        .addComponent(platforms, 0, 205, Short.MAX_VALUE)
  88.160 +                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  88.161 +                        .addComponent(manage)
  88.162 +                        .addGap(30, 30, 30))
  88.163 +                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
  88.164 +                        .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
  88.165 +                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  88.166 +                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  88.167 +                            .addComponent(moveDownPythonPath)
  88.168 +                            .addComponent(moveUpPythonPath)
  88.169 +                            .addComponent(removePythonPath)
  88.170 +                            .addComponent(addPythonPath)))
  88.171 +                    .addComponent(jLabel2))
  88.172 +                .addContainerGap())
  88.173 +        );
  88.174 +
  88.175 +        layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {addPythonPath, moveDownPythonPath, moveUpPythonPath, removePythonPath});
  88.176 +
  88.177 +        layout.setVerticalGroup(
  88.178 +            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  88.179 +            .addGroup(layout.createSequentialGroup()
  88.180 +                .addContainerGap()
  88.181 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
  88.182 +                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  88.183 +                        .addComponent(platforms, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
  88.184 +                        .addComponent(jLabel1))
  88.185 +                    .addComponent(manage))
  88.186 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  88.187 +                .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
  88.188 +                .addGap(8, 8, 8)
  88.189 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  88.190 +                    .addGroup(layout.createSequentialGroup()
  88.191 +                        .addComponent(addPythonPath)
  88.192 +                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  88.193 +                        .addComponent(removePythonPath)
  88.194 +                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  88.195 +                        .addComponent(moveUpPythonPath)
  88.196 +                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  88.197 +                        .addComponent(moveDownPythonPath))
  88.198 +                    .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 214, Short.MAX_VALUE))
  88.199 +                .addGap(19, 19, 19))
  88.200 +        );
  88.201 +    }// </editor-fold>//GEN-END:initComponents
  88.202 +
  88.203 +    private void addPythonPathActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addPythonPathActionPerformed
  88.204 +        final JFileChooser fc = new JFileChooser();
  88.205 +        fc.setFileHidingEnabled(false);
  88.206 +        fc.setDialogTitle("Select Python Egg or Python Lib Directory");
  88.207 +        fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
  88.208 +        /* jean-Yves for practical reasons @@@ ENHANCE to have multiple selection enabled here */
  88.209 +        fc.setMultiSelectionEnabled(true);
  88.210 +        int returnVal = fc.showOpenDialog(this);
  88.211 +        if (returnVal == JFileChooser.APPROVE_OPTION) {
  88.212 +            File[] files = fc.getSelectedFiles();
  88.213 +            for (File file : files) {
  88.214 +                String cmd = file.getAbsolutePath();
  88.215 +                pythonPathModel.add(cmd);
  88.216 +            }
  88.217 +        }
  88.218 +
  88.219 +}//GEN-LAST:event_addPythonPathActionPerformed
  88.220 +
  88.221 +    private void removePythonPathActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removePythonPathActionPerformed
  88.222 +        int selectedIndex = pythonPath.getSelectedIndex();
  88.223 +        if (selectedIndex != -1) {
  88.224 +            pythonPathModel.remove(selectedIndex);
  88.225 +        }
  88.226 +}//GEN-LAST:event_removePythonPathActionPerformed
  88.227 +
  88.228 +    private void moveUpPythonPathActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_moveUpPythonPathActionPerformed
  88.229 +        int selectedIndex = pythonPath.getSelectedIndex();
  88.230 +        if (selectedIndex != -1) {
  88.231 +            pythonPathModel.moveUp(selectedIndex);
  88.232 +        }
  88.233 +}//GEN-LAST:event_moveUpPythonPathActionPerformed
  88.234 +
  88.235 +    private void moveDownPythonPathActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_moveDownPythonPathActionPerformed
  88.236 +        pythonPathModel.moveDown(pythonPath.getSelectedIndex());
  88.237 +}//GEN-LAST:event_moveDownPythonPathActionPerformed
  88.238 +
  88.239 +    private void manageActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_manageActionPerformed
  88.240 +        //Workaround, Needs an API to display platform customizer
  88.241 +        final FileObject fo = FileUtil.getConfigFile("Actions/Python/org-netbeans-modules-python-platform-PythonManagerAction.instance");  //NOI18N
  88.242 +        if (fo != null) {
  88.243 +            try {
  88.244 +                InstanceDataObject ido = (InstanceDataObject) DataObject.find(fo);
  88.245 +                CallableSystemAction action = (CallableSystemAction) ido.instanceCreate();
  88.246 +                action.performAction();
  88.247 +                platforms.setModel(Utils.createPlatformModel()); //Currentl the PythonManager doesn't fire events, we need to replace model.
  88.248 +            } catch (IOException | ClassNotFoundException ex) {
  88.249 +                Exceptions.printStackTrace(ex);
  88.250 +            }
  88.251 +        }
  88.252 +}//GEN-LAST:event_manageActionPerformed
  88.253 +
  88.254 +    private void platformsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_platformsActionPerformed
  88.255 +        PythonPlatform selectedPlatform = (PythonPlatform) platforms.getSelectedItem();
  88.256 +        if (selectedPlatform != null) {
  88.257 +            project.setActivePlatform(selectedPlatform);
  88.258 +        }
  88.259 +    }//GEN-LAST:event_platformsActionPerformed
  88.260 +
  88.261 +    // Variables declaration - do not modify//GEN-BEGIN:variables
  88.262 +    private javax.swing.JButton addPythonPath;
  88.263 +    private javax.swing.JLabel jLabel1;
  88.264 +    private javax.swing.JLabel jLabel2;
  88.265 +    private javax.swing.JScrollPane jScrollPane2;
  88.266 +    private javax.swing.JButton manage;
  88.267 +    private javax.swing.JButton moveDownPythonPath;
  88.268 +    private javax.swing.JButton moveUpPythonPath;
  88.269 +    private javax.swing.JComboBox platforms;
  88.270 +    private javax.swing.JList pythonPath;
  88.271 +    private javax.swing.JButton removePythonPath;
  88.272 +    // End of variables declaration//GEN-END:variables
  88.273 +
  88.274 +}
    89.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    89.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/customizer/CustomizerRun.form	Tue Feb 24 01:58:36 2015 -0800
    89.3 @@ -0,0 +1,113 @@
    89.4 +<?xml version="1.0" encoding="UTF-8" ?>
    89.5 +
    89.6 +<Form version="1.5" maxVersion="1.6" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
    89.7 +  <AuxValues>
    89.8 +    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
    89.9 +    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
   89.10 +    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
   89.11 +    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
   89.12 +    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
   89.13 +    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
   89.14 +    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
   89.15 +    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
   89.16 +    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
   89.17 +  </AuxValues>
   89.18 +
   89.19 +  <Layout>
   89.20 +    <DimensionLayout dim="0">
   89.21 +      <Group type="103" groupAlignment="0" attributes="0">
   89.22 +          <Group type="102" attributes="0">
   89.23 +              <Group type="103" groupAlignment="0" attributes="0">
   89.24 +                  <Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
   89.25 +                  <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
   89.26 +              </Group>
   89.27 +              <EmptySpace max="-2" attributes="0"/>
   89.28 +              <Group type="103" groupAlignment="0" attributes="0">
   89.29 +                  <Component id="mainModule" alignment="0" pref="364" max="32767" attributes="0"/>
   89.30 +                  <Component id="appArgs" alignment="0" pref="364" max="32767" attributes="0"/>
   89.31 +              </Group>
   89.32 +              <EmptySpace type="unrelated" max="-2" attributes="0"/>
   89.33 +              <Component id="jButton1" min="-2" max="-2" attributes="0"/>
   89.34 +              <EmptySpace max="-2" attributes="0"/>
   89.35 +          </Group>
   89.36 +      </Group>
   89.37 +    </DimensionLayout>
   89.38 +    <DimensionLayout dim="1">
   89.39 +      <Group type="103" groupAlignment="0" attributes="0">
   89.40 +          <Group type="102" alignment="0" attributes="0">
   89.41 +              <Group type="103" groupAlignment="3" attributes="0">
   89.42 +                  <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
   89.43 +                  <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/>
   89.44 +                  <Component id="mainModule" alignment="3" min="-2" max="-2" attributes="0"/>
   89.45 +              </Group>
   89.46 +              <EmptySpace max="-2" attributes="0"/>
   89.47 +              <Group type="103" groupAlignment="3" attributes="0">
   89.48 +                  <Component id="jLabel2" alignment="3" min="-2" max="-2" attributes="0"/>
   89.49 +                  <Component id="appArgs" alignment="3" min="-2" max="-2" attributes="0"/>
   89.50 +              </Group>
   89.51 +              <EmptySpace pref="234" max="32767" attributes="0"/>
   89.52 +          </Group>
   89.53 +      </Group>
   89.54 +    </DimensionLayout>
   89.55 +  </Layout>
   89.56 +  <SubComponents>
   89.57 +    <Component class="javax.swing.JLabel" name="jLabel1">
   89.58 +      <Properties>
   89.59 +        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
   89.60 +          <ComponentRef name="mainModule"/>
   89.61 +        </Property>
   89.62 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   89.63 +          <ResourceString bundle="org/netbeans/modules/python/project2/ui/customizer/Bundle.properties" key="CustomizerRun.mainModule.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   89.64 +        </Property>
   89.65 +      </Properties>
   89.66 +      <AuxValues>
   89.67 +        <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
   89.68 +      </AuxValues>
   89.69 +    </Component>
   89.70 +    <Component class="javax.swing.JTextField" name="mainModule">
   89.71 +      <AccessibilityProperties>
   89.72 +        <Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   89.73 +          <ResourceString bundle="org/netbeans/modules/python/project/ui/customizer/Bundle.properties" key="CustomizerRun.mainModule.ad" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   89.74 +        </Property>
   89.75 +      </AccessibilityProperties>
   89.76 +    </Component>
   89.77 +    <Component class="javax.swing.JButton" name="jButton1">
   89.78 +      <Properties>
   89.79 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   89.80 +          <ResourceString bundle="org/netbeans/modules/python/project2/ui/customizer/Bundle.properties" key="CustomizerRun.browseMain.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   89.81 +        </Property>
   89.82 +      </Properties>
   89.83 +      <AccessibilityProperties>
   89.84 +        <Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   89.85 +          <ResourceString bundle="org/netbeans/modules/python/project/ui/customizer/Bundle.properties" key="CustomizerRun.browseMain.ad" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   89.86 +        </Property>
   89.87 +      </AccessibilityProperties>
   89.88 +      <Events>
   89.89 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/>
   89.90 +      </Events>
   89.91 +      <AuxValues>
   89.92 +        <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
   89.93 +      </AuxValues>
   89.94 +    </Component>
   89.95 +    <Component class="javax.swing.JLabel" name="jLabel2">
   89.96 +      <Properties>
   89.97 +        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
   89.98 +          <ComponentRef name="appArgs"/>
   89.99 +        </Property>
  89.100 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  89.101 +          <ResourceString bundle="org/netbeans/modules/python/project2/ui/customizer/Bundle.properties" key="CustomizerRun.appArgs.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  89.102 +        </Property>
  89.103 +      </Properties>
  89.104 +      <AuxValues>
  89.105 +        <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
  89.106 +      </AuxValues>
  89.107 +    </Component>
  89.108 +    <Component class="javax.swing.JTextField" name="appArgs">
  89.109 +      <AccessibilityProperties>
  89.110 +        <Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  89.111 +          <ResourceString bundle="org/netbeans/modules/python/project/ui/customizer/Bundle.properties" key="CustomizerRun.appArgs.ad" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  89.112 +        </Property>
  89.113 +      </AccessibilityProperties>
  89.114 +    </Component>
  89.115 +  </SubComponents>
  89.116 +</Form>
    90.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    90.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/customizer/CustomizerRun.java	Tue Feb 24 01:58:36 2015 -0800
    90.3 @@ -0,0 +1,152 @@
    90.4 +/*
    90.5 + * CustomizerRun.java
    90.6 + *
    90.7 + * Created on August 22, 2008, 4:52 PM
    90.8 + */
    90.9 +
   90.10 +package org.netbeans.modules.python.project2.ui.customizer;
   90.11 +
   90.12 +import javax.swing.event.DocumentEvent;
   90.13 +import javax.swing.event.DocumentListener;
   90.14 +import javax.swing.text.Document;
   90.15 +import org.netbeans.modules.python.project2.PythonProject2;
   90.16 +import org.netbeans.modules.python.project2.ui.Utils;
   90.17 +
   90.18 +/**
   90.19 + *
   90.20 + * @author  Tomas Zezula
   90.21 + */
   90.22 +public class CustomizerRun extends javax.swing.JPanel {
   90.23 +
   90.24 +    private final PythonProject2 project;
   90.25 +    private final DocListener listener;
   90.26 +
   90.27 +    /** Creates new form CustomizerRun */
   90.28 +    public CustomizerRun(final PythonProject2 project) {
   90.29 +        assert project != null;
   90.30 +        this.project = project;
   90.31 +        initComponents();
   90.32 +        String mainModule = project.getMainModule();
   90.33 +        if (mainModule != null) {
   90.34 +            this.mainModule.setText(mainModule);
   90.35 +        }
   90.36 +        String appArgs = project.getApplicationArgs();
   90.37 +        if (appArgs != null) {
   90.38 +            this.appArgs.setText(appArgs);
   90.39 +        }
   90.40 +        this.listener = new DocListener ();
   90.41 +        this.mainModule.getDocument().addDocumentListener(listener);
   90.42 +        this.appArgs.getDocument().addDocumentListener(listener);
   90.43 +    }
   90.44 +
   90.45 +    /** This method is called from within the constructor to
   90.46 +     * initialize the form.
   90.47 +     * WARNING: Do NOT modify this code. The content of this method is
   90.48 +     * always regenerated by the Form Editor.
   90.49 +     */
   90.50 +    @SuppressWarnings("unchecked")
   90.51 +    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
   90.52 +    private void initComponents() {
   90.53 +
   90.54 +        jLabel1 = new javax.swing.JLabel();
   90.55 +        mainModule = new javax.swing.JTextField();
   90.56 +        jButton1 = new javax.swing.JButton();
   90.57 +        jLabel2 = new javax.swing.JLabel();
   90.58 +        appArgs = new javax.swing.JTextField();
   90.59 +
   90.60 +        jLabel1.setLabelFor(mainModule);
   90.61 +        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CustomizerRun.class, "CustomizerRun.mainModule.text")); // NOI18N
   90.62 +
   90.63 +        org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(CustomizerRun.class, "CustomizerRun.browseMain.text")); // NOI18N
   90.64 +        jButton1.addActionListener(new java.awt.event.ActionListener() {
   90.65 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
   90.66 +                jButton1ActionPerformed(evt);
   90.67 +            }
   90.68 +        });
   90.69 +
   90.70 +        jLabel2.setLabelFor(appArgs);
   90.71 +        org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(CustomizerRun.class, "CustomizerRun.appArgs.text")); // NOI18N
   90.72 +
   90.73 +        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
   90.74 +        this.setLayout(layout);
   90.75 +        layout.setHorizontalGroup(
   90.76 +            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
   90.77 +            .addGroup(layout.createSequentialGroup()
   90.78 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
   90.79 +                    .addComponent(jLabel2)
   90.80 +                    .addComponent(jLabel1))
   90.81 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
   90.82 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
   90.83 +                    .addComponent(mainModule, javax.swing.GroupLayout.DEFAULT_SIZE, 364, Short.MAX_VALUE)
   90.84 +                    .addComponent(appArgs, javax.swing.GroupLayout.DEFAULT_SIZE, 364, Short.MAX_VALUE))
   90.85 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
   90.86 +                .addComponent(jButton1)
   90.87 +                .addContainerGap())
   90.88 +        );
   90.89 +        layout.setVerticalGroup(
   90.90 +            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
   90.91 +            .addGroup(layout.createSequentialGroup()
   90.92 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
   90.93 +                    .addComponent(jLabel1)
   90.94 +                    .addComponent(jButton1)
   90.95 +                    .addComponent(mainModule, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
   90.96 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
   90.97 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
   90.98 +                    .addComponent(jLabel2)
   90.99 +                    .addComponent(appArgs, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
  90.100 +                .addContainerGap(234, Short.MAX_VALUE))
  90.101 +        );
  90.102 +
  90.103 +        mainModule.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CustomizerRun.class, "CustomizerRun.mainModule.ad")); // NOI18N
  90.104 +        jButton1.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CustomizerRun.class, "CustomizerRun.browseMain.ad")); // NOI18N
  90.105 +        appArgs.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CustomizerRun.class, "CustomizerRun.appArgs.ad")); // NOI18N
  90.106 +    }// </editor-fold>//GEN-END:initComponents
  90.107 +
  90.108 +private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
  90.109 +    String main = Utils.chooseMainModule(project);
  90.110 +    if (main != null) {
  90.111 +        mainModule.setText(main);
  90.112 +    }
  90.113 +}//GEN-LAST:event_jButton1ActionPerformed
  90.114 +
  90.115 +
  90.116 +    // Variables declaration - do not modify//GEN-BEGIN:variables
  90.117 +    private javax.swing.JTextField appArgs;
  90.118 +    private javax.swing.JButton jButton1;
  90.119 +    private javax.swing.JLabel jLabel1;
  90.120 +    private javax.swing.JLabel jLabel2;
  90.121 +    private javax.swing.JTextField mainModule;
  90.122 +    // End of variables declaration//GEN-END:variables
  90.123 +
  90.124 +
  90.125 +    private class DocListener implements DocumentListener {
  90.126 +
  90.127 +        @Override
  90.128 +        public void insertUpdate(DocumentEvent e) {
  90.129 +            handleDocEvent (e);
  90.130 +        }
  90.131 +
  90.132 +        @Override
  90.133 +        public void removeUpdate(DocumentEvent e) {
  90.134 +            handleDocEvent(e);
  90.135 +        }
  90.136 +
  90.137 +        @Override
  90.138 +        public void changedUpdate(DocumentEvent e) {
  90.139 +            handleDocEvent(e);
  90.140 +        }
  90.141 +
  90.142 +        private void handleDocEvent (final DocumentEvent e) {
  90.143 +            final Document doc = e.getDocument();
  90.144 +            if (doc == mainModule.getDocument()) {
  90.145 +                //System.out.println("Updating main Module to " + mainModule.getText() );
  90.146 +
  90.147 +                project.setMainModule(mainModule.getText());
  90.148 +            }
  90.149 +            else if (doc == appArgs.getDocument()) {
  90.150 +                project.setApplicationArgs(appArgs.getText());
  90.151 +            }
  90.152 +        }
  90.153 +
  90.154 +    }
  90.155 +}
    91.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    91.2 +++ b/python.project2/src/org/netbeans/modules/python/project2/ui/customizer/PythonCustomizerProvider.java	Tue Feb 24 01:58:36 2015 -0800
    91.3 @@ -0,0 +1,118 @@
    91.4 +/*
    91.5 + * To change this template, choose Tools | Templates
    91.6 + * and open the template in the editor.
    91.7 + */
    91.8 +package org.netbeans.modules.python.project2.ui.customizer;
    91.9 +
   91.10 +import java.awt.Dialog;
   91.11 +import java.awt.event.ActionEvent;
   91.12 +import java.awt.event.ActionListener;
   91.13 +import java.awt.event.WindowAdapter;
   91.14 +import java.awt.event.WindowEvent;
   91.15 +import java.text.MessageFormat;
   91.16 +import java.util.HashMap;
   91.17 +import java.util.Map;
   91.18 +import org.netbeans.modules.python.project2.PythonProject2;
   91.19 +import org.netbeans.api.project.Project;
   91.20 +import org.netbeans.api.project.ProjectUtils;
   91.21 +import org.netbeans.spi.project.ui.CustomizerProvider;
   91.22 +import org.netbeans.spi.project.ui.support.ProjectCustomizer;
   91.23 +import org.openide.util.Lookup;
   91.24 +import org.openide.util.NbBundle;
   91.25 +import org.openide.util.lookup.Lookups;
   91.26 +
   91.27 +/**
   91.28 + *
   91.29 + * @author Tomas Zezula
   91.30 + */
   91.31 +public class PythonCustomizerProvider implements CustomizerProvider {
   91.32 +
   91.33 +    private static final String CUSTOMIZER_FOLDER_PATH = "Projects/org-netbeans-modules-python-project2//Customizer"; //NO18N
   91.34 +
   91.35 +    private static final Map<Project, Dialog> PROJECT_2_DIALOG = new HashMap<>();
   91.36 +
   91.37 +    private final PythonProject2 project;
   91.38 +
   91.39 +    public PythonCustomizerProvider(final PythonProject2 project) {
   91.40 +        assert project != null;
   91.41 +        this.project = project;
   91.42 +    }
   91.43 +
   91.44 +    @Override
   91.45 +    public void showCustomizer() {
   91.46 +        showCustomizer(null);
   91.47 +    }
   91.48 +
   91.49 +    public void showCustomizer(String preselectedCategory) {
   91.50 +        Dialog dialog = PROJECT_2_DIALOG.get(project);
   91.51 +        if (dialog != null) {
   91.52 +            dialog.setVisible(true);
   91.53 +            return;
   91.54 +        }
   91.55 +
   91.56 +        final Lookup context = Lookups.fixed(project);
   91.57 +        final OptionListener optionListener = new OptionListener(project);
   91.58 +        final StoreListener storeListener = new StoreListener(project);
   91.59 +        dialog = ProjectCustomizer.createCustomizerDialog(CUSTOMIZER_FOLDER_PATH, context, preselectedCategory,
   91.60 +                optionListener, storeListener, null);
   91.61 +        dialog.addWindowListener(optionListener);
   91.62 +        dialog.setTitle(MessageFormat.format(
   91.63 +                NbBundle.getMessage(PythonCustomizerProvider.class, "LBL_Customizer_Title"),
   91.64 +                ProjectUtils.getInformation(project).getDisplayName()));
   91.65 +
   91.66 +        PROJECT_2_DIALOG.put(project, dialog);
   91.67 +        dialog.setVisible(true);
   91.68 +    }
   91.69 +
   91.70 +    private class StoreListener implements ActionListener {
   91.71 +
   91.72 +        private final PythonProject2 uiProperties;
   91.73 +
   91.74 +        StoreListener(PythonProject2 uiProperties) {
   91.75 +            this.uiProperties = uiProperties;
   91.76 +        }
   91.77 +
   91.78 +        @Override
   91.79 +        public void actionPerformed(ActionEvent e) {
   91.80 +//            uiProperties.save();
   91.81 +        }
   91.82 +    }
   91.83 +
   91.84 +    private static class OptionListener extends WindowAdapter implements ActionListener {
   91.85 +
   91.86 +        private final Project project;
   91.87 +
   91.88 +        OptionListener(Project project) {
   91.89 +            this.project = project;
   91.90 +        }
   91.91 +
   91.92 +        // Listening to OK button ----------------------------------------------
   91.93 +        @Override
   91.94 +        public void actionPerformed(ActionEvent e) {
   91.95 +            // Close & dispose the the dialog
   91.96 +            Dialog dialog = PROJECT_2_DIALOG.get(project);
   91.97 +            if (dialog != null) {
   91.98 +                dialog.setVisible(false);
   91.99 +                dialog.dispose();
  91.100 +            }
  91.101 +        }
  91.102 +
  91.103 +        // Listening to window events ------------------------------------------
  91.104 +        @Override
  91.105 +        public void windowClosed(WindowEvent e) {
  91.106 +            PROJECT_2_DIALOG.remove(project);
  91.107 +        }
  91.108 +
  91.109 +        @Override
  91.110 +        public void windowClosing(WindowEvent e) {
  91.111 +            //Dispose the dialog otherwsie the {@link WindowAdapter#windowClosed}
  91.112 +            //may not be called
  91.113 +            Dialog dialog = PROJECT_2_DIALOG.get(project);
  91.114 +            if (dialog != null) {
  91.115 +                dialog.setVisible(false);
  91.116 +                dialog.dispose();
  91.117 +            }
  91.118 +        }
  91.119 +    }
  91.120 +
  91.121 +}
    92.1 --- a/python.source/nbproject/project.xml	Wed Feb 04 01:21:44 2015 -0800
    92.2 +++ b/python.source/nbproject/project.xml	Tue Feb 24 01:58:36 2015 -0800
    92.3 @@ -16,6 +16,15 @@
    92.4                      </run-dependency>
    92.5                  </dependency>
    92.6                  <dependency>
    92.7 +                    <code-name-base>org.netbeans.modules.projectapi</code-name-base>
    92.8 +                    <build-prerequisite/>
    92.9 +                    <compile-dependency/>
   92.10 +                    <run-dependency>
   92.11 +                        <release-version>1</release-version>
   92.12 +                        <specification-version>1.60.2</specification-version>
   92.13 +                    </run-dependency>
   92.14 +                </dependency>
   92.15 +                <dependency>
   92.16                      <code-name-base>org.openide.filesystems</code-name-base>
   92.17                      <build-prerequisite/>
   92.18                      <compile-dependency/>
    93.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    93.2 +++ b/python.source/src/org/netbeans/modules/python/source/PythonProjectSourceLevelQuery.java	Tue Feb 24 01:58:36 2015 -0800
    93.3 @@ -0,0 +1,29 @@
    93.4 +/*
    93.5 + * To change this license header, choose License Headers in Project Properties.
    93.6 + * To change this template file, choose Tools | Templates
    93.7 + * and open the template in the editor.
    93.8 + */
    93.9 +package org.netbeans.modules.python.source;
   93.10 +
   93.11 +import org.netbeans.modules.python.source.queries.SourceLevelQueryImplementation;
   93.12 +import org.netbeans.api.project.FileOwnerQuery;
   93.13 +import org.netbeans.api.project.Project;
   93.14 +import org.openide.filesystems.FileObject;
   93.15 +import org.openide.util.lookup.ServiceProvider;
   93.16 +
   93.17 +@ServiceProvider(service = SourceLevelQueryImplementation.class, position = 400)
   93.18 +public class PythonProjectSourceLevelQuery implements SourceLevelQueryImplementation {
   93.19 +
   93.20 +    @Override
   93.21 +    public Result getSourceLevel(FileObject pythonFile) {
   93.22 +        final Project project = FileOwnerQuery.getOwner(pythonFile);
   93.23 +        if (project != null) {
   93.24 +            SourceLevelQueryImplementation impl = project.getLookup().lookup(SourceLevelQueryImplementation.class);
   93.25 +            if (impl != null) {
   93.26 +                return impl.getSourceLevel(pythonFile);
   93.27 +            }
   93.28 +        }
   93.29 +        return null;
   93.30 +    }
   93.31 +
   93.32 +}