| /* |
| * Copyright (C) 2007 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.editors.manifest.model; |
| |
| import com.android.ide.eclipse.adt.AdtPlugin; |
| import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; |
| import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; |
| import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor; |
| import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper; |
| import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; |
| import com.android.ide.eclipse.adt.internal.editors.uimodel.UiTextAttributeNode; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.ui.JavaUI; |
| import org.eclipse.jdt.ui.actions.OpenNewPackageWizardAction; |
| import org.eclipse.jdt.ui.actions.ShowInPackageViewAction; |
| import org.eclipse.jface.dialogs.IMessageProvider; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| 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.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.IEditorInput; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IWorkbenchPartSite; |
| import org.eclipse.ui.dialogs.SelectionDialog; |
| 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 java.util.ArrayList; |
| |
| /** |
| * Represents an XML attribute for a package, that can be modified using a simple text field or |
| * a dialog to choose an existing package. Also, there's a link to create a new package. |
| * <p/> |
| * See {@link UiTextAttributeNode} for more information. |
| */ |
| public class UiPackageAttributeNode extends UiTextAttributeNode { |
| |
| /** |
| * Creates a {@link UiPackageAttributeNode} object that will display ui to select or create |
| * a package. |
| * @param attributeDescriptor the {@link AttributeDescriptor} object linked to the Ui Node. |
| */ |
| public UiPackageAttributeNode(AttributeDescriptor attributeDescriptor, UiElementNode uiParent) { |
| super(attributeDescriptor, uiParent); |
| } |
| |
| /* (non-java doc) |
| * Creates a label widget and an associated text field. |
| * <p/> |
| * 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("<form><p><a href='unused'>"); //$NON-NLS-1$ |
| label.append(desc.getUiName()); |
| label.append("</a></p></form>"); //$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() { |
| @Override |
| 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() { |
| @Override |
| 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() { |
| Text text = getTextWidget(); |
| |
| // we need to get the project of the manifest. |
| IProject project = getProject(); |
| if (project != null) { |
| |
| try { |
| SelectionDialog dlg = JavaUI.createPackageDialog(text.getShell(), |
| JavaCore.create(project), 0); |
| dlg.setTitle("Select Android Package"); |
| dlg.setMessage("Select the package for the Android project."); |
| SelectionDialog.setDefaultImage(AdtPlugin.getAndroidLogo()); |
| |
| if (dlg.open() == Window.OK) { |
| Object[] results = dlg.getResult(); |
| if (results.length == 1) { |
| setPackageTextField((IPackageFragment)results[0]); |
| } |
| } |
| } catch (JavaModelException e1) { |
| } |
| } |
| } |
| |
| /** |
| * 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) { |
| createNewPackage(); |
| } else { |
| // Try to select the package in the Package Explorer for the current |
| // project and the current editor's site. |
| |
| IProject project = getProject(); |
| if (project == null) { |
| AdtPlugin.log(IStatus.ERROR, "Failed to get project for UiPackageAttribute"); //$NON-NLS-1$ |
| return; |
| } |
| |
| IWorkbenchPartSite site = getUiParent().getEditor().getSite(); |
| if (site == null) { |
| AdtPlugin.log(IStatus.ERROR, "Failed to get editor site for UiPackageAttribute"); //$NON-NLS-1$ |
| return; |
| } |
| |
| for (IPackageFragmentRoot root : getPackageFragmentRoots(project)) { |
| IPackageFragment fragment = root.getPackageFragment(package_name); |
| if (fragment != null && fragment.exists()) { |
| ShowInPackageViewAction action = new ShowInPackageViewAction(site); |
| action.run(fragment); |
| // This action's run() doesn't provide the status (although internally it could) |
| // so we just assume it worked. |
| return; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Utility method that returns the project for the current file being edited. |
| * |
| * @return The IProject for the current file being edited or null. |
| */ |
| private IProject getProject() { |
| UiElementNode uiNode = getUiParent(); |
| AndroidXmlEditor editor = uiNode.getEditor(); |
| IEditorInput input = editor.getEditorInput(); |
| if (input instanceof IFileEditorInput) { |
| // from the file editor we can get the IFile object, and from it, the IProject. |
| IFile file = ((IFileEditorInput)input).getFile(); |
| return file.getProject(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Utility method that computes and returns the list of {@link IPackageFragmentRoot} |
| * corresponding to the source folder of the specified project. |
| * |
| * @param project the project |
| * @return an array of IPackageFragmentRoot. Can be empty but not null. |
| */ |
| private IPackageFragmentRoot[] getPackageFragmentRoots(IProject project) { |
| ArrayList<IPackageFragmentRoot> result = new ArrayList<IPackageFragmentRoot>(); |
| try { |
| IJavaProject javaProject = JavaCore.create(project); |
| IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots(); |
| for (int i = 0; i < roots.length; i++) { |
| IClasspathEntry entry = roots[i].getRawClasspathEntry(); |
| if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { |
| result.add(roots[i]); |
| } |
| } |
| } catch (JavaModelException e) { |
| } |
| |
| return result.toArray(new IPackageFragmentRoot[result.size()]); |
| } |
| |
| /** |
| * Utility method that sets the package's text field to the package fragment's name. |
| * */ |
| private void setPackageTextField(IPackageFragment type) { |
| Text text = getTextWidget(); |
| |
| String name = type.getElementName(); |
| |
| text.setText(name); |
| } |
| |
| |
| /** |
| * Displays and handles a "Create Package Wizard". |
| * |
| * This is invoked by doLabelClick() when clicking on the hyperlink label with an |
| * empty package text field. |
| */ |
| private void createNewPackage() { |
| OpenNewPackageWizardAction action = new OpenNewPackageWizardAction(); |
| |
| IProject project = getProject(); |
| action.setSelection(new StructuredSelection(project)); |
| action.run(); |
| |
| IJavaElement element = action.getCreatedElement(); |
| if (element != null && |
| element.exists() && |
| element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) { |
| setPackageTextField((IPackageFragment) element); |
| } |
| } |
| |
| @Override |
| public String[] getPossibleValues(String prefix) { |
| // TODO: compute a list of existing packages for content assist completion |
| return null; |
| } |
| } |
| |