001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.spi.project.ui.support;
043:
044: import java.awt.Dialog;
045: import java.awt.Image;
046: import java.awt.event.ActionListener;
047: import java.io.IOException;
048: import java.util.ArrayList;
049: import java.util.Collections;
050: import java.util.HashMap;
051: import java.util.List;
052: import java.util.Map;
053: import java.util.logging.Level;
054: import java.util.logging.Logger;
055: import javax.swing.JComponent;
056: import javax.swing.JPanel;
057:
058: import org.netbeans.modules.project.uiapi.CategoryModel;
059: import org.netbeans.modules.project.uiapi.CategoryView;
060: import org.netbeans.modules.project.uiapi.CategoryChangeSupport;
061: import org.netbeans.modules.project.uiapi.CustomizerDialog;
062: import org.netbeans.modules.project.uiapi.CustomizerPane;
063: import org.netbeans.modules.project.uiapi.Utilities;
064: import org.netbeans.spi.project.ui.support.ProjectCustomizer.Category;
065: import org.openide.cookies.InstanceCookie;
066: import org.openide.filesystems.FileObject;
067: import org.openide.filesystems.FileStateInvalidException;
068: import org.openide.filesystems.Repository;
069: import org.openide.loaders.DataFolder;
070: import org.openide.loaders.DataObject;
071: import org.openide.util.HelpCtx;
072: import org.openide.util.Lookup;
073:
074: /** Support for creating dialogs which can be used as project
075: * customizers. The dialog may display multiple panels or categories.
076: * @see org.netbeans.spi.project.ui.CustomizerProvider
077: * @see ProjectCustomizer.Category
078: *
079: * @author Petr Hrebejk, Martin Krauskopf
080: */
081: public final class ProjectCustomizer {
082:
083: /** Factory/Namespace class only. */
084: private ProjectCustomizer() {
085: }
086:
087: /** Creates standard customizer dialog which can be used for implementation
088: * of {@link org.netbeans.spi.project.ui.CustomizerProvider}. You don't need
089: * to call <code>pack()</code> method on the dialog. The resulting dialog will
090: * be non-modal. <br>
091: * Call <code>show()</code> on the dialog to make it visible. The dialog
092: * will be closed automatically after click on "OK" or "Cancel" button.
093: *
094: * @param categories array of descriptions of categories to be shown in the
095: * dialog. Note that categories have the <code>valid</code>
096: * property. If any of the given categories is not valid cusomizer's
097: * OK button will be disabled until all categories become valid
098: * again.
099: * @param componentProvider creator of GUI components for categories in the
100: * customizer dialog.
101: * @param preselectedCategory name of one of the supplied categories or null.
102: * Category with given name will be selected. If <code>null</code>
103: * or if the category of given name does not exist the first category will
104: * be selected.
105: * @param okOptionListener listener which will be notified when the user presses
106: * the OK button.
107: * @param helpCtx Help context for the dialog, which will be used when the
108: * panels in the customizer do not specify their own help context.
109: * @return standard project customizer dialog.
110: */
111: public static Dialog createCustomizerDialog(Category[] categories,
112: CategoryComponentProvider componentProvider,
113: String preselectedCategory,
114: ActionListener okOptionListener, HelpCtx helpCtx) {
115: return createCustomizerDialog(categories, componentProvider,
116: preselectedCategory, okOptionListener, null, helpCtx);
117: }
118:
119: /** Creates standard customizer dialog which can be used for implementation
120: * of {@link org.netbeans.spi.project.ui.CustomizerProvider}. Use this version if you need
121: * to run processing of the customizer data partially off AWT Event Queue. You don't need
122: * to call <code>pack()</code> method on the dialog. The resulting dialog will
123: * be non-modal. <br>
124: * Call <code>show()</code> on the dialog to make it visible. If you want the dialog to be
125: * closed after user presses the "OK" button you have to call hide() and dispose() on it.
126: * (Usually in the <code>actionPerformed(...)</code> method of the listener
127: * you provided as a parameter. In case of the click on the "Cancel" button
128: * the dialog will be closed automatically.
129: * @since org.netbeans.modules.projectuiapi/1 1.26
130: * @param categories array of descriptions of categories to be shown in the
131: * dialog. Note that categories have the <code>valid</code>
132: * property. If any of the given categories is not valid cusomizer's
133: * OK button will be disabled until all categories become valid
134: * again.
135: * @param componentProvider creator of GUI components for categories in the
136: * customizer dialog.
137: * @param preselectedCategory name of one of the supplied categories or null.
138: * Category with given name will be selected. If <code>null</code>
139: * or if the category of given name does not exist the first category will
140: * be selected.
141: * @param okOptionListener listener which will be notified when the user presses
142: * the OK button.
143: * @param storeListener listener which will be notified when the user presses OK button.
144: * Listener will be executed after okOptionListener outside of AWT EventQueue.
145: * Usually to be used to save modified files on disk.
146: * @param helpCtx Help context for the dialog, which will be used when the
147: * panels in the customizer do not specify their own help context.
148: * @return standard project customizer dialog.
149: */
150: public static Dialog createCustomizerDialog(Category[] categories,
151: CategoryComponentProvider componentProvider,
152: String preselectedCategory,
153: ActionListener okOptionListener,
154: ActionListener storeListener, HelpCtx helpCtx) {
155: CustomizerPane innerPane = createCustomizerPane(categories,
156: componentProvider, preselectedCategory);
157: Dialog dialog = CustomizerDialog.createDialog(okOptionListener,
158: storeListener, innerPane, helpCtx, categories,
159: componentProvider);
160: return dialog;
161: }
162:
163: /**
164: * Creates standard customizer dialog that can be used for implementation of
165: * {@link org.netbeans.spi.project.ui.CustomizerProvider} based on content of a folder in Layers.
166: * Use this method when you want to allow composition and 3rd party additions to your customizer UI.
167: * You don't need to call <code>pack()</code> method on the dialog. The resulting dialog will
168: * be non-modal. <br>
169: * Call <code>show()</code> on the dialog to make it visible. The dialog
170: * will be closed automatically after click on "OK" or "Cancel" button.
171: *
172: * @since org.netbeans.modules.projectuiapi/1 1.15
173: * @param folderPath the path in the System Filesystem that is used as root for panel composition.
174: * The content of the folder is assummed to be {@link org.netbeans.spi.project.ui.support.ProjectCustomizer.CompositeCategoryProvider} instances
175: * @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
176: * @param preselectedCategory name of one of the supplied categories or null.
177: * Category with given name will be selected. If <code>null</code>
178: * or if the category of given name does not exist the first category will
179: * be selected.
180: * @param okOptionListener listener which will be notified when the user presses
181: * the OK button.
182: * @param helpCtx Help context for the dialog, which will be used when the
183: * panels in the customizer do not specify their own help context.
184: * @return standard project customizer dialog.
185: */
186: public static Dialog createCustomizerDialog(String folderPath,
187: Lookup context, String preselectedCategory,
188: ActionListener okOptionListener, HelpCtx helpCtx) {
189: return createCustomizerDialog(folderPath, context,
190: preselectedCategory, okOptionListener, null, helpCtx);
191: }
192:
193: /**
194: * Creates standard customizer dialog that can be used for implementation of
195: * {@link org.netbeans.spi.project.ui.CustomizerProvider} based on content of a folder in Layers.
196: * Use this method when you want to allow composition and 3rd party additions to your customizer UI.
197: * This version runs processing of the customizer data partially off AWT Event Queue.
198: * You don't need to call <code>pack()</code> method on the dialog. The resulting dialog will
199: * be non-modal. <br>
200: * Call <code>show()</code> on the dialog to make it visible. If you want the dialog to be
201: * closed after user presses the "OK" button you have to call hide() and dispose() on it.
202: * (Usually in the <code>actionPerformed(...)</code> method of the listener
203: * you provided as a parameter. In case of the click on the "Cancel" button
204: * the dialog will be closed automatically.
205: *
206: * @since org.netbeans.modules.projectuiapi/1 1.26
207: * @param folderPath the path in the System Filesystem that is used as root for panel composition.
208: * The content of the folder is assummed to be {@link org.netbeans.spi.project.ui.support.ProjectCustomizer.CompositeCategoryProvider} instances
209: * @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
210: * @param preselectedCategory name of one of the supplied categories or null.
211: * Category with given name will be selected. If <code>null</code>
212: * or if the category of given name does not exist the first category will
213: * be selected.
214: * @param okOptionListener listener which will be notified when the user presses
215: * the OK button.
216: * @param storeListener listener which will be notified when the user presses OK button.
217: * Listener will be executed after okOptionListener outside of AWT EventQueue.
218: * Usually to be used to save modified files on disk
219: * @param helpCtx Help context for the dialog, which will be used when the
220: * panels in the customizer do not specify their own help context.
221: * @return standard project customizer dialog.
222: */
223: public static Dialog createCustomizerDialog(String folderPath,
224: Lookup context, String preselectedCategory,
225: ActionListener okOptionListener,
226: ActionListener storeListener, HelpCtx helpCtx) {
227: FileObject root = Repository.getDefault()
228: .getDefaultFileSystem().findResource(folderPath);
229: if (root == null) {
230: throw new IllegalArgumentException("The designated path "
231: + folderPath
232: + " doesn't exist. Cannot create customizer.");
233: }
234: DataFolder def = DataFolder.findFolder(root);
235: assert def != null : "Cannot find DataFolder for " + folderPath;
236: DelegateCategoryProvider prov = new DelegateCategoryProvider(
237: def, context);
238: return createCustomizerDialog(prov.getSubCategories(), prov,
239: preselectedCategory, okOptionListener, storeListener,
240: helpCtx);
241: }
242:
243: /** Creates standard innerPane for customizer dialog.
244: */
245: private static CustomizerPane createCustomizerPane(
246: Category[] categories,
247: CategoryComponentProvider componentProvider,
248: String preselectedCategory) {
249:
250: CategoryChangeSupport changeSupport = new CategoryChangeSupport();
251: registerCategoryChangeSupport(changeSupport, categories);
252:
253: CategoryModel categoryModel = new CategoryModel(categories);
254: JPanel categoryView = new CategoryView(categoryModel);
255: CustomizerPane customizerPane = new CustomizerPane(
256: categoryView, categoryModel, componentProvider);
257:
258: if (preselectedCategory == null) {
259: preselectedCategory = categories[0].getName();
260: }
261:
262: Category c = categoryModel.getCategory(preselectedCategory);
263: if (c != null) {
264: categoryModel.setCurrentCategory(c);
265: }
266:
267: return customizerPane;
268: }
269:
270: private static void registerCategoryChangeSupport(
271: final CategoryChangeSupport changeSupport,
272: final Category[] categories) {
273: for (int i = 0; i < categories.length; i++) {
274: Utilities.putCategoryChangeSupport(categories[i],
275: changeSupport);
276: Category[] subCategories = categories[i].getSubcategories();
277: if (subCategories != null) {
278: registerCategoryChangeSupport(changeSupport,
279: subCategories);
280: }
281: }
282: }
283:
284: /** Provides components for categories.
285: */
286: public static interface CategoryComponentProvider {
287:
288: /** Creates component which has to be shown for given category.
289: * @param category The Category
290: * @return UI component for category customization
291: */
292: JComponent create(Category category);
293:
294: }
295:
296: /**
297: * Interface for creation of Customizer categories and their respective UI panels.
298: * Implementations are to be registered in System FileSystem via module layers. Used by the
299: * {@link org.netbeans.spi.project.ui.support.ProjectCustomizer#createCustomizerDialog(String,Lookup,String,ActionListener,HelpCtx)}
300: * The panel/category created by the provider can get notified that the customizer got
301: * closed by setting an <code>ActionListener</code> to
302: * {@link org.netbeans.spi.project.ui.support.ProjectCustomizer.Category#setOkButtonListener} .
303: * UI Component can be defined for category folder that is represented as node with subnodes in the category
304: * tree of project customizer. Name of the file that defines the instance class in layer for such category
305: * must be named "Self". Such CompositeCategory won't have the createCategory() method called, but will have the category created by
306: * the infrastructure based on the folder content.
307: * For details and usage see issue #91276.
308: * @since org.netbeans.modules.projectuiapi/1 1.22
309: */
310: public static interface CompositeCategoryProvider {
311:
312: /**
313: * create the Category instance for the given project customizer context.
314: * @param context Lookup instance passed from project The content is up to the project type, please consult documentation
315: * for the project type you want to integrate your panel into.
316: * @return A category instance, can be null, in which case no category and no panels are created for given context.
317: * The instance is expected to have no subcategories.
318: */
319: Category createCategory(Lookup context);
320:
321: /**
322: * create the UI component for given category and context.
323: * The panel/category created by the provider can get notified that the customizer got
324: * closed by setting an <code>ActionListener</code> to
325: * {@link org.netbeans.spi.project.ui.support.ProjectCustomizer.Category#setOkButtonListener}.
326: * @param category Category instance that was created in the createCategory method.
327: * @param context Lookup instance passed from project The content is up to the project type, please consult documentation
328: * for the project type you want to integrate your panel into.
329: */
330: JComponent createComponent(Category category, Lookup context);
331: }
332:
333: /** Describes category of properties to be customized by given component
334: */
335: public static final class Category {
336:
337: private String name;
338: private String displayName;
339: private Image icon;
340: private Category[] subcategories;
341: private boolean valid;
342: private String errorMessage;
343: private ActionListener okListener;
344: private ActionListener storeListener;
345:
346: /** Private constructor. See the factory method.
347: */
348: private Category(String name, String displayName, Image icon,
349: Category[] subcategories) {
350:
351: this .name = name;
352: this .displayName = displayName;
353: this .icon = icon;
354: this .subcategories = subcategories;
355: this .valid = true; // default
356: }
357:
358: /** Factory method which creates new category description.
359: * @param name Programmatic name of the category
360: * @param displayName Name to be shown to the user
361: * @param icon Icon for given category. Will use default icon if null.
362: * @param subcategories Subcategories to be shown under given category.
363: * Category won't be expandable if null or empty array.
364: * @return a new category description
365: */
366: public static Category create(String name, String displayName,
367: Image icon, Category... subcategories) {
368: return new Category(name, displayName, icon, subcategories);
369: }
370:
371: // Public methods ------------------------------------------------------
372:
373: /** Gets programmatic name of given category.
374: * @return Programmatic name of the category
375: */
376: public String getName() {
377: return this .name;
378: }
379:
380: /** Gets display name of given category.
381: * @return Display name of the category
382: */
383: public String getDisplayName() {
384: return this .displayName;
385: }
386:
387: /** Gets icon of given category.
388: * @return Icon name of the category or null
389: */
390: public Image getIcon() {
391: return this .icon;
392: }
393:
394: /** Gets subcategories of given category.
395: * @return Subcategories of the category or null
396: */
397: public Category[] getSubcategories() {
398: return this .subcategories;
399: }
400:
401: /**
402: * Returns an error message for this category.
403: * @return the error message (could be null)
404: */
405: public String getErrorMessage() {
406: return errorMessage;
407: }
408:
409: /**
410: * Returns whether this category is valid or not. See {@link
411: * ProjectCustomizer#createCustomizerDialog} for more details.
412: * @return whether this category is valid or not (true by default)
413: */
414: public boolean isValid() {
415: return valid;
416: }
417:
418: /**
419: * Set a validity of this category. See {@link
420: * ProjectCustomizer#createCustomizerDialog} for more details.
421: * @param valid set whether this category is valid or not
422: */
423: public void setValid(boolean valid) {
424: if (this .valid != valid) {
425: this .valid = valid;
426: Utilities.getCategoryChangeSupport(this )
427: .firePropertyChange(
428: CategoryChangeSupport.VALID_PROPERTY,
429: !valid, valid);
430: }
431: }
432:
433: /**
434: * Set an errror message for this category which than may be shown in a
435: * project customizer.
436: *
437: * @param message message for this category. To <em>reset</em> a
438: * message usually <code>null</code> or an empty string is
439: * passed. (similar to behaviour of {@link
440: * javax.swing.text.JTextComponent#setText(String)})
441: */
442: public void setErrorMessage(String message) {
443: if (message == null) {
444: message = "";
445: }
446: if (!message.equals(this .errorMessage)) {
447: String oldMessage = this .errorMessage;
448: this .errorMessage = message;
449: Utilities
450: .getCategoryChangeSupport(this )
451: .firePropertyChange(
452: CategoryChangeSupport.ERROR_MESSAGE_PROPERTY,
453: oldMessage, message);
454: }
455: }
456:
457: /**
458: * Set the action listener that will get notified when the changes in the customizer
459: * are to be applied.
460: * @param okButtonListener ActionListener to notify
461: * @since org.netbeans.modules.projectuiapi/1 1.20
462: */
463: public void setOkButtonListener(ActionListener okButtonListener) {
464: okListener = okButtonListener;
465: }
466:
467: /**
468: * Returns the action listener associated with this category that gets notified
469: * when OK button is pressed on the customizer.
470: * @return instance of ActionListener or null if not set.
471: * @since org.netbeans.modules.projectuiapi/1 1.20
472: */
473: public ActionListener getOkButtonListener() {
474: return okListener;
475: }
476:
477: /**
478: * Set the action listener that will get notified when the changes in the customizer
479: * are to be applied. Listener is executed after OkButtonListener outside of AWT EventQueue.
480: * Usually to be used to save modified files on disk.
481: * @param listener ActionListener to notify
482: * @since org.netbeans.modules.projectuiapi/1 1.25
483: */
484: public void setStoreListener(ActionListener listener) {
485: storeListener = listener;
486: }
487:
488: /**
489: * Returns the action listener that is executed outside of AWT EQ and is associated
490: * with this category that gets notified when OK button is pressed on the customizer.
491: * @return instance of ActionListener or null if not set.
492: * @since org.netbeans.modules.projectuiapi/1 1.25
493: */
494: public ActionListener getStoreListener() {
495: return storeListener;
496: }
497:
498: }
499:
500: /*private*/static class DelegateCategoryProvider implements
501: CategoryComponentProvider, CompositeCategoryProvider,
502: Lookup.Provider {
503:
504: private final Lookup context;
505: private final Map<ProjectCustomizer.Category, CompositeCategoryProvider> category2provider;
506: private final DataFolder folder;
507: private final CompositeCategoryProvider selfProvider;
508:
509: public DelegateCategoryProvider(DataFolder folder,
510: Lookup context) {
511: this (
512: folder,
513: context,
514: new HashMap<ProjectCustomizer.Category, CompositeCategoryProvider>());
515: }
516:
517: private DelegateCategoryProvider(
518: DataFolder folder,
519: Lookup context,
520: Map<ProjectCustomizer.Category, CompositeCategoryProvider> cat2Provider) {
521: this (folder, context, cat2Provider, null);
522: }
523:
524: private DelegateCategoryProvider(
525: DataFolder folder,
526: Lookup context,
527: Map<ProjectCustomizer.Category, CompositeCategoryProvider> cat2Provider,
528: CompositeCategoryProvider sProv) {
529: this .context = context;
530: this .folder = folder;
531: category2provider = cat2Provider;
532: selfProvider = sProv;
533: }
534:
535: public JComponent create(ProjectCustomizer.Category category) {
536: CompositeCategoryProvider prov = category2provider
537: .get(category);
538: assert prov != null : "Category doesn't have a provider associated.";
539: return prov.createComponent(category, context);
540: }
541:
542: public ProjectCustomizer.Category[] getSubCategories() {
543: try {
544: return readCategories(folder);
545: } catch (IOException exc) {
546: Logger.getAnonymousLogger().log(Level.WARNING,
547: "Cannot construct Project UI panels", exc);
548: return new ProjectCustomizer.Category[0];
549: } catch (ClassNotFoundException ex) {
550: Logger.getAnonymousLogger().log(Level.WARNING,
551: "Cannot construct Project UI panels", ex);
552: return new ProjectCustomizer.Category[0];
553: }
554: }
555:
556: /*private*/ProjectCustomizer.Category[] readCategories(
557: DataFolder folder) throws IOException,
558: ClassNotFoundException {
559: List<ProjectCustomizer.Category> toRet = new ArrayList<ProjectCustomizer.Category>();
560: for (DataObject dob : folder.getChildren()) {
561: if (dob instanceof DataFolder) {
562: CompositeCategoryProvider sProvider = null;
563: DataObject subDobs[] = ((DataFolder) dob)
564: .getChildren();
565: for (DataObject subDob : subDobs) {
566: if (subDob.getName().equals("Self")) { // NOI18N
567: InstanceCookie cookie = subDob
568: .getCookie(InstanceCookie.class);
569: if (cookie != null
570: && CompositeCategoryProvider.class
571: .isAssignableFrom(cookie
572: .instanceClass())) {
573: sProvider = (CompositeCategoryProvider) cookie
574: .instanceCreate();
575: }
576: }
577: }
578: CompositeCategoryProvider prov = null;
579: if (sProvider != null) {
580: prov = new DelegateCategoryProvider(
581: (DataFolder) dob, context,
582: category2provider, sProvider);
583: } else {
584: prov = new DelegateCategoryProvider(
585: (DataFolder) dob, context,
586: category2provider);
587: }
588: ProjectCustomizer.Category cat = prov
589: .createCategory(context);
590: toRet.add(cat);
591: category2provider.put(cat, prov);
592: }
593: if (!dob.getName().equals("Self")) { // NOI18N
594: InstanceCookie cook = dob
595: .getCookie(InstanceCookie.class);
596: if (cook != null
597: && CompositeCategoryProvider.class
598: .isAssignableFrom(cook
599: .instanceClass())) {
600: CompositeCategoryProvider provider = (CompositeCategoryProvider) cook
601: .instanceCreate();
602: ProjectCustomizer.Category cat = provider
603: .createCategory(context);
604: if (cat != null) {
605: toRet.add(cat);
606: category2provider.put(cat, provider);
607: includeSubcats(cat.getSubcategories(),
608: provider);
609: }
610: }
611: }
612: }
613: return toRet.toArray(new ProjectCustomizer.Category[toRet
614: .size()]);
615: }
616:
617: private void includeSubcats(ProjectCustomizer.Category[] cats,
618: ProjectCustomizer.CompositeCategoryProvider provider) {
619: if (cats != null) {
620: for (ProjectCustomizer.Category cat : cats) {
621: category2provider.put(cat, provider);
622: includeSubcats(cat.getSubcategories(), provider);
623: }
624: }
625: }
626:
627: /**
628: * provides category for folder..
629: */
630: public ProjectCustomizer.Category createCategory(Lookup context) {
631: FileObject fo = folder.getPrimaryFile();
632: String dn = fo.getNameExt();
633: try {
634: dn = fo.getFileSystem().getStatus().annotateName(
635: fo.getNameExt(), Collections.singleton(fo));
636: } catch (FileStateInvalidException ex) {
637: Logger.getAnonymousLogger().log(
638: Level.WARNING,
639: "Cannot retrieve display name for folder "
640: + fo.getPath(), ex);
641: }
642: return ProjectCustomizer.Category.create(folder.getName(),
643: dn, null, getSubCategories());
644: }
645:
646: /**
647: * provides component for folder category
648: */
649: public JComponent createComponent(
650: ProjectCustomizer.Category category, Lookup context) {
651: if (selfProvider != null) {
652: return selfProvider.createComponent(category, context);
653: }
654: return new JPanel();
655: }
656:
657: //#97998 related
658: public Lookup getLookup() {
659: return context;
660: }
661: }
662: }
|