blob: e6a2007b3e622d3f7b58f8de12d2392d80a0f1c4 [file] [log] [blame]
/*
* 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;
}
}