/* * 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; } p.suggest.indexOf(id); 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); } }