/* * Copyright (C) 2014 The Android Open Source Project * * Licensed 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 com.android.tools.idea.wizard; import com.android.tools.idea.templates.Template; import com.android.tools.idea.templates.TemplateManager; import com.android.tools.idea.templates.TemplateMetadata; import com.google.common.base.Function; import com.google.common.base.Strings; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.components.JBList; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.imageio.ImageIO; import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.*; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import static com.android.tools.idea.templates.TemplateMetadata.*; /** * ChooseTemplateStep is a wizard page that shows the user a list of templates * of a given type and lets the user choose one. */ public class TemplateGalleryStep extends TemplateWizardStep implements ListSelectionListener { private final TemplateChangeListener myTemplateChangeListener; private JPanel myPanel; private JLabel myDescription; private JLabel myError; private ASGallery<MetadataListItem> myGallery; private JBScrollPane myGalleryScroller; private int myPreviousSelection = -1; public TemplateGalleryStep(TemplateWizardState state, String templateCategory, @Nullable Project project, @Nullable Module module, @Nullable Icon sidePanelIcon, UpdateListener updateListener, @Nullable TemplateChangeListener templateChangeListener) { this(state, templateCategory, project, module, sidePanelIcon, updateListener, templateChangeListener, null); } public TemplateGalleryStep(TemplateWizardState state, String templateCategory, @Nullable Project project, @Nullable Module module, @Nullable Icon sidePanelIcon, UpdateListener updateListener, @Nullable TemplateChangeListener templateChangeListener, @Nullable Set<String> excluded) { super(state, project, module, sidePanelIcon, updateListener); myGallery.setImageProvider(new TemplateImageProvider()); myGallery.setLabelProvider(new TemplateLabelProvider()); myGallery.setThumbnailSize(new Dimension(256, 256)); myTemplateChangeListener = templateChangeListener; if (templateCategory != null) { List<MetadataListItem> templates = getTemplateList(state, templateCategory, excluded); setListData(templates); validate(); } } /** * Search the given folder for a list of templates and populate the display list. */ protected static List<MetadataListItem> getTemplateList(TemplateWizardState state, String templateFolder, @Nullable Set<String> excluded) { TemplateManager manager = TemplateManager.getInstance(); List<File> templates = manager.getTemplates(templateFolder); List<MetadataListItem> metadataList = new ArrayList<MetadataListItem>(templates.size()); for (File template : templates) { TemplateMetadata metadata = manager.getTemplate(template); if (metadata == null || !metadata.isSupported()) { continue; } // If we're trying to create a launchable activity, don't include templates that // lack the isLauncher parameter. Boolean isLauncher = (Boolean)state.get(ATTR_IS_LAUNCHER); if (isLauncher != null && isLauncher && metadata.getParameter(TemplateMetadata.ATTR_IS_LAUNCHER) == null) { continue; } // Don't include this template if it's been excluded if (excluded != null && excluded.contains(metadata.getTitle())) { continue; } metadataList.add(new MetadataListItem(template, metadata)); } Collections.sort(metadataList); return metadataList; } private void createUIComponents() { myGalleryScroller = new JBScrollPane(); myGalleryScroller.setBorder(BorderFactory.createLoweredBevelBorder()); } /** * Populate the JBList of templates from the given list of metadata. */ protected void setListData(List<MetadataListItem> metadataList) { myGallery.setModel(JBList.createDefaultListModel(ArrayUtil.toObjectArray(metadataList))); if (!metadataList.isEmpty()) { myGallery.setSelectedIndex(0); } myGallery.addListSelectionListener(this); } @Override public JComponent getComponent() { return myPanel; } @Override public JComponent getPreferredFocusedComponent() { return myGallery; } @Override public void valueChanged(ListSelectionEvent listSelectionEvent) { update(); } @Override public boolean validate() { if (!super.validate()) { return false; } setDescriptionHtml(""); MetadataListItem templateListItem = getSelectedTemplate(); if (templateListItem != null) { myTemplateState.setTemplateLocation(templateListItem.getTemplateFile()); Template.convertApisToInt(myTemplateState.getParameters()); setDescriptionHtml(templateListItem.myMetadata.getDescription()); int minSdk = templateListItem.myMetadata.getMinSdk(); Integer minApi = (Integer)myTemplateState.get(ATTR_MIN_API_LEVEL); if (minApi != null && minSdk > minApi) { setErrorHtml(String.format("The component %s has a minimum SDK level of %d.", templateListItem.myMetadata.getTitle(), minSdk)); return false; } int minBuildApi = templateListItem.myMetadata.getMinBuildApi(); Integer buildApi = (Integer)myTemplateState.get(ATTR_BUILD_API); if (buildApi != null && minSdk > buildApi) { setErrorHtml( String.format("The component %s has a minimum build API level of %d.", templateListItem.myMetadata.getTitle(), minBuildApi)); return false; } int index = myGallery.getSelectedIndex(); if (myTemplateChangeListener != null && myPreviousSelection != index) { myPreviousSelection = index; myTemplateChangeListener.templateChanged(templateListItem.toString()); } } return true; } @Nullable private MetadataListItem getSelectedTemplate() { return myGallery.getSelectedElement(); } @NotNull @Override protected JLabel getDescription() { return myDescription; } @NotNull @Override protected JLabel getError() { return myError; } public interface TemplateChangeListener { void templateChanged(String templateName); } public static class MetadataListItem implements Comparable<MetadataListItem> { private TemplateMetadata myMetadata; private final File myTemplate; public MetadataListItem(@NotNull File template, @NotNull TemplateMetadata metadata) { myTemplate = template; myMetadata = metadata; } @Override public String toString() { return Strings.nullToEmpty(myMetadata.getTitle()); } /** * Get the folder containing this template */ public File getTemplateFile() { return myTemplate; } @Override public int compareTo(@NotNull MetadataListItem other) { return StringUtil.naturalCompare(this.myMetadata.getTitle(), other.myMetadata.getTitle()); } } private static final class TemplateImageProvider implements Function<MetadataListItem, Image> { @Override public Image apply(MetadataListItem input) { String thumb = input.myMetadata.getThumbnailPath(); if (thumb != null && !thumb.isEmpty()) { try { File file = new File(input.myTemplate, thumb.replace('/', File.separatorChar)); if (file.isFile()) { return ImageIO.read(file); } else { return null; } } catch (IOException e) { Logger.getInstance(getClass()).warn(e); } } return null; } } private static final class TemplateLabelProvider implements Function<MetadataListItem, String> { @Override public String apply(MetadataListItem input) { return input.myMetadata.getTitle(); } } }