Sometimes our project structure separates files by its extension (or, to be specific, its filetype), for example folder scenes in POV-Ray project here contains .inc
and .pov
files only. So it makes sense if our New File action on project root folder is restricted to several predefined filetypes, and these new files will be directly created in their respective folder.
That’s the structure of my project, where only those .cor files will be displayed under “Corak files”, and .lay files in “Layout files”. Actually those two nodes represent two directories on disk, so it’s possible for them to contain any file but my FilterNode will only display those files with .cor and .lay extension.
The first, default approach is by using CommonProjectActions.newFileAction()
and put it on project’s root node. This way, we let user to select the filetype first, then our InstantiatingIterator.instantiate()
will take care about the target folder.
But my client want a more to-the-point approach : the “Corak files” node, for example, should have an action to create new Corak file, that is, those with .cor extension. A little googling brings me to TemplateWizard
class, which is part of Datasystems API. It has instantiate(DataObject template, DataFolder targetFolder)
method which allows us to skip the first panel of new file wizard (assuming template
is not null and a valid, registered template). This way, indeed we can skip the first panel, but the Back button will always be there and makes it possible for curious user to click it, select another filetype and finally ruin our plan.
Eventually I use a hardcoded approach, in which both template and target folder are specified on InstantiatingIterator
subclasses. The only information I need from the action invoked is the Project that will own the file, so I create my action to be context-sensitive to Project instance. Below is the code snippet from the Action..
public final class NewLayoutWizardAction implements ActionListener { private Project context; public NewLayoutWizardAction(Project context) { this.context = context; } @Override public void actionPerformed(ActionEvent e) { WizardDescriptor wiz = new WizardDescriptor(new NewLayoutWizardIterator(context)); // {0} will be replaced by WizardDesriptor.Panel.getComponent().getName() wiz.setTitleFormat(new MessageFormat("{0}")); wiz.setTitle(Bundle.CTL_NewLayoutFile()); if (DialogDisplayer.getDefault().notify(wiz) == WizardDescriptor.FINISH_OPTION) { try { executeInstantiated(wiz); } catch (IOException ex) { Exceptions.printStackTrace(ex); } } } /** * The code to execute default action for the newly created dataobject. */ private void executeInstantiated(WizardDescriptor wiz) throws IOException { for (Object o : wiz.getInstantiatedObjects()) { if (o instanceof FileObject) { DataObject obj = DataObject.find((FileObject) o); // run default action (hopefully should be here) final Node node = obj.getNodeDelegate(); Action _a = node.getPreferredAction(); if (_a instanceof ContextAwareAction) { _a = ((ContextAwareAction) _a).createContextAwareInstance(node.getLookup()); } final Action a = _a; if (a != null) { SwingUtilities.invokeLater(() -> { a.actionPerformed(new ActionEvent(node, ActionEvent.ACTION_PERFORMED, "")); }); } } } } }
The executeInstantiated
is just for ethical reason, since it’s confusing if a new file is created on disk but nothing happened on GUI. So we open it.
And below is the snippet from the Iterator..
public final class NewLayoutWizardIterator implements WizardDescriptor.InstantiatingIterator<WizardDescriptor> { private static final String LAYOUT_TEMPLATE_PATH = "Templates/JArsi/LayTemplate.lay"; //NO18N private Project project; ... public NewLayoutWizardIterator(Project context) { this.project = context; } private List<WizardDescriptor.Panel<WizardDescriptor>> getPanels() { if (panels == null) { panels = new ArrayList<>(); panels.add(new NewLayoutWizardPanel1(project,wizard)); String[] steps = new String[panels.size()]; //REMOVE THAT createSteps ... } return panels; } @Override public Set<?> instantiate() throws IOException { //Get the new file name: String targetName = (String) wizard.getProperty(NewLayoutWizardPanel1.WIZARD_KEY_TARGET_NAME); //Specify the target directory FileObject laysDirFO = LayoutFileUtil.getLayoutsFolder(project, false); DataFolder laysDirDF = DataFolder.findFolder(laysDirFO); //Specify the template defined somewhere in XML layer FileObject template = FileUtil.getConfigFile(LAYOUT_TEMPLATE_PATH); DataObject dTemplate = DataObject.find(template); //create Map for freemarker template engine Map hashMap = new HashMap(); hashMap.put("width", wizard.getProperty(NewLayoutWizardPanel1.WIDTH)); hashMap.put("height", wizard.getProperty(NewLayoutWizardPanel1.HEIGHT)); hashMap.put("unit", wizard.getProperty(NewLayoutWizardPanel1.UNIT)); //Create new DataObject DataObject dobj = dTemplate.createFromTemplate(laysDirDF, targetName, hashMap); //Obtain a FileObject: FileObject createdFile = dobj.getPrimaryFile(); //Create the new file: return Collections.singleton(createdFile); } ... }
NB : A less restricted approach can be achieved using PrivilegedTemplates
and RecommendedTemplates
as described here.