diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java | 946 |
1 files changed, 946 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java new file mode 100644 index 000000000..57cf5c824 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplatePage.java @@ -0,0 +1,946 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.internal.wizards.templates; + +import static com.android.SdkConstants.CLASS_ACTIVITY; +import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_API; +import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_BUILD_API; +import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_DEFAULT; +import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_ID; +import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_NAME; +import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.PREVIEW_PADDING; +import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.PREVIEW_WIDTH; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.editors.IconFactory; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageControl; +import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; +import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper; +import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.ProjectCombo; +import com.android.ide.eclipse.adt.internal.wizards.templates.Parameter.Constraint; +import com.android.ide.eclipse.adt.internal.wizards.templates.Parameter.Type; +import com.android.tools.lint.detector.api.LintUtils; +import com.google.common.collect.Lists; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeHierarchy; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.ui.IJavaElementSearchConstants; +import org.eclipse.jdt.ui.JavaUI; +import org.eclipse.jdt.ui.dialogs.ITypeInfoFilterExtension; +import org.eclipse.jdt.ui.dialogs.ITypeInfoRequestor; +import org.eclipse.jdt.ui.dialogs.TypeSelectionExtension; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.fieldassist.FieldDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.dialogs.SelectionDialog; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.ByteArrayInputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * First wizard page in the "New Project From Template" wizard (which is parameterized + * via template.xml files) + */ +public class NewTemplatePage extends WizardPage + implements ModifyListener, SelectionListener, FocusListener { + /** The default width to use for the wizard page */ + static final int WIZARD_PAGE_WIDTH = 600; + + private final NewTemplateWizardState mValues; + private final boolean mChooseProject; + private int mCustomMinSdk = -1; + private int mCustomBuildApi = -1; + private boolean mIgnore; + private boolean mShown; + private Control mFirst; + // TODO: Move decorators to the Parameter objects? + private Map<String, ControlDecoration> mDecorations = new HashMap<String, ControlDecoration>(); + private Label mHelpIcon; + private Label mTipLabel; + private ImageControl mPreview; + private Image mPreviewImage; + private boolean mDisposePreviewImage; + private ProjectCombo mProjectButton; + private StringEvaluator mEvaluator; + + private TemplateMetadata mShowingTemplate; + + /** + * Creates a new {@link NewTemplatePage} + * + * @param values the wizard state + * @param chooseProject whether the wizard should present a project chooser, + * and update {@code values}' project field + */ + NewTemplatePage(NewTemplateWizardState values, boolean chooseProject) { + super("newTemplatePage"); //$NON-NLS-1$ + mValues = values; + mChooseProject = chooseProject; + } + + /** + * @param minSdk a minimum SDK to use, provided chooseProject is false. If + * it is true, then the minimum SDK used for validation will be + * the one of the project + * @param buildApi the build API to use + */ + void setCustomMinSdk(int minSdk, int buildApi) { + assert !mChooseProject; + //assert buildApi >= minSdk; + mCustomMinSdk = minSdk; + mCustomBuildApi = buildApi; + } + + @Override + public void createControl(Composite parent2) { + Composite parent = new Composite(parent2, SWT.NULL); + setControl(parent); + GridLayout parentLayout = new GridLayout(3, false); + parentLayout.verticalSpacing = 0; + parentLayout.marginWidth = 0; + parentLayout.marginHeight = 0; + parentLayout.horizontalSpacing = 0; + parent.setLayout(parentLayout); + + // Reserve enough width (since the panel is created lazily later) + Label label = new Label(parent, SWT.NONE); + GridData data = new GridData(); + data.widthHint = WIZARD_PAGE_WIDTH; + label.setLayoutData(data); + } + + @SuppressWarnings("unused") // SWT constructors have side effects and aren't unused + private void onEnter() { + TemplateMetadata template = mValues.getTemplateHandler().getTemplate(); + if (template == mShowingTemplate) { + return; + } + mShowingTemplate = template; + + Composite parent = (Composite) getControl(); + + Control[] children = parent.getChildren(); + if (children.length > 0) { + for (Control c : parent.getChildren()) { + c.dispose(); + } + for (ControlDecoration decoration : mDecorations.values()) { + decoration.dispose(); + } + mDecorations.clear(); + } + + Composite container = new Composite(parent, SWT.NULL); + container.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + GridLayout gl_container = new GridLayout(3, false); + gl_container.horizontalSpacing = 10; + container.setLayout(gl_container); + + if (mChooseProject) { + // Project: [button] + String tooltip = "The Android Project where the new resource will be created."; + Label projectLabel = new Label(container, SWT.NONE); + projectLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + projectLabel.setText("Project:"); + projectLabel.setToolTipText(tooltip); + + ProjectChooserHelper helper = + new ProjectChooserHelper(getShell(), null /* filter */); + mProjectButton = new ProjectCombo(helper, container, mValues.project); + mProjectButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + mProjectButton.setToolTipText(tooltip); + mProjectButton.addSelectionListener(this); + + //Label projectSeparator = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL); + //projectSeparator.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1)); + } + + // Add parameters + mFirst = null; + String thumb = null; + if (template != null) { + thumb = template.getThumbnailPath(); + String title = template.getTitle(); + if (title != null && !title.isEmpty()) { + setTitle(title); + } + String description = template.getDescription(); + if (description != null && !description.isEmpty()) { + setDescription(description); + } + + Map<String, String> defaults = mValues.defaults; + Set<String> seen = null; + if (LintUtils.assertionsEnabled()) { + seen = new HashSet<String>(); + } + + List<Parameter> parameters = template.getParameters(); + for (Parameter parameter : parameters) { + Parameter.Type type = parameter.type; + + if (type == Parameter.Type.SEPARATOR) { + Label separator = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL); + separator.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1)); + continue; + } + + String id = parameter.id; + assert id != null && !id.isEmpty() : ATTR_ID; + Object value = defaults.get(id); + if (value == null) { + value = parameter.value; + } + + String name = parameter.name; + String help = parameter.help; + + // Required + assert name != null && !name.isEmpty() : ATTR_NAME; + // Ensure id's are unique: + assert seen != null && seen.add(id) : id; + + // Skip attributes that were already provided by the surrounding + // context. For example, when adding into an existing project, + // provide the minimum SDK automatically from the project. + if (mValues.hidden != null && mValues.hidden.contains(id)) { + continue; + } + + switch (type) { + case STRING: { + // TODO: Look at the constraints to add validators here + // TODO: If I type.equals("layout") add resource validator for layout + // names + // TODO: If I type.equals("class") make class validator + + // TODO: Handle package and id better later + Label label = new Label(container, SWT.NONE); + label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, + 1, 1)); + label.setText(name); + + Text text = new Text(container, SWT.BORDER); + text.setData(parameter); + parameter.control = text; + + if (parameter.constraints.contains(Constraint.EXISTS)) { + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, + 1, 1)); + + Button button = new Button(container, SWT.FLAT); + button.setData(parameter); + button.setText("..."); + button.addSelectionListener(this); + } else { + text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, + 2, 1)); + } + + boolean hasValue = false; + if (value instanceof String) { + String stringValue = (String) value; + hasValue = !stringValue.isEmpty(); + text.setText(stringValue); + mValues.parameters.put(id, value); + } + + if (!hasValue) { + if (parameter.constraints.contains(Constraint.EMPTY)) { + text.setMessage("Optional"); + } else if (parameter.constraints.contains(Constraint.NONEMPTY)) { + text.setMessage("Required"); + } + } + + text.addModifyListener(this); + text.addFocusListener(this); + + if (mFirst == null) { + mFirst = text; + } + + if (help != null && !help.isEmpty()) { + text.setToolTipText(help); + ControlDecoration decoration = createFieldDecoration(id, text, help); + } + break; + } + case BOOLEAN: { + Label label = new Label(container, SWT.NONE); + label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, + 1, 1)); + + Button checkBox = new Button(container, SWT.CHECK); + checkBox.setText(name); + checkBox.setData(parameter); + parameter.control = checkBox; + checkBox.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, + 2, 1)); + + if (value instanceof Boolean) { + Boolean selected = (Boolean) value; + checkBox.setSelection(selected); + mValues.parameters.put(id, value); + } + + checkBox.addSelectionListener(this); + checkBox.addFocusListener(this); + + if (mFirst == null) { + mFirst = checkBox; + } + + if (help != null && !help.isEmpty()) { + checkBox.setToolTipText(help); + ControlDecoration decoration = createFieldDecoration(id, checkBox, + help); + } + break; + } + case ENUM: { + Label label = new Label(container, SWT.NONE); + label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, + 1, 1)); + label.setText(name); + + Combo combo = createOptionCombo(parameter, container, mValues.parameters, + this, this); + combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, + 2, 1)); + + if (mFirst == null) { + mFirst = combo; + } + + if (help != null && !help.isEmpty()) { + ControlDecoration decoration = createFieldDecoration(id, combo, help); + } + break; + } + case SEPARATOR: + // Already handled above + assert false : type; + break; + default: + assert false : type; + } + } + } + + // Preview + mPreview = new ImageControl(parent, SWT.NONE, null); + mPreview.setDisposeImage(false); // Handled manually in this class + GridData gd_mImage = new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1); + gd_mImage.widthHint = PREVIEW_WIDTH + 2 * PREVIEW_PADDING; + mPreview.setLayoutData(gd_mImage); + + Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL); + GridData separatorData = new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1); + separatorData.heightHint = 16; + separator.setLayoutData(separatorData); + + // Generic help + mHelpIcon = new Label(parent, SWT.NONE); + mHelpIcon.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false, 1, 1)); + Image icon = IconFactory.getInstance().getIcon("quickfix"); + mHelpIcon.setImage(icon); + mHelpIcon.setVisible(false); + mTipLabel = new Label(parent, SWT.WRAP); + mTipLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); + + setPreview(thumb); + + parent.layout(true, true); + // TODO: This is a workaround for the fact that (at least on OSX) you end up + // with some visual artifacts from the control decorations in the upper left corner + // (outside the parent widget itself) from the initial control decoration placement + // prior to layout. Therefore, perform a redraw. A better solution would be to + // delay creation of the control decorations until layout has been performed. + // Let's do that soon. + parent.getParent().redraw(); + } + + @NonNull + static Combo createOptionCombo( + @NonNull Parameter parameter, + @NonNull Composite container, + @NonNull Map<String, Object> valueMap, + @NonNull SelectionListener selectionListener, + @NonNull FocusListener focusListener) { + Combo combo = new Combo(container, SWT.READ_ONLY); + + List<Element> options = parameter.getOptions(); + assert options.size() > 0; + int selected = 0; + List<String> ids = Lists.newArrayList(); + List<Integer> minSdks = Lists.newArrayList(); + List<Integer> minBuildApis = Lists.newArrayList(); + List<String> labels = Lists.newArrayList(); + for (int i = 0, n = options.size(); i < n; i++) { + Element option = options.get(i); + String optionId = option.getAttribute(ATTR_ID); + assert optionId != null && !optionId.isEmpty() : ATTR_ID; + String isDefault = option.getAttribute(ATTR_DEFAULT); + if (isDefault != null && !isDefault.isEmpty() && + Boolean.valueOf(isDefault)) { + selected = i; + } + NodeList childNodes = option.getChildNodes(); + assert childNodes.getLength() == 1 && + childNodes.item(0).getNodeType() == Node.TEXT_NODE; + String optionLabel = childNodes.item(0).getNodeValue().trim(); + + String minApiString = option.getAttribute(ATTR_MIN_API); + int minSdk = 1; + if (minApiString != null && !minApiString.isEmpty()) { + try { + minSdk = Integer.parseInt(minApiString); + } catch (NumberFormatException nufe) { + // Templates aren't allowed to contain codenames, should + // always be an integer + AdtPlugin.log(nufe, null); + minSdk = 1; + } + } + String minBuildApiString = option.getAttribute(ATTR_MIN_BUILD_API); + int minBuildApi = 1; + if (minBuildApiString != null && !minBuildApiString.isEmpty()) { + try { + minBuildApi = Integer.parseInt(minBuildApiString); + } catch (NumberFormatException nufe) { + // Templates aren't allowed to contain codenames, should + // always be an integer + AdtPlugin.log(nufe, null); + minBuildApi = 1; + } + } + minSdks.add(minSdk); + minBuildApis.add(minBuildApi); + ids.add(optionId); + labels.add(optionLabel); + } + combo.setData(parameter); + parameter.control = combo; + combo.setData(ATTR_ID, ids.toArray(new String[ids.size()])); + combo.setData(ATTR_MIN_API, minSdks.toArray(new Integer[minSdks.size()])); + combo.setData(ATTR_MIN_BUILD_API, minBuildApis.toArray( + new Integer[minBuildApis.size()])); + assert labels.size() > 0; + combo.setItems(labels.toArray(new String[labels.size()])); + combo.select(selected); + + combo.addSelectionListener(selectionListener); + combo.addFocusListener(focusListener); + + valueMap.put(parameter.id, ids.get(selected)); + + if (parameter.help != null && !parameter.help.isEmpty()) { + combo.setToolTipText(parameter.help); + } + + return combo; + } + + private void setPreview(String thumb) { + Image oldImage = mPreviewImage; + boolean dispose = mDisposePreviewImage; + mPreviewImage = null; + + if (thumb == null || thumb.isEmpty()) { + mPreviewImage = TemplateMetadata.getDefaultTemplateIcon(); + mDisposePreviewImage = false; + } else { + byte[] data = mValues.getTemplateHandler().readTemplateResource(thumb); + if (data != null) { + try { + mPreviewImage = new Image(getControl().getDisplay(), + new ByteArrayInputStream(data)); + mDisposePreviewImage = true; + } catch (Exception e) { + AdtPlugin.log(e, null); + } + } + if (mPreviewImage == null) { + return; + } + } + + mPreview.setImage(mPreviewImage); + mPreview.fitToWidth(PREVIEW_WIDTH); + + if (oldImage != null && dispose) { + oldImage.dispose(); + } + } + + @Override + public void dispose() { + super.dispose(); + + if (mPreviewImage != null && mDisposePreviewImage) { + mDisposePreviewImage = false; + mPreviewImage.dispose(); + mPreviewImage = null; + } + } + + private ControlDecoration createFieldDecoration(String id, Control control, + String description) { + ControlDecoration decoration = new ControlDecoration(control, SWT.LEFT); + decoration.setMarginWidth(2); + FieldDecoration errorFieldIndicator = FieldDecorationRegistry.getDefault(). + getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION); + decoration.setImage(errorFieldIndicator.getImage()); + decoration.setDescriptionText(description); + control.setToolTipText(description); + mDecorations.put(id, decoration); + + return decoration; + } + + @Override + public boolean isPageComplete() { + // Force user to reach this page before hitting Finish + return mShown && super.isPageComplete(); + } + + @Override + public void setVisible(boolean visible) { + if (visible) { + onEnter(); + } + + super.setVisible(visible); + + if (mFirst != null) { + mFirst.setFocus(); + } + + if (visible) { + mShown = true; + } + + validatePage(); + } + + /** Returns the parameter associated with the given control */ + @Nullable + static Parameter getParameter(Control control) { + return (Parameter) control.getData(); + } + + /** + * Returns the current string evaluator, if any + * + * @return the evaluator or null + */ + @Nullable + public StringEvaluator getEvaluator() { + return mEvaluator; + } + + // ---- Validation ---- + + private void validatePage() { + int minSdk = getMinSdk(); + int buildApi = getBuildApi(); + IStatus status = mValues.getTemplateHandler().validateTemplate(minSdk, buildApi); + + if (status == null || status.isOK()) { + if (mChooseProject && mValues.project == null) { + status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, + "Please select an Android project."); + } + } + + for (Parameter parameter : mShowingTemplate.getParameters()) { + if (parameter.type == Parameter.Type.SEPARATOR) { + continue; + } + IInputValidator validator = parameter.getValidator(mValues.project); + if (validator != null) { + ControlDecoration decoration = mDecorations.get(parameter.id); + String value = parameter.value == null ? "" : parameter.value.toString(); + String error = validator.isValid(value); + if (error != null) { + IStatus s = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, error); + if (decoration != null) { + updateDecorator(decoration, s, parameter.help); + } + if (status == null || status.isOK()) { + status = s; + } + } else if (decoration != null) { + updateDecorator(decoration, null, parameter.help); + } + } + + if (status == null || status.isOK()) { + if (parameter.control instanceof Combo) { + status = validateCombo(status, parameter, minSdk, buildApi); + } + } + } + + setPageComplete(status == null || status.getSeverity() != IStatus.ERROR); + if (status != null) { + setMessage(status.getMessage(), + status.getSeverity() == IStatus.ERROR + ? IMessageProvider.ERROR : IMessageProvider.WARNING); + } else { + setErrorMessage(null); + setMessage(null); + } + } + + /** Validates the given combo */ + static IStatus validateCombo(IStatus status, Parameter parameter, int minSdk, int buildApi) { + Combo combo = (Combo) parameter.control; + int index = combo.getSelectionIndex(); + return validateCombo(status, parameter, index, minSdk, buildApi); + } + + /** Validates the given combo assuming the value at the given index is chosen */ + static IStatus validateCombo(IStatus status, Parameter parameter, int index, + int minSdk, int buildApi) { + Combo combo = (Combo) parameter.control; + Integer[] optionIds = (Integer[]) combo.getData(ATTR_MIN_API); + // Check minSdk + if (index != -1 && index < optionIds.length) { + Integer requiredMinSdk = optionIds[index]; + if (requiredMinSdk > minSdk) { + status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, + String.format( + "%1$s \"%2$s\" requires a minimum SDK version of at " + + "least %3$d, and the current min version is %4$d", + parameter.name, combo.getItems()[index], requiredMinSdk, minSdk)); + } + } + + // Check minimum build target + optionIds = (Integer[]) combo.getData(ATTR_MIN_BUILD_API); + if (index != -1 && index < optionIds.length) { + Integer requiredBuildApi = optionIds[index]; + if (requiredBuildApi > buildApi) { + status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, + String.format( + "%1$s \"%2$s\" requires a build target API version of at " + + "least %3$d, and the current version is %4$d", + parameter.name, combo.getItems()[index], requiredBuildApi, buildApi)); + } + } + return status; + } + + private int getMinSdk() { + return mChooseProject ? mValues.getMinSdk() : mCustomMinSdk; + } + + private int getBuildApi() { + return mChooseProject ? mValues.getBuildApi() : mCustomBuildApi; + } + + private void updateDecorator(ControlDecoration decorator, IStatus status, String help) { + if (help != null && !help.isEmpty()) { + decorator.setDescriptionText(status != null ? status.getMessage() : help); + + int severity = status != null ? status.getSeverity() : IStatus.OK; + String id; + if (severity == IStatus.ERROR) { + id = FieldDecorationRegistry.DEC_ERROR; + } else if (severity == IStatus.WARNING) { + id = FieldDecorationRegistry.DEC_WARNING; + } else { + id = FieldDecorationRegistry.DEC_INFORMATION; + } + FieldDecoration errorFieldIndicator = FieldDecorationRegistry.getDefault(). + getFieldDecoration(id); + decorator.setImage(errorFieldIndicator.getImage()); + } else { + if (status == null || status.isOK()) { + decorator.hide(); + } else { + decorator.show(); + } + } + } + + // ---- Implements ModifyListener ---- + + @Override + public void modifyText(ModifyEvent e) { + if (mIgnore) { + return; + } + + Object source = e.getSource(); + if (source instanceof Text) { + Text text = (Text) source; + editParameter(text, text.getText().trim()); + } + + validatePage(); + } + + // ---- Implements SelectionListener ---- + + @Override + public void widgetSelected(SelectionEvent e) { + if (mIgnore) { + return; + } + + Object source = e.getSource(); + if (source == mProjectButton) { + mValues.project = mProjectButton.getSelectedProject(); + } else if (source instanceof Combo) { + Combo combo = (Combo) source; + String[] optionIds = (String[]) combo.getData(ATTR_ID); + int index = combo.getSelectionIndex(); + if (index != -1 && index < optionIds.length) { + String optionId = optionIds[index]; + editParameter(combo, optionId); + TemplateMetadata template = mValues.getTemplateHandler().getTemplate(); + if (template != null) { + setPreview(template.getThumbnailPath()); + } + } + } else if (source instanceof Button) { + Button button = (Button) source; + Parameter parameter = (Parameter) button.getData(); + if (parameter.type == Type.BOOLEAN) { + // Checkbox parameter + editParameter(button, button.getSelection()); + + TemplateMetadata template = mValues.getTemplateHandler().getTemplate(); + if (template != null) { + setPreview(template.getThumbnailPath()); + } + } else { + // Choose button for some other parameter, usually a text + String activity = chooseActivity(); + if (activity != null) { + setValue(parameter, activity); + } + } + } + + validatePage(); + } + + private String chooseActivity() { + try { + // Compute a search scope: We need to merge all the subclasses + // android.app.Fragment and android.support.v4.app.Fragment + IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); + IProject project = mValues.project; + IJavaProject javaProject = BaseProjectHelper.getJavaProject(project); + IType activityType = null; + + if (javaProject != null) { + activityType = javaProject.findType(CLASS_ACTIVITY); + } + if (activityType == null) { + IJavaProject[] projects = BaseProjectHelper.getAndroidProjects(null); + for (IJavaProject p : projects) { + activityType = p.findType(CLASS_ACTIVITY); + if (activityType != null) { + break; + } + } + } + if (activityType != null) { + NullProgressMonitor monitor = new NullProgressMonitor(); + ITypeHierarchy hierarchy = activityType.newTypeHierarchy(monitor); + IType[] classes = hierarchy.getAllSubtypes(activityType); + scope = SearchEngine.createJavaSearchScope(classes, IJavaSearchScope.SOURCES); + } + + Shell parent = AdtPlugin.getShell(); + final SelectionDialog dialog = JavaUI.createTypeDialog( + parent, + new ProgressMonitorDialog(parent), + scope, + IJavaElementSearchConstants.CONSIDER_CLASSES, false, + // Use ? as a default filter to fill dialog with matches + "?", //$NON-NLS-1$ + new TypeSelectionExtension() { + @Override + public ITypeInfoFilterExtension getFilterExtension() { + return new ITypeInfoFilterExtension() { + @Override + public boolean select(ITypeInfoRequestor typeInfoRequestor) { + int modifiers = typeInfoRequestor.getModifiers(); + if (!Flags.isPublic(modifiers) + || Flags.isInterface(modifiers) + || Flags.isEnum(modifiers)) { + return false; + } + return true; + } + }; + } + }); + + dialog.setTitle("Choose Activity Class"); + dialog.setMessage("Select an Activity class (? = any character, * = any string):"); + if (dialog.open() == IDialogConstants.CANCEL_ID) { + return null; + } + + Object[] types = dialog.getResult(); + if (types != null && types.length > 0) { + return ((IType) types[0]).getFullyQualifiedName(); + } + } catch (JavaModelException e) { + AdtPlugin.log(e, null); + } catch (CoreException e) { + AdtPlugin.log(e, null); + } + return null; + } + + private void editParameter(Control control, Object value) { + Parameter parameter = getParameter(control); + if (parameter != null) { + String id = parameter.id; + parameter.value = value; + parameter.edited = value != null && !value.toString().isEmpty(); + mValues.parameters.put(id, value); + + // Update dependent variables, if any + List<Parameter> parameters = mShowingTemplate.getParameters(); + for (Parameter p : parameters) { + if (p == parameter || p.suggest == null || p.edited || + p.type == Parameter.Type.SEPARATOR) { + continue; + } + if (!p.suggest.contains(id)) { + continue; + } + + try { + if (mEvaluator == null) { + mEvaluator = new StringEvaluator(); + } + String updated = mEvaluator.evaluate(p.suggest, parameters); + if (updated != null && !updated.equals(p.value)) { + setValue(p, updated); + } + } catch (Throwable t) { + // Pass: Ignore updating if something wrong happens + t.printStackTrace(); // during development only + } + } + } + } + + private void setValue(Parameter p, String value) { + p.value = value; + mValues.parameters.put(p.id, value); + + // Update form widgets + boolean prevIgnore = mIgnore; + try { + mIgnore = true; + if (p.control instanceof Text) { + ((Text) p.control).setText(value); + } else if (p.control instanceof Button) { + // TODO: Handle + } else if (p.control instanceof Combo) { + // TODO: Handle + } else if (p.control != null) { + assert false : p.control; + } + } finally { + mIgnore = prevIgnore; + } + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + + // ---- Implements FocusListener ---- + + @Override + public void focusGained(FocusEvent e) { + Object source = e.getSource(); + String tip = ""; + + if (source instanceof Control) { + Control control = (Control) source; + Parameter parameter = getParameter(control); + if (parameter != null) { + ControlDecoration decoration = mDecorations.get(parameter.id); + if (decoration != null) { + tip = decoration.getDescriptionText(); + } + } + } + + mTipLabel.setText(tip); + mHelpIcon.setVisible(tip.length() > 0); + } + + @Override + public void focusLost(FocusEvent e) { + mTipLabel.setText(""); + mHelpIcon.setVisible(false); + } +} |