# This patch file was generated by NetBeans IDE # Following Index: paths are relative to: E:\sources\netbeans.org\newtrunk\projects\projectuiapi # This patch can be applied using context Tools: Patch action on respective folder. # It uses platform neutral UTF-8 encoding and \n newlines. # Above lines and this line are ignored by the patching process. Index: src/org/netbeans/modules/project/uiapi/CustomizerDialog.java *** E:\sources\netbeans.org\newtrunk\projects\projectuiapi\src\org\netbeans\modules\project\uiapi\CustomizerDialog.java Base (1.12) --- E:\sources\netbeans.org\newtrunk\projects\projectuiapi\src\org\netbeans\modules\project\uiapi\CustomizerDialog.java Locally Modified (Based On 1.12) *************** *** 20,25 **** --- 20,28 ---- package org.netbeans.modules.project.uiapi; import java.awt.Dialog; + import java.awt.Dimension; + import java.awt.Frame; + import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; *************** *** 33,39 **** --- 36,47 ---- import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; + import javax.swing.JComponent; + import javax.swing.JDialog; import javax.swing.JPanel; + import javax.swing.SwingUtilities; + import org.netbeans.api.progress.ProgressHandle; + import org.netbeans.api.progress.ProgressHandleFactory; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectManager; import org.netbeans.spi.project.ui.support.ProjectCustomizer; *************** *** 44,49 **** --- 52,59 ---- import org.openide.util.Lookup; import org.openide.util.Mutex; import org.openide.util.NbBundle; + import org.openide.util.RequestProcessor; + import org.openide.windows.WindowManager; /** Implementation of standard customizer dialog. * *************** *** 63,69 **** private static final String COMMAND_OK = "OK"; // NOI18N private static final String COMMAND_CANCEL = "CANCEL"; // NOI18N ! public static Dialog createDialog( ActionListener okOptionListener, final CustomizerPane innerPane, HelpCtx helpCtx, final ProjectCustomizer.Category[] categories, //#97998 related ProjectCustomizer.CategoryComponentProvider componentProvider ) { --- 73,79 ---- private static final String COMMAND_OK = "OK"; // NOI18N private static final String COMMAND_CANCEL = "CANCEL"; // NOI18N ! public static Dialog createDialog( ActionListener okOptionListener, ActionListener storeListener, final CustomizerPane innerPane, HelpCtx helpCtx, final ProjectCustomizer.Category[] categories, //#97998 related ProjectCustomizer.CategoryComponentProvider componentProvider ) { *************** *** 89,95 **** // RegisterListener ! ActionListener optionsListener = new OptionListener( okOptionListener, categories , componentProvider); options[ OPTION_OK ].addActionListener( optionsListener ); options[ OPTION_CANCEL ].addActionListener( optionsListener ); --- 99,105 ---- // RegisterListener ! ActionListener optionsListener = new OptionListener(okOptionListener, storeListener, categories , componentProvider); options[ OPTION_OK ].addActionListener( optionsListener ); options[ OPTION_CANCEL ].addActionListener( optionsListener ); *************** *** 159,173 **** private static class OptionListener implements ActionListener { private ActionListener okOptionListener; private ProjectCustomizer.Category[] categories; private Lookup.Provider prov; ! OptionListener( ActionListener okOptionListener, ProjectCustomizer.Category[] categs, ProjectCustomizer.CategoryComponentProvider componentProvider) { this.okOptionListener = okOptionListener; categories = categs; //#97998 related --- 169,182 ---- private static class OptionListener implements ActionListener { private ActionListener okOptionListener; + private ActionListener storeListener; private ProjectCustomizer.Category[] categories; private Lookup.Provider prov; ! OptionListener( ActionListener okOptionListener, ActionListener storeListener, ProjectCustomizer.Category[] categs, ProjectCustomizer.CategoryComponentProvider componentProvider) { this.okOptionListener = okOptionListener; + this.storeListener = storeListener; categories = categs; //#97998 related if (componentProvider instanceof Lookup.Provider) { *************** *** 181,222 **** public Object run() { okOptionListener.actionPerformed( e ); // XXX maybe create new event actionPerformed(e, categories); ! //#97998 related ! if (prov != null) { ! Project prj = prov.getLookup().lookup(Project.class); ! if (ProjectManager.getDefault().isModified(prj)) { try { ! ProjectManager.getDefault().saveProject(prj); ! } catch (IOException ex) { ! Exceptions.printStackTrace(ex); ! } catch (IllegalArgumentException ex) { ! Exceptions.printStackTrace(ex); } } } ! return null; } }); } } private void actionPerformed(ActionEvent e, ProjectCustomizer.Category[] categs) { ! for (int i = 0; i < categs.length; i++) { ! ActionListener list = categs[i].getOkButtonListener(); if (list != null) { list.actionPerformed(e);// XXX maybe create new event } ! if (categs[i].getSubcategories() != null) { ! actionPerformed(e, categs[i].getSubcategories()); } } } } private static class HelpCtxChangeListener implements PropertyChangeListener { --- 193,292 ---- public Object run() { okOptionListener.actionPerformed( e ); // XXX maybe create new event actionPerformed(e, categories); ! return null; ! } ! }); ! ! final ProgressHandle handle = ProgressHandleFactory.createHandle(NbBundle.getMessage(CustomizerDialog.class, "LBL_Saving_Project_data_progress")); ! JComponent component = ProgressHandleFactory.createProgressComponent(handle); ! Frame mainWindow = WindowManager.getDefault().getMainWindow(); ! final JDialog dialog = new JDialog(mainWindow, ! NbBundle.getMessage(CustomizerDialog.class, "LBL_Saving_Project_data"), true); ! SavingProjectDataPanel panel = new SavingProjectDataPanel(component); ! ! dialog.getContentPane().add(panel); ! dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); ! dialog.pack(); ! ! Rectangle bounds = mainWindow.getBounds(); ! int middleX = bounds.x + bounds.width / 2; ! int middleY = bounds.y + bounds.height / 2; ! Dimension size = dialog.getPreferredSize(); ! dialog.setBounds(middleX - size.width / 2, middleY - size.height / 2, size.width, size.height); ! ! // Call storeListeners out of AWT EQ ! RequestProcessor.getDefault().post(new Runnable() { ! public void run() { try { ! ProjectManager.mutex().writeAccess(new Mutex.Action() { ! public Object run() { ! handle.start(); ! if (storeListener != null) { ! storeListener.actionPerformed(e); } + storePerformed(e, categories); + // #97998 related + saveModifiedProject(); + return null; } + }); + } finally { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + dialog.setVisible(false); + dialog.dispose(); } ! }); } + } }); + + dialog.setVisible(true); + } } private void actionPerformed(ActionEvent e, ProjectCustomizer.Category[] categs) { ! for (ProjectCustomizer.Category category : categs) { ! ActionListener list = category.getOkButtonListener(); if (list != null) { list.actionPerformed(e);// XXX maybe create new event } ! if (category.getSubcategories() != null) { ! actionPerformed(e, category.getSubcategories()); } } } + private void storePerformed(ActionEvent e, ProjectCustomizer.Category[] categories) { + for (ProjectCustomizer.Category category : categories) { + ActionListener listener = category.getStoreListener(); + if (listener != null) { + listener.actionPerformed(e); // XXX maybe create new event } + if (category.getSubcategories() != null) { + storePerformed(e, category.getSubcategories()); + } + } + } + private void saveModifiedProject() { + if (prov != null) { + Project prj = prov.getLookup().lookup(Project.class); + if (ProjectManager.getDefault().isModified(prj)) { + try { + ProjectManager.getDefault().saveProject(prj); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } catch (IllegalArgumentException ex) { + Exceptions.printStackTrace(ex); + } + } + } + } + + } + private static class HelpCtxChangeListener implements PropertyChangeListener { DialogDescriptor dialogDescriptor; Index: src/org/netbeans/modules/project/uiapi/Bundle.properties *** E:\sources\netbeans.org\newtrunk\projects\projectuiapi\src\org\netbeans\modules\project\uiapi\Bundle.properties Base (1.16) --- E:\sources\netbeans.org\newtrunk\projects\projectuiapi\src\org\netbeans\modules\project\uiapi\Bundle.properties Locally Modified (Based On 1.16) *************** *** 131,133 **** --- 131,137 ---- ERR_Project_Name_Must_Entered=Project name must be entered. ERR_Location_Read_Only=Project location is read only. ERR_Not_Valid_Filename=Project name "{0}" is not a valid filename. + + LBL_savingDataLabel=Saving Project data ... + LBL_Saving_Project_data=Saving Project data + LBL_Saving_Project_data_progress=Saving Project data Index: test/unit/src/org/netbeans/spi/project/ui/support/ProjectCustomizerListenersTest.java *** E:\sources\netbeans.org\newtrunk\projects\projectuiapi\test\unit\src\org\netbeans\spi\project\ui\support\ProjectCustomizerListenersTest.java No Base Revision --- E:\sources\netbeans.org\newtrunk\projects\projectuiapi\test\unit\src\org\netbeans\spi\project\ui\support\ProjectCustomizerListenersTest.java Locally New *************** *** 1,0 **** --- 1,237 ---- + /* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Portions Copyrighted 2007 Sun Microsystems, Inc. + */ + + package org.netbeans.spi.project.ui.support; + + import java.awt.Dialog; + import java.awt.event.ActionEvent; + import java.awt.event.ActionListener; + import java.lang.reflect.InvocationTargetException; + import java.util.ArrayList; + import java.util.List; + import javax.swing.JButton; + import javax.swing.JComponent; + import javax.swing.JDialog; + import javax.swing.JPanel; + import javax.swing.SwingUtilities; + import org.netbeans.junit.MockServices; + + import org.netbeans.junit.NbTestCase; + import org.openide.DialogDescriptor; + import org.openide.DialogDisplayer; + import org.openide.NotifyDescriptor; + + import org.netbeans.spi.project.ui.support.ProjectCustomizer.Category; + import org.netbeans.spi.project.ui.support.ProjectCustomizer.CategoryComponentProvider; + import org.openide.util.Exceptions; + import org.openide.util.HelpCtx; + + /** + * Test of OK and Store listeners of ProjectCustomzier dialog + * + * @author Milan Kubec + */ + public class ProjectCustomizerListenersTest extends NbTestCase { + + private List events = new ArrayList(); + private enum LType { OK, STORE }; + + public ProjectCustomizerListenersTest(String name) { + super(name); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + MockServices.setServices(TestDialogDisplayer.class); + events.clear(); + } + + public void testOKAndStoreListeners() { + + Category testCat1 = Category.create("test1", "test1", null); + final Category testCat2 = Category.create("test2", "test2", null, testCat1); + final Category testCat3 = Category.create("test3", "test3", null); + + testCat1.setOkButtonListener(new Listener(LType.OK, "testCat1", true)); + testCat1.setStoreListener(new Listener(LType.STORE, "testCat1", false)); + testCat2.setOkButtonListener(new Listener(LType.OK, "testCat2", true)); + testCat2.setStoreListener(new Listener(LType.STORE, "testCat2", false)); + testCat3.setOkButtonListener(new Listener(LType.OK, "testCat3", true)); + testCat3.setStoreListener(new Listener(LType.STORE, "testCat3", false)); + + final Listener mainOKListener = new Listener(LType.OK, "Properties", true); + final Listener mainStoreListener = new Listener(LType.STORE, "Properties", false); + + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + ProjectCustomizer.createCustomizerDialog(new Category[]{ testCat2, testCat3 }, + new CategoryComponentProviderImpl(), null, mainOKListener, mainStoreListener, + HelpCtx.DEFAULT_HELP); + } + }); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } catch (InvocationTargetException ex) { + Exceptions.printStackTrace(ex); + } + + // wait until all events are delivered + try { + Thread.sleep(3000); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + + // for (EventRecord er : events) { + // System.out.println(er); + // } + + assertEquals(8, events.size()); + assertEquals(new EventRecord(LType.OK, "Properties", 0), events.get(0)); + assertEquals(new EventRecord(LType.OK, "testCat2", 0), events.get(1)); + assertEquals(new EventRecord(LType.OK, "testCat1", 0), events.get(2)); + assertEquals(new EventRecord(LType.OK, "testCat3", 0), events.get(3)); + assertEquals(new EventRecord(LType.STORE, "Properties", 0), events.get(4)); + assertEquals(new EventRecord(LType.STORE, "testCat2", 0), events.get(5)); + assertEquals(new EventRecord(LType.STORE, "testCat1", 0), events.get(6)); + assertEquals(new EventRecord(LType.STORE, "testCat3", 0), events.get(7)); + + } + + public void testOKListener() { + + Category testCat1 = Category.create("test1", "test1", null); + final Category testCat2 = Category.create("test2", "test2", null, testCat1); + final Category testCat3 = Category.create("test3", "test3", null); + + testCat1.setOkButtonListener(new Listener(LType.OK, "testCat1", true)); + testCat2.setOkButtonListener(new Listener(LType.OK, "testCat2", true)); + testCat3.setOkButtonListener(new Listener(LType.OK, "testCat3", true)); + + final Listener mainOKListener = new Listener(LType.OK, "Properties", true); + + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + ProjectCustomizer.createCustomizerDialog(new Category[]{ testCat2, testCat3 }, + new CategoryComponentProviderImpl(), null, mainOKListener, + HelpCtx.DEFAULT_HELP); + } + }); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } catch (InvocationTargetException ex) { + Exceptions.printStackTrace(ex); + } + + // wait until all events are delivered + try { + Thread.sleep(3000); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + + // for (EventRecord er : events) { + // System.out.println(er); + // } + + assertEquals(4, events.size()); + assertEquals(new EventRecord(LType.OK, "Properties", 0), events.get(0)); + assertEquals(new EventRecord(LType.OK, "testCat2", 0), events.get(1)); + assertEquals(new EventRecord(LType.OK, "testCat1", 0), events.get(2)); + assertEquals(new EventRecord(LType.OK, "testCat3", 0), events.get(3)); + + } + + private class Listener implements ActionListener { + + private LType type; + private String id; + private long when; + private boolean inEQ; + + public Listener(LType type, String id, boolean inEQ) { + this.type = type; + this.id = id; + this.inEQ = inEQ; + } + + public void actionPerformed(ActionEvent e) { + when = System.nanoTime(); + events.add(new EventRecord(type, id, when)); + if (inEQ) { + assertTrue(SwingUtilities.isEventDispatchThread()); + } else { + assertFalse(SwingUtilities.isEventDispatchThread()); + } + } + + } + + public static final class TestDialogDisplayer extends DialogDisplayer { + + public Object notify(NotifyDescriptor descriptor) { + return null; + } + + public Dialog createDialog(DialogDescriptor descriptor) { + Object[] options = descriptor.getOptions(); + if (options[0] instanceof JButton) { + ((JButton) options[0]).doClick(); + } + return new JDialog(); + } + + } + + private static final class CategoryComponentProviderImpl implements CategoryComponentProvider { + public JComponent create(Category category) { + return new JPanel(); + } + } + + private static final class EventRecord { + + public LType type; + public String id; + public long when; + + public EventRecord(LType type, String id, long when) { + this.type = type; + this.id = id; + this.when = when; + } + + @Override + public String toString() { + return type + ", " + id + ", " + when; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof EventRecord) { + return (type.equals(((EventRecord) obj).type)) && + (id.equals(((EventRecord) obj).id)); + } + return false; + } + + } + + } Index: apichanges.xml *** E:\sources\netbeans.org\newtrunk\projects\projectuiapi\apichanges.xml Base (1.32) --- E:\sources\netbeans.org\newtrunk\projects\projectuiapi\apichanges.xml Locally Modified (Based On 1.32) *************** *** 81,86 **** --- 81,103 ---- + + + + Added methods for creating customizer UI with additional listener for saving outside of AWT EQ + + + + + + New methods were added to allow to use additional listener for saving data after user presses OK button + on Project Customzer UI. The listener will be executed after all OkListeners and will be executed off the + AWT Event Queue. During save operation modal dialog with progress bar is displayed. + + + + + Add LookupMergerimplementation for ProjectOpenedHook *************** *** 96,101 **** --- 113,119 ---- + Adding template attribute project.license Index: src/org/netbeans/modules/project/uiapi/SavingProjectDataPanel.java *** E:\sources\netbeans.org\newtrunk\projects\projectuiapi\src\org\netbeans\modules\project\uiapi\SavingProjectDataPanel.java No Base Revision --- E:\sources\netbeans.org\newtrunk\projects\projectuiapi\src\org\netbeans\modules\project\uiapi\SavingProjectDataPanel.java Locally New *************** *** 1,0 **** --- 1,77 ---- + /* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + */ + + package org.netbeans.modules.project.uiapi; + + import javax.swing.JComponent; + + /** + * Simple panel to show progress of project data saving operation + * + * @author Milan Kubec + */ + public class SavingProjectDataPanel extends javax.swing.JPanel { + + JComponent component; + + /** Creates new form SavingProjectDataPanel */ + public SavingProjectDataPanel(JComponent component) { + this.component = component; + initComponents(); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + savingDataLabel = new javax.swing.JLabel(); + + setPreferredSize(new java.awt.Dimension(450, 50)); + setLayout(new java.awt.GridBagLayout()); + + savingDataLabel.setText(org.openide.util.NbBundle.getMessage(SavingProjectDataPanel.class, "LBL_savingDataLabel")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(8, 8, 0, 8); + add(savingDataLabel, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; + gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(6, 8, 8, 8); + gridBagConstraints.weightx = 1.0; + add(component, gridBagConstraints); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel savingDataLabel; + // End of variables declaration//GEN-END:variables + + } Index: nbproject/project.xml *** E:\sources\netbeans.org\newtrunk\projects\projectuiapi\nbproject\project.xml Base (1.20) --- E:\sources\netbeans.org\newtrunk\projects\projectuiapi\nbproject\project.xml Locally Modified (Based On 1.20) *************** *** 104,109 **** --- 104,117 ---- 7.8 + + org.openide.windows + + + + 6.16 + + *************** *** 138,146 **** org.netbeans.modules.masterfs - - org.netbeans.api.project.ui org.netbeans.spi.project.ui --- 146,152 ---- Index: src/org/netbeans/modules/project/uiapi/SavingProjectDataPanel.form *** E:\sources\netbeans.org\newtrunk\projects\projectuiapi\src\org\netbeans\modules\project\uiapi\SavingProjectDataPanel.form No Base Revision --- E:\sources\netbeans.org\newtrunk\projects\projectuiapi\src\org\netbeans\modules\project\uiapi\SavingProjectDataPanel.form Locally New *************** *** 1,0 **** --- 1,38 ---- + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: src/org/netbeans/spi/project/ui/support/ProjectCustomizer.java *** E:\sources\netbeans.org\newtrunk\projects\projectuiapi\src\org\netbeans\spi\project\ui\support\ProjectCustomizer.java Base (1.15) --- E:\sources\netbeans.org\newtrunk\projects\projectuiapi\src\org\netbeans\spi\project\ui\support\ProjectCustomizer.java Locally Modified (Based On 1.15) *************** *** 93,100 **** String preselectedCategory, ActionListener okOptionListener, HelpCtx helpCtx ) { CustomizerPane innerPane = createCustomizerPane(categories, componentProvider, preselectedCategory); ! Dialog dialog = CustomizerDialog.createDialog( okOptionListener, innerPane, helpCtx, categories, componentProvider); return dialog; } --- 93,139 ---- String preselectedCategory, ActionListener okOptionListener, HelpCtx helpCtx ) { + return createCustomizerDialog(categories, componentProvider, preselectedCategory, okOptionListener, null, helpCtx); + } + + /** Creates standard which can be used for implementation + * of {@link org.netbeans.spi.project.ui.CustomizerProvider}. You don't need + * to call pack() method on the dialog. The resulting dialog will + * be non-modal.
+ * Call show() on the dialog to make it visible. If you want the dialog to be + * closed after user presses the "OK" button you have to call hide() and dispose() on it. + * (Usually in the actionPerformed(...) method of the listener + * you provided as a parameter. In case of the click on the "Cancel" button + * the dialog will be closed automatically. + * @since org.netbeans.modules.projectuiapi/1 1.25 + * @param categories array of descriptions of categories to be shown in the + * dialog. Note that categories have the valid + * property. If any of the given categories is not valid cusomizer's + * OK button will be disabled until all categories become valid + * again. + * @param componentProvider creator of GUI components for categories in the + * customizer dialog. + * @param preselectedCategory name of one of the supplied categories or null. + * Category with given name will be selected. If null + * or if the category of given name does not exist the first category will + * be selected. + * @param okOptionListener listener which will be notified when the user presses + * the OK button. + * @param storeListener listener which will be notified when the user presses OK button. + * Listener will be executed after okOptionListener outside of AWT EventQueue. + * Usually to be used to save modified files on disk. + * @param helpCtx Help context for the dialog, which will be used when the + * panels in the customizer do not specify their own help context. + * @return standard project customizer dialog. + */ + public static Dialog createCustomizerDialog( Category[] categories, + CategoryComponentProvider componentProvider, + String preselectedCategory, + ActionListener okOptionListener, + ActionListener storeListener, + HelpCtx helpCtx ) { CustomizerPane innerPane = createCustomizerPane(categories, componentProvider, preselectedCategory); ! Dialog dialog = CustomizerDialog.createDialog(okOptionListener, storeListener, innerPane, helpCtx, categories, componentProvider); return dialog; } *************** *** 128,133 **** --- 167,210 ---- String preselectedCategory, ActionListener okOptionListener, HelpCtx helpCtx) { + return createCustomizerDialog(folderPath, context, preselectedCategory, + okOptionListener, null, helpCtx); + } + + /** + * Creates standard customizer dialog that can be used for implementation of + * {@link org.netbeans.spi.project.ui.CustomizerProvider} based on content of a folder in Layers. + * Use this method when you want to allow composition and 3rd party additions to your customizer UI. + * You don't need to call pack() method on the dialog. The resulting dialog will + * be non-modal.
+ * Call show() on the dialog to make it visible. If you want the dialog to be + * closed after user presses the "OK" button you have to call hide() and dispose() on it. + * (Usually in the actionPerformed(...) method of the listener + * you provided as a parameter. In case of the click on the "Cancel" button + * the dialog will be closed automatically. + * @since org.netbeans.modules.projectuiapi/1 1.25 + * @param folderPath the path in the System Filesystem that is used as root for panel composition. + * The content of the folder is assummed to be {@link org.netbeans.spi.project.ui.support.ProjectCustomizer.CompositeCategoryProvider} instances + * @param context the context for the panels, up to the project type what the context shall be, for example org.netbeans.api.project.Project instance + * @param preselectedCategory name of one of the supplied categories or null. + * Category with given name will be selected. If null + * or if the category of given name does not exist the first category will + * be selected. + * @param okOptionListener listener which will be notified when the user presses + * the OK button. + * @param storeListener listener which will be notified when the user presses OK button. + * Listener will be executed after okOptionListener outside of AWT EventQueue. + * Usually to be used to save modified files on disk + * @param helpCtx Help context for the dialog, which will be used when the + * panels in the customizer do not specify their own help context. + * @return standard project customizer dialog. + */ + public static Dialog createCustomizerDialog( String folderPath, + Lookup context, + String preselectedCategory, + ActionListener okOptionListener, + ActionListener storeListener, + HelpCtx helpCtx) { FileObject root = Repository.getDefault().getDefaultFileSystem().findResource(folderPath); if (root == null) { throw new IllegalArgumentException("The designated path " + folderPath + " doesn't exist. Cannot create customizer."); *************** *** 135,144 **** DataFolder def = DataFolder.findFolder(root); assert def != null : "Cannot find DataFolder for " + folderPath; DelegateCategoryProvider prov = new DelegateCategoryProvider(def, context); ! return createCustomizerDialog(prov.getSubCategories(), ! prov, ! preselectedCategory, okOptionListener, helpCtx); ! } /** Creates standard innerPane for customizer dialog. --- 212,219 ---- DataFolder def = DataFolder.findFolder(root); assert def != null : "Cannot find DataFolder for " + folderPath; DelegateCategoryProvider prov = new DelegateCategoryProvider(def, context); ! return createCustomizerDialog(prov.getSubCategories(), prov, preselectedCategory, ! okOptionListener, storeListener, helpCtx); } /** Creates standard innerPane for customizer dialog. *************** *** 238,243 **** --- 313,319 ---- private boolean valid; private String errorMessage; private ActionListener okListener; + private ActionListener storeListener; /** Private constructor. See the factory method. */ *************** *** 369,376 **** --- 445,473 ---- return okListener; } + /** + * Set the action listener that will get notified when the changes in the customizer + * are to be applied. Listener is executed after OkButtonListener outside of AWT EventQueue. + * Usually to be used to save modified files on disk. + * @param listener ActionListener to notify + * @since org.netbeans.modules.projectuiapi/1 1.25 + */ + public void setStoreListener(ActionListener listener) { + storeListener = listener; } + /** + * Returns the action listener that is executed outside of AWT EQ and is associated + * with this category that gets notified when OK button is pressed on the customizer. + * @return instance of ActionListener or null if not set. + * @since org.netbeans.modules.projectuiapi/1 1.25 + */ + public ActionListener getStoreListener() { + return storeListener; + } + + } + /*private*/ static class DelegateCategoryProvider implements CategoryComponentProvider, CompositeCategoryProvider, Lookup.Provider { private final Lookup context;