Index: projects/projectuiapi/apichanges.xml =================================================================== RCS file: /cvs/projects/projectuiapi/apichanges.xml,v retrieving revision 1.15 diff -u -w -r1.15 apichanges.xml --- projects/projectuiapi/apichanges.xml 21 Jul 2005 18:10:17 -0000 1.15 +++ projects/projectuiapi/apichanges.xml 21 Jul 2005 19:00:33 -0000 @@ -95,6 +95,48 @@ + + + Added ability to set an error message for project customizer's categories. + + + + + +

+ Added ProjectCustomizer.Category.setErrorMessage and + ProjectCustomizer.Category.getErrorMessage + methods. With those methods client can set an error message + for individual categories. + Also the default implementation of a customizer dialog uses + these methods for showing a message on the bottom of + category views and to mark category nodes for invalid + categories (similar to the error message in wizards). +

+
+ + +
+ + + + Added ability to set whether a project customizer's categories are valid. + + + + + +

+ Added ProjectCustomizer.Category.setValid and + ProjectCustomizer.Category.isValid methods. + With those methods client can affect enability of the OK + button of the Customizer dialog. +

+
+ + +
+ Delete Action Added Index: projects/projectuiapi/src/org/netbeans/modules/project/uiapi/CategoryView.java =================================================================== RCS file: /cvs/projects/projectuiapi/src/org/netbeans/modules/project/uiapi/CategoryView.java,v retrieving revision 1.1 diff -u -w -r1.1 CategoryView.java --- projects/projectuiapi/src/org/netbeans/modules/project/uiapi/CategoryView.java 16 Jan 2005 19:13:18 -0000 1.1 +++ projects/projectuiapi/src/org/netbeans/modules/project/uiapi/CategoryView.java 21 Jul 2005 19:00:33 -0000 @@ -1,4 +1,4 @@ -/*4 +/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License @@ -24,6 +24,7 @@ import java.util.Collections; import javax.swing.JPanel; import javax.swing.tree.TreeSelectionModel; +import org.netbeans.modules.project.uiapi.Utilities; import org.netbeans.spi.project.ui.support.ProjectCustomizer; import org.openide.explorer.ExplorerManager; import org.openide.explorer.view.BeanTreeView; @@ -31,7 +32,6 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.NbBundle; -import org.openide.util.Utilities; import org.openide.util.lookup.Lookups; /** @@ -44,8 +44,6 @@ private BeanTreeView btv; private CategoryModel categoryModel; - private ProjectCustomizer.Category currentCategory; - public CategoryView( CategoryModel categoryModel ) { this.categoryModel = categoryModel; @@ -169,21 +167,30 @@ /** Node to be used for configuration */ - private static class CategoryNode extends AbstractNode { + private static class CategoryNode extends AbstractNode implements PropertyChangeListener { + + private Image icon = org.openide.util.Utilities.loadImage( "org/netbeans/modules/project/uiapi/defaultCategory.gif" ); // NOI18N - private Image icon = Utilities.loadImage( "org/netbeans/modules/project/uiapi/defaultCategory.gif" ); // NOI18N + private ProjectCustomizer.Category category; public CategoryNode( ProjectCustomizer.Category category ) { super( ( category.getSubcategories() == null || category.getSubcategories().length == 0 ) ? Children.LEAF : new CategoryChildren( category.getSubcategories() ), Lookups.fixed( new Object[] { category } ) ); setName( category.getName() ); + this.category = category; setDisplayName( category.getDisplayName() ); if ( category.getIcon() != null ) { this.icon = category.getIcon(); } + Utilities.getCategoryWrapper(category).addPropertyChangeListener(this); + } + public String getHtmlDisplayName() { + return category.isValid() ? null : + "" + // NOI18N + category.getDisplayName() + ""; // NOI18N } public Image getIcon( int type ) { @@ -193,6 +200,13 @@ public Image getOpenedIcon( int type ) { return getIcon( type ); } + + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName() == CategoryWrapper.VALID_PROPERTY) { + fireDisplayNameChange(null, null); + } + } + } /** Children used for configuration Index: projects/projectuiapi/src/org/netbeans/modules/project/uiapi/CustomizerDialog.java =================================================================== RCS file: /cvs/projects/projectuiapi/src/org/netbeans/modules/project/uiapi/CustomizerDialog.java,v retrieving revision 1.2 diff -u -w -r1.2 CustomizerDialog.java --- projects/projectuiapi/src/org/netbeans/modules/project/uiapi/CustomizerDialog.java 3 Mar 2005 17:43:36 -0000 1.2 +++ projects/projectuiapi/src/org/netbeans/modules/project/uiapi/CustomizerDialog.java 21 Jul 2005 19:00:33 -0000 @@ -20,6 +20,7 @@ import java.beans.PropertyChangeListener; import javax.swing.JButton; import javax.swing.JPanel; +import org.netbeans.spi.project.ui.support.ProjectCustomizer; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.util.HelpCtx; @@ -44,11 +45,17 @@ private static final String COMMAND_OK = "OK"; // NOI18N private static final String COMMAND_CANCEL = "CANCEL"; // NOI18N - public static Dialog createDialog( ActionListener okOptionListener, JPanel innerPane, HelpCtx helpCtx ) { + public static Dialog createDialog( ActionListener okOptionListener, JPanel innerPane, + HelpCtx helpCtx, ProjectCustomizer.Category[] categories ) { + + ListeningButton okButton = new ListeningButton( + NbBundle.getMessage(CustomizerDialog.class, "LBL_Customizer_Ok_Option"), // NOI18N + categories); + okButton.setEnabled(CustomizerDialog.checkValidity(categories)); // Create options JButton options[] = new JButton[] { - new JButton( NbBundle.getMessage( CustomizerDialog.class, "LBL_Customizer_Ok_Option") ), // NOI18N + okButton, new JButton( NbBundle.getMessage( CustomizerDialog.class, "LBL_Customizer_Cancel_Option" ) ) , // NOI18N }; @@ -94,6 +101,16 @@ } + /** Returns whether all given categories are valid or not. */ + private static boolean checkValidity(ProjectCustomizer.Category[] categories) { + for (int i = 0; i < categories.length; i++) { + if (!categories[i].isValid()) { + return false; + } + } + return true; + } + /** Listens to the actions on the Customizer's option buttons */ private static class OptionListener implements ActionListener { @@ -136,5 +153,27 @@ } + private static class ListeningButton extends JButton implements PropertyChangeListener { + + private ProjectCustomizer.Category[] categories; + + public ListeningButton(String label, ProjectCustomizer.Category[] categories) { + super(label); + this.categories = categories; + for (int i = 0; i < categories.length; i++) { + Utilities.getCategoryWrapper(categories[i]).addPropertyChangeListener(this); + } + + } + + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName() == CategoryWrapper.VALID_PROPERTY) { + boolean valid = ((Boolean) evt.getNewValue()).booleanValue(); + // enable only if all categories are valid + setEnabled(valid && CustomizerDialog.checkValidity(categories)); + } + } + + } } Index: projects/projectuiapi/src/org/netbeans/modules/project/uiapi/CustomizerPane.java =================================================================== RCS file: /cvs/projects/projectuiapi/src/org/netbeans/modules/project/uiapi/CustomizerPane.java,v retrieving revision 1.2 diff -u -w -r1.2 CustomizerPane.java --- projects/projectuiapi/src/org/netbeans/modules/project/uiapi/CustomizerPane.java 23 Jan 2005 17:40:12 -0000 1.2 +++ projects/projectuiapi/src/org/netbeans/modules/project/uiapi/CustomizerPane.java 21 Jul 2005 19:00:33 -0000 @@ -15,10 +15,14 @@ import java.awt.Component; import java.awt.GridBagConstraints; +import java.awt.Insets; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.UIManager; import org.netbeans.spi.project.ui.support.ProjectCustomizer; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; @@ -27,14 +31,18 @@ * * @author phrebejk */ -public class CustomizerPane extends javax.swing.JPanel implements HelpCtx.Provider { +public class CustomizerPane extends JPanel + implements HelpCtx.Provider, PropertyChangeListener { public static final String HELP_CTX_PROPERTY = "helpCtxProperty"; private Component currentCustomizer; + private JTextArea errorMessageValue = new JTextArea(); private HelpCtx currentHelpCtx; private GridBagConstraints fillConstraints; + private GridBagConstraints errMessConstraints = new GridBagConstraints(); + private GridBagConstraints errorMessageConstraints; private ProjectCustomizer.CategoryComponentProvider componentProvider; @@ -47,13 +55,32 @@ this.getAccessibleContext().setAccessibleDescription (NbBundle.getMessage(CustomizerPane.class,"AD_CustomizerPane")); // NOI18N this.componentProvider = componentProvider; fillConstraints = new GridBagConstraints(); - fillConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; - fillConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER; - fillConstraints.fill = java.awt.GridBagConstraints.BOTH; + fillConstraints.gridwidth = GridBagConstraints.REMAINDER; + fillConstraints.gridheight = 1; + fillConstraints.fill = GridBagConstraints.BOTH; fillConstraints.weightx = 1.0; fillConstraints.weighty = 1.0; categoryModel.addPropertyChangeListener( new CategoryChangeListener() ); categoryPanel.add( categoryView, fillConstraints ); + + // init errorMessageValue + errorMessageValue.setWrapStyleWord(true); + errorMessageValue.setLineWrap(true); + errorMessageValue.setBorder(BorderFactory.createEmptyBorder()); + errorMessageValue.setEditable(false); + errorMessageValue.setForeground(UIManager.getColor("nb.errorForeground")); // NOI18N + errorMessageValue.setBackground(customizerPanel.getBackground()); + + // put it into under categoryView + errMessConstraints = new GridBagConstraints(); + errMessConstraints.gridx = 0; + errMessConstraints.gridy = 1; + errMessConstraints.gridwidth = 1; + errMessConstraints.gridheight = 1; + errMessConstraints.insets = new Insets(12, 0, 0, 0); + errMessConstraints.fill = GridBagConstraints.HORIZONTAL; + customizerPanel.add(errorMessageValue, errMessConstraints); + setCategory( categoryModel.getCurrentCategory() ); } @@ -62,7 +89,8 @@ * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ - private void initComponents() {//GEN-BEGIN:initComponents + // //GEN-BEGIN:initComponents + private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; jLabel1 = new javax.swing.JLabel(); @@ -105,7 +133,8 @@ gridBagConstraints.insets = new java.awt.Insets(3, 0, 8, 11); add(customizerPanel, gridBagConstraints); - }//GEN-END:initComponents + } + // //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables @@ -153,6 +182,7 @@ JComponent newCustomizer = componentProvider.create( newCategory ); if ( newCustomizer != null ) { + Utilities.getCategoryWrapper(newCategory).addPropertyChangeListener(this); currentCustomizer = newCustomizer; currentHelpCtx = HelpCtx.findHelp( currentCustomizer ); @@ -165,6 +195,7 @@ customizerPanel.validate(); customizerPanel.repaint(); + setErrorMessage(newCategory.getErrorMessage()); firePropertyChange( HELP_CTX_PROPERTY, null, getHelpCtx() ); } else { @@ -173,6 +204,22 @@ } + private void setErrorMessage(String errMessage) { + errorMessageValue.setText(errMessage); + if (errMessage == null || errMessage.trim().equals("")) { + customizerPanel.remove(errorMessageValue); + } else { + customizerPanel.add(errorMessageValue, errMessConstraints); + } + customizerPanel.revalidate(); + } + + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName() == CategoryWrapper.ERROR_MESSAGE_PROPERTY) { + String errMessage = (String) evt.getNewValue(); + setErrorMessage(errMessage); + } + } // Private innerclasses ---------------------------------------------------- Index: projects/projectuiapi/src/org/netbeans/modules/project/uiapi/Utilities.java =================================================================== RCS file: /cvs/projects/projectuiapi/src/org/netbeans/modules/project/uiapi/Utilities.java,v retrieving revision 1.5 diff -u -w -r1.5 Utilities.java --- projects/projectuiapi/src/org/netbeans/modules/project/uiapi/Utilities.java 3 Aug 2004 16:44:51 -0000 1.5 +++ projects/projectuiapi/src/org/netbeans/modules/project/uiapi/Utilities.java 21 Jul 2005 19:00:33 -0000 @@ -13,6 +13,9 @@ package org.netbeans.modules.project.uiapi; +import java.util.HashMap; +import java.util.Map; +import org.netbeans.spi.project.ui.support.ProjectCustomizer; import org.openide.util.Lookup; /** @@ -21,6 +24,8 @@ */ public class Utilities { + private static final Map/**/ CATEGORIES = new HashMap(); + private Utilities() {} /** Gets action factory from the global Lookup. @@ -45,6 +50,15 @@ OpenProjectsTrampoline instance = (OpenProjectsTrampoline) Lookup.getDefault().lookup(OpenProjectsTrampoline.class); assert instance != null; return instance; + } + + public static CategoryWrapper getCategoryWrapper(ProjectCustomizer.Category category) { + CategoryWrapper cw = (CategoryWrapper) Utilities.CATEGORIES.get(category); + return cw == null ? CategoryWrapper.NULL_INSTANCE : cw; + } + + public static void putCategoryWrapper(ProjectCustomizer.Category category, CategoryWrapper wrapper) { + Utilities.CATEGORIES.put(category, wrapper); } } Index: projects/projectuiapi/src/org/netbeans/spi/project/ui/support/ProjectCustomizer.java =================================================================== RCS file: /cvs/projects/projectuiapi/src/org/netbeans/spi/project/ui/support/ProjectCustomizer.java,v retrieving revision 1.2 diff -u -w -r1.2 ProjectCustomizer.java --- projects/projectuiapi/src/org/netbeans/spi/project/ui/support/ProjectCustomizer.java 15 May 2005 15:43:57 -0000 1.2 +++ projects/projectuiapi/src/org/netbeans/spi/project/ui/support/ProjectCustomizer.java 21 Jul 2005 19:00:33 -0000 @@ -21,8 +21,11 @@ import org.netbeans.modules.project.uiapi.CategoryModel; import org.netbeans.modules.project.uiapi.CategoryView; +import org.netbeans.modules.project.uiapi.CategoryWrapper; import org.netbeans.modules.project.uiapi.CustomizerDialog; import org.netbeans.modules.project.uiapi.CustomizerPane; +import org.netbeans.modules.project.uiapi.Utilities; +import org.netbeans.spi.project.ui.support.ProjectCustomizer.Category; import org.openide.util.HelpCtx; /** Support for creating dialogs which can be used as project @@ -41,13 +44,16 @@ * 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 wnat the dialog to be + * 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. - * @param categories array of descriptions of categories to be shown in - * the dialog. + * @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. @@ -65,8 +71,8 @@ String preselectedCategory, ActionListener okOptionListener, HelpCtx helpCtx ) { - JPanel innerPane = createCustomizerPane( categories, componentProvider, preselectedCategory ); - Dialog dialog = CustomizerDialog.createDialog( okOptionListener, innerPane, helpCtx ); + CustomizerPane innerPane = (CustomizerPane) createCustomizerPane( categories, componentProvider, preselectedCategory ); + Dialog dialog = CustomizerDialog.createDialog( okOptionListener, innerPane, helpCtx, categories ); return dialog; } @@ -76,6 +82,11 @@ CategoryComponentProvider componentProvider, String preselectedCategory ) { + CategoryWrapper catWrappers[] = new CategoryWrapper[categories.length]; + for (int i = 0; i < categories.length; i++) { + Utilities.putCategoryWrapper(categories[i], new CategoryWrapper(categories[i])); + } + CategoryModel categoryModel = new CategoryModel( categories ); JPanel categoryView = new CategoryView( categoryModel ); JPanel customizerPane = new CustomizerPane( categoryView, categoryModel, componentProvider ); @@ -112,6 +123,8 @@ private String displayName; private Image icon; private Category[] subcategories; + private boolean valid; + private String errorMessage; /** Private constructor. See the factory method. */ @@ -124,6 +137,7 @@ this.displayName = displayName; this.icon = icon; this.subcategories = subcategories; + this.valid = true; // default } /** Factory method which creates new category description. @@ -170,6 +184,53 @@ public Category[] getSubcategories() { return this.subcategories; } + + /** + * Returns an error message for this category. + */ + public String getErrorMessage() { + return errorMessage; + } + + /** + * Returns whether this category is valid or not. See {@link + * ProjectCustomizer#createCustomizerDialog} for more details. + * @return whether this category is valid or not (true by default) + */ + public boolean isValid() { + return valid; + } + + /** + * Set a validity of this category. See {@link + * ProjectCustomizer#createCustomizerDialog} for more details. + * @param valid set whether this category is valid or not + */ + public void setValid(boolean valid) { + if (this.valid != valid) { + this.valid = valid; + Utilities.getCategoryWrapper(this).firePropertyChange( + CategoryWrapper.VALID_PROPERTY, Boolean.valueOf(!valid), Boolean.valueOf(valid)); +; + } + } + + /** + * Set an errror message for this category. It can than be shown in the + * customizer. + */ + public void setErrorMessage(String message) { + if (message == null) { + message = ""; + } + if (!message.equals(this.errorMessage)) { + String oldMessage = this.errorMessage; + this.errorMessage = message; + Utilities.getCategoryWrapper(this).firePropertyChange( + CategoryWrapper.ERROR_MESSAGE_PROPERTY, oldMessage, message); + } + } + } }