| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
| |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| |
| <head> |
| <!-- -*- xhtml -*- --> |
| <title>Writing POV-Ray Support for NetBeans III—Implementing a Project Type</title> |
| <link rel="stylesheet" type="text/css" href="https://netbeans.org/netbeans.css"/> |
| <meta name="AUDIENCE" content="NBUSER"/> |
| <meta name="TYPE" content="ARTICLE"/> |
| <meta name="EXPIRES" content="N"/> |
| <meta name="developer" content="geertjan.wielenga@oracle.com"/> |
| <meta name="indexed" content="y"/> |
| <meta name="description" |
| content="NetBeans POV-Ray Support Tutorial Part III—Implementing a custom project type"/> |
| <!-- Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved. --> |
| <!-- Use is subject to license terms.--> |
| </head> |
| <body> |
| |
| <h1>Writing POV-Ray Support for NetBeans III—Implementing a Project Type</h1> |
| |
| <p>This is a continuation of the tutorial for building a POV-Ray rendering application on |
| the NetBeans Platform. If you have not read the <a href="nbm-povray-1.html">first</a> |
| and <a href="nbm-povray-2.html">second</a> parts of this tutorial, you may |
| want to start there.</p> |
| |
| <h2 class="tutorial"><a name="setup"></a>Setting Up Dependencies</h2> |
| |
| <p>There are a few APIs which we will need to use classes from—so before |
| starting to code, let's add dependencies from the Povray Projects module |
| to those:</p> |
| |
| <div class="indent"> |
| |
| <ol> |
| |
| <li><p>The Project APIs are not part of the NetBeans Platform, but of |
| NetBeans IDE. The NetBeans Platform Application template we used, |
| at the start of part 1, only included modules into our application |
| from the NetBeans Platform. Therefore, to start with, we need to |
| include the Project APIs into our application, before we can set |
| dependencies on them in our modules.</p> |
| <p>Right-click the <tt>povsuite</tt> application, choose Properties, and |
| then select the Libraries tab. In the Libraries tab, expand the |
| "ide" node, and select Project API, Project UI, and Project UI API, |
| as shown below:</p> |
| <p><img alt="" src="../images/tutorials/povray/71/ch3/pic0.png"/></p> |
| <p>Finally, click the Resolve button, which includes a number of |
| additional modules into your application.</p> |
| <p>Click OK above.</p> |
| |
| </li> |
| |
| <li><p>Right-click the <tt>povsuite</tt> project and choose Run. The |
| application starts up. Now that it includes several project-related |
| modules, you should see a number of new features, accessed from |
| many new menu items, as shown below:</p> |
| <p><img alt="" src="../images/tutorials/povray/71/ch3/pic2.png"/></p> |
| |
| <p>Now we're going to extend the project functionality to |
| include support for a new project type for PovRay projects. That |
| means we'll need to use several APIs, on which we need to |
| set dependencies in the Povray Projects module.</p> |
| </li> |
| |
| <li><p>Right-click the Povray Projects module in the Projects window in the IDE |
| package, and choose Properties from the popup menu. Open |
| the Libraries tab of the dialog:</p> |
| <p><img alt="" src="../images/tutorials/povray/71/ch3/pic1.png"/></p> |
| </li> |
| |
| <li>On the Libraries page of the project properties dialog, shown above, click the |
| Add Dependency button.</li> |
| |
| <li><p>In the dialog that appears, type "ProjectFactory". The |
| Projects API module should become selected in the list below—it is |
| the module that provides this class, so we need a dependency on it to |
| be able to use the class.</p> |
| <p><img alt="" src="../images/tutorials/povray/71/ch3/pic3.png"/></p> |
| <p>Press Enter or Click OK, and then either press Tab and then Space, |
| or click the Add Dependency button to add another dependency.</p></li> |
| |
| <li><p>Repeat the steps above with the following classes:</p> |
| |
| <ul> |
| |
| <li>In the add dependency dialog, type "FileObject". When |
| "FileSystems API" becomes selected, press Enter or click OK.</li> |
| |
| <li>Repeat these steps again, typing the name "Lookup", and |
| adding a dependency on the Lookup API.</li> |
| |
| <li>Repeat these steps yet again, typing the name "AbstractNode", and |
| adding a dependency on the Nodes API.</li> |
| |
| <li>Repeat these steps once again, typing the name "DataFolder", and |
| adding a dependency on the Loaders API.</li> |
| |
| <li>Repeat these steps once more, typing the name "LogicalViewProvider", and |
| adding a dependency on the Project UI API.</li> |
| |
| <li>Repeat these steps once more, typing the name "HelpCtx", and |
| adding a dependency on the Utilities API.</li> |
| |
| </ul> |
| </li> |
| <li><p>You should now see the following:</p> |
| <p><img alt="" src="../images/tutorials/povray/71/ch3/pic4.png"/></p> |
| <p>Click OK to dismiss the dialog. The Libraries node of the project should now show you |
| the dependencies you have set:</p> |
| <p><img alt="" src="../images/tutorials/povray/71/ch3/pic5.png"/></p> |
| </li> |
| |
| </ol> |
| |
| </div> |
| |
| <h2>Creating the Project Factory</h2> |
| |
| <p>As with <code>DataObjects</code> and <code>DataLoaders</code>, the system |
| keeps a registry of things that can identify a directory as being a project |
| and creates a <code>Project</code> object to represent it. So, the first step |
| in creating a our own project type is to create a factory—an implementation of |
| <code>ProjectFactory</code> from the Projects API—which can figure |
| out if a directory is a POV-Ray project and, if it is one, make an instance of our |
| <code>Project</code> implementation for it.</p> |
| |
| <div class="indent"> |
| |
| <ol> |
| |
| <li><p>Right-click the <code>org.netbeans.examples.modules.povproject</code> |
| package, and choose New > Java Class:</p> |
| <p><img alt="" src="../images/tutorials/povray/71/ch3/pic6.png"/></p> |
| </li> |
| |
| <li>In the New File wizard that appears, enter the name |
| <tt>PovProjectFactory</tt>. Press Enter or click OK to create the new file.</li> |
| |
| <li>In the code editor, modify the class signature of <tt>PovProjectFactory</tt> |
| as follows: |
| <pre class="examplecode">public class PovProjectFactory implements ProjectFactory {</pre> |
| <p>Press Alt-Shift-F (Ctrl-Shift-F on Macintosh) to Fix Imports.</p></li> |
| |
| <li><p>Position the caret somewhere in the class signature line. When the |
| lightbulb glyph appears in the margin, press Alt-Enter, and then Enter |
| again to accept the hint "Implement All Abstract Methods". The class |
| definition should now be as follows:</p> |
| |
| <pre class="examplecode">package org.netbeans.examples.modules.povproject; |
| |
| import java.io.IOException; |
| import org.netbeans.api.project.Project; |
| import org.netbeans.spi.project.ProjectFactory; |
| import org.netbeans.spi.project.ProjectState; |
| import org.openide.filesystems.FileObject; |
| |
| public class PovProjectFactory implements ProjectFactory { |
| |
| @Override |
| public boolean isProject(FileObject projectDirectory) { |
| throw new UnsupportedOperationException("Not supported yet."); |
| } |
| |
| @Override |
| public Project loadProject(FileObject projectDirectory, ProjectState state) throws IOException { |
| throw new UnsupportedOperationException("Not supported yet."); |
| } |
| |
| @Override |
| public void saveProject(Project project) throws IOException, ClassCastException { |
| throw new UnsupportedOperationException("Not supported yet."); |
| } |
| |
| }</pre> |
| |
| |
| </li> |
| |
| <li>Add the following constants to the head of the <code>PovProjectFactory</code> |
| class definition, as follows: |
| |
| <pre class="examplecode">public class PovProjectFactory implements ProjectFactory { |
| |
| <b>public static final String PROJECT_DIR = "pvproject"; |
| public static final String PROJECT_PROPFILE = "project.properties";</b></pre> |
| |
| </li> |
| |
| <li> |
| |
| <p>The first method we will implement is the <code>isProject()</code> |
| method. This method needs to be very fast—it should determine whether |
| or not a directory is a project as quickly as possible, because it will |
| be called once for each directory shown in the file chooser when the |
| user selects File > Open Project.</p> |
| |
| <p> |
| Implement the method as follows:</p> |
| |
| <pre class="examplecode">@Override |
| public boolean isProject(FileObject projectDirectory) { |
| <b>return projectDirectory.getFileObject(PROJECT_DIR) != null;</b> |
| }</pre> |
| |
| <p>This simple test for the presence of a subdirectory called |
| "pvproject" is all we need to determine that something is |
| <i>not</i> one of our projects.</p> |
| |
| </li> |
| |
| <li> |
| |
| <p>Next, we will implement the code that actually loads a project, given a |
| directory. The project system handles caching of projects, so all that's |
| needed here is to create a new project:</p> |
| |
| <pre class="examplecode">@Override |
| public Project loadProject(FileObject projectDirectory, ProjectState state) throws IOException { |
| <b>return isProject (projectDirectory) ? new PovrayProject (projectDirectory, state) : null;</b> |
| }</pre> |
| |
| <p>The only interesting thing here is the <code>ProjectState</code> object, which |
| we pass along with the directory to our project's constructor. It is provided |
| to us by the project system, and can be used to mark a project as needing to |
| be saved. We will use it later to do that when the user changes the main file |
| of the project, which will be written to disk in the <code>project.properties</code> |
| when our project is closed.</p> |
| |
| </li> |
| |
| <li> |
| <p>The final thing to implement is <code>saveProject()</code>—this is what will |
| write out any unsaved changes to disk when a POV-Ray project is closed, or |
| when the application shuts down:</p> |
| |
| <pre class="examplecode">@Override |
| public void saveProject(Project project) throws IOException, ClassCastException { |
| |
| FileObject projectRoot = project.getProjectDirectory(); |
| if (projectRoot.getFileObject(PROJECT_DIR) == null) { |
| throw new IOException ("Project dir " + projectRoot.getPath() + " deleted," + |
| " cannot save project"); |
| } |
| |
| //Force creation of the scenes/ dir if it was deleted |
| project.getLookup(PovrayProject.class).getScenesFolder(true); |
| |
| //Find the properties file pvproject/project.properties, |
| //creating it if necessary |
| String propsPath = PROJECT_DIR + "/" + PROJECT_PROPFILE; |
| FileObject propertiesFile = projectRoot.getFileObject(propsPath); |
| if (propertiesFile == null) { |
| //Recreate the properties file if needed |
| propertiesFile = projectRoot.createData(propsPath); |
| } |
| |
| Properties properties = (Properties) project.getLookup().lookup (Properties.class); |
| File f = FileUtil.toFile(propertiesFile); |
| properties.store(new FileOutputStream(f), "NetBeans Povray Project Properties"); |
| |
| }</pre> |
| |
| </li> |
| </ol> |
| |
| </div> |
| |
| <p>We haven't written the <code>PovrayProject</code> yet, which is why you have |
| some red underline error marks in your code, but from this code it's pretty clear |
| what it will look like—we are creating the <code>scenes/</code> directory if it |
| does not exist or was deleted; we fetch a Properties object out of the project's |
| <code>Lookup</code>, and save it into <code>pvproject/project.properties</code>—that's |
| all there is or will be to saving a POV-Ray project.</p> |
| |
| <h2 class="tutorial"><a name="implementing"></a>Implementing PovrayProject</h2> |
| |
| <p>Now we need to create the Java class that represents a POV-Ray project—this is |
| what our <code>PovProjectFactory</code> will create if the user opens a project |
| that it owns. The Project API in NetBeans is quite simple. A "project", |
| programmatically is the association of a directory on disk with a <code>Lookup</code> - |
| a bag-o-stuff that can be queried for known interfaces. The Project API then |
| defines some interfaces and classes that should be available from a <code>Project</code>'s |
| Lookup.</p> |
| |
| <p>So the first thing will be to create our implementation of |
| <code>org.netbeans.api.project.Project</code>.</p> |
| |
| <div class="indent"> |
| |
| <ol> |
| |
| <li>Right-click the <code>org.netbeans.examples.modules.povproject</code> |
| package in the Povray Projects project, and choose New > Java Class again. |
| In the New File wizard that appears, enter the name |
| <tt>PovrayProject</tt>. Press Enter or click OK to create the new file.</li> |
| |
| <li><p>In the code editor, modify the signature line of <code>PovrayProject</code> |
| as follows:</p> |
| <pre class="examplecode">public final class PovrayProject implements Project {</pre> |
| <p>Press Alt-Shift-F (Ctrl-Shift-F on Macintosh) to Fix Imports.</p></li> |
| |
| <li>Position the caret somewhere in the class signature line. When the |
| lightbulb glyph appears in the margin, press Alt-Enter, and then Enter |
| again to accept the hint "Implement All Abstract Methods". You should |
| now see the following: |
| <pre class="examplecode">package org.netbeans.examples.modules.povproject; |
| |
| import org.netbeans.api.project.Project; |
| import org.openide.filesystems.FileObject; |
| import org.openide.util.Lookup; |
| |
| public class PovrayProject implements Project { |
| |
| @Override |
| public FileObject getProjectDirectory() { |
| throw new UnsupportedOperationException("Not supported yet."); |
| } |
| |
| @Override |
| public Lookup getLookup() { |
| throw new UnsupportedOperationException("Not supported yet."); |
| } |
| |
| }</pre> |
| |
| </li> |
| |
| <li>Implement the top of the class as follows: |
| <pre class="examplecode">public class PovrayProject implements Project { |
| |
| public static final String SCENES_DIR = "scenes"; //NOI18N |
| public static final String IMAGES_DIR = "images"; //NOI18N |
| |
| private final FileObject projectDir; |
| LogicalViewProvider logicalView = new PovrayLogicalView(this); |
| private final ProjectState state; |
| |
| public PovrayProject(FileObject projectDir, ProjectState state) { |
| this.projectDir = projectDir; |
| this.state = state; |
| } |
| |
| @Override |
| public FileObject getProjectDirectory() { |
| return projectDir; |
| } |
| |
| FileObject getScenesFolder(boolean create) { |
| FileObject result = |
| projectDir.getFileObject(SCENES_DIR); |
| |
| if (result == null && create) { |
| try { |
| result = projectDir.createFolder (SCENES_DIR); |
| } catch (IOException ioe) { |
| Exceptions.printStackTrace(ioe); |
| } |
| } |
| return result; |
| } |
| |
| FileObject getImagesFolder(boolean create) { |
| FileObject result = |
| projectDir.getFileObject(IMAGES_DIR); |
| if (result == null && create) { |
| try { |
| result = projectDir.createFolder (IMAGES_DIR); |
| } catch (IOException ioe) { |
| Exceptions.printStackTrace(ioe); |
| } |
| } |
| return result; |
| } |
| </pre> |
| <p>The last two methods we will use later on in this tutorial—they define (and can create) the |
| <code>scenes</code> code and <code>images</code> folders that POV-Ray source |
| files and their resulting image files will go into when the project is |
| rendered.</p> |
| |
| </li> |
| |
| <li><p>The actually interesting code goes into our implementation of <code>getLookup()</code>. |
| Eventually we will put some of our own interfaces into the project's <code>Lookup</code>; |
| for now it will be mainly standard stuff—interfaces provided by the Project API |
| module which we will implement. Implement <code>getLookup()</code> as follows:</p> |
| |
| <pre class="examplecode"> private Lookup lkp; |
| |
| @Override |
| public Lookup getLookup() { |
| if (lkp == null) { |
| lkp = Lookups.fixed(new Object[]{ |
| this, //handy to expose a project in its own lookup |
| state, //allow outside code to mark the project as needing saving |
| new ActionProviderImpl(), //Provides standard actions like Build and Clean |
| loadProperties(), //The project properties |
| new Info(), //Project information implementation |
| logicalView, //Logical view of project implementation |
| }); |
| } |
| return lkp; |
| }</pre> |
| |
| </li> |
| |
| <li><p>The one interesting thing in the code above |
| is the call to <code>loadProperties()</code>—for |
| storing project settings, we will use a properties file. So all we do here |
| is read it into a <code>Properties</code> object, and make that object available |
| through the project's <code>Lookup</code>. We will want to save any changes |
| made to this properties object, so we'll use a bit of cleverness and create |
| a <code>Properties</code> subclass that will mark the project as needing |
| saving whenever something calls <code>put()</code>:</p> |
| |
| <pre class="examplecode"> private Properties loadProperties() { |
| |
| FileObject fob = projectDir.getFileObject(PovProjectFactory.PROJECT_DIR |
| + "/" + PovProjectFactory.PROJECT_PROPFILE); |
| |
| Properties properties = new NotifyProperties(state); |
| if (fob != null) { |
| try { |
| properties.load(fob.getInputStream()); |
| } catch (Exception e) { |
| Exceptions.printStackTrace(e); |
| } |
| } |
| |
| return properties; |
| |
| } |
| |
| private static class NotifyProperties extends Properties { |
| |
| private final ProjectState state; |
| |
| NotifyProperties(ProjectState state) { |
| this.state = state; |
| } |
| |
| @Override |
| public Object put(Object key, Object val) { |
| |
| Object result = super.put(key, val); |
| |
| if (((result == null) != (val == null)) || (result != null |
| && val != null && !val.equals(result))) { |
| state.markModified(); |
| } |
| |
| return result; |
| |
| } |
| |
| }</pre> |
| |
| <p>Other than that, the things in the <code>Lookup</code> are what should typically be found |
| in the <code>Lookup</code> of any project—the project itself (the project |
| infrastructure reserves the right to wrap any Project type in a wrapper Project |
| object, so this guarantees being able to get at the real project instance), |
| its state, an <code>ActionProvider</code> to handle standard commands like |
| Build and Clean, a <code>ProjectInformation</code> implementation that |
| supplies the display name and icon for the project. The last thing in the |
| lookup is the <i>logical view</i> which we will come to next—this is what |
| provides a <code>Node</code> for the project that will be displayed on the |
| <i>Projects</i> tab in NetBeans.</p> |
| |
| </li> |
| |
| <li> |
| <p>There are two remaining classes we need to create—the implementations of |
| <code>ActionProvider</code> and <code>ProjectInformation</code>. We will simply |
| stub these for now—add these two classes as inner classes of <code>PovrayProject</code>:</p> |
| |
| <pre class="examplecode">private final class ActionProviderImpl implements ActionProvider { |
| |
| @Override |
| public String[] getSupportedActions() { |
| return new String[0]; |
| } |
| |
| @Override |
| public void invokeAction(String string, Lookup lookup) throws IllegalArgumentException { |
| //do nothing |
| } |
| |
| @Override |
| public boolean isActionEnabled(String string, Lookup lookup) throws IllegalArgumentException { |
| return false; |
| } |
| |
| } |
| |
| /** |
| * Implementation of project system's ProjectInformation class |
| */ |
| private final class Info implements ProjectInformation { |
| |
| @Override |
| public Icon getIcon() { |
| return new ImageIcon(ImageUtilities.loadImage( |
| "org/netbeans/examples/modules/povrayproject/resources/scenes.gif")); |
| } |
| |
| @Override |
| public String getName() { |
| return getProjectDirectory().getName(); |
| } |
| |
| @Override |
| public String getDisplayName() { |
| return getName(); |
| } |
| |
| @Override |
| public void addPropertyChangeListener(PropertyChangeListener pcl) { |
| //do nothing, won't change |
| } |
| |
| @Override |
| public void removePropertyChangeListener(PropertyChangeListener pcl) { |
| //do nothing, won't change |
| } |
| |
| @Override |
| public Project getProject() { |
| return PovrayProject.this; |
| } |
| |
| }</pre> |
| |
| </ol> |
| |
| </div> |
| |
| <h2 class="logical-view"><a name="implementing"></a>Implementing the Logical View</h2> |
| |
| <p>One line in the code you entered above should still be marked as being an error:</p> |
| |
| <pre class="examplecode">LogicalViewProvider logicalView = new PovrayLogicalView(this);</pre> |
| |
| <p>In NetBeans IDE, what you see in the Projects window is a "logical |
| view" of your project. This is a view that may not exactly reflect |
| the structure of files on disk (the Files window and Favorites window is for that), but is more |
| convenient to work with—for example, collapsing a tree of directories |
| into a single node with a Java package name.</p> |
| <p> |
| What we will implement now is a <code>LogicalViewProvider</code>. This is |
| basically a factory that produces a <code>Node</code> |
| that represents the project. What child <code>Node</code>s that <code>Node</code> |
| has, and what actions are available on them is up to us. |
| </p> |
| |
| <div class="indent"> |
| |
| <ol> |
| |
| <li>Right-click the <code>org.netbeans.examples.modules.povproject</code> |
| package in the Povray Projects project, and choose New > Java Class. |
| In the New File wizard that appears, enter the name |
| "PovrayLogicalView". Press Enter or click OK to create the new file.</li> |
| |
| <li>In the code editor, modify the signature line of <code>PovrayLogicalView</code> |
| as follows: |
| <pre class="examplecode">class PovrayLogicalView implements LogicalViewProvider {</pre><p> |
| <p>Press Alt-Shift-F (Ctrl-Shift-F on Macintosh) to Fix Imports.</p></li> |
| |
| <li><p>Position the caret somewhere in the class signature line. When the |
| lightbulb glyph appears in the margin, press Alt-Enter, and then Enter |
| again to accept the hint "Implement All Abstract Methods".</p> |
| |
| <p>We now have a skeleton implementation of our logical view:</p> |
| |
| <pre class="examplecode">class PovrayLogicalView implements LogicalViewProvider { |
| |
| public PovrayLogicalView(PovrayProject aThis) { |
| } |
| |
| @Override |
| public Node createLogicalView() { |
| throw new UnsupportedOperationException("Not supported yet."); |
| } |
| |
| @Override |
| public Node findPath(Node root, Object target) { |
| throw new UnsupportedOperationException("Not supported yet."); |
| } |
| |
| }</pre> |
| |
| <p class="notes"><b>Note:</b> Part of the value of having a concept of a project is the ability to present |
| data in a way that is closer to the way a user will <i>think</i> about their |
| project than the structure of files on disk may be. The logical view of a project |
| should present a simplified structure showing users what they need to |
| get their work done.</p> |
| |
| <p class="notes"><b>Note:</b> In our case, we <a href="nbm-povray-2.html#phys">already decided</a> that |
| the user did not need to see the <code>images/</code> subdirectory, they should |
| just be able to click a scene file and choose <i>View</i>, and that we want |
| to put scene files in a <code>scenes/</code> subdirectory. So the logical |
| thing to do for our logical view is to have it show the contents of that |
| <code>scenes/</code> directory. We can return whatever <code>Node</code> we |
| want as the root of our logical view of the project, and NetBeans makes |
| using the content of the <code>scenes/</code> subdirectory very easy.</p> |
| |
| <p class="tips">In the Nodes API is a class called <code>FilterNode</code>. What it does is |
| wrap an existing <code>Node</code>, and by default, simply expose the same |
| child nodes, display name, icon, actions, etc. as the original. We can |
| subclass <code>FilterNode</code> to change its icon and the set of actions |
| available on it. The <code>DataLoader</code> infrastructure already provides |
| a loader that recognizes Filesystem folders—an API class called |
| <code>DataFolder</code>. So we get the original node for the folder for free—we |
| just need to provide a subclass that uses our icon and (eventually) actions.</p> |
| </li> |
| <li>We can now implement <code>PovrayLogicalView</code> as follows: |
| <pre class="examplecode">class PovrayLogicalView implements LogicalViewProvider { |
| |
| private final PovrayProject project; |
| |
| public PovrayLogicalView(PovrayProject project) { |
| this.project = project; |
| } |
| |
| @Override |
| public Node createLogicalView() { |
| |
| try { |
| |
| //Get the scenes directory, creating if deleted: |
| FileObject scenes = project.getScenesFolder(true); |
| |
| //Get the DataObject that represents it: |
| DataFolder scenesDataObject = |
| DataFolder.findFolder(scenes); |
| |
| //Get its default node—we'll wrap our node around it to change the |
| //display name, icon, etc: |
| Node realScenesFolderNode = scenesDataObject.getNodeDelegate(); |
| |
| //This FilterNode will be our project node: |
| return new ScenesNode(realScenesFolderNode, project); |
| |
| } catch (DataObjectNotFoundException donfe) { |
| |
| Exceptions.printStackTrace(donfe); |
| |
| //Fallback—the directory couldn't be created - |
| //read-only filesystem or something evil happened: |
| return new AbstractNode (Children.LEAF); |
| |
| } |
| |
| } |
| |
| /** |
| * This is the node you actually see in the Projects window for the project |
| */ |
| private static final class ScenesNode extends FilterNode { |
| |
| final PovrayProject project; |
| |
| public ScenesNode(Node node, PovrayProject project) throws DataObjectNotFoundException { |
| super(node, new FilterNode.Children(node), |
| //The projects system wants the project in the Node's lookup. |
| //NewAction and friends want the original Node's lookup. |
| //Make a merge of both: |
| new ProxyLookup( |
| Lookups.singleton(project), |
| node.getLookup()) |
| ); |
| this.project = project; |
| } |
| |
| @Override |
| public Image getIcon(int type) { |
| return ImageUtilities.loadImage( |
| "org/netbeans/examples/modules/povproject/resources/scenes.gif"); |
| } |
| |
| @Override |
| public Image getOpenedIcon(int type) { |
| return getIcon(type); |
| } |
| |
| @Override |
| public String getDisplayName() { |
| return project.getProjectDirectory().getName(); |
| } |
| |
| } |
| |
| @Override |
| public Node findPath(Node root, Object target) { |
| //leave unimplemented for now |
| return null; |
| } |
| |
| }</pre> |
| |
| <p>The interesting code above is in the method <code>createLogicalView()</code>. |
| What we do there is quite simple and elegant—we have already decided that |
| there will be a <code>scenes/</code> directory in our project, and that's |
| where new <code>.pov</code> and <code>.inc</code> files will be created. |
| And that is all we want to expose to the user when they interact with one of |
| our projects. So, we simply find the <code>Node</code> for that folder in |
| the real filesystem on disk, and wrap it in our own <code>FilterNode</code>, |
| which can expose whatever actions, icon, child <code>Node</code>s or |
| properties we choose. Essentially, the logical view of the project is a |
| view of a subdirectory of the project, with a special icon and (eventually) |
| set of actions.</p> |
| |
| <p>The final method, <code>findPath()</code> allows a user to use a keystroke to |
| select whatever they're editing in the <i>Projects</i> tab in the main window - |
| we will leave that unimplemented for now.</p> |
| |
| </li> |
| |
| <li><p>One final thing we need to do is to provide the icon referenced from |
| <code>PovrayLogicalView.ScenesNode.getIcon()</code> above. Any 16x16 <code>.gif</code> |
| or <code>.png</code> file will do, or you can use <a href="../images/tutorials/povray/scenes.gif"> |
| this one <img src="../images/tutorials/povray/scenes.gif" border="0"/></a>. Create |
| a new java package "resources" underneath |
| <code>org.netbeans.examples.modules.povproject</code>, and copy or save the |
| image file there, modifying the file name in the source code if necesary.</p> |
| </li> |
| |
| </ol> |
| |
| </div> |
| |
| <h2 class="tutorial"><a name="registering"></a>Registering the Project Factory</h2> |
| |
| <p>The system needs to know about our project type, for this module to do anything. |
| We will register our project type into the default |
| lookup using the technique of adding a file to <code>META-INF/services</code> |
| in our module's JAR:</p> |
| |
| <div class="indent"> |
| |
| <ol> |
| |
| <li>Add the @ServiceProvider annotation above the class signature |
| of the factory class: |
| |
| <pre class="examplecode">@ServiceProvider(service=ProjectFactory.class) |
| public class PovProjectFactory implements ProjectFactory {</pre> |
| </li> |
| |
| <li>Right-click the Povray Project and choose Build.</li> |
| |
| <li>In the Files window, expand the 'build' folder |
| and notice that the META-INF/services folder has |
| been created for you, via the build process, at which |
| time a NetBeans annotation processor processed |
| the @ServiceProvider annotation: |
| <p><img alt="" src="../images/tutorials/povray/71/ch3/pic7.png"/></p> |
| </li> |
| <li><p>When you open the file above, notice that it consists of one |
| line, which is the name of the implementing class:</p> |
| <pre class="examplecode">org.netbeans.examples.modules.povproject.PovProjectFactory</pre> |
| |
| </ol> |
| |
| </div> |
| |
| <p> |
| That's all it takes to register our project type, so that when our module is |
| loaded, the NetBeans Platform will start recognizing POV-Ray projects.</p> |
| |
| |
| <h2 class="logical-view"><a name="next"></a>Trying Out the New Project</h2> |
| |
| <p>We now have a working (albeit not terribly useful) implementation of POV-Ray |
| projects. As yet we have no way to create such a project on disk, but if you |
| were to have one, you could open it and view it.</p> |
| |
| <div class="indent"> |
| |
| <ol> |
| |
| <li>To try out your code at this point, download <a href="../images/tutorials/povray/71/ch3/Wonderland.zip">Wonderland.zip</a> which contains such a project, unpack it to a temporary |
| directory and attempt to open it as a project, following the steps below.</li> |
| |
| <Li><p>Run the application, go to File | Open Project, and when you |
| browse to the temporary directory created above, you should |
| see that your project is recognized:</p> |
| |
| <p><img alt="" src="../images/tutorials/povray/71/ch3/pic8.png"/></p></li> |
| |
| <li><p>Open the project. In the Projects window, you will |
| see the logical view created above:</p> |
| <p><img alt="" src="../images/tutorials/povray/71/ch3/pic9.png"/></p></li> |
| |
| <li><p>In the Files window, you will see all the files in the project:</p> |
| |
| <p><img alt="" src="../images/tutorials/povray/71/ch3/pic10.png"/></p> |
| </li> |
| |
| </ol> |
| |
| </div> |
| |
| <h2 class="logical-view"><a name="next"></a>Next Steps</h2> |
| |
| <p>The <a href="nbm-povray-4.html">next tutorial</a> will begin to add truly |
| useful functionality to our projects.</p> |
| |
| </body> |
| |
| </html> |