Home | History | Annotate | Download | only in model
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.editors.manifest.model;
     18 
     19 import com.android.ide.eclipse.adt.AdtPlugin;
     20 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
     21 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
     22 import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor;
     23 import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper;
     24 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
     25 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiTextAttributeNode;
     26 
     27 import org.eclipse.core.resources.IFile;
     28 import org.eclipse.core.resources.IProject;
     29 import org.eclipse.core.runtime.IStatus;
     30 import org.eclipse.jdt.core.IClasspathEntry;
     31 import org.eclipse.jdt.core.IJavaElement;
     32 import org.eclipse.jdt.core.IJavaProject;
     33 import org.eclipse.jdt.core.IPackageFragment;
     34 import org.eclipse.jdt.core.IPackageFragmentRoot;
     35 import org.eclipse.jdt.core.JavaCore;
     36 import org.eclipse.jdt.core.JavaModelException;
     37 import org.eclipse.jdt.ui.JavaUI;
     38 import org.eclipse.jdt.ui.actions.OpenNewPackageWizardAction;
     39 import org.eclipse.jdt.ui.actions.ShowInPackageViewAction;
     40 import org.eclipse.jface.dialogs.IMessageProvider;
     41 import org.eclipse.jface.viewers.StructuredSelection;
     42 import org.eclipse.jface.window.Window;
     43 import org.eclipse.swt.SWT;
     44 import org.eclipse.swt.events.DisposeEvent;
     45 import org.eclipse.swt.events.DisposeListener;
     46 import org.eclipse.swt.events.ModifyEvent;
     47 import org.eclipse.swt.events.ModifyListener;
     48 import org.eclipse.swt.events.SelectionAdapter;
     49 import org.eclipse.swt.events.SelectionEvent;
     50 import org.eclipse.swt.layout.GridData;
     51 import org.eclipse.swt.layout.GridLayout;
     52 import org.eclipse.swt.widgets.Button;
     53 import org.eclipse.swt.widgets.Composite;
     54 import org.eclipse.swt.widgets.Text;
     55 import org.eclipse.ui.IEditorInput;
     56 import org.eclipse.ui.IFileEditorInput;
     57 import org.eclipse.ui.IWorkbenchPartSite;
     58 import org.eclipse.ui.dialogs.SelectionDialog;
     59 import org.eclipse.ui.forms.IManagedForm;
     60 import org.eclipse.ui.forms.events.HyperlinkAdapter;
     61 import org.eclipse.ui.forms.events.HyperlinkEvent;
     62 import org.eclipse.ui.forms.widgets.FormText;
     63 import org.eclipse.ui.forms.widgets.FormToolkit;
     64 import org.eclipse.ui.forms.widgets.TableWrapData;
     65 
     66 import java.util.ArrayList;
     67 
     68 /**
     69  * Represents an XML attribute for a package, that can be modified using a simple text field or
     70  * a dialog to choose an existing package. Also, there's a link to create a new package.
     71  * <p/>
     72  * See {@link UiTextAttributeNode} for more information.
     73  */
     74 public class UiPackageAttributeNode extends UiTextAttributeNode {
     75 
     76     /**
     77      * Creates a {@link UiPackageAttributeNode} object that will display ui to select or create
     78      * a package.
     79      * @param attributeDescriptor the {@link AttributeDescriptor} object linked to the Ui Node.
     80      */
     81     public UiPackageAttributeNode(AttributeDescriptor attributeDescriptor, UiElementNode uiParent) {
     82         super(attributeDescriptor, uiParent);
     83     }
     84 
     85     /* (non-java doc)
     86      * Creates a label widget and an associated text field.
     87      * <p/>
     88      * As most other parts of the android manifest editor, this assumes the
     89      * parent uses a table layout with 2 columns.
     90      */
     91     @Override
     92     public void createUiControl(final Composite parent, final IManagedForm managedForm) {
     93         setManagedForm(managedForm);
     94         FormToolkit toolkit = managedForm.getToolkit();
     95         TextAttributeDescriptor desc = (TextAttributeDescriptor) getDescriptor();
     96 
     97         StringBuilder label = new StringBuilder();
     98         label.append("<form><p><a href='unused'>");  //$NON-NLS-1$
     99         label.append(desc.getUiName());
    100         label.append("</a></p></form>");  //$NON-NLS-1$
    101         FormText formText = SectionHelper.createFormText(parent, toolkit, true /* isHtml */,
    102                 label.toString(), true /* setupLayoutData */);
    103         formText.addHyperlinkListener(new HyperlinkAdapter() {
    104             @Override
    105             public void linkActivated(HyperlinkEvent e) {
    106                 super.linkActivated(e);
    107                 doLabelClick();
    108             }
    109         });
    110         formText.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE));
    111         SectionHelper.addControlTooltip(formText, desc.getTooltip());
    112 
    113         Composite composite = toolkit.createComposite(parent);
    114         composite.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE));
    115         GridLayout gl = new GridLayout(2, false);
    116         gl.marginHeight = gl.marginWidth = 0;
    117         composite.setLayout(gl);
    118         // Fixes missing text borders under GTK... also requires adding a 1-pixel margin
    119         // for the text field below
    120         toolkit.paintBordersFor(composite);
    121 
    122         final Text text = toolkit.createText(composite, getCurrentValue());
    123         GridData gd = new GridData(GridData.FILL_HORIZONTAL);
    124         gd.horizontalIndent = 1;  // Needed by the fixed composite borders under GTK
    125         text.setLayoutData(gd);
    126 
    127         setTextWidget(text);
    128 
    129         Button browseButton = toolkit.createButton(composite, "Browse...", SWT.PUSH);
    130 
    131         browseButton.addSelectionListener(new SelectionAdapter() {
    132             @Override
    133             public void widgetSelected(SelectionEvent e) {
    134                 super.widgetSelected(e);
    135                 doBrowseClick();
    136             }
    137         });
    138 
    139     }
    140 
    141     /* (non-java doc)
    142      * Adds a validator to the text field that calls managedForm.getMessageManager().
    143      */
    144     @Override
    145     protected void onAddValidators(final Text text) {
    146         ModifyListener listener = new ModifyListener() {
    147             @Override
    148             public void modifyText(ModifyEvent e) {
    149                 String package_name = text.getText();
    150                 if (package_name.indexOf('.') < 1) {
    151                     getManagedForm().getMessageManager().addMessage(text,
    152                             "Package name should contain at least two identifiers.",
    153                             null /* data */, IMessageProvider.ERROR, text);
    154                 } else {
    155                     getManagedForm().getMessageManager().removeMessage(text, text);
    156                 }
    157             }
    158         };
    159 
    160         text.addModifyListener(listener);
    161 
    162         // Make sure the validator removes its message(s) when the widget is disposed
    163         text.addDisposeListener(new DisposeListener() {
    164             @Override
    165             public void widgetDisposed(DisposeEvent e) {
    166                 getManagedForm().getMessageManager().removeMessage(text, text);
    167             }
    168         });
    169 
    170         // Finally call the validator once to make sure the initial value is processed
    171         listener.modifyText(null);
    172     }
    173 
    174     /**
    175      * Handles response to the Browse button by creating a Package dialog.
    176      * */
    177     private void doBrowseClick() {
    178         Text text = getTextWidget();
    179 
    180         // we need to get the project of the manifest.
    181         IProject project = getProject();
    182         if (project != null) {
    183 
    184             try {
    185                 SelectionDialog dlg = JavaUI.createPackageDialog(text.getShell(),
    186                         JavaCore.create(project), 0);
    187                 dlg.setTitle("Select Android Package");
    188                 dlg.setMessage("Select the package for the Android project.");
    189                 SelectionDialog.setDefaultImage(AdtPlugin.getAndroidLogo());
    190 
    191                 if (dlg.open() == Window.OK) {
    192                     Object[] results = dlg.getResult();
    193                     if (results.length == 1) {
    194                         setPackageTextField((IPackageFragment)results[0]);
    195                     }
    196                 }
    197             } catch (JavaModelException e1) {
    198             }
    199         }
    200     }
    201 
    202     /**
    203      * Handles response to the Label hyper link being activated.
    204      */
    205     private void doLabelClick() {
    206         // get the current package name
    207         String package_name = getTextWidget().getText().trim();
    208 
    209         if (package_name.length() == 0) {
    210             createNewPackage();
    211         } else {
    212             // Try to select the package in the Package Explorer for the current
    213             // project and the current editor's site.
    214 
    215             IProject project = getProject();
    216             if (project == null) {
    217                 AdtPlugin.log(IStatus.ERROR, "Failed to get project for UiPackageAttribute"); //$NON-NLS-1$
    218                 return;
    219             }
    220 
    221             IWorkbenchPartSite site = getUiParent().getEditor().getSite();
    222             if (site == null) {
    223                 AdtPlugin.log(IStatus.ERROR, "Failed to get editor site for UiPackageAttribute"); //$NON-NLS-1$
    224                 return;
    225             }
    226 
    227             for (IPackageFragmentRoot root : getPackageFragmentRoots(project)) {
    228                 IPackageFragment fragment = root.getPackageFragment(package_name);
    229                 if (fragment != null && fragment.exists()) {
    230                     ShowInPackageViewAction action = new ShowInPackageViewAction(site);
    231                     action.run(fragment);
    232                     // This action's run() doesn't provide the status (although internally it could)
    233                     // so we just assume it worked.
    234                     return;
    235                 }
    236             }
    237         }
    238     }
    239 
    240     /**
    241      * Utility method that returns the project for the current file being edited.
    242      *
    243      * @return The IProject for the current file being edited or null.
    244      */
    245     private IProject getProject() {
    246         UiElementNode uiNode = getUiParent();
    247         AndroidXmlEditor editor = uiNode.getEditor();
    248         IEditorInput input = editor.getEditorInput();
    249         if (input instanceof IFileEditorInput) {
    250             // from the file editor we can get the IFile object, and from it, the IProject.
    251             IFile file = ((IFileEditorInput)input).getFile();
    252             return file.getProject();
    253         }
    254 
    255         return null;
    256     }
    257 
    258     /**
    259      * Utility method that computes and returns the list of {@link IPackageFragmentRoot}
    260      * corresponding to the source folder of the specified project.
    261      *
    262      * @param project the project
    263      * @return an array of IPackageFragmentRoot. Can be empty but not null.
    264      */
    265     private IPackageFragmentRoot[] getPackageFragmentRoots(IProject project) {
    266         ArrayList<IPackageFragmentRoot> result = new ArrayList<IPackageFragmentRoot>();
    267         try {
    268             IJavaProject javaProject = JavaCore.create(project);
    269             IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
    270             for (int i = 0; i < roots.length; i++) {
    271                 IClasspathEntry entry = roots[i].getRawClasspathEntry();
    272                 if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
    273                     result.add(roots[i]);
    274                 }
    275             }
    276         } catch (JavaModelException e) {
    277         }
    278 
    279         return result.toArray(new IPackageFragmentRoot[result.size()]);
    280     }
    281 
    282     /**
    283      * Utility method that sets the package's text field to the package fragment's name.
    284      * */
    285     private void setPackageTextField(IPackageFragment type) {
    286         Text text = getTextWidget();
    287 
    288         String name = type.getElementName();
    289 
    290         text.setText(name);
    291     }
    292 
    293 
    294     /**
    295      * Displays and handles a "Create Package Wizard".
    296      *
    297      * This is invoked by doLabelClick() when clicking on the hyperlink label with an
    298      * empty package text field.
    299      */
    300     private void createNewPackage() {
    301         OpenNewPackageWizardAction action = new OpenNewPackageWizardAction();
    302 
    303         IProject project = getProject();
    304         action.setSelection(new StructuredSelection(project));
    305         action.run();
    306 
    307         IJavaElement element = action.getCreatedElement();
    308         if (element != null &&
    309                 element.exists() &&
    310                 element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
    311             setPackageTextField((IPackageFragment) element);
    312         }
    313     }
    314 
    315     @Override
    316     public String[] getPossibleValues(String prefix) {
    317         // TODO: compute a list of existing packages for content assist completion
    318         return null;
    319     }
    320 }
    321 
    322