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.modules.project.ui;
043:
044: import java.awt.*;
045: import java.awt.event.ActionEvent;
046: import java.awt.event.ActionListener;
047: import java.beans.PropertyChangeEvent;
048: import java.beans.PropertyChangeListener;
049: import java.util.ArrayList;
050: import java.util.Arrays;
051: import java.util.Collections;
052: import java.util.List;
053: import javax.swing.DefaultComboBoxModel;
054: import javax.swing.JLabel;
055: import javax.swing.JList;
056: import javax.swing.ListCellRenderer;
057: import javax.swing.event.ChangeListener;
058: import javax.swing.plaf.UIResource;
059: import org.netbeans.api.project.Project;
060: import org.netbeans.api.project.ProjectUtils;
061: import org.netbeans.api.project.ProjectInformation;
062: import org.openide.filesystems.FileObject;
063: import org.openide.filesystems.Repository;
064: import org.openide.loaders.DataFolder;
065: import org.openide.loaders.DataObject;
066: import org.openide.loaders.DataShadow;
067: import org.openide.nodes.Children;
068: import org.openide.nodes.FilterNode;
069: import org.openide.nodes.Node;
070: import org.openide.util.AsyncGUIJob;
071: import org.openide.util.ChangeSupport;
072: import org.openide.util.NbBundle;
073: import org.openide.util.Utilities;
074:
075: /** If you are looking for the non-GUI part of the panel please look
076: * into new file wizard
077: */
078:
079: /**
080: * Provides the GUI for the template chooser panel.
081: * @author Jesse Glick
082: */
083: final class TemplateChooserPanelGUI extends javax.swing.JPanel
084: implements PropertyChangeListener, AsyncGUIJob {
085:
086: /** prefered dimmension of the panels */
087: private static final java.awt.Dimension PREF_DIM = new java.awt.Dimension(
088: 500, 340);
089:
090: // private final String[] recommendedTypes = null;
091: private final ChangeSupport changeSupport = new ChangeSupport(this );
092:
093: //Templates folder root
094: private FileObject templatesFolder;
095:
096: //GUI Builder
097: private TemplatesPanelGUI.Builder builder;
098: private Project project;
099: private String category;
100: private String template;
101: private boolean isWarmUp = true;
102: private ListCellRenderer projectCellRenderer;
103: private boolean firstTime = true;
104:
105: public TemplateChooserPanelGUI() {
106: this .builder = new FileChooserBuilder();
107: initComponents();
108: setPreferredSize(PREF_DIM);
109: setName(org.openide.util.NbBundle.getMessage(
110: TemplateChooserPanelGUI.class,
111: "LBL_TemplateChooserPanelGUI_Name")); // NOI18N
112: projectCellRenderer = new ProjectCellRenderer();
113: projectsComboBox.setRenderer(projectCellRenderer);
114: }
115:
116: public void readValues(Project p, String category, String template) {
117: assert p != null : "Project can not be null"; //NOI18N
118: boolean wf;
119: synchronized (this ) {
120: this .project = p;
121: this .category = category;
122: this .template = template;
123: wf = this .isWarmUp;
124: }
125: if (!wf) {
126: this .selectProject(project);
127: ((TemplatesPanelGUI) this .templatesPanel)
128: .setSelectedCategoryByName(this .category);
129: ((TemplatesPanelGUI) this .templatesPanel)
130: .setSelectedTemplateByName(this .template);
131: }
132: }
133:
134: @Override
135: public void removeNotify() {
136: super .removeNotify();
137: project = null;
138: }
139:
140: /** Called from readSettings, to initialize the GUI with proper components
141: */
142: private void initValues(Project p) {
143: // Populate the combo box with list of projects
144: Project openProjects[] = OpenProjectList.getDefault()
145: .getOpenProjects();
146: Arrays.sort(openProjects,
147: OpenProjectList.PROJECT_BY_DISPLAYNAME);
148: DefaultComboBoxModel projectsModel = new DefaultComboBoxModel(
149: openProjects);
150: projectsComboBox.setModel(projectsModel);
151: this .selectProject(p);
152: }
153:
154: private void selectProject(Project p) {
155: if (p != null) {
156: DefaultComboBoxModel projectsModel = (DefaultComboBoxModel) projectsComboBox
157: .getModel();
158: if (projectsModel.getIndexOf(p) == -1) {
159: projectsModel.insertElementAt(p, 0);
160: }
161: projectsComboBox.setSelectedItem(p);
162: }
163: }
164:
165: public void addChangeListener(ChangeListener l) {
166: changeSupport.addChangeListener(l);
167: }
168:
169: public void removeChangeListener(ChangeListener l) {
170: changeSupport.removeChangeListener(l);
171: }
172:
173: private void fireChange() {
174: changeSupport.fireChange();
175: }
176:
177: public Project getProject() {
178: boolean wf;
179: synchronized (this ) {
180: wf = isWarmUp;
181: }
182: if (wf) {
183: return this .project;
184: } else {
185: return (Project) projectsComboBox.getSelectedItem();
186: }
187: }
188:
189: public FileObject getTemplate() {
190: return ((TemplatesPanelGUI) this .templatesPanel)
191: .getSelectedTemplate();
192: }
193:
194: public void propertyChange(PropertyChangeEvent evt) {
195: fireChange();
196: }
197:
198: public java.awt.Dimension getPreferredSize() {
199: return PREF_DIM;
200: }
201:
202: public String getCategoryName() {
203: return ((TemplatesPanelGUI) this .templatesPanel)
204: .getSelectedCategoryName();
205: }
206:
207: public String getTemplateName() {
208: return ((TemplatesPanelGUI) this .templatesPanel)
209: .getSelectedTemplateName();
210: }
211:
212: public void setCategory(String category) {
213: ((TemplatesPanelGUI) this .templatesPanel)
214: .setSelectedCategoryByName(category);
215: }
216:
217: public void addNotify() {
218: if (firstTime) {
219: //77244 prevent multiple initializations..
220: Utilities.attachInitJob(this , this );
221: firstTime = false;
222: }
223: super .addNotify();
224: }
225:
226: /** This method is called from within the constructor to
227: * initialize the form.
228: * WARNING: Do NOT modify this code. The content of this method is
229: * always regenerated by the Form Editor.
230: */
231: // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
232: private void initComponents() {
233: java.awt.GridBagConstraints gridBagConstraints;
234:
235: jLabel1 = new javax.swing.JLabel();
236: projectsComboBox = new javax.swing.JComboBox();
237: templatesPanel = new TemplatesPanelGUI(this .builder);
238:
239: setLayout(new java.awt.GridBagLayout());
240:
241: jLabel1.setLabelFor(projectsComboBox);
242: org.openide.awt.Mnemonics.setLocalizedText(jLabel1,
243: org.openide.util.NbBundle.getMessage(
244: TemplateChooserPanelGUI.class,
245: "LBL_TemplateChooserPanelGUI_jLabel1")); // NOI18N
246: gridBagConstraints = new java.awt.GridBagConstraints();
247: gridBagConstraints.insets = new java.awt.Insets(0, 0, 13, 0);
248: add(jLabel1, gridBagConstraints);
249: jLabel1.getAccessibleContext().setAccessibleName(
250: org.openide.util.NbBundle.getMessage(
251: TemplateChooserPanelGUI.class, "ACSN_jLabel1")); // NOI18N
252: jLabel1.getAccessibleContext().setAccessibleDescription(
253: org.openide.util.NbBundle.getMessage(
254: TemplateChooserPanelGUI.class, "ACSD_jLabel1")); // NOI18N
255:
256: gridBagConstraints = new java.awt.GridBagConstraints();
257: gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
258: gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
259: gridBagConstraints.weightx = 1.0;
260: gridBagConstraints.insets = new java.awt.Insets(0, 6, 12, 0);
261: add(projectsComboBox, gridBagConstraints);
262: gridBagConstraints = new java.awt.GridBagConstraints();
263: gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
264: gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
265: gridBagConstraints.weightx = 1.0;
266: gridBagConstraints.weighty = 1.0;
267: add(templatesPanel, gridBagConstraints);
268: }// </editor-fold>//GEN-END:initComponents
269:
270: // Variables declaration - do not modify//GEN-BEGIN:variables
271: private javax.swing.JLabel jLabel1;
272: private javax.swing.JComboBox projectsComboBox;
273: private javax.swing.JPanel templatesPanel;
274:
275: // End of variables declaration//GEN-END:variables
276:
277: // private static final Comparator NATURAL_NAME_SORT = Collator.getInstance();
278:
279: private final class TemplateChildren extends
280: Children.Keys<DataFolder> implements ActionListener {
281:
282: private final DataFolder folder;
283:
284: TemplateChildren(DataFolder folder) {
285: this .folder = folder;
286: }
287:
288: protected void addNotify() {
289: super .addNotify();
290: projectsComboBox.addActionListener(this );
291: updateKeys();
292: }
293:
294: protected void removeNotify() {
295: setKeys(Collections.<DataFolder> emptySet());
296: projectsComboBox.removeActionListener(this );
297: super .removeNotify();
298: }
299:
300: private void updateKeys() {
301: List<DataFolder> l = new ArrayList<DataFolder>();
302: for (DataObject d : folder.getChildren()) {
303: FileObject prim = d.getPrimaryFile();
304: if (acceptTemplate(d, prim)) {
305: // has children?
306: if (hasChildren((Project) projectsComboBox
307: .getSelectedItem(), d)) {
308: l.add((DataFolder) d);
309: }
310: }
311: }
312: setKeys(l);
313: }
314:
315: protected Node[] createNodes(DataFolder d) {
316: boolean haveChildren = false;
317: for (DataObject child : d.getChildren()) {
318: if ((child instanceof DataFolder) && !isTemplate(child)) {
319: haveChildren = true;
320: break;
321: }
322: }
323: if (!haveChildren) {
324: return new Node[] { new FilterNode(d.getNodeDelegate(),
325: Children.LEAF) };
326: } else {
327: return new Node[] { new FilterNode(d.getNodeDelegate(),
328: new TemplateChildren(d)) };
329: }
330: }
331:
332: public void actionPerformed(ActionEvent event) {
333: final String cat = getCategoryName();
334: String template = ((TemplatesPanelGUI) TemplateChooserPanelGUI.this .templatesPanel)
335: .getSelectedTemplateName();
336: this .setKeys(Collections.<DataFolder> emptySet());
337: this .updateKeys();
338: setCategory(cat);
339: ((TemplatesPanelGUI) TemplateChooserPanelGUI.this .templatesPanel)
340: .setSelectedTemplateByName(template);
341: }
342:
343: /** Uncoment if you want to have the templates sorted alphabeticaly
344:
345: // Comparator ----------------------------------------------------------
346:
347: public int compare(Object o1, Object o2) {
348: DataObject d1 = (DataObject)o1;
349: DataObject d2 = (DataObject)o2;
350: if ((d1 instanceof DataFolder) && !(d2 instanceof DataFolder)) {
351: return 1;
352: } else if (!(d1 instanceof DataFolder) && (d2 instanceof DataFolder)) {
353: return -1;
354: } else {
355: return NATURAL_NAME_SORT.compare(d1.getNodeDelegate().getDisplayName(), d2.getNodeDelegate().getDisplayName());
356: }
357: }
358: */
359:
360: // Private methods -----------------------------------------------------
361: private boolean acceptTemplate(DataObject d,
362: FileObject primaryFile) {
363: if (d instanceof DataFolder && !isTemplate((DataFolder) d)) {
364: Object o = primaryFile.getAttribute("simple"); // NOI18N
365: return o == null || Boolean.TRUE.equals(o);
366: }
367: return false;
368: }
369:
370: }
371:
372: private final class FileChildren extends Children.Keys<DataObject> {
373:
374: private DataFolder root;
375:
376: public FileChildren(DataFolder folder) {
377: this .root = folder;
378: assert this .root != null : "Root can not be null"; //NOI18N
379: }
380:
381: protected void addNotify() {
382: this .setKeys(this .root.getChildren());
383: }
384:
385: protected void removeNotify() {
386: this .setKeys(new DataObject[0]);
387: }
388:
389: protected Node[] createNodes(DataObject dobj) {
390: if (isTemplate(dobj)
391: && OpenProjectList.isRecommended(getProject(), dobj
392: .getPrimaryFile())) {
393: if (dobj instanceof DataShadow) {
394: dobj = ((DataShadow) dobj).getOriginal();
395: }
396: return new Node[] { new FilterNode(dobj
397: .getNodeDelegate(), Children.LEAF) };
398: } else {
399: return new Node[0];
400: }
401: }
402:
403: }
404:
405: private final class FileChooserBuilder implements
406: TemplatesPanelGUI.Builder {
407:
408: public Children createCategoriesChildren(DataFolder folder) {
409: return new TemplateChildren(folder);
410: }
411:
412: public Children createTemplatesChildren(DataFolder folder) {
413: return new FileChildren(folder);
414: }
415:
416: public void fireChange() {
417: TemplateChooserPanelGUI.this .fireChange();
418: }
419:
420: public String getCategoriesName() {
421: return NbBundle.getMessage(TemplateChooserPanelGUI.class,
422: "CTL_Categories");
423: }
424:
425: public String getTemplatesName() {
426: return NbBundle.getMessage(TemplateChooserPanelGUI.class,
427: "CTL_Files");
428: }
429:
430: }
431:
432: // #89393: GTK needs cell renderer to implement UIResource to look "natively"
433: private static class ProjectCellRenderer extends JLabel implements
434: ListCellRenderer, UIResource {
435:
436: public ProjectCellRenderer() {
437: setOpaque(true);
438: }
439:
440: public Component getListCellRendererComponent(JList list,
441: Object value, int index, boolean isSelected,
442: boolean cellHasFocus) {
443:
444: // #89393: GTK needs name to render cell renderer "natively"
445: setName("ComboBox.listRenderer"); // NOI18N
446:
447: if (value instanceof Project) {
448: ProjectInformation pi = ProjectUtils
449: .getInformation((Project) value);
450: setText(pi.getDisplayName());
451: setIcon(pi.getIcon());
452: } else {
453: setText(value == null ? "" : value.toString()); // NOI18N
454: setIcon(null);
455: }
456: if (isSelected) {
457: setBackground(list.getSelectionBackground());
458: setForeground(list.getSelectionForeground());
459: } else {
460: setBackground(list.getBackground());
461: setForeground(list.getForeground());
462: }
463:
464: return this ;
465: }
466:
467: // #89393: GTK needs name to render cell renderer "natively"
468: public String getName() {
469: String name = super .getName();
470: return name == null ? "ComboBox.renderer" : name; // NOI18N
471: }
472:
473: }
474:
475: private static boolean isTemplate(DataObject dobj) {
476: if (dobj.isTemplate())
477: return true;
478: if (dobj instanceof DataShadow) {
479: return ((DataShadow) dobj).getOriginal().isTemplate();
480: }
481: return false;
482: }
483:
484: private boolean hasChildren(Project p, DataObject folder) {
485: if (!(folder instanceof DataFolder)) {
486: return false;
487: }
488:
489: DataFolder f = (DataFolder) folder;
490: if (!OpenProjectList.isRecommended(p, f.getPrimaryFile())) {
491: // Eg. Licenses folder.
492: //see #102508
493: return false;
494: }
495: DataObject[] ch = f.getChildren();
496: boolean ok = false;
497: for (int i = 0; i < ch.length; i++) {
498: if (isTemplate(ch[i])
499: && OpenProjectList.isRecommended(p, ch[i]
500: .getPrimaryFile())) {
501: // XXX: how to filter link to Package template in each java types folder?
502: if (!(ch[i] instanceof DataShadow)) {
503: ok = true;
504: break;
505: }
506: } else if (ch[i] instanceof DataFolder
507: && hasChildren(p, ch[i])) {
508: ok = true;
509: break;
510: }
511: }
512: return ok;
513:
514: // simplied but more counts
515: //return new FileChildren (p, (DataFolder) folder).getNodesCount () > 0;
516:
517: }
518:
519: public void construct() {
520: this .templatesFolder = Repository.getDefault()
521: .getDefaultFileSystem().findResource("Templates");
522: ((TemplatesPanelGUI) this .templatesPanel)
523: .warmUp(this .templatesFolder);
524: }
525:
526: public void finished() {
527: //In the awt
528: Cursor cursor = null;
529: try {
530: Project p;
531: String c, t;
532: synchronized (this ) {
533: p = this .project;
534: c = this .category;
535: t = this .template;
536: }
537: cursor = TemplateChooserPanelGUI.this .getCursor();
538: TemplateChooserPanelGUI.this .setCursor(Cursor
539: .getPredefinedCursor(Cursor.WAIT_CURSOR));
540: initValues(p);
541: ((TemplatesPanelGUI) this .templatesPanel).doFinished(
542: this .templatesFolder, c, t);
543: } finally {
544: synchronized (this ) {
545: isWarmUp = false;
546: }
547: if (cursor != null) {
548: this.setCursor(cursor);
549: }
550: }
551: }
552:
553: }
|