Home | History | Annotate | Download | only in model
      1 /*
      2  * Copyright (C) 2009 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.descriptors.AttributeDescriptor;
     21 import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor;
     22 import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
     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 import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
     27 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
     28 import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
     29 import com.android.ide.eclipse.adt.internal.wizards.actions.NewProjectAction;
     30 import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizard;
     31 import com.android.sdklib.xml.ManifestData;
     32 
     33 import org.eclipse.core.resources.IFile;
     34 import org.eclipse.jdt.core.IJavaProject;
     35 import org.eclipse.jface.dialogs.Dialog;
     36 import org.eclipse.jface.dialogs.IMessageProvider;
     37 import org.eclipse.jface.viewers.ILabelProvider;
     38 import org.eclipse.jface.viewers.ILabelProviderListener;
     39 import org.eclipse.jface.window.Window;
     40 import org.eclipse.swt.SWT;
     41 import org.eclipse.swt.events.DisposeEvent;
     42 import org.eclipse.swt.events.DisposeListener;
     43 import org.eclipse.swt.events.ModifyEvent;
     44 import org.eclipse.swt.events.ModifyListener;
     45 import org.eclipse.swt.events.SelectionAdapter;
     46 import org.eclipse.swt.events.SelectionEvent;
     47 import org.eclipse.swt.graphics.Image;
     48 import org.eclipse.swt.layout.GridData;
     49 import org.eclipse.swt.layout.GridLayout;
     50 import org.eclipse.swt.widgets.Button;
     51 import org.eclipse.swt.widgets.Composite;
     52 import org.eclipse.swt.widgets.Text;
     53 import org.eclipse.ui.IWorkbenchPage;
     54 import org.eclipse.ui.IWorkbenchWindow;
     55 import org.eclipse.ui.PartInitException;
     56 import org.eclipse.ui.PlatformUI;
     57 import org.eclipse.ui.dialogs.ElementListSelectionDialog;
     58 import org.eclipse.ui.forms.IManagedForm;
     59 import org.eclipse.ui.forms.events.HyperlinkAdapter;
     60 import org.eclipse.ui.forms.events.HyperlinkEvent;
     61 import org.eclipse.ui.forms.widgets.FormText;
     62 import org.eclipse.ui.forms.widgets.FormToolkit;
     63 import org.eclipse.ui.forms.widgets.TableWrapData;
     64 import org.eclipse.ui.part.FileEditorInput;
     65 
     66 import java.util.TreeSet;
     67 
     68 /**
     69  * Represents an XML attribute to select an existing manifest package, that can be modified using
     70  * a simple text field or a dialog to choose an existing package.
     71  * <p/>
     72  * See {@link UiTextAttributeNode} for more information.
     73  */
     74 public class UiManifestPkgAttrNode extends UiTextAttributeNode {
     75 
     76     /**
     77      * Creates a {@link UiManifestPkgAttrNode} object that will display ui to select or create
     78      * a manifest package.
     79      * @param attributeDescriptor the {@link AttributeDescriptor} object linked to the Ui Node.
     80      */
     81     public UiManifestPkgAttrNode(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 
    177         // Display the list of AndroidManifest packages in a selection dialog
    178         ElementListSelectionDialog dialog = new ElementListSelectionDialog(
    179                 getTextWidget().getShell(),
    180                 new ILabelProvider() {
    181                     public Image getImage(Object element) {
    182                         return null;
    183                     }
    184 
    185                     public String getText(Object element) {
    186                         return element.toString();
    187                     }
    188 
    189                     public void addListener(ILabelProviderListener listener) {
    190                     }
    191 
    192                     public void dispose() {
    193                     }
    194 
    195                     public boolean isLabelProperty(Object element, String property) {
    196                         return false;
    197                     }
    198 
    199                     public void removeListener(ILabelProviderListener listener) {
    200                     }
    201                 });
    202 
    203         dialog.setTitle("Android Manifest Package Selection");
    204         dialog.setMessage("Select the Android Manifest package to target.");
    205 
    206         dialog.setElements(getPossibleValues(null));
    207 
    208         // open the dialog and use the object selected if OK was clicked, or null otherwise
    209         if (dialog.open() == Window.OK) {
    210             String result = (String) dialog.getFirstResult();
    211             if (result != null && result.length() > 0) {
    212                 getTextWidget().setText(result);
    213             }
    214         }
    215     }
    216 
    217     /**
    218      * Handles response to the Label hyper link being activated.
    219      */
    220     private void doLabelClick() {
    221         // get the current package name
    222         String package_name = getTextWidget().getText().trim();
    223 
    224         if (package_name.length() == 0) {
    225             createNewProject();
    226         } else {
    227             displayExistingManifest(package_name);
    228         }
    229     }
    230 
    231     /**
    232      * When the label is clicked and there's already a package name, this method
    233      * attempts to find the project matching the android package name and it attempts
    234      * to open the manifest editor.
    235      *
    236      * @param package_name The android package name to find. Must not be null.
    237      */
    238     private void displayExistingManifest(String package_name) {
    239 
    240         // Look for the first project that uses this package name
    241         for (IJavaProject project : BaseProjectHelper.getAndroidProjects(null /*filter*/)) {
    242             // check that there is indeed a manifest file.
    243             IFile manifestFile = ProjectHelper.getManifest(project.getProject());
    244             if (manifestFile == null) {
    245                 // no file? skip this project.
    246                 continue;
    247             }
    248 
    249             ManifestData manifestData = AndroidManifestHelper.parseForData(manifestFile);
    250             if (manifestData == null) {
    251                 // skip this project.
    252                 continue;
    253             }
    254 
    255             if (package_name.equals(manifestData.getPackage())) {
    256                 // Found the project.
    257 
    258                 IWorkbenchWindow win = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
    259                 if (win != null) {
    260                     IWorkbenchPage page = win.getActivePage();
    261                     if (page != null) {
    262                         try {
    263                             page.openEditor(
    264                                     new FileEditorInput(manifestFile),
    265                                     ManifestEditor.ID,
    266                                     true, /* activate */
    267                                     IWorkbenchPage.MATCH_INPUT);
    268                         } catch (PartInitException e) {
    269                             AdtPlugin.log(e,
    270                                     "Opening editor failed for %s",  //$NON-NLS-1$
    271                                     manifestFile.getFullPath());
    272                         }
    273                     }
    274                 }
    275 
    276                 // We found the project; even if we failed there's no need to keep looking.
    277                 return;
    278             }
    279         }
    280     }
    281 
    282     /**
    283      * Displays the New Project Wizard to create a new project.
    284      * If one is successfully created, use the Android Package name.
    285      */
    286     private void createNewProject() {
    287 
    288         NewProjectAction npwAction = new NewProjectAction();
    289         npwAction.run(null /*action*/);
    290         if (npwAction.getDialogResult() == Dialog.OK) {
    291             NewProjectWizard npw = (NewProjectWizard) npwAction.getWizard();
    292             String name = npw.getPackageName();
    293             if (name != null && name.length() > 0) {
    294                 getTextWidget().setText(name);
    295             }
    296         }
    297     }
    298 
    299     /**
    300      * Returns all the possible android package names that could be used.
    301      * The prefix is not used.
    302      *
    303      * {@inheritDoc}
    304      */
    305     @Override
    306     public String[] getPossibleValues(String prefix) {
    307         TreeSet<String> packages = new TreeSet<String>();
    308 
    309         for (IJavaProject project : BaseProjectHelper.getAndroidProjects(null /*filter*/)) {
    310             // check that there is indeed a manifest file.
    311             ManifestData manifestData = AndroidManifestHelper.parseForData(project.getProject());
    312             if (manifestData == null) {
    313                 // skip this project.
    314                 continue;
    315             }
    316 
    317             packages.add(manifestData.getPackage());
    318         }
    319 
    320         return packages.toArray(new String[packages.size()]);
    321     }
    322 }
    323 
    324