diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/OpenWizardAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/OpenWizardAction.java index 4fc9deef1..13031ff6b 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/OpenWizardAction.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/OpenWizardAction.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.wizards.actions; import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; @@ -52,6 +53,21 @@ import org.eclipse.ui.internal.util.Util; */ private static final int SIZING_WIZARD_HEIGHT = 500; + /** The wizard that was created by {@link #run(IAction)}. */ + private IWorkbenchWizard mWizard; + /** The result from the dialog */ + private int mDialogResult; + + /** Returns the wizard that was created by {@link #run(IAction)}. */ + public IWorkbenchWizard getWizard() { + return mWizard; + } + + /** Returns the result from {@link Dialog#open()}, available after + * the completion of {@link #run(IAction)}. */ + public int getDialogResult() { + return mDialogResult; + } /* (non-Javadoc) * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#dispose() @@ -72,6 +88,7 @@ import org.eclipse.ui.internal.util.Util; *

* Most of this implementation is extracted from {@link NewWizardShortcutAction#run()}. * + * @param action The action that got us here. Can be null when used internally. * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) */ public void run(IAction action) { @@ -102,12 +119,12 @@ import org.eclipse.ui.internal.util.Util; } // Create the wizard and initialize it with the selection - IWorkbenchWizard wizard = instanciateWizard(action); - wizard.init(workbench, selectionToPass); + mWizard = instanciateWizard(action); + mWizard.init(workbench, selectionToPass); // It's not visible yet until a dialog is created and opened Shell parent = window.getShell(); - WizardDialog dialog = new WizardDialog(parent, wizard); + WizardDialog dialog = new WizardDialog(parent, mWizard); dialog.create(); // This code comes straight from NewWizardShortcutAction#run() @@ -118,13 +135,14 @@ import org.eclipse.ui.internal.util.Util; window.getWorkbench().getHelpSystem().setHelp(dialog.getShell(), IWorkbenchHelpContextIds.NEW_WIZARD_SHORTCUT); - dialog.open(); + mDialogResult = dialog.open(); } /** * Called by {@link #run(IAction)} to instantiate the actual wizard. * * @param action The action parameter from {@link #run(IAction)}. + * This can be null. * @return A new wizard instance. Must not be null. */ protected abstract IWorkbenchWizard instanciateWizard(IAction action); diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java index af45fa9e6..d3eb9d403 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java @@ -143,6 +143,8 @@ public class NewProjectWizard extends Wizard implements INewWizard { protected static final String MAIN_PAGE_NAME = "newAndroidProjectPage"; //$NON-NLS-1$ private NewProjectCreationPage mMainPage; + /** Package name available when the wizard completes. */ + private String mPackageName; /** * Initializes this creation wizard using the passed workbench and object @@ -203,6 +205,13 @@ public class NewProjectWizard extends Wizard implements INewWizard { return true; } + // -- Public Fields -- + + /** Returns the package name. Only valid once the wizard finishes. */ + public String getPackageName() { + return mPackageName; + } + // -- Custom Methods -- /** @@ -234,16 +243,20 @@ public class NewProjectWizard extends Wizard implements INewWizard { final IProject project = workspace.getRoot().getProject(mMainPage.getProjectName()); final IProjectDescription description = workspace.newProjectDescription(project.getName()); + + // keep some variables to make them available once the wizard closes + mPackageName = mMainPage.getPackageName(); + final Map parameters = new HashMap(); parameters.put(PARAM_PROJECT, mMainPage.getProjectName()); - parameters.put(PARAM_PACKAGE, mMainPage.getPackageName()); + parameters.put(PARAM_PACKAGE, mPackageName); parameters.put(PARAM_APPLICATION, STRING_RSRC_PREFIX + STRING_APP_NAME); parameters.put(PARAM_SDK_TOOLS_DIR, AdtPlugin.getOsSdkToolsFolder()); parameters.put(PARAM_IS_NEW_PROJECT, mMainPage.isNewProject()); parameters.put(PARAM_SRC_FOLDER, mMainPage.getSourceFolder()); parameters.put(PARAM_SDK_TARGET, mMainPage.getSdkTarget()); parameters.put(PARAM_MIN_SDK_VERSION, mMainPage.getMinSdkVersion()); - + if (mMainPage.isCreateActivity()) { // An activity name can be of the form ".package.Class" or ".Class". // The initial dot is ignored, as it is always added later in the templates. diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestEditor.java index dc32383d9..c2cd9754d 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestEditor.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestEditor.java @@ -55,8 +55,10 @@ import javax.xml.xpath.XPathExpressionException; * Multi-page form editor for AndroidManifest.xml. */ public final class ManifestEditor extends AndroidEditor { - private final static String EMPTY = ""; //$NON-NLS-1$ + public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".manifest.ManifestEditor"; //$NON-NLS-1$ + + private final static String EMPTY = ""; //$NON-NLS-1$ /** Root node of the UI element hierarchy */ private UiElementNode mUiManifestNode; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java index 5d1ababc2..2da79c361 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java @@ -193,7 +193,7 @@ public final class AndroidManifestDescriptors implements IDescriptorProvider { overrides.put("*/theme", ThemeAttributeDescriptor.class); //$NON-NLS-1$ overrides.put("*/permission", ListAttributeDescriptor.class); //$NON-NLS-1$ - overrides.put("*/targetPackage", PackageAttributeDescriptor.class); //$NON-NLS-1$ + overrides.put("*/targetPackage", ManifestPkgAttrDescriptor.class); //$NON-NLS-1$ overrides.put("uses-library/name", ListAttributeDescriptor.class); //$NON-NLS-1$ diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ManifestPkgAttrDescriptor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ManifestPkgAttrDescriptor.java new file mode 100755 index 000000000..804d0ad63 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ManifestPkgAttrDescriptor.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 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.editors.manifest.descriptors; + +import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor; +import com.android.ide.eclipse.editors.manifest.model.UiManifestPkgAttrNode; +import com.android.ide.eclipse.editors.uimodel.UiAttributeNode; +import com.android.ide.eclipse.editors.uimodel.UiElementNode; + +/** + * Describes a package XML attribute. It is displayed by a {@link UiManifestPkgAttrNode}. + */ +public class ManifestPkgAttrDescriptor extends TextAttributeDescriptor { + + public ManifestPkgAttrDescriptor(String xmlLocalName, String uiName, String nsUri, + String tooltip) { + super(xmlLocalName, uiName, nsUri, tooltip); + } + + /** + * @return A new {@link UiManifestPkgAttrNode} linked to this descriptor. + */ + @Override + public UiAttributeNode createUiNode(UiElementNode uiParent) { + return new UiManifestPkgAttrNode(this, uiParent); + } +} diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiManifestPkgAttrNode.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiManifestPkgAttrNode.java new file mode 100755 index 000000000..46771292e --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiManifestPkgAttrNode.java @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2009 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.editors.manifest.model; + +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.wizards.actions.NewProjectAction; +import com.android.ide.eclipse.adt.wizards.newproject.NewProjectWizard; +import com.android.ide.eclipse.common.project.AndroidManifestParser; +import com.android.ide.eclipse.common.project.BaseProjectHelper; +import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor; +import com.android.ide.eclipse.editors.manifest.ManifestEditor; +import com.android.ide.eclipse.editors.ui.SectionHelper; +import com.android.ide.eclipse.editors.uimodel.UiElementNode; +import com.android.ide.eclipse.editors.uimodel.UiTextAttributeNode; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +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.Composite; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.dialogs.ElementListSelectionDialog; +import org.eclipse.ui.forms.IManagedForm; +import org.eclipse.ui.forms.events.HyperlinkAdapter; +import org.eclipse.ui.forms.events.HyperlinkEvent; +import org.eclipse.ui.forms.widgets.FormText; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.TableWrapData; +import org.eclipse.ui.part.FileEditorInput; + +import java.util.TreeSet; + +/** + * Represents an XML attribute to select an exisintg manifest package, that can be modified using + * a simple text field or a dialog to choose an existing package. + *

+ * See {@link UiTextAttributeNode} for more information. + */ +public class UiManifestPkgAttrNode extends UiTextAttributeNode { + + /** + * Creates a {@link UiManifestPkgAttrNode} object that will display ui to select or create + * a manifest package. + * @param attributeDescriptor the {@link AttributeDescriptor} object linked to the Ui Node. + */ + public UiManifestPkgAttrNode(AttributeDescriptor attributeDescriptor, UiElementNode uiParent) { + super(attributeDescriptor, uiParent); + } + + /* (non-java doc) + * Creates a label widget and an associated text field. + *

+ * As most other parts of the android manifest editor, this assumes the + * parent uses a table layout with 2 columns. + */ + @Override + public void createUiControl(final Composite parent, final IManagedForm managedForm) { + setManagedForm(managedForm); + FormToolkit toolkit = managedForm.getToolkit(); + TextAttributeDescriptor desc = (TextAttributeDescriptor) getDescriptor(); + + StringBuilder label = new StringBuilder(); + label.append("

"); //$NON-NLS-1$ + label.append(desc.getUiName()); + label.append("

"); //$NON-NLS-1$ + FormText formText = SectionHelper.createFormText(parent, toolkit, true /* isHtml */, + label.toString(), true /* setupLayoutData */); + formText.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(HyperlinkEvent e) { + super.linkActivated(e); + doLabelClick(); + } + }); + formText.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE)); + SectionHelper.addControlTooltip(formText, desc.getTooltip()); + + Composite composite = toolkit.createComposite(parent); + composite.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE)); + GridLayout gl = new GridLayout(2, false); + gl.marginHeight = gl.marginWidth = 0; + composite.setLayout(gl); + // Fixes missing text borders under GTK... also requires adding a 1-pixel margin + // for the text field below + toolkit.paintBordersFor(composite); + + final Text text = toolkit.createText(composite, getCurrentValue()); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalIndent = 1; // Needed by the fixed composite borders under GTK + text.setLayoutData(gd); + + setTextWidget(text); + + Button browseButton = toolkit.createButton(composite, "Browse...", SWT.PUSH); + + browseButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + super.widgetSelected(e); + doBrowseClick(); + } + }); + + } + + /* (non-java doc) + * Adds a validator to the text field that calls managedForm.getMessageManager(). + */ + @Override + protected void onAddValidators(final Text text) { + ModifyListener listener = new ModifyListener() { + public void modifyText(ModifyEvent e) { + String package_name = text.getText(); + if (package_name.indexOf('.') < 1) { + getManagedForm().getMessageManager().addMessage(text, + "Package name should contain at least two identifiers.", + null /* data */, IMessageProvider.ERROR, text); + } else { + getManagedForm().getMessageManager().removeMessage(text, text); + } + } + }; + + text.addModifyListener(listener); + + // Make sure the validator removes its message(s) when the widget is disposed + text.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + getManagedForm().getMessageManager().removeMessage(text, text); + } + }); + + // Finally call the validator once to make sure the initial value is processed + listener.modifyText(null); + } + + /** + * Handles response to the Browse button by creating a Package dialog. + * */ + private void doBrowseClick() { + + // Display the list of AndroidManifest packages in a selection dialog + ElementListSelectionDialog dialog = new ElementListSelectionDialog( + getTextWidget().getShell(), + new ILabelProvider() { + public Image getImage(Object element) { + return null; + } + + public String getText(Object element) { + return element.toString(); + } + + public void addListener(ILabelProviderListener listener) { + } + + public void dispose() { + } + + public boolean isLabelProperty(Object element, String property) { + return false; + } + + public void removeListener(ILabelProviderListener listener) { + } + }); + + dialog.setTitle("Android Manifest Package Selection"); + dialog.setMessage("Select the Android Manifest package to target."); + + dialog.setElements(getPossibleValues(null)); + + // open the dialog and use the object selected if OK was clicked, or null otherwise + if (dialog.open() == Window.OK) { + String result = (String) dialog.getFirstResult(); + if (result != null && result.length() > 0) { + getTextWidget().setText(result); + } + } + } + + /** + * Handles response to the Label hyper link being activated. + */ + private void doLabelClick() { + // get the current package name + String package_name = getTextWidget().getText().trim(); + + if (package_name.length() == 0) { + createNewProject(); + } else { + displayExistingManifest(package_name); + } + } + + /** + * When the label is clicked and there's already a package name, this method + * attempts to find the project matching the android package name and it attempts + * to open the manifest editor. + * + * @param package_name The android package name to find. Must not be null. + */ + private void displayExistingManifest(String package_name) { + + // Look for the first project that uses this package name + for (IJavaProject project : BaseProjectHelper.getAndroidProjects()) { + // check that there is indeed a manifest file. + IFile manifestFile = AndroidManifestParser.getManifest(project.getProject()); + if (manifestFile == null) { + // no file? skip this project. + continue; + } + + AndroidManifestParser parser = null; + try { + parser = AndroidManifestParser.parseForData(manifestFile); + } catch (CoreException e) { + // ignore, handled below. + } + if (parser == null) { + // skip this project. + continue; + } + + if (package_name.equals(parser.getPackage())) { + // Found the project. + + IWorkbenchWindow win = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (win != null) { + IWorkbenchPage page = win.getActivePage(); + if (page != null) { + try { + page.openEditor( + new FileEditorInput(manifestFile), + ManifestEditor.ID, + true, /* activate */ + IWorkbenchPage.MATCH_INPUT); + } catch (PartInitException e) { + AdtPlugin.log(e, + "Opening editor failed for %s", //$NON-NLS-1$ + manifestFile.getFullPath()); + } + } + } + + // We found the project; even if we failed there's no need to keep looking. + return; + } + } + } + + /** + * Displays the New Project Wizard to create a new project. + * If one is successfully created, use the Android Package name. + */ + private void createNewProject() { + + NewProjectAction npwAction = new NewProjectAction(); + npwAction.run(null /*action*/); + if (npwAction.getDialogResult() == Dialog.OK) { + NewProjectWizard npw = (NewProjectWizard) npwAction.getWizard(); + String name = npw.getPackageName(); + if (name != null && name.length() > 0) { + getTextWidget().setText(name); + } + } + } + + /** + * Returns all the possible android package names that could be used. + * The prefix is not used. + * + * {@inheritDoc} + */ + @Override + public String[] getPossibleValues(String prefix) { + TreeSet packages = new TreeSet(); + + for (IJavaProject project : BaseProjectHelper.getAndroidProjects()) { + // check that there is indeed a manifest file. + IFile manifestFile = AndroidManifestParser.getManifest(project.getProject()); + if (manifestFile == null) { + // no file? skip this project. + continue; + } + + AndroidManifestParser parser = null; + try { + parser = AndroidManifestParser.parseForData(manifestFile); + } catch (CoreException e) { + // ignore, handled below. + } + if (parser == null) { + // skip this project. + continue; + } + + packages.add(parser.getPackage()); + } + + return packages.toArray(new String[packages.size()]); + } +} +