blob: 5ea065863b29b8a15c5e37be9f204183014381ca [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.project.ui;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ListCellRenderer;
import javax.swing.event.ChangeListener;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.project.Project;
import static org.netbeans.modules.project.ui.Bundle.*;
import org.netbeans.modules.project.ui.spi.TemplateCategorySorter;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataShadow;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.FilterNode;
import org.openide.nodes.Node;
import org.openide.util.AsyncGUIJob;
import org.openide.util.ChangeSupport;
import org.openide.util.NbBundle.Messages;
import org.openide.util.Utilities;
/**
* Provides the GUI for the template chooser panel.
* @author Jesse Glick
*/
final class TemplateChooserPanelGUI extends javax.swing.JPanel implements PropertyChangeListener, AsyncGUIJob {
/** preferred dimension of the panels */
private static final Dimension PREF_DIM = new Dimension(500, 340);
// private final String[] recommendedTypes = null;
private final ChangeSupport changeSupport = new ChangeSupport(this);
//Templates folder root
private FileObject templatesFolder;
//GUI Builder
private TemplatesPanelGUI.Builder builder;
@NullAllowed
private Project project;
private @NonNull String[] projectRecommendedTypes;
private String category;
private String template;
private boolean isWarmUp = true;
private ListCellRenderer projectCellRenderer;
private boolean firstTime = true;
private ActionListener defaultActionListener;
private boolean includeTemplatesWithProjects;
@Messages("LBL_TemplateChooserPanelGUI_Name=Choose File Type")
public TemplateChooserPanelGUI(boolean includeTemplatesWithProject) {
this.builder = new FileChooserBuilder ();
this.includeTemplatesWithProjects = includeTemplatesWithProject;
initComponents();
setPreferredSize( PREF_DIM );
setName(LBL_TemplateChooserPanelGUI_Name());
projectCellRenderer = new ProjectCellRenderer ();
projectsComboBox.setRenderer (projectCellRenderer);
}
public void readValues (@NullAllowed Project p, String category, String template) {
boolean wf;
synchronized (this) {
this.project = p;
this.projectRecommendedTypes = OpenProjectList.getRecommendedTypes(p);
this.category = category;
this.template = template;
wf = this.isWarmUp;
}
if (!wf) {
this.selectProject ( project );
((TemplatesPanelGUI)this.templatesPanel).setSelectedCategoryByName (this.category);
((TemplatesPanelGUI)this.templatesPanel).setSelectedTemplateByName (this.template);
}
}
@Override
public void removeNotify() {
super.removeNotify();
project = null;
}
/** Called from readSettings, to initialize the GUI with proper components
*/
private void initValues( @NullAllowed Project p ) {
// Populate the combo box with list of projects
DefaultComboBoxModel projectsModel;
if (includeTemplatesWithProjects) {
Project openProjects[] = OpenProjectList.getDefault().getOpenProjects();
Arrays.sort(openProjects, OpenProjectList.projectByDisplayName());
projectsModel = new DefaultComboBoxModel( openProjects );
selectProject(p);
} else {
projectsModel = new DefaultComboBoxModel();
}
projectsComboBox.setModel( projectsModel );
projectsComboBox.setEnabled(includeTemplatesWithProjects);
this.selectProject (p);
}
private void selectProject (@NullAllowed Project p) {
if (p != null) {
DefaultComboBoxModel projectsModel = (DefaultComboBoxModel) projectsComboBox.getModel ();
if ( projectsModel.getIndexOf( p ) == -1 ) {
projectsModel.insertElementAt( p, 0 );
}
projectsComboBox.setSelectedItem( p );
}
else {
projectsComboBox.setSelectedItem(null);
}
}
public void addChangeListener(ChangeListener l) {
changeSupport.addChangeListener(l);
}
public void removeChangeListener(ChangeListener l) {
changeSupport.removeChangeListener(l);
}
private void fireChange() {
changeSupport.fireChange();
}
void setDefaultActionListener( ActionListener al ) {
this.defaultActionListener = al;
}
@CheckForNull
public Project getProject() {
boolean wf;
synchronized (this) {
wf = isWarmUp;
}
if (wf) {
return this.project;
}
else {
return (Project)projectsComboBox.getSelectedItem();
}
}
public FileObject getTemplate() {
return ((TemplatesPanelGUI)this.templatesPanel).getSelectedTemplate ();
}
@Override public void propertyChange(PropertyChangeEvent evt) {
fireChange();
}
@Override public Dimension getPreferredSize() {
return PREF_DIM;
}
public String getCategoryName () {
return ((TemplatesPanelGUI)this.templatesPanel).getSelectedCategoryName ();
}
public String getTemplateName () {
return ((TemplatesPanelGUI)this.templatesPanel).getSelectedTemplateName ();
}
public void setCategory (String category) {
((TemplatesPanelGUI)this.templatesPanel).setSelectedCategoryByName (category);
}
@Override public void addNotify () {
if (firstTime) {
//77244 prevent multiple initializations..
Utilities.attachInitJob (this, this);
firstTime = false;
}
super.addNotify ();
}
/** 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.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
jLabel1 = new javax.swing.JLabel();
projectsComboBox = new javax.swing.JComboBox();
templatesPanel = new TemplatesPanelGUI (this.builder);
setLayout(new java.awt.GridBagLayout());
jLabel1.setLabelFor(projectsComboBox);
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(TemplateChooserPanelGUI.class, "LBL_TemplateChooserPanelGUI_jLabel1")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.insets = new java.awt.Insets(0, 0, 13, 0);
add(jLabel1, gridBagConstraints);
jLabel1.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(TemplateChooserPanelGUI.class, "ACSN_jLabel1")); // NOI18N
jLabel1.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(TemplateChooserPanelGUI.class, "ACSD_jLabel1")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 6, 12, 0);
add(projectsComboBox, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(templatesPanel, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel jLabel1;
private javax.swing.JComboBox projectsComboBox;
private javax.swing.JPanel templatesPanel;
// End of variables declaration//GEN-END:variables
// private static final Comparator NATURAL_NAME_SORT = Collator.getInstance();
private static final class TemplateKey {
final DataObject d;
final boolean leaf;
TemplateKey(DataObject d, boolean leaf) {
this.d = d;
this.leaf = leaf;
}
@Override public boolean equals(Object o) {
if (!(o instanceof TemplateKey)) {
return false;
}
return d == ((TemplateKey) o).d && leaf == ((TemplateKey) o).leaf;
}
@Override public int hashCode() {
return d.hashCode();
}
}
private final class TemplateChildren extends ChildFactory.Detachable<TemplateKey> implements ActionListener {
private final DataFolder folder;
private final boolean isRoot;
private final String filterText;
TemplateChildren(DataFolder folder, boolean root, String filterText) {
this.folder = folder;
isRoot = root;
this.filterText = filterText;
}
@Override protected void addNotify() {
projectsComboBox.addActionListener( this );
}
@Override protected void removeNotify() {
projectsComboBox.removeActionListener( this );
}
@Override protected boolean createKeys(List<TemplateKey> keys) {
DataObject[] children = folder.getChildren();
if (isRoot) {
Project p = getProject();
TemplateCategorySorter tcs = p != null ? p.getLookup().lookup(TemplateCategorySorter.class) : null;
if (tcs != null) {
List<DataObject> dobjs = new ArrayList<DataObject>();
for (DataObject d : children) {
if (isFolderOfTemplates(d)) {
dobjs.add(d);
}
}
List<DataObject> sorted = tcs.sort(dobjs);
assert sorted.size() == dobjs.size() && new HashSet<DataObject>(dobjs).equals(new HashSet<DataObject>(sorted));
children = sorted.toArray(new DataObject[children.length]);
}
}
for (DataObject d : children) {
if (isFolderOfTemplates(d)) {
boolean leaf = true;
for (DataObject child : ((DataFolder) d).getChildren()) {
if (isFolderOfTemplates(child)) {
leaf = false;
break;
}
}
if( null == filterText || hasFilteredFileTemplates(d, filterText) )
keys.add(new TemplateKey(d, leaf));
}
}
return true;
}
private boolean hasFilteredFileTemplates( DataObject root, String filterText ) {
boolean res = false;
if( root instanceof DataFolder ) {
for (DataObject dobj : ((DataFolder)root).getChildren()) {
res = isFilteredFileTemplate( dobj, filterText, project, projectRecommendedTypes );
if( !res && dobj instanceof DataFolder ) {
res = hasFilteredFileTemplates( dobj, filterText );
}
if( res )
break;
}
}
return res;
}
@Override protected Node createNodeForKey(TemplateKey k) {
return new FilterNode(k.d.getNodeDelegate(), k.leaf ? Children.LEAF : Children.create(new TemplateChildren((DataFolder) k.d, false, filterText), true));
}
@Override public void actionPerformed (ActionEvent event) {
projectRecommendedTypes = OpenProjectList.getRecommendedTypes(getProject());
final String cat = getCategoryName ();
String template = ((TemplatesPanelGUI)TemplateChooserPanelGUI.this.templatesPanel).getSelectedTemplateName();
refresh(false);
setCategory (cat);
((TemplatesPanelGUI)TemplateChooserPanelGUI.this.templatesPanel).setSelectedTemplateByName(template);
}
// Private methods -----------------------------------------------------
private boolean isFolderOfTemplates(DataObject d) {
if (d instanceof DataFolder && !isTemplate((DataFolder)d)) {
Object o = d.getPrimaryFile().getAttribute("simple"); // NOI18N
if (o == null || Boolean.TRUE.equals(o)) {
boolean hasChildren = hasChildren((Project) projectsComboBox.getSelectedItem(), d);
if (hasChildren)
return hasChildren;
else {
for (DataObject child : ((DataFolder) d).getChildren()) {
return isFolderOfTemplates(child);
}
}
}
}
return false;
}
}
private final class FileChildren extends ChildFactory<DataObject> {
private DataFolder root;
private final String filterText;
public FileChildren (DataFolder folder, String filterText) {
this.root = folder;
assert this.root != null : "Root can not be null"; //NOI18N
this.filterText = filterText;
}
@Override protected boolean createKeys(List<DataObject> keys) {
for (DataObject dobj : root.getChildren()) {
if( isFilteredFileTemplate( dobj, filterText, project, projectRecommendedTypes ) ) {
keys.add(dobj);
}
}
return true;
}
@Override protected Node createNodeForKey(DataObject d) {
return new FilterNode(d.getNodeDelegate(), Children.LEAF);
}
}
private static boolean isFilteredFileTemplate( DataObject dobj, String filterText, Project project, String[] projectRecommendedTypes ) {
boolean res = false;
if (isTemplate(dobj) && OpenProjectList.isRecommended(project, projectRecommendedTypes, dobj.getPrimaryFile())) {
if (dobj instanceof DataShadow) {
dobj = ((DataShadow) dobj).getOriginal();
}
res = null == filterText || dobj.getNodeDelegate().getDisplayName().toLowerCase().contains( filterText.toLowerCase() );
}
return res;
}
final class FileChooserBuilder implements TemplatesPanelGUI.Builder {
@Override public Children createCategoriesChildren(DataFolder folder, String filterText) {
return Children.create(new TemplateChildren(folder, true, filterText), true);
}
@Override public Children createTemplatesChildren(DataFolder folder, String filterText) {
return Children.create(new FileChildren(folder, filterText), true);
}
@Override public void fireChange() {
TemplateChooserPanelGUI.this.fireChange();
}
@Messages("CTL_Categories=&Categories:")
@Override public String getCategoriesName() {
return CTL_Categories();
}
@Messages("CTL_Files=&File Types:")
@Override public String getTemplatesName() {
return CTL_Files();
}
@Override
public void actionPerformed( ActionEvent e ) {
if( null != defaultActionListener ) {
defaultActionListener.actionPerformed( e );
}
}
}
private static boolean isTemplate (DataObject dobj) {
if (dobj.isTemplate())
return true;
if (dobj instanceof DataShadow) {
return ((DataShadow)dobj).getOriginal().isTemplate();
}
return false;
}
private boolean hasChildren (Project p, DataObject folder) {
if (!(folder instanceof DataFolder)) {
return false;
}
DataFolder f = (DataFolder) folder;
if (!OpenProjectList.isRecommended(p, projectRecommendedTypes, f.getPrimaryFile())) {
// Eg. Licenses folder.
//see #102508
return false;
}
DataObject[] ch = f.getChildren ();
for (int i = 0; i < ch.length; i++) {
if (isTemplate (ch[i]) && OpenProjectList.isRecommended(p, projectRecommendedTypes, ch[i].getPrimaryFile ())) {
// XXX: how to filter link to Package template in each java types folder?
if (!(ch[i] instanceof DataShadow)) {
return true;
}
} else if (ch[i] instanceof DataFolder && hasChildren (p, ch[i])) {
return true;
}
}
return false;
// simplied but more counts
//return new FileChildren (p, (DataFolder) folder).getNodesCount () > 0;
}
@Override public void construct() {
this.templatesFolder = FileUtil.getConfigFile("Templates");
((TemplatesPanelGUI)this.templatesPanel).warmUp(this.templatesFolder);
}
@Override public void finished() {
//In the awt
Cursor cursor = null;
try {
Project p;
String c,t;
synchronized (this) {
p = this.project;
c = this.category;
t = this.template;
}
cursor = TemplateChooserPanelGUI.this.getCursor();
TemplateChooserPanelGUI.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
initValues( p );
((TemplatesPanelGUI)this.templatesPanel).doFinished (this.templatesFolder, c, t);
} finally {
synchronized (this) {
isWarmUp = false;
}
if (cursor != null) {
this.setCursor (cursor);
}
}
}
}