• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
ResourceChooser(IProject project, ResourceType type, ResourceRepository projectResources, ResourceRepository frameworkResources, Shell parent)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 
setPreviewHelper(ResourcePreviewHelper previewHelper)120     public void setPreviewHelper(ResourcePreviewHelper previewHelper) {
121         mPreviewHelper = previewHelper;
122     }
123 
124     @Override
createButtonsForButtonBar(Composite parent)125     protected void createButtonsForButtonBar(Composite parent) {
126         createButton(parent, CLEAR_BUTTON_ID, "Clear", false /*defaultButton*/);
127         super.createButtonsForButtonBar(parent);
128     }
129 
130     @Override
buttonPressed(int buttonId)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 
setCurrentResource(String resource)141     public void setCurrentResource(String resource) {
142         mCurrentResource = resource;
143     }
144 
getCurrentResource()145     public String getCurrentResource() {
146         return mCurrentResource;
147     }
148 
setInputValidator(IInputValidator inputValidator)149     public void setInputValidator(IInputValidator inputValidator) {
150         mInputValidator = inputValidator;
151     }
152 
153     @Override
computeResult()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
createDialogArea(Composite parent)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      */
createButtons(Composite top)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      */
createNewResButtons(Composite top)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
handleSelectionChanged()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 
updatePreview()319     private void updatePreview() {
320         if (mPreviewHelper != null) {
321             computeResult();
322             mPreviewHelper.updatePreview(mResourceType, mCurrentResource);
323         }
324     }
325 
createNewFile(ResourceType type)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 
createNewValue(ResourceType type)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 
createNewString()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      */
setupResourceList()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      */
selectItemName(String itemName, ResourceItem[] items)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      */
selectResourceString(String resourceString)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 
updateNewButton(boolean isSystem)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 
NameValueDialog(Shell parent, String initialName)495         public NameValueDialog(Shell parent, String initialName) {
496             super(parent);
497             mInitialName = initialName;
498         }
499 
500         @Override
createDialogArea(Composite parent)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
computeResult()537         protected void computeResult() {
538             mName = mNameText.getText().trim();
539             mValue = mValueText.getText().trim();
540         }
541 
getName()542         private String getName() {
543             return mName;
544         }
545 
getValue()546         private String getValue() {
547             return mValue;
548         }
549 
handleEvent(Event event)550         public void handleEvent(Event event) {
551             validate();
552         }
553 
validate()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