KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > spi > project > ui > support > ProjectCustomizer


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.spi.project.ui.support;
21
22 import java.awt.Dialog JavaDoc;
23 import java.awt.Image JavaDoc;
24 import java.awt.event.ActionListener JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.Collections JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.logging.Level JavaDoc;
32 import java.util.logging.Logger JavaDoc;
33 import javax.swing.JComponent JavaDoc;
34 import javax.swing.JPanel JavaDoc;
35
36 import org.netbeans.modules.project.uiapi.CategoryModel;
37 import org.netbeans.modules.project.uiapi.CategoryView;
38 import org.netbeans.modules.project.uiapi.CategoryChangeSupport;
39 import org.netbeans.modules.project.uiapi.CustomizerDialog;
40 import org.netbeans.modules.project.uiapi.CustomizerPane;
41 import org.netbeans.modules.project.uiapi.Utilities;
42 import org.netbeans.spi.project.ui.support.ProjectCustomizer.Category;
43 import org.openide.cookies.InstanceCookie;
44 import org.openide.filesystems.FileObject;
45 import org.openide.filesystems.FileStateInvalidException;
46 import org.openide.filesystems.Repository;
47 import org.openide.loaders.DataFolder;
48 import org.openide.loaders.DataObject;
49 import org.openide.util.HelpCtx;
50 import org.openide.util.Lookup;
51
52 /** Support for creating dialogs which can be used as project
53  * customizers. The dialog may display multiple panels or categories.
54  * @see org.netbeans.spi.project.ui.CustomizerProvider
55  * @see ProjectCustomizer.Category
56  *
57  * @author Petr Hrebejk, Martin Krauskopf
58  */

59 public final class ProjectCustomizer {
60     
61     /** Factory/Namespace class only. */
62     private ProjectCustomizer() {
63     }
64     
65     /** Creates standard which can be used for implementation
66      * of {@link org.netbeans.spi.project.ui.CustomizerProvider}. You don't need
67      * to call <code>pack()</code> method on the dialog. The resulting dialog will
68      * be non-modal. <br>
69      * Call <code>show()</code> on the dialog to make it visible. If you want the dialog to be
70      * closed after user presses the "OK" button you have to call hide() and dispose() on it.
71      * (Usually in the <code>actionPerformed(...)</code> method of the listener
72      * you provided as a parameter. In case of the click on the "Cancel" button
73      * the dialog will be closed automatically.
74      * @param categories array of descriptions of categories to be shown in the
75      * dialog. Note that categories have the <code>valid</code>
76      * property. If any of the given categories is not valid cusomizer's
77      * OK button will be disabled until all categories become valid
78      * again.
79      * @param componentProvider creator of GUI components for categories in the
80      * customizer dialog.
81      * @param preselectedCategory name of one of the supplied categories or null.
82      * Category with given name will be selected. If <code>null</code>
83      * or if the category of given name does not exist the first category will
84      * be selected.
85      * @param okOptionListener listener which will be notified when the user presses
86      * the OK button.
87      * @param helpCtx Help context for the dialog, which will be used when the
88      * panels in the customizer do not specify their own help context.
89      * @return standard project customizer dialog.
90      */

91     public static Dialog JavaDoc createCustomizerDialog( Category[] categories,
92                                                  CategoryComponentProvider componentProvider,
93                                                  String JavaDoc preselectedCategory,
94                                                  ActionListener JavaDoc okOptionListener,
95                                                  HelpCtx helpCtx ) {
96         CustomizerPane innerPane = createCustomizerPane(categories, componentProvider, preselectedCategory);
97         Dialog JavaDoc dialog = CustomizerDialog.createDialog( okOptionListener, innerPane, helpCtx, categories );
98         return dialog;
99     }
100
101     /**
102      * Creates standard customizer dialog that can be used for implementation of
103      * {@link org.netbeans.spi.project.ui.CustomizerProvider} based on content of a folder in Layers.
104      * Use this method when you want to allow composition and 3rd party additions to your customizer UI.
105      * You don't need to call <code>pack()</code> method on the dialog. The resulting dialog will
106      * be non-modal. <br>
107      * Call <code>show()</code> on the dialog to make it visible. If you want the dialog to be
108      * closed after user presses the "OK" button you have to call hide() and dispose() on it.
109      * (Usually in the <code>actionPerformed(...)</code> method of the listener
110      * you provided as a parameter. In case of the click on the "Cancel" button
111      * the dialog will be closed automatically.
112      * @since org.netbeans.modules.projectuiapi/1 1.15
113      * @param folderPath the path in the System Filesystem that is used as root for panel composition.
114      * The content of the folder is assummed to be {@link org.netbeans.spi.project.ui.support.ProjectCustomizer.CompositeCategoryProvider} instances
115      * @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
116      * @param preselectedCategory name of one of the supplied categories or null.
117      * Category with given name will be selected. If <code>null</code>
118      * or if the category of given name does not exist the first category will
119      * be selected.
120      * @param okOptionListener listener which will be notified when the user presses
121      * the OK button.
122      * @param helpCtx Help context for the dialog, which will be used when the
123      * panels in the customizer do not specify their own help context.
124      * @return standard project customizer dialog.
125      */

126     public static Dialog JavaDoc createCustomizerDialog( String JavaDoc folderPath,
127                                                  Lookup context,
128                                                  String JavaDoc preselectedCategory,
129                                                  ActionListener JavaDoc okOptionListener,
130                                                  HelpCtx helpCtx) {
131         FileObject root = Repository.getDefault().getDefaultFileSystem().findResource(folderPath);
132         if (root == null) {
133             throw new IllegalArgumentException JavaDoc("The designated path " + folderPath + " doesn't exist. Cannot create customizer.");
134         }
135         DataFolder def = DataFolder.findFolder(root);
136         assert def != null : "Cannot find DataFolder for " + folderPath;
137         DelegateCategoryProvider prov = new DelegateCategoryProvider(def, context);
138         return createCustomizerDialog(prov.getSubCategories(),
139                                       prov,
140                                       preselectedCategory, okOptionListener, helpCtx);
141
142     }
143     
144     /** Creates standard innerPane for customizer dialog.
145      */

146     private static CustomizerPane createCustomizerPane( Category[] categories,
147                                                 CategoryComponentProvider componentProvider,
148                                                 String JavaDoc preselectedCategory ) {
149         
150         CategoryChangeSupport changeSupport = new CategoryChangeSupport();
151         registerCategoryChangeSupport(changeSupport, categories);
152         
153         CategoryModel categoryModel = new CategoryModel( categories );
154         JPanel JavaDoc categoryView = new CategoryView( categoryModel );
155         CustomizerPane customizerPane = new CustomizerPane( categoryView, categoryModel, componentProvider );
156         
157         if ( preselectedCategory == null ) {
158             preselectedCategory = categories[0].getName();
159         }
160         
161         Category c = categoryModel.getCategory( preselectedCategory );
162         if ( c != null ) {
163             categoryModel.setCurrentCategory( c );
164         }
165         
166         return customizerPane;
167     }
168
169     private static void registerCategoryChangeSupport(final CategoryChangeSupport changeSupport,
170             final Category[] categories) {
171         for (int i = 0; i < categories.length; i++) {
172             Utilities.putCategoryChangeSupport(categories[i], changeSupport);
173             Category[] subCategories = categories[i].getSubcategories();
174             if (subCategories != null) {
175                 registerCategoryChangeSupport(changeSupport, subCategories);
176             }
177         }
178     }
179
180     
181     /** Provides components for categories.
182      */

183     public static interface CategoryComponentProvider {
184         
185         /** Creates component which has to be shown for given category.
186          * @param category The Category
187          * @return UI component for category customization
188          */

189         JComponent JavaDoc create( Category category );
190         
191     }
192
193     /**
194      * Interface for creation of Customizer categories and their respective UI panels.
195      * Implementations are to be registered in System FileSystem via module layers. Used by the
196      * {@link org.netbeans.spi.project.ui.support.ProjectCustomizer#createCustomizerDialog(String,Lookup,String,ActionListener,HelpCtx)}
197      * The panel/category created by the provider can get notified that the customizer got
198      * closed by setting an <code>ActionListener</code> to
199      * {@link org.netbeans.spi.project.ui.support.ProjectCustomizer.Category#setOkButtonListener} .
200      * UI Component can be defined for category folder that is represented as node with subnodes in the category
201      * tree of project customizer. Name of the file that defines the instance class in layer for such category
202      * must be named "Self". Such CompositeCategory won't have the createCategory() method called, but will have the category created by
203      * the infrastructure based on the folder content.
204      * For details and usage see issue #91276.
205      * @since org.netbeans.modules.projectuiapi/1 1.22
206      */

207     public static interface CompositeCategoryProvider {
208
209         /**
210          * create the Category instance for the given project customizer context.
211          * @param context Lookup instance passed from project The content is up to the project type, please consult documentation
212          * for the project type you want to integrate your panel into.
213          * @return A category instance, can be null, in which case no category and no panels are created for given context.
214          * The instance is expected to have no subcategories.
215          */

216         Category createCategory( Lookup context );
217
218         /**
219          * create the UI component for given category and context.
220          * The panel/category created by the provider can get notified that the customizer got
221          * closed by setting an <code>ActionListener</code> to
222          * {@link org.netbeans.spi.project.ui.support.ProjectCustomizer.Category#setOkButtonListener}.
223          * @param category Category instance that was created in the createCategory method.
224          * @param context Lookup instance passed from project The content is up to the project type, please consult documentation
225          * for the project type you want to integrate your panel into.
226          */

227         JComponent JavaDoc createComponent (Category category, Lookup context );
228     }
229     
230     /** Describes category of properties to be customized by given component
231      */

232     public static final class Category {
233         
234         private String JavaDoc name;
235         private String JavaDoc displayName;
236         private Image JavaDoc icon;
237         private Category[] subcategories;
238         private boolean valid;
239         private String JavaDoc errorMessage;
240         private ActionListener JavaDoc okListener;
241         
242         /** Private constructor. See the factory method.
243          */

244         private Category( String JavaDoc name,
245                          String JavaDoc displayName,
246                          Image JavaDoc icon,
247                          Category[] subcategories ) {
248             
249             this.name = name;
250             this.displayName = displayName;
251             this.icon = icon;
252             this.subcategories = subcategories;
253             this.valid = true; // default
254
}
255         
256         /** Factory method which creates new category description.
257          * @param name Programmatic name of the category
258          * @param displayName Name to be shown to the user
259          * @param icon Icon for given category. Will use default icon if null.
260          * @param subcategories Subcategories to be shown under given category.
261          * Category won't be expandable if null or empty array.
262          * @return a new category description
263          */

264         public static Category create( String JavaDoc name,
265                                        String JavaDoc displayName,
266                                        Image JavaDoc icon,
267                                        Category... subcategories ) {
268             return new Category( name, displayName, icon, subcategories );
269         }
270         
271         // Public methods ------------------------------------------------------
272

273         /** Gets programmatic name of given category.
274          * @return Programmatic name of the category
275          */

276         public String JavaDoc getName() {
277             return this.name;
278         }
279         
280         /** Gets display name of given category.
281          * @return Display name of the category
282          */

283         public String JavaDoc getDisplayName() {
284             return this.displayName;
285         }
286         
287         /** Gets icon of given category.
288          * @return Icon name of the category or null
289          */

290         public Image JavaDoc getIcon() {
291             return this.icon;
292         }
293         
294         /** Gets subcategories of given category.
295          * @return Subcategories of the category or null
296          */

297         public Category[] getSubcategories() {
298             return this.subcategories;
299         }
300         
301         /**
302          * Returns an error message for this category.
303          * @return the error message (could be null)
304          */

305         public String JavaDoc getErrorMessage() {
306             return errorMessage;
307         }
308         
309         /**
310          * Returns whether this category is valid or not. See {@link
311          * ProjectCustomizer#createCustomizerDialog} for more details.
312          * @return whether this category is valid or not (true by default)
313          */

314         public boolean isValid() {
315             return valid;
316         }
317         
318         /**
319          * Set a validity of this category. See {@link
320          * ProjectCustomizer#createCustomizerDialog} for more details.
321          * @param valid set whether this category is valid or not
322          */

323         public void setValid(boolean valid) {
324             if (this.valid != valid) {
325                 this.valid = valid;
326                 Utilities.getCategoryChangeSupport(this).firePropertyChange(
327                         CategoryChangeSupport.VALID_PROPERTY, !valid, valid);
328             }
329         }
330         
331         /**
332          * Set an errror message for this category which than may be shown in a
333          * project customizer.
334          *
335          * @param message message for this category. To <em>reset</em> a
336          * message usually <code>null</code> or an empty string is
337          * passed. (similar to behaviour of {@link
338          * javax.swing.text.JTextComponent#setText(String)})
339          */

340         public void setErrorMessage(String JavaDoc message) {
341             if (message == null) {
342                 message = "";
343             }
344             if (!message.equals(this.errorMessage)) {
345                 String JavaDoc oldMessage = this.errorMessage;
346                 this.errorMessage = message;
347                 Utilities.getCategoryChangeSupport(this).firePropertyChange(
348                         CategoryChangeSupport.ERROR_MESSAGE_PROPERTY, oldMessage, message);
349             }
350         }
351         
352         /**
353          * Set the action listener that will get notified when the changes in the customizer
354          * are to be applied.
355          * @param okButtonListener ActionListener to notify
356          * @since org.netbeans.modules.projectuiapi/1 1.20
357          */

358         public void setOkButtonListener(ActionListener JavaDoc okButtonListener) {
359             okListener = okButtonListener;
360         }
361         
362         /**
363          * Returns the action listener associated with this category that gets notified
364          * when OK button is pressed on the customizer.
365          * @return instance of ActionListener or null if not set.
366          * @since org.netbeans.modules.projectuiapi/1 1.20
367          */

368         public ActionListener JavaDoc getOkButtonListener() {
369             return okListener;
370         }
371         
372     }
373
374     /*private*/ static class DelegateCategoryProvider implements CategoryComponentProvider, CompositeCategoryProvider {
375
376         private final Lookup context;
377         private final Map JavaDoc<ProjectCustomizer.Category,CompositeCategoryProvider> category2provider;
378         private final DataFolder folder;
379         private final CompositeCategoryProvider selfProvider;
380
381         public DelegateCategoryProvider(DataFolder folder, Lookup context) {
382             this(folder, context, new HashMap JavaDoc<ProjectCustomizer.Category,CompositeCategoryProvider>());
383         }
384
385         private DelegateCategoryProvider(DataFolder folder, Lookup context, Map JavaDoc<ProjectCustomizer.Category,CompositeCategoryProvider> cat2Provider) {
386             this(folder, context, cat2Provider, null);
387         }
388         
389         private DelegateCategoryProvider(DataFolder folder, Lookup context, Map JavaDoc<ProjectCustomizer.Category,CompositeCategoryProvider> cat2Provider, CompositeCategoryProvider sProv) {
390             this.context = context;
391             this.folder = folder;
392             category2provider = cat2Provider;
393             selfProvider = sProv;
394         }
395
396         public JComponent JavaDoc create(ProjectCustomizer.Category category) {
397             CompositeCategoryProvider prov = category2provider.get(category);
398             assert prov != null : "Category doesn't have a provider associated.";
399             return prov.createComponent(category, context);
400         }
401
402         public ProjectCustomizer.Category[] getSubCategories() {
403             try {
404                return readCategories(folder);
405             } catch (IOException JavaDoc exc) {
406                 Logger.getAnonymousLogger().log(Level.WARNING, "Cannot construct Project UI panels", exc);
407                 return new ProjectCustomizer.Category[0];
408             } catch (ClassNotFoundException JavaDoc ex) {
409                 Logger.getAnonymousLogger().log(Level.WARNING, "Cannot construct Project UI panels", ex);
410                 return new ProjectCustomizer.Category[0];
411             }
412         }
413
414
415         /*private*/ ProjectCustomizer.Category[] readCategories(DataFolder folder) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
416             List JavaDoc<ProjectCustomizer.Category> toRet = new ArrayList JavaDoc<ProjectCustomizer.Category>();
417             for (DataObject dob : folder.getChildren()) {
418                 if (dob instanceof DataFolder) {
419                     CompositeCategoryProvider sProvider = null;
420                     DataObject subDobs[] = ((DataFolder) dob).getChildren();
421                     for (DataObject subDob : subDobs) {
422                         if (subDob.getName().equals("Self")) { // NOI18N
423
InstanceCookie cookie = subDob.getCookie(InstanceCookie.class);
424                             if (cookie != null && CompositeCategoryProvider.class.isAssignableFrom(cookie.instanceClass())) {
425                                 sProvider = (CompositeCategoryProvider) cookie.instanceCreate();
426                             }
427                         }
428                     }
429                     CompositeCategoryProvider prov = null;
430                     if (sProvider != null) {
431                         prov = new DelegateCategoryProvider((DataFolder) dob, context, category2provider, sProvider);
432                     } else {
433                         prov = new DelegateCategoryProvider((DataFolder) dob, context, category2provider);
434                     }
435                     ProjectCustomizer.Category cat = prov.createCategory(context);
436                     toRet.add(cat);
437                     category2provider.put(cat, prov);
438                 }
439                 if (!dob.getName().equals("Self")) { // NOI18N
440
InstanceCookie cook = dob.getCookie(InstanceCookie.class);
441                     if (cook != null && CompositeCategoryProvider.class.isAssignableFrom(cook.instanceClass())) {
442                         CompositeCategoryProvider provider = (CompositeCategoryProvider)cook.instanceCreate();
443                         ProjectCustomizer.Category cat = provider.createCategory(context);
444                         if (cat != null) {
445                             toRet.add(cat);
446                             category2provider.put(cat, provider);
447                             includeSubcats(cat.getSubcategories(), provider);
448                         }
449                     }
450                 }
451             }
452             return toRet.toArray(new ProjectCustomizer.Category[toRet.size()]);
453         }
454         
455         private void includeSubcats(ProjectCustomizer.Category[] cats, ProjectCustomizer.CompositeCategoryProvider provider) {
456             if (cats != null) {
457                 for (ProjectCustomizer.Category cat : cats) {
458                     category2provider.put(cat, provider);
459                     includeSubcats(cat.getSubcategories(), provider);
460                 }
461             }
462         }
463
464         /**
465          * provides category for folder..
466          */

467         public ProjectCustomizer.Category createCategory(Lookup context) {
468             FileObject fo = folder.getPrimaryFile();
469             String JavaDoc dn = fo.getNameExt();
470             try {
471                 dn = fo.getFileSystem().getStatus().annotateName(fo.getNameExt(), Collections.singleton(fo));
472             } catch (FileStateInvalidException ex) {
473                 Logger.getAnonymousLogger().log(Level.WARNING, "Cannot retrieve display name for folder " + fo.getPath(), ex);
474             }
475             return ProjectCustomizer.Category.create(folder.getName(), dn, null, getSubCategories());
476         }
477
478         /**
479          * provides component for folder category
480          */

481         public JComponent JavaDoc createComponent(ProjectCustomizer.Category category, Lookup context) {
482             if (selfProvider != null) {
483                 return selfProvider.createComponent(category, context);
484             }
485             return new JPanel JavaDoc();
486         }
487     }
488 }
489
Popular Tags