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.common.xml.ManifestData;
     20 import com.android.ide.eclipse.adt.AdtPlugin;
     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.manifest.ManifestEditor;
     24 import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper;
     25 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
     26 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiTextAttributeNode;
     27 import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
     28 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
     29 import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
     30 import com.android.ide.eclipse.adt.internal.wizards.actions.NewProjectAction;
     31 import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizard;
     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             @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 
    179         // Display the list of AndroidManifest packages in a selection dialog
    180         ElementListSelectionDialog dialog = new ElementListSelectionDialog(
    181                 getTextWidget().getShell(),
    182                 new ILabelProvider() {
    183                     @Override
    184                     public Image getImage(Object element) {
    185                         return null;
    186                     }
    187 
    188                     @Override
    189                     public String getText(Object element) {
    190                         return element.toString();
    191                     }
    192 
    193                     @Override
    194                     public void addListener(ILabelProviderListener listener) {
    195                     }
    196 
    197                     @Override
    198                     public void dispose() {
    199                     }
    200 
    201                     @Override
    202                     public boolean isLabelProperty(Object element, String property) {
    203                         return false;
    204                     }
    205 
    206                     @Override
    207                     public void removeListener(ILabelProviderListener listener) {
    208                     }
    209                 });
    210 
    211         dialog.setTitle("Android Manifest Package Selection");
    212         dialog.setMessage("Select the Android Manifest package to target.");
    213 
    214         dialog.setElements(getPossibleValues(null));
    215 
    216         // open the dialog and use the object selected if OK was clicked, or null otherwise
    217         if (dialog.open() == Window.OK) {
    218             String result = (String) dialog.getFirstResult();
    219             if (result != null && result.length() > 0) {
    220                 getTextWidget().setText(result);
    221             }
    222         }
    223     }
    224 
    225     /**
    226      * Handles response to the Label hyper link being activated.
    227      */
    228     private void doLabelClick() {
    229         // get the current package name
    230         String package_name = getTextWidget().getText().trim();
    231 
    232         if (package_name.length() == 0) {
    233             createNewProject();
    234         } else {
    235             displayExistingManifest(package_name);
    236         }
    237     }
    238 
    239     /**
    240      * When the label is clicked and there's already a package name, this method
    241      * attempts to find the project matching the android package name and it attempts
    242      * to open the manifest editor.
    243      *
    244      * @param package_name The android package name to find. Must not be null.
    245      */
    246     private void displayExistingManifest(String package_name) {
    247 
    248         // Look for the first project that uses this package name
    249         for (IJavaProject project : BaseProjectHelper.getAndroidProjects(null /*filter*/)) {
    250             // check that there is indeed a manifest file.
    251             IFile manifestFile = ProjectHelper.getManifest(project.getProject());
    252             if (manifestFile == null) {
    253                 // no file? skip this project.
    254                 continue;
    255             }
    256 
    257             ManifestData manifestData = AndroidManifestHelper.parseForData(manifestFile);
    258             if (manifestData == null) {
    259                 // skip this project.
    260                 continue;
    261             }
    262 
    263             if (package_name.equals(manifestData.getPackage())) {
    264                 // Found the project.
    265 
    266                 IWorkbenchWindow win = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
    267                 if (win != null) {
    268                     IWorkbenchPage page = win.getActivePage();
    269                     if (page != null) {
    270                         try {
    271                             page.openEditor(
    272                                     new FileEditorInput(manifestFile),
    273                                     ManifestEditor.ID,
    274                                     true, /* activate */
    275                                     IWorkbenchPage.MATCH_INPUT);
    276                         } catch (PartInitException e) {
    277                             AdtPlugin.log(e,
    278                                     "Opening editor failed for %s",  //$NON-NLS-1$
    279                                     manifestFile.getFullPath());
    280                         }
    281                     }
    282                 }
    283 
    284                 // We found the project; even if we failed there's no need to keep looking.
    285                 return;
    286             }
    287         }
    288     }
    289 
    290     /**
    291      * Displays the New Project Wizard to create a new project.
    292      * If one is successfully created, use the Android Package name.
    293      */
    294     private void createNewProject() {
    295 
    296         NewProjectAction npwAction = new NewProjectAction();
    297         npwAction.run(null /*action*/);
    298         if (npwAction.getDialogResult() == Dialog.OK) {
    299             NewProjectWizard npw = (NewProjectWizard) npwAction.getWizard();
    300             String name = npw.getPackageName();
    301             if (name != null && name.length() > 0) {
    302                 getTextWidget().setText(name);
    303             }
    304         }
    305     }
    306 
    307     /**
    308      * Returns all the possible android package names that could be used.
    309      * The prefix is not used.
    310      *
    311      * {@inheritDoc}
    312      */
    313     @Override
    314     public String[] getPossibleValues(String prefix) {
    315         TreeSet<String> packages = new TreeSet<String>();
    316 
    317         for (IJavaProject project : BaseProjectHelper.getAndroidProjects(null /*filter*/)) {
    318             // check that there is indeed a manifest file.
    319             ManifestData manifestData = AndroidManifestHelper.parseForData(project.getProject());
    320             if (manifestData == null) {
    321                 // skip this project.
    322                 continue;
    323             }
    324 
    325             packages.add(manifestData.getPackage());
    326         }
    327 
    328         return packages.toArray(new String[packages.size()]);
    329     }
    330 }
    331 
    332