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             public void modifyText(ModifyEvent e) {
    148                 String package_name = text.getText();
    149                 if (package_name.indexOf('.') < 1) {
    150                     getManagedForm().getMessageManager().addMessage(text,
    151                             "Package name should contain at least two identifiers.",
    152                             null /* data */, IMessageProvider.ERROR, text);
    153                 } else {
    154                     getManagedForm().getMessageManager().removeMessage(text, text);
    155                 }
    156             }
    157         };
    158 
    159         text.addModifyListener(listener);
    160 
    161         // Make sure the validator removes its message(s) when the widget is disposed
    162         text.addDisposeListener(new DisposeListener() {
    163             public void widgetDisposed(DisposeEvent e) {
    164                 getManagedForm().getMessageManager().removeMessage(text, text);
    165             }
    166         });
    167 
    168         // Finally call the validator once to make sure the initial value is processed
    169         listener.modifyText(null);
    170     }
    171 
    172     /**
    173      * Handles response to the Browse button by creating a Package dialog.
    174      * */
    175     private void doBrowseClick() {
    176         Text text = getTextWidget();
    177 
    178         // we need to get the project of the manifest.
    179         IProject project = getProject();
    180         if (project != null) {
    181 
    182             try {
    183                 SelectionDialog dlg = JavaUI.createPackageDialog(text.getShell(),
    184                         JavaCore.create(project), 0);
    185                 dlg.setTitle("Select Android Package");
    186                 dlg.setMessage("Select the package for the Android project.");
    187                 SelectionDialog.setDefaultImage(AdtPlugin.getAndroidLogo());
    188 
    189                 if (dlg.open() == Window.OK) {
    190                     Object[] results = dlg.getResult();
    191                     if (results.length == 1) {
    192                         setPackageTextField((IPackageFragment)results[0]);
    193                     }
    194                 }
    195             } catch (JavaModelException e1) {
    196             }
    197         }
    198     }
    199 
    200     /**
    201      * Handles response to the Label hyper link being activated.
    202      */
    203     private void doLabelClick() {
    204         // get the current package name
    205         String package_name = getTextWidget().getText().trim();
    206 
    207         if (package_name.length() == 0) {
    208             createNewPackage();
    209         } else {
    210             // Try to select the package in the Package Explorer for the current
    211             // project and the current editor's site.
    212 
    213             IProject project = getProject();
    214             if (project == null) {
    215                 AdtPlugin.log(IStatus.ERROR, "Failed to get project for UiPackageAttribute"); //$NON-NLS-1$
    216                 return;
    217             }
    218 
    219             IWorkbenchPartSite site = getUiParent().getEditor().getSite();
    220             if (site == null) {
    221                 AdtPlugin.log(IStatus.ERROR, "Failed to get editor site for UiPackageAttribute"); //$NON-NLS-1$
    222                 return;
    223             }
    224 
    225             for (IPackageFragmentRoot root : getPackageFragmentRoots(project)) {
    226                 IPackageFragment fragment = root.getPackageFragment(package_name);
    227                 if (fragment != null && fragment.exists()) {
    228                     ShowInPackageViewAction action = new ShowInPackageViewAction(site);
    229                     action.run(fragment);
    230                     // This action's run() doesn't provide the status (although internally it could)
    231                     // so we just assume it worked.
    232                     return;
    233                 }
    234             }
    235         }
    236     }
    237 
    238     /**
    239      * Utility method that returns the project for the current file being edited.
    240      *
    241      * @return The IProject for the current file being edited or null.
    242      */
    243     private IProject getProject() {
    244         UiElementNode uiNode = getUiParent();
    245         AndroidXmlEditor editor = uiNode.getEditor();
    246         IEditorInput input = editor.getEditorInput();
    247         if (input instanceof IFileEditorInput) {
    248             // from the file editor we can get the IFile object, and from it, the IProject.
    249             IFile file = ((IFileEditorInput)input).getFile();
    250             return file.getProject();
    251         }
    252 
    253         return null;
    254     }
    255 
    256     /**
    257      * Utility method that computes and returns the list of {@link IPackageFragmentRoot}
    258      * corresponding to the source folder of the specified project.
    259      *
    260      * @param project the project
    261      * @return an array of IPackageFragmentRoot. Can be empty but not null.
    262      */
    263     private IPackageFragmentRoot[] getPackageFragmentRoots(IProject project) {
    264         ArrayList<IPackageFragmentRoot> result = new ArrayList<IPackageFragmentRoot>();
    265         try {
    266             IJavaProject javaProject = JavaCore.create(project);
    267             IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
    268             for (int i = 0; i < roots.length; i++) {
    269                 IClasspathEntry entry = roots[i].getRawClasspathEntry();
    270                 if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
    271                     result.add(roots[i]);
    272                 }
    273             }
    274         } catch (JavaModelException e) {
    275         }
    276 
    277         return result.toArray(new IPackageFragmentRoot[result.size()]);
    278     }
    279 
    280     /**
    281      * Utility method that sets the package's text field to the package fragment's name.
    282      * */
    283     private void setPackageTextField(IPackageFragment type) {
    284         Text text = getTextWidget();
    285 
    286         String name = type.getElementName();
    287 
    288         text.setText(name);
    289     }
    290 
    291 
    292     /**
    293      * Displays and handles a "Create Package Wizard".
    294      *
    295      * This is invoked by doLabelClick() when clicking on the hyperlink label with an
    296      * empty package text field.
    297      */
    298     private void createNewPackage() {
    299         OpenNewPackageWizardAction action = new OpenNewPackageWizardAction();
    300 
    301         IProject project = getProject();
    302         action.setSelection(new StructuredSelection(project));
    303         action.run();
    304 
    305         IJavaElement element = action.getCreatedElement();
    306         if (element != null &&
    307                 element.exists() &&
    308                 element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
    309             setPackageTextField((IPackageFragment) element);
    310         }
    311     }
    312 
    313     @Override
    314     public String[] getPossibleValues(String prefix) {
    315         // TODO: compute a list of existing packages for content assist completion
    316         return null;
    317     }
    318 }
    319 
    320