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 
     20 import com.android.ide.common.resources.ResourceItem;
     21 import com.android.ide.common.resources.ResourceRepository;
     22 import com.android.ide.eclipse.adt.AdtPlugin;
     23 import com.android.ide.eclipse.adt.AdtUtils;
     24 import com.android.ide.eclipse.adt.internal.assetstudio.OpenCreateAssetSetWizardAction;
     25 import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring;
     26 import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard;
     27 import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
     28 import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
     29 import com.android.resources.ResourceType;
     30 import com.android.util.Pair;
     31 
     32 import org.eclipse.core.resources.IFile;
     33 import org.eclipse.core.resources.IProject;
     34 import org.eclipse.core.resources.IResource;
     35 import org.eclipse.core.runtime.IStatus;
     36 import org.eclipse.core.runtime.Status;
     37 import org.eclipse.jface.dialogs.IDialogConstants;
     38 import org.eclipse.jface.dialogs.IInputValidator;
     39 import org.eclipse.jface.dialogs.InputDialog;
     40 import org.eclipse.jface.text.IRegion;
     41 import org.eclipse.jface.window.Window;
     42 import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
     43 import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
     44 import org.eclipse.swt.SWT;
     45 import org.eclipse.swt.events.SelectionAdapter;
     46 import org.eclipse.swt.events.SelectionEvent;
     47 import org.eclipse.swt.layout.GridData;
     48 import org.eclipse.swt.layout.GridLayout;
     49 import org.eclipse.swt.widgets.Button;
     50 import org.eclipse.swt.widgets.Composite;
     51 import org.eclipse.swt.widgets.Control;
     52 import org.eclipse.swt.widgets.Event;
     53 import org.eclipse.swt.widgets.Label;
     54 import org.eclipse.swt.widgets.Listener;
     55 import org.eclipse.swt.widgets.Shell;
     56 import org.eclipse.ui.IWorkbench;
     57 import org.eclipse.ui.PlatformUI;
     58 import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
     59 import org.eclipse.ui.dialogs.SelectionStatusDialog;
     60 
     61 import java.util.Arrays;
     62 import java.util.Collection;
     63 import java.util.Collections;
     64 import java.util.List;
     65 import java.util.regex.Matcher;
     66 import java.util.regex.Pattern;
     67 
     68 /**
     69  * A dialog to let the user select a resource based on a resource type.
     70  */
     71 public class ResourceChooser extends AbstractElementListSelectionDialog {
     72     /** The return code from the dialog for the user choosing "Clear" */
     73     public static final int CLEAR_RETURN_CODE = -5;
     74     /** The dialog button ID for the user choosing "Clear" */
     75     private static final int CLEAR_BUTTON_ID = CLEAR_RETURN_CODE;
     76 
     77     private Pattern mProjectResourcePattern;
     78     private ResourceType mResourceType;
     79     private final ResourceRepository mProjectResources;
     80     private final ResourceRepository mFrameworkResources;
     81     private Pattern mSystemResourcePattern;
     82     private Button mProjectButton;
     83     private Button mSystemButton;
     84     private Button mNewButton;
     85     private String mCurrentResource;
     86     private final IProject mProject;
     87     private IInputValidator mInputValidator;
     88     private ResourcePreviewHelper mPreviewHelper;
     89 
     90     /**
     91      * Creates a Resource Chooser dialog.
     92      * @param project Project being worked on
     93      * @param type The type of the resource to choose
     94      * @param projectResources The repository for the project
     95      * @param frameworkResources The Framework resource repository
     96      * @param parent the parent shell
     97      */
     98     public ResourceChooser(IProject project, ResourceType type,
     99             ResourceRepository projectResources,
    100             ResourceRepository frameworkResources,
    101             Shell parent) {
    102         super(parent, new ResourceLabelProvider());
    103         mProject = project;
    104 
    105         mResourceType = type;
    106         mProjectResources = projectResources;
    107         mFrameworkResources = frameworkResources;
    108 
    109         mProjectResourcePattern = Pattern.compile(
    110                 "@" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
    111 
    112         mSystemResourcePattern = Pattern.compile(
    113                 "@android:" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
    114 
    115         setTitle("Resource Chooser");
    116         setMessage(String.format("Choose a %1$s resource",
    117                 mResourceType.getDisplayName().toLowerCase()));
    118     }
    119 
    120     public void setPreviewHelper(ResourcePreviewHelper previewHelper) {
    121         mPreviewHelper = previewHelper;
    122     }
    123 
    124     @Override
    125     protected void createButtonsForButtonBar(Composite parent) {
    126         createButton(parent, CLEAR_BUTTON_ID, "Clear", false /*defaultButton*/);
    127         super.createButtonsForButtonBar(parent);
    128     }
    129 
    130     @Override
    131     protected void buttonPressed(int buttonId) {
    132         super.buttonPressed(buttonId);
    133 
    134         if (buttonId == CLEAR_BUTTON_ID) {
    135             assert CLEAR_RETURN_CODE != Window.OK && CLEAR_RETURN_CODE != Window.CANCEL;
    136             setReturnCode(CLEAR_RETURN_CODE);
    137             close();
    138         }
    139     }
    140 
    141     public void setCurrentResource(String resource) {
    142         mCurrentResource = resource;
    143     }
    144 
    145     public String getCurrentResource() {
    146         return mCurrentResource;
    147     }
    148 
    149     public void setInputValidator(IInputValidator inputValidator) {
    150         mInputValidator = inputValidator;
    151     }
    152 
    153     @Override
    154     protected void computeResult() {
    155         if (getSelectionIndex() == -1) {
    156             mCurrentResource = null;
    157             return;
    158         }
    159 
    160         Object[] elements = getSelectedElements();
    161         if (elements.length == 1 && elements[0] instanceof ResourceItem) {
    162             ResourceItem item = (ResourceItem)elements[0];
    163 
    164             mCurrentResource = item.getXmlString(mResourceType, mSystemButton.getSelection());
    165 
    166             if (mInputValidator != null && mInputValidator.isValid(mCurrentResource) != null) {
    167                 mCurrentResource = null;
    168             }
    169         }
    170     }
    171 
    172     @Override
    173     protected Control createDialogArea(Composite parent) {
    174         Composite top = (Composite)super.createDialogArea(parent);
    175 
    176         createMessageArea(top);
    177 
    178         createButtons(top);
    179         createFilterText(top);
    180         createFilteredList(top);
    181 
    182         // create the "New Resource" button
    183         createNewResButtons(top);
    184 
    185         setupResourceList();
    186         selectResourceString(mCurrentResource);
    187 
    188         return top;
    189     }
    190 
    191     /**
    192      * Creates the radio button to switch between project and system resources.
    193      * @param top the parent composite
    194      */
    195     private void createButtons(Composite top) {
    196         mProjectButton = new Button(top, SWT.RADIO);
    197         mProjectButton.setText("Project Resources");
    198         mProjectButton.addSelectionListener(new SelectionAdapter() {
    199             @Override
    200             public void widgetSelected(SelectionEvent e) {
    201                 super.widgetSelected(e);
    202                 if (mProjectButton.getSelection()) {
    203                     setupResourceList();
    204                     updateNewButton(false /*isSystem*/);
    205                     updatePreview();
    206                 }
    207             }
    208         });
    209         mSystemButton = new Button(top, SWT.RADIO);
    210         mSystemButton.setText("System Resources");
    211         mSystemButton.addSelectionListener(new SelectionAdapter() {
    212             @Override
    213             public void widgetSelected(SelectionEvent e) {
    214                 super.widgetSelected(e);
    215                 if (mSystemButton.getSelection()) {
    216                     setupResourceList();
    217                     updateNewButton(true /*isSystem*/);
    218                     updatePreview();
    219                 }
    220             }
    221         });
    222     }
    223 
    224     /**
    225      * Creates the "New Resource" button.
    226      * @param top the parent composite
    227      */
    228     private void createNewResButtons(Composite top) {
    229         mNewButton = new Button(top, SWT.NONE);
    230 
    231         String title = String.format("New %1$s...", mResourceType.getDisplayName());
    232         if (mResourceType == ResourceType.DRAWABLE) {
    233             title = "Create New Icon...";
    234         }
    235         mNewButton.setText(title);
    236 
    237         mNewButton.addSelectionListener(new SelectionAdapter() {
    238             @Override
    239             public void widgetSelected(SelectionEvent e) {
    240                 super.widgetSelected(e);
    241 
    242                 if (mResourceType == ResourceType.STRING) {
    243                     // Special case: Use Extract String refactoring wizard UI
    244                     String newName = createNewString();
    245                     selectAddedItem(newName);
    246                 } else if (mResourceType == ResourceType.DRAWABLE) {
    247                     // Special case: Use the "Create Icon Set" wizard
    248                     OpenCreateAssetSetWizardAction action =
    249                             new OpenCreateAssetSetWizardAction(mProject);
    250                     action.run();
    251                     List<IResource> files = action.getCreatedFiles();
    252                     if (files != null && files.size() > 0) {
    253                         String newName = AdtUtils.stripAllExtensions(files.get(0).getName());
    254                         // Recompute the "current resource" to select the new id
    255                         ResourceItem[] items = setupResourceList();
    256                         selectItemName(newName, items);
    257                     }
    258                 } else {
    259                     if (ResourceHelper.isValueBasedResourceType(mResourceType)) {
    260                         String newName = createNewValue(mResourceType);
    261                         selectAddedItem(newName);
    262                     } else {
    263                         String newName = createNewFile(mResourceType);
    264                         selectAddedItem(newName);
    265                     }
    266                 }
    267             }
    268 
    269             private void selectAddedItem(String newName) {
    270                 // Recompute the "current resource" to select the new id
    271                 ResourceItem[] items = setupResourceList();
    272 
    273                 // Ensure that the name is in the list. There's a delay after
    274                 // an item is added (until the builder runs and processes the delta)
    275                 // so if it's not in the list, add it
    276                 boolean found = false;
    277                 for (ResourceItem item : items) {
    278                     if (newName.equals(item.getName())) {
    279                         found = true;
    280                         break;
    281                     }
    282                 }
    283                 if (!found) {
    284                     ResourceItem[] newItems = new ResourceItem[items.length + 1];
    285                     System.arraycopy(items, 0, newItems, 0, items.length);
    286                     newItems[items.length] = new ResourceItem(newName);
    287                     items = newItems;
    288                     Arrays.sort(items);
    289                     setListElements(items);
    290                 }
    291 
    292                 selectItemName(newName, items);
    293             }
    294         });
    295     }
    296 
    297     @Override
    298     protected void handleSelectionChanged() {
    299         super.handleSelectionChanged();
    300         if (mInputValidator != null) {
    301             Object[] elements = getSelectedElements();
    302             if (elements.length == 1 && elements[0] instanceof ResourceItem) {
    303                 ResourceItem item = (ResourceItem)elements[0];
    304                 String current = item.getXmlString(mResourceType, mSystemButton.getSelection());
    305                 String error = mInputValidator.isValid(current);
    306                 IStatus status;
    307                 if (error != null) {
    308                     status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, error);
    309                 } else {
    310                     status = new Status(IStatus.OK, AdtPlugin.PLUGIN_ID, null);
    311                 }
    312                 updateStatus(status);
    313             }
    314         }
    315 
    316         updatePreview();
    317     }
    318 
    319     private void updatePreview() {
    320         if (mPreviewHelper != null) {
    321             computeResult();
    322             mPreviewHelper.updatePreview(mResourceType, mCurrentResource);
    323         }
    324     }
    325 
    326     private String createNewFile(ResourceType type) {
    327         // Show a name/value dialog entering the key name and the value
    328         Shell shell = AdtPlugin.getDisplay().getActiveShell();
    329         if (shell == null) {
    330             return null;
    331         }
    332 
    333         ResourceNameValidator validator = ResourceNameValidator.create(true /*allowXmlExtension*/,
    334                 mProject, mResourceType);
    335         InputDialog d = new InputDialog(
    336                 AdtPlugin.getDisplay().getActiveShell(),
    337                 "Enter name",  // title
    338                 "Enter name",
    339                 "", //$NON-NLS-1$
    340                 validator);
    341         if (d.open() == Window.OK) {
    342             String name = d.getValue().trim();
    343             if (name.length() == 0) {
    344                 return null;
    345             }
    346 
    347             Pair<IFile, IRegion> resource = ResourceHelper.createResource(mProject, type, name,
    348                     null);
    349             if (resource != null) {
    350                 return name;
    351             }
    352         }
    353 
    354         return null;
    355     }
    356 
    357 
    358     private String createNewValue(ResourceType type) {
    359         // Show a name/value dialog entering the key name and the value
    360         Shell shell = AdtPlugin.getDisplay().getActiveShell();
    361         if (shell == null) {
    362             return null;
    363         }
    364         NameValueDialog dialog = new NameValueDialog(shell, getFilter());
    365         if (dialog.open() != Window.OK) {
    366             return null;
    367         }
    368 
    369         String name = dialog.getName();
    370         String value = dialog.getValue();
    371         if (name.length() == 0 || value.length() == 0) {
    372             return null;
    373         }
    374 
    375         Pair<IFile, IRegion> resource = ResourceHelper.createResource(mProject, type, name, value);
    376         if (resource != null) {
    377             return name;
    378         }
    379 
    380         return null;
    381     }
    382 
    383     private String createNewString() {
    384         ExtractStringRefactoring ref = new ExtractStringRefactoring(
    385                 mProject, true /*enforceNew*/);
    386         RefactoringWizard wizard = new ExtractStringWizard(ref, mProject);
    387         RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
    388         try {
    389             IWorkbench w = PlatformUI.getWorkbench();
    390             if (op.run(w.getDisplay().getActiveShell(), wizard.getDefaultPageTitle()) ==
    391                     IDialogConstants.OK_ID) {
    392                 return ref.getXmlStringId();
    393             }
    394         } catch (InterruptedException ex) {
    395             // Interrupted. Pass.
    396         }
    397 
    398         return null;
    399     }
    400 
    401     /**
    402      * Setups the current list.
    403      */
    404     private ResourceItem[] setupResourceList() {
    405         Collection<ResourceItem> items = null;
    406         if (mProjectButton.getSelection()) {
    407             items = mProjectResources.getResourceItemsOfType(mResourceType);
    408         } else if (mSystemButton.getSelection()) {
    409             items = mFrameworkResources.getResourceItemsOfType(mResourceType);
    410         }
    411 
    412         if (items == null) {
    413             items = Collections.emptyList();
    414         }
    415 
    416         ResourceItem[] arrayItems = items.toArray(new ResourceItem[items.size()]);
    417 
    418         // sort the array
    419         Arrays.sort(arrayItems);
    420 
    421         setListElements(arrayItems);
    422 
    423         return arrayItems;
    424     }
    425 
    426     /**
    427      * Select an item by its name, if possible.
    428      */
    429     private void selectItemName(String itemName, ResourceItem[] items) {
    430         if (itemName == null || items == null) {
    431             return;
    432         }
    433 
    434         for (ResourceItem item : items) {
    435             if (itemName.equals(item.getName())) {
    436                 setSelection(new Object[] { item });
    437                 break;
    438             }
    439         }
    440     }
    441 
    442     /**
    443      * Select an item by its full resource string.
    444      * This also selects between project and system repository based on the resource string.
    445      */
    446     private void selectResourceString(String resourceString) {
    447         boolean isSystem = false;
    448         String itemName = null;
    449 
    450         if (resourceString != null) {
    451             // Is this a system resource?
    452             // If not a system resource or if they are not available, this will be a project res.
    453             Matcher m = mSystemResourcePattern.matcher(resourceString);
    454             if (m.matches()) {
    455                 itemName = m.group(1);
    456                 isSystem = true;
    457             }
    458 
    459             if (!isSystem && itemName == null) {
    460                 // Try to match project resource name
    461                 m = mProjectResourcePattern.matcher(resourceString);
    462                 if (m.matches()) {
    463                     itemName = m.group(1);
    464                 }
    465             }
    466         }
    467 
    468         // Update the repository selection
    469         mProjectButton.setSelection(!isSystem);
    470         mSystemButton.setSelection(isSystem);
    471         updateNewButton(isSystem);
    472 
    473         // Update the list
    474         ResourceItem[] items = setupResourceList();
    475 
    476         // If we have a selection name, select it
    477         if (itemName != null) {
    478             selectItemName(itemName, items);
    479         }
    480     }
    481 
    482     private void updateNewButton(boolean isSystem) {
    483         mNewButton.setEnabled(!isSystem && ResourceHelper.canCreateResourceType(mResourceType));
    484     }
    485 
    486     /** Dialog asking for a Name/Value pair */
    487     private class NameValueDialog extends SelectionStatusDialog implements Listener {
    488         private org.eclipse.swt.widgets.Text mNameText;
    489         private org.eclipse.swt.widgets.Text mValueText;
    490         private String mInitialName;
    491         private String mName;
    492         private String mValue;
    493         private ResourceNameValidator mValidator;
    494 
    495         public NameValueDialog(Shell parent, String initialName) {
    496             super(parent);
    497             mInitialName = initialName;
    498         }
    499 
    500         @Override
    501         protected Control createDialogArea(Composite parent) {
    502             Composite container = new Composite(parent, SWT.NONE);
    503             container.setLayout(new GridLayout(2, false));
    504             GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
    505             // Wide enough to accommodate the error label
    506             gridData.widthHint = 500;
    507             container.setLayoutData(gridData);
    508 
    509 
    510             Label nameLabel = new Label(container, SWT.NONE);
    511             nameLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
    512             nameLabel.setText("Name:");
    513 
    514             mNameText = new org.eclipse.swt.widgets.Text(container, SWT.BORDER);
    515             mNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    516             if (mInitialName != null) {
    517                 mNameText.setText(mInitialName);
    518                 mNameText.selectAll();
    519             }
    520 
    521             Label valueLabel = new Label(container, SWT.NONE);
    522             valueLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
    523             valueLabel.setText("Value:");
    524 
    525             mValueText = new org.eclipse.swt.widgets.Text(container, SWT.BORDER);
    526             mValueText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    527 
    528             mNameText.addListener(SWT.Modify, this);
    529             mValueText.addListener(SWT.Modify, this);
    530 
    531             validate();
    532 
    533             return container;
    534         }
    535 
    536         @Override
    537         protected void computeResult() {
    538             mName = mNameText.getText().trim();
    539             mValue = mValueText.getText().trim();
    540         }
    541 
    542         private String getName() {
    543             return mName;
    544         }
    545 
    546         private String getValue() {
    547             return mValue;
    548         }
    549 
    550         public void handleEvent(Event event) {
    551             validate();
    552         }
    553 
    554         private void validate() {
    555             IStatus status;
    556             computeResult();
    557             if (mName.length() == 0) {
    558                 status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "Enter a name");
    559             } else if (mValue.length() == 0) {
    560                 status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "Enter a value");
    561             } else {
    562                 if (mValidator == null) {
    563                     mValidator = ResourceNameValidator.create(false, mProject, mResourceType);
    564                 }
    565                 String error = mValidator.isValid(mName);
    566                 if (error != null) {
    567                     status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, error);
    568                 } else {
    569                     status = new Status(IStatus.OK, AdtPlugin.PLUGIN_ID, null);
    570                 }
    571             }
    572             updateStatus(status);
    573         }
    574     }
    575 }
    576