Home | History | Annotate | Download | only in ui
      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.ui;
     18 
     19 import static com.android.SdkConstants.ANDROID_PREFIX;
     20 import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
     21 
     22 import com.android.annotations.NonNull;
     23 import com.android.annotations.Nullable;
     24 import com.android.ide.common.rendering.api.ResourceValue;
     25 import com.android.ide.common.resources.ResourceItem;
     26 import com.android.ide.common.resources.ResourceRepository;
     27 import com.android.ide.common.resources.ResourceResolver;
     28 import com.android.ide.eclipse.adt.AdtPlugin;
     29 import com.android.ide.eclipse.adt.AdtUtils;
     30 import com.android.ide.eclipse.adt.internal.assetstudio.OpenCreateAssetSetWizardAction;
     31 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
     32 import com.android.ide.eclipse.adt.internal.editors.layout.properties.PropertyFactory;
     33 import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring;
     34 import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard;
     35 import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
     36 import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
     37 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
     38 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
     39 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
     40 import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
     41 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
     42 import com.android.resources.ResourceType;
     43 import com.android.utils.Pair;
     44 import com.google.common.collect.Maps;
     45 
     46 import org.eclipse.core.resources.IFile;
     47 import org.eclipse.core.resources.IProject;
     48 import org.eclipse.core.resources.IResource;
     49 import org.eclipse.core.runtime.IStatus;
     50 import org.eclipse.core.runtime.Status;
     51 import org.eclipse.jface.dialogs.IDialogConstants;
     52 import org.eclipse.jface.dialogs.IInputValidator;
     53 import org.eclipse.jface.dialogs.InputDialog;
     54 import org.eclipse.jface.text.IRegion;
     55 import org.eclipse.jface.window.Window;
     56 import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
     57 import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
     58 import org.eclipse.swt.SWT;
     59 import org.eclipse.swt.events.ModifyEvent;
     60 import org.eclipse.swt.events.ModifyListener;
     61 import org.eclipse.swt.events.SelectionAdapter;
     62 import org.eclipse.swt.events.SelectionEvent;
     63 import org.eclipse.swt.layout.GridData;
     64 import org.eclipse.swt.layout.GridLayout;
     65 import org.eclipse.swt.widgets.Button;
     66 import org.eclipse.swt.widgets.Composite;
     67 import org.eclipse.swt.widgets.Control;
     68 import org.eclipse.swt.widgets.Event;
     69 import org.eclipse.swt.widgets.Label;
     70 import org.eclipse.swt.widgets.Listener;
     71 import org.eclipse.swt.widgets.Shell;
     72 import org.eclipse.swt.widgets.Text;
     73 import org.eclipse.ui.IWorkbench;
     74 import org.eclipse.ui.PlatformUI;
     75 import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
     76 import org.eclipse.ui.dialogs.SelectionStatusDialog;
     77 
     78 import java.util.ArrayList;
     79 import java.util.Arrays;
     80 import java.util.Collection;
     81 import java.util.Collections;
     82 import java.util.List;
     83 import java.util.Locale;
     84 import java.util.Map;
     85 import java.util.regex.Matcher;
     86 import java.util.regex.Pattern;
     87 
     88 /**
     89  * A dialog to let the user select a resource based on a resource type.
     90  */
     91 public class ResourceChooser extends AbstractElementListSelectionDialog implements ModifyListener {
     92     /** The return code from the dialog for the user choosing "Clear" */
     93     public static final int CLEAR_RETURN_CODE = -5;
     94     /** The dialog button ID for the user choosing "Clear" */
     95     private static final int CLEAR_BUTTON_ID = CLEAR_RETURN_CODE;
     96 
     97     private Pattern mProjectResourcePattern;
     98     private ResourceType mResourceType;
     99     private final List<ResourceRepository> mProjectResources;
    100     private final ResourceRepository mFrameworkResources;
    101     private Pattern mSystemResourcePattern;
    102     private Button mProjectButton;
    103     private Button mSystemButton;
    104     private Button mNewButton;
    105     private String mCurrentResource;
    106     private final IProject mProject;
    107     private IInputValidator mInputValidator;
    108 
    109     /** Helper object used to draw previews for drawables and colors. */
    110     private ResourcePreviewHelper mPreviewHelper;
    111 
    112     /**
    113      * Textfield for editing the actual returned value, updated when selection
    114      * changes. Only shown if {@link #mShowValueText} is true.
    115      */
    116     private Text mEditValueText;
    117 
    118     /**
    119      * Whether the {@link #mEditValueText} textfield should be shown when the dialog is created.
    120      */
    121     private boolean mShowValueText;
    122 
    123     /**
    124      * Flag indicating whether it's the first time {@link #handleSelectionChanged()} is called.
    125      * This is used to filter out the first selection event, always called by the superclass
    126      * when the widget is created, to distinguish between "the dialog was created" and
    127      * "the user clicked on a selection result", since only the latter should wipe out the
    128      * manual user edit shown in the value text.
    129      */
    130     private boolean mFirstSelect = true;
    131 
    132     /**
    133      * Label used to show the resolved value in the resource chooser. Only shown
    134      * if the {@link #mResourceResolver} field is set.
    135      */
    136     private Label mResolvedLabel;
    137 
    138     /** Resource resolver used to show actual values for resources selected. (Optional). */
    139     private ResourceResolver mResourceResolver;
    140 
    141     /**
    142      * Creates a Resource Chooser dialog.
    143      * @param project Project being worked on
    144      * @param type The type of the resource to choose
    145      * @param projectResources The repository for the project
    146      * @param frameworkResources The Framework resource repository
    147      * @param parent the parent shell
    148      */
    149     private ResourceChooser(
    150             @NonNull IProject project,
    151             @NonNull ResourceType type,
    152             @NonNull List<ResourceRepository> projectResources,
    153             @Nullable ResourceRepository frameworkResources,
    154             @NonNull Shell parent) {
    155         super(parent, new ResourceLabelProvider());
    156         mProject = project;
    157 
    158         mResourceType = type;
    159         mProjectResources = projectResources;
    160         mFrameworkResources = frameworkResources;
    161 
    162         mProjectResourcePattern = Pattern.compile(
    163                 PREFIX_RESOURCE_REF + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$
    164 
    165         mSystemResourcePattern = Pattern.compile(
    166                 ANDROID_PREFIX + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$
    167 
    168         setTitle("Resource Chooser");
    169         setMessage(String.format("Choose a %1$s resource",
    170                 mResourceType.getDisplayName().toLowerCase(Locale.US)));
    171     }
    172 
    173     /**
    174      * Creates a new {@link ResourceChooser}
    175      *
    176      * @param editor the associated layout editor
    177      * @param type the resource type to choose
    178      * @return a new {@link ResourceChooser}
    179      */
    180     @NonNull
    181     public static ResourceChooser create(
    182             @NonNull GraphicalEditorPart editor,
    183             @NonNull ResourceType type) {
    184         IProject project = editor.getProject();
    185         Shell parent = editor.getCanvasControl().getShell();
    186         AndroidTargetData targetData = editor.getEditorDelegate().getEditor().getTargetData();
    187         ResourceChooser chooser = create(project, type, targetData, parent);
    188 
    189         // When editing Strings, allow editing the value text directly. When we
    190         // get inline editing support (where values entered directly into the
    191         // textual widget are translated automatically into a resource) this can
    192         // go away.
    193         if (type == ResourceType.STRING) {
    194             chooser.setResourceResolver(editor.getResourceResolver());
    195             chooser.setShowValueText(true);
    196         } else if (type == ResourceType.DIMEN || type == ResourceType.INTEGER) {
    197             chooser.setResourceResolver(editor.getResourceResolver());
    198         }
    199 
    200         chooser.setPreviewHelper(new ResourcePreviewHelper(chooser, editor));
    201         return chooser;
    202     }
    203 
    204     /**
    205      * Creates a new {@link ResourceChooser}
    206      *
    207      * @param project the associated project
    208      * @param type the resource type to choose
    209      * @param targetData the associated framework target data
    210      * @param parent the target shell
    211      * @return a new {@link ResourceChooser}
    212      */
    213     @NonNull
    214     public static ResourceChooser create(
    215             @NonNull IProject project,
    216             @NonNull ResourceType type,
    217             @Nullable AndroidTargetData targetData,
    218             @NonNull Shell parent) {
    219         ResourceManager manager = ResourceManager.getInstance();
    220 
    221         List<ResourceRepository> projectResources = new ArrayList<ResourceRepository>();
    222         ProjectResources resources = manager.getProjectResources(project);
    223         projectResources.add(resources);
    224 
    225         // Add in library project resources
    226         ProjectState projectState = Sdk.getProjectState(project);
    227         if (projectState != null) {
    228             List<IProject> libraries = projectState.getFullLibraryProjects();
    229             if (libraries != null && !libraries.isEmpty()) {
    230                 for (IProject library : libraries) {
    231                     projectResources.add(manager.getProjectResources(library));
    232                 }
    233             }
    234         }
    235 
    236         ResourceRepository frameworkResources =
    237                 targetData != null ? targetData.getFrameworkResources() : null;
    238         return new ResourceChooser(project, type, projectResources, frameworkResources, parent);
    239     }
    240 
    241     /**
    242      * Sets whether this dialog should show the value field as a separate text
    243      * value (and take the resulting value of the dialog from this text field
    244      * rather than from the selection)
    245      *
    246      * @param showValueText if true, show the value text field
    247      * @return this, for constructor chaining
    248      */
    249     public ResourceChooser setShowValueText(boolean showValueText) {
    250         mShowValueText = showValueText;
    251 
    252         return this;
    253     }
    254 
    255     /**
    256      * Sets the resource resolver to use to show resolved values for the current
    257      * selection
    258      *
    259      * @param resourceResolver the resource resolver to use
    260      * @return this, for constructor chaining
    261      */
    262     public ResourceChooser setResourceResolver(ResourceResolver resourceResolver) {
    263         mResourceResolver = resourceResolver;
    264 
    265         return this;
    266     }
    267 
    268     /**
    269      * Sets the {@link ResourcePreviewHelper} to use to preview drawable
    270      * resources, if any
    271      *
    272      * @param previewHelper the helper to use
    273      * @return this, for constructor chaining
    274      */
    275     public ResourceChooser setPreviewHelper(ResourcePreviewHelper previewHelper) {
    276         mPreviewHelper = previewHelper;
    277 
    278         return this;
    279     }
    280 
    281     /**
    282      * Sets the initial dialog size
    283      *
    284      * @param width the initial width
    285      * @param height the initial height
    286      * @return this, for constructor chaining
    287      */
    288     public ResourceChooser setInitialSize(int width, int height) {
    289         setSize(width, height);
    290 
    291         return this;
    292     }
    293 
    294     @Override
    295     public void create() {
    296         super.create();
    297 
    298         if (mShowValueText) {
    299             mEditValueText.selectAll();
    300             mEditValueText.setFocus();
    301         }
    302     }
    303 
    304     @Override
    305     protected void createButtonsForButtonBar(Composite parent) {
    306         createButton(parent, CLEAR_BUTTON_ID, "Clear", false /*defaultButton*/);
    307         super.createButtonsForButtonBar(parent);
    308     }
    309 
    310     @Override
    311     protected void buttonPressed(int buttonId) {
    312         super.buttonPressed(buttonId);
    313 
    314         if (buttonId == CLEAR_BUTTON_ID) {
    315             assert CLEAR_RETURN_CODE != Window.OK && CLEAR_RETURN_CODE != Window.CANCEL;
    316             setReturnCode(CLEAR_RETURN_CODE);
    317             close();
    318         }
    319     }
    320 
    321     /**
    322      * Sets the currently selected item
    323      *
    324      * @param resource the resource url for the currently selected item
    325      * @return this, for constructor chaining
    326      */
    327     public ResourceChooser setCurrentResource(@Nullable String resource) {
    328         mCurrentResource = resource;
    329 
    330         if (mShowValueText && mEditValueText != null) {
    331             mEditValueText.setText(resource);
    332         }
    333 
    334         return this;
    335     }
    336 
    337     /**
    338      * Returns the currently selected url
    339      *
    340      * @return the currently selected url
    341      */
    342     @Nullable
    343     public String getCurrentResource() {
    344         return mCurrentResource;
    345     }
    346 
    347     /**
    348      * Sets the input validator to use, if any
    349      *
    350      * @param inputValidator the validator
    351      * @return this, for constructor chaining
    352      */
    353     public ResourceChooser setInputValidator(@Nullable IInputValidator inputValidator) {
    354         mInputValidator = inputValidator;
    355 
    356         return this;
    357     }
    358 
    359     @Override
    360     protected void computeResult() {
    361         if (mShowValueText) {
    362             mCurrentResource = mEditValueText.getText();
    363             if (mCurrentResource.length() == 0) {
    364                 mCurrentResource = null;
    365             }
    366             return;
    367         }
    368 
    369         computeResultFromSelection();
    370     }
    371 
    372     private void computeResultFromSelection() {
    373         if (getSelectionIndex() == -1) {
    374             mCurrentResource = null;
    375             return;
    376         }
    377 
    378         Object[] elements = getSelectedElements();
    379         if (elements.length == 1 && elements[0] instanceof ResourceItem) {
    380             ResourceItem item = (ResourceItem)elements[0];
    381 
    382             mCurrentResource = item.getXmlString(mResourceType, mSystemButton.getSelection());
    383 
    384             if (mInputValidator != null && mInputValidator.isValid(mCurrentResource) != null) {
    385                 mCurrentResource = null;
    386             }
    387         }
    388     }
    389 
    390     @Override
    391     protected Control createDialogArea(Composite parent) {
    392         Composite top = (Composite)super.createDialogArea(parent);
    393 
    394         createMessageArea(top);
    395 
    396         createButtons(top);
    397         createFilterText(top);
    398         createFilteredList(top);
    399 
    400         // create the "New Resource" button
    401         createNewResButtons(top);
    402 
    403         // Optionally create the value text field, if {@link #mShowValueText} is true
    404         createValueField(top);
    405 
    406         setupResourceList();
    407         selectResourceString(mCurrentResource);
    408 
    409         return top;
    410     }
    411 
    412     /**
    413      * Creates the radio button to switch between project and system resources.
    414      * @param top the parent composite
    415      */
    416     private void createButtons(Composite top) {
    417         mProjectButton = new Button(top, SWT.RADIO);
    418         mProjectButton.setText("Project Resources");
    419         mProjectButton.addSelectionListener(new SelectionAdapter() {
    420             @Override
    421             public void widgetSelected(SelectionEvent e) {
    422                 super.widgetSelected(e);
    423                 if (mProjectButton.getSelection()) {
    424                     // Clear selection before changing the list contents. This works around
    425                     // a bug in the superclass where switching to the framework resources,
    426                     // choosing one of the last resources, then switching to the project
    427                     // resources would cause an exception when calling getSelection() because
    428                     // selection state doesn't get cleared when we set new contents on
    429                     // the filtered list.
    430                     fFilteredList.setSelection(new int[0]);
    431                     setupResourceList();
    432                     updateNewButton(false /*isSystem*/);
    433                     updateValue();
    434                 }
    435             }
    436         });
    437         mSystemButton = new Button(top, SWT.RADIO);
    438         mSystemButton.setText("System Resources");
    439         mSystemButton.addSelectionListener(new SelectionAdapter() {
    440             @Override
    441             public void widgetSelected(SelectionEvent e) {
    442                 super.widgetSelected(e);
    443                 if (mSystemButton.getSelection()) {
    444                     fFilteredList.setSelection(new int[0]);
    445                     setupResourceList();
    446                     updateNewButton(true /*isSystem*/);
    447                     updateValue();
    448                 }
    449             }
    450         });
    451         if (mFrameworkResources == null) {
    452             mSystemButton.setVisible(false);
    453         }
    454     }
    455 
    456     /**
    457      * Creates the "New Resource" button.
    458      * @param top the parent composite
    459      */
    460     private void createNewResButtons(Composite top) {
    461         mNewButton = new Button(top, SWT.NONE);
    462 
    463         String title = String.format("New %1$s...", mResourceType.getDisplayName());
    464         if (mResourceType == ResourceType.DRAWABLE) {
    465             title = "Create New Icon...";
    466         }
    467         mNewButton.setText(title);
    468 
    469         mNewButton.addSelectionListener(new SelectionAdapter() {
    470             @Override
    471             public void widgetSelected(SelectionEvent e) {
    472                 super.widgetSelected(e);
    473 
    474                 if (mResourceType == ResourceType.STRING) {
    475                     // Special case: Use Extract String refactoring wizard UI
    476                     String newName = createNewString();
    477                     selectAddedItem(newName);
    478                 } else if (mResourceType == ResourceType.DRAWABLE) {
    479                     // Special case: Use the "Create Icon Set" wizard
    480                     OpenCreateAssetSetWizardAction action =
    481                             new OpenCreateAssetSetWizardAction(mProject);
    482                     action.run();
    483                     List<IResource> files = action.getCreatedFiles();
    484                     if (files != null && files.size() > 0) {
    485                         String newName = AdtUtils.stripAllExtensions(files.get(0).getName());
    486                         // Recompute the "current resource" to select the new id
    487                         ResourceItem[] items = setupResourceList();
    488                         selectItemName(newName, items);
    489                     }
    490                 } else {
    491                     if (ResourceHelper.isValueBasedResourceType(mResourceType)) {
    492                         String newName = createNewValue(mResourceType);
    493                         if (newName != null) {
    494                             selectAddedItem(newName);
    495                         }
    496                     } else {
    497                         String newName = createNewFile(mResourceType);
    498                         if (newName != null) {
    499                             selectAddedItem(newName);
    500                         }
    501                     }
    502                 }
    503             }
    504 
    505             private void selectAddedItem(@NonNull String newName) {
    506                 // Recompute the "current resource" to select the new id
    507                 ResourceItem[] items = setupResourceList();
    508 
    509                 // Ensure that the name is in the list. There's a delay after
    510                 // an item is added (until the builder runs and processes the delta)
    511                 // so if it's not in the list, add it
    512                 boolean found = false;
    513                 for (ResourceItem item : items) {
    514                     if (newName.equals(item.getName())) {
    515                         found = true;
    516                         break;
    517                     }
    518                 }
    519                 if (!found) {
    520                     ResourceItem[] newItems = new ResourceItem[items.length + 1];
    521                     System.arraycopy(items, 0, newItems, 0, items.length);
    522                     newItems[items.length] = new ResourceItem(newName);
    523                     items = newItems;
    524                     Arrays.sort(items);
    525                     setListElements(items);
    526                     fFilteredList.setEnabled(newItems.length > 0);
    527                 }
    528 
    529                 selectItemName(newName, items);
    530             }
    531         });
    532     }
    533 
    534     /**
    535      * Creates the value text field.
    536      *
    537      * @param top the parent composite
    538      */
    539     private void createValueField(Composite top) {
    540         if (mShowValueText) {
    541             mEditValueText = new Text(top, SWT.BORDER);
    542             if (mCurrentResource != null) {
    543                 mEditValueText.setText(mCurrentResource);
    544             }
    545             mEditValueText.addModifyListener(this);
    546 
    547             GridData data = new GridData();
    548             data.grabExcessVerticalSpace = false;
    549             data.grabExcessHorizontalSpace = true;
    550             data.horizontalAlignment = GridData.FILL;
    551             data.verticalAlignment = GridData.BEGINNING;
    552             mEditValueText.setLayoutData(data);
    553             mEditValueText.setFont(top.getFont());
    554         }
    555 
    556         if (mResourceResolver != null) {
    557             mResolvedLabel = new Label(top, SWT.NONE);
    558             GridData data = new GridData();
    559             data.grabExcessVerticalSpace = false;
    560             data.grabExcessHorizontalSpace = true;
    561             data.horizontalAlignment = GridData.FILL;
    562             data.verticalAlignment = GridData.BEGINNING;
    563             mResolvedLabel.setLayoutData(data);
    564         }
    565 
    566         Composite workaround = PropertyFactory.addWorkaround(top);
    567         if (workaround != null) {
    568             workaround.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
    569         }
    570     }
    571 
    572     private void updateResolvedLabel() {
    573         if (mResourceResolver == null) {
    574             return;
    575         }
    576 
    577         String v = null;
    578         if (mCurrentResource != null) {
    579             v = mCurrentResource;
    580             if (mCurrentResource.startsWith(PREFIX_RESOURCE_REF)) {
    581                 ResourceValue value = mResourceResolver.findResValue(mCurrentResource, false);
    582                 if (value != null) {
    583                     v = value.getValue();
    584                 }
    585             }
    586         }
    587 
    588         if (v == null) {
    589             v = "";
    590         }
    591 
    592         mResolvedLabel.setText(String.format("Resolved Value: %1$s", v));
    593     }
    594 
    595     @Override
    596     protected void handleSelectionChanged() {
    597         super.handleSelectionChanged();
    598         if (mInputValidator != null) {
    599             Object[] elements = getSelectedElements();
    600             if (elements.length == 1 && elements[0] instanceof ResourceItem) {
    601                 ResourceItem item = (ResourceItem)elements[0];
    602                 String current = item.getXmlString(mResourceType, mSystemButton.getSelection());
    603                 String error = mInputValidator.isValid(current);
    604                 IStatus status;
    605                 if (error != null) {
    606                     status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, error);
    607                 } else {
    608                     status = new Status(IStatus.OK, AdtPlugin.PLUGIN_ID, null);
    609                 }
    610                 updateStatus(status);
    611             }
    612         }
    613 
    614         updateValue();
    615     }
    616 
    617     private void updateValue() {
    618         if (mPreviewHelper != null) {
    619             computeResult();
    620             mPreviewHelper.updatePreview(mResourceType, mCurrentResource);
    621         }
    622 
    623         if (mShowValueText) {
    624             if (mFirstSelect) {
    625                 mFirstSelect = false;
    626                 mEditValueText.selectAll();
    627             } else {
    628                 computeResultFromSelection();
    629                 mEditValueText.setText(mCurrentResource != null ? mCurrentResource : "");
    630             }
    631         }
    632 
    633         if (mResourceResolver != null) {
    634             if (!mShowValueText) {
    635                 computeResultFromSelection();
    636             }
    637             updateResolvedLabel();
    638         }
    639     }
    640 
    641     @Nullable
    642     private String createNewFile(ResourceType type) {
    643         // Show a name/value dialog entering the key name and the value
    644         Shell shell = AdtPlugin.getShell();
    645         if (shell == null) {
    646             return null;
    647         }
    648 
    649         ResourceNameValidator validator = ResourceNameValidator.create(true /*allowXmlExtension*/,
    650                 mProject, mResourceType);
    651         InputDialog d = new InputDialog(
    652                 AdtPlugin.getShell(),
    653                 "Enter name",  // title
    654                 "Enter name",
    655                 "", //$NON-NLS-1$
    656                 validator);
    657         if (d.open() == Window.OK) {
    658             String name = d.getValue().trim();
    659             if (name.length() == 0) {
    660                 return null;
    661             }
    662 
    663             Pair<IFile, IRegion> resource = ResourceHelper.createResource(mProject, type, name,
    664                     null);
    665             if (resource != null) {
    666                 return name;
    667             }
    668         }
    669 
    670         return null;
    671     }
    672 
    673 
    674     @Nullable
    675     private String createNewValue(ResourceType type) {
    676         // Show a name/value dialog entering the key name and the value
    677         Shell shell = AdtPlugin.getShell();
    678         if (shell == null) {
    679             return null;
    680         }
    681         NameValueDialog dialog = new NameValueDialog(shell, getFilter());
    682         if (dialog.open() != Window.OK) {
    683             return null;
    684         }
    685 
    686         String name = dialog.getName();
    687         String value = dialog.getValue();
    688         if (name.length() == 0 || value.length() == 0) {
    689             return null;
    690         }
    691 
    692         Pair<IFile, IRegion> resource = ResourceHelper.createResource(mProject, type, name, value);
    693         if (resource != null) {
    694             return name;
    695         }
    696 
    697         return null;
    698     }
    699 
    700     private String createNewString() {
    701         ExtractStringRefactoring ref = new ExtractStringRefactoring(
    702                 mProject, true /*enforceNew*/);
    703         RefactoringWizard wizard = new ExtractStringWizard(ref, mProject);
    704         RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
    705         try {
    706             IWorkbench w = PlatformUI.getWorkbench();
    707             if (op.run(w.getDisplay().getActiveShell(), wizard.getDefaultPageTitle()) ==
    708                     IDialogConstants.OK_ID) {
    709                 return ref.getXmlStringId();
    710             }
    711         } catch (InterruptedException ex) {
    712             // Interrupted. Pass.
    713         }
    714 
    715         return null;
    716     }
    717 
    718     /**
    719      * Setups the current list.
    720      */
    721     private ResourceItem[] setupResourceList() {
    722         Collection<ResourceItem> items = null;
    723         if (mProjectButton.getSelection()) {
    724             if (mProjectResources.size() == 1) {
    725                 items = mProjectResources.get(0).getResourceItemsOfType(mResourceType);
    726             } else {
    727                 Map<String, ResourceItem> merged = Maps.newHashMapWithExpectedSize(200);
    728                 for (ResourceRepository repository : mProjectResources) {
    729                     for (ResourceItem item : repository.getResourceItemsOfType(mResourceType)) {
    730                         if (!merged.containsKey(item.getName())) {
    731                             merged.put(item.getName(), item);
    732                         }
    733                     }
    734                 }
    735                 items = merged.values();
    736             }
    737         } else if (mSystemButton.getSelection()) {
    738             items = mFrameworkResources.getResourceItemsOfType(mResourceType);
    739         }
    740 
    741         if (items == null) {
    742             items = Collections.emptyList();
    743         }
    744 
    745         ResourceItem[] arrayItems = items.toArray(new ResourceItem[items.size()]);
    746 
    747         // sort the array
    748         Arrays.sort(arrayItems);
    749 
    750         setListElements(arrayItems);
    751         fFilteredList.setEnabled(arrayItems.length > 0);
    752 
    753         return arrayItems;
    754     }
    755 
    756     /**
    757      * Select an item by its name, if possible.
    758      */
    759     private void selectItemName(String itemName, ResourceItem[] items) {
    760         if (itemName == null || items == null) {
    761             return;
    762         }
    763 
    764         for (ResourceItem item : items) {
    765             if (itemName.equals(item.getName())) {
    766                 setSelection(new Object[] { item });
    767                 break;
    768             }
    769         }
    770     }
    771 
    772     /**
    773      * Select an item by its full resource string.
    774      * This also selects between project and system repository based on the resource string.
    775      */
    776     private void selectResourceString(String resourceString) {
    777         boolean isSystem = false;
    778         String itemName = null;
    779 
    780         if (resourceString != null) {
    781             // Is this a system resource?
    782             // If not a system resource or if they are not available, this will be a project res.
    783             Matcher m = mSystemResourcePattern.matcher(resourceString);
    784             if (m.matches()) {
    785                 itemName = m.group(1);
    786                 isSystem = true;
    787             }
    788 
    789             if (!isSystem && itemName == null) {
    790                 // Try to match project resource name
    791                 m = mProjectResourcePattern.matcher(resourceString);
    792                 if (m.matches()) {
    793                     itemName = m.group(1);
    794                 }
    795             }
    796         }
    797 
    798         // Update the repository selection
    799         mProjectButton.setSelection(!isSystem);
    800         mSystemButton.setSelection(isSystem);
    801         updateNewButton(isSystem);
    802 
    803         // Update the list
    804         ResourceItem[] items = setupResourceList();
    805 
    806         // If we have a selection name, select it
    807         if (itemName != null) {
    808             selectItemName(itemName, items);
    809         }
    810     }
    811 
    812     private void updateNewButton(boolean isSystem) {
    813         mNewButton.setEnabled(!isSystem && ResourceHelper.canCreateResourceType(mResourceType));
    814     }
    815 
    816     // ---- Implements ModifyListener ----
    817 
    818     @Override
    819     public void modifyText(ModifyEvent e) {
    820        if (e.getSource() == mEditValueText && mResourceResolver != null) {
    821            mCurrentResource = mEditValueText.getText();
    822 
    823            if (mCurrentResource.startsWith(PREFIX_RESOURCE_REF)) {
    824                if (mProjectResourcePattern.matcher(mCurrentResource).matches() ||
    825                        mSystemResourcePattern.matcher(mCurrentResource).matches()) {
    826                    updateResolvedLabel();
    827                }
    828            } else {
    829                updateResolvedLabel();
    830            }
    831        }
    832     }
    833 
    834     /** Dialog asking for a Name/Value pair */
    835     private class NameValueDialog extends SelectionStatusDialog implements Listener {
    836         private org.eclipse.swt.widgets.Text mNameText;
    837         private org.eclipse.swt.widgets.Text mValueText;
    838         private String mInitialName;
    839         private String mName;
    840         private String mValue;
    841         private ResourceNameValidator mValidator;
    842 
    843         public NameValueDialog(Shell parent, String initialName) {
    844             super(parent);
    845             mInitialName = initialName;
    846         }
    847 
    848         @Override
    849         protected Control createDialogArea(Composite parent) {
    850             Composite container = new Composite(parent, SWT.NONE);
    851             container.setLayout(new GridLayout(2, false));
    852             GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
    853             // Wide enough to accommodate the error label
    854             gridData.widthHint = 500;
    855             container.setLayoutData(gridData);
    856 
    857 
    858             Label nameLabel = new Label(container, SWT.NONE);
    859             nameLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
    860             nameLabel.setText("Name:");
    861 
    862             mNameText = new org.eclipse.swt.widgets.Text(container, SWT.BORDER);
    863             mNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    864             if (mInitialName != null) {
    865                 mNameText.setText(mInitialName);
    866                 mNameText.selectAll();
    867             }
    868 
    869             Label valueLabel = new Label(container, SWT.NONE);
    870             valueLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
    871             valueLabel.setText("Value:");
    872 
    873             mValueText = new org.eclipse.swt.widgets.Text(container, SWT.BORDER);
    874             mValueText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    875 
    876             mNameText.addListener(SWT.Modify, this);
    877             mValueText.addListener(SWT.Modify, this);
    878 
    879             validate();
    880 
    881             return container;
    882         }
    883 
    884         @Override
    885         protected void computeResult() {
    886             mName = mNameText.getText().trim();
    887             mValue = mValueText.getText().trim();
    888         }
    889 
    890         private String getName() {
    891             return mName;
    892         }
    893 
    894         private String getValue() {
    895             return mValue;
    896         }
    897 
    898         @Override
    899         public void handleEvent(Event event) {
    900             validate();
    901         }
    902 
    903         private void validate() {
    904             IStatus status;
    905             computeResult();
    906             if (mName.length() == 0) {
    907                 status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "Enter a name");
    908             } else if (mValue.length() == 0) {
    909                 status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "Enter a value");
    910             } else {
    911                 if (mValidator == null) {
    912                     mValidator = ResourceNameValidator.create(false, mProject, mResourceType);
    913                 }
    914                 String error = mValidator.isValid(mName);
    915                 if (error != null) {
    916                     status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, error);
    917                 } else {
    918                     status = new Status(IStatus.OK, AdtPlugin.PLUGIN_ID, null);
    919                 }
    920             }
    921             updateStatus(status);
    922         }
    923     }
    924 
    925     /**
    926      * Open the resource chooser for the given type, associated with the given
    927      * editor
    928      *
    929      * @param graphicalEditor the editor associated with the resource to be
    930      *            chosen (used to find the associated Android target to be used
    931      *            for framework resources etc)
    932      * @param type the resource type to be chosen
    933      * @param currentValue the current value, or null
    934      * @param validator a validator to be used, or null
    935      * @return the chosen resource, null if cancelled and "" if value should be
    936      *         cleared
    937      */
    938     public static String chooseResource(
    939             @NonNull GraphicalEditorPart graphicalEditor,
    940             @NonNull ResourceType type,
    941             String currentValue, IInputValidator validator) {
    942         ResourceChooser chooser = create(graphicalEditor, type).
    943                 setCurrentResource(currentValue);
    944         if (validator != null) {
    945             // Ensure wide enough to accommodate validator error message
    946             chooser.setSize(85, 10);
    947             chooser.setInputValidator(validator);
    948         }
    949         int result = chooser.open();
    950         if (result == ResourceChooser.CLEAR_RETURN_CODE) {
    951             return ""; //$NON-NLS-1$
    952         } else if (result == Window.OK) {
    953             return chooser.getCurrentResource();
    954         }
    955 
    956         return null;
    957     }
    958 }
    959