• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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 com.android.ide.common.resources.ResourceItem;
20 import com.android.ide.common.resources.ResourceRepository;
21 import com.android.ide.eclipse.adt.AdtPlugin;
22 import com.android.ide.eclipse.adt.internal.editors.layout.properties.PropertyFactory;
23 import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring;
24 import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard;
25 import com.android.resources.ResourceType;
26 
27 import org.eclipse.core.resources.IProject;
28 import org.eclipse.core.runtime.IStatus;
29 import org.eclipse.core.runtime.Status;
30 import org.eclipse.jface.dialogs.DialogSettings;
31 import org.eclipse.jface.dialogs.IDialogConstants;
32 import org.eclipse.jface.dialogs.IDialogSettings;
33 import org.eclipse.jface.viewers.ISelection;
34 import org.eclipse.jface.viewers.TreePath;
35 import org.eclipse.jface.viewers.TreeSelection;
36 import org.eclipse.jface.viewers.TreeViewer;
37 import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
38 import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
39 import org.eclipse.swt.SWT;
40 import org.eclipse.swt.events.SelectionAdapter;
41 import org.eclipse.swt.events.SelectionEvent;
42 import org.eclipse.swt.events.SelectionListener;
43 import org.eclipse.swt.layout.GridData;
44 import org.eclipse.swt.widgets.Button;
45 import org.eclipse.swt.widgets.Composite;
46 import org.eclipse.swt.widgets.Control;
47 import org.eclipse.swt.widgets.Shell;
48 import org.eclipse.swt.widgets.Tree;
49 import org.eclipse.ui.IWorkbench;
50 import org.eclipse.ui.PlatformUI;
51 import org.eclipse.ui.dialogs.FilteredTree;
52 import org.eclipse.ui.dialogs.PatternFilter;
53 import org.eclipse.ui.dialogs.SelectionStatusDialog;
54 
55 import java.util.Collection;
56 import java.util.regex.Matcher;
57 import java.util.regex.Pattern;
58 
59 /**
60  * A dialog to let the user choose a reference to a resource.
61  *
62  */
63 public class ReferenceChooserDialog extends SelectionStatusDialog {
64 
65     private static Pattern sResourcePattern = Pattern.compile("@(.*)/(.+)"); //$NON-NLS-1$
66     private static Pattern sInlineIdResourcePattern = Pattern.compile("@\\+id/(.+)"); //$NON-NLS-1$
67 
68     private static IDialogSettings sDialogSettings = new DialogSettings("");
69 
70     private ResourceRepository mProjectResources;
71     private String mCurrentResource;
72     private FilteredTree mFilteredTree;
73     private Button mNewResButton;
74     private final IProject mProject;
75     private TreeViewer mTreeViewer;
76     private ResourcePreviewHelper mPreviewHelper;
77 
78     /**
79      * @param project
80      * @param parent
81      */
ReferenceChooserDialog(IProject project, ResourceRepository projectResources, Shell parent)82     public ReferenceChooserDialog(IProject project, ResourceRepository projectResources,
83             Shell parent) {
84         super(parent);
85         mProject = project;
86         mProjectResources = projectResources;
87 
88         int shellStyle = getShellStyle();
89         setShellStyle(shellStyle | SWT.MAX | SWT.RESIZE);
90 
91         setTitle("Reference Chooser");
92         setMessage(String.format("Choose a resource"));
93 
94         setDialogBoundsSettings(sDialogSettings, getDialogBoundsStrategy());
95     }
96 
setPreviewHelper(ResourcePreviewHelper previewHelper)97     public void setPreviewHelper(ResourcePreviewHelper previewHelper) {
98         mPreviewHelper = previewHelper;
99     }
100 
setCurrentResource(String resource)101     public void setCurrentResource(String resource) {
102         mCurrentResource = resource;
103     }
104 
getCurrentResource()105     public String getCurrentResource() {
106         return mCurrentResource;
107     }
108 
109 
110     /* (non-Javadoc)
111      * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
112      */
113     @Override
computeResult()114     protected void computeResult() {
115         // get the selection
116         TreePath treeSelection = getSelection();
117         if (treeSelection != null) {
118             if (treeSelection.getSegmentCount() == 2) {
119                 // get the resource type and the resource item
120                 ResourceType resourceType = (ResourceType)treeSelection.getFirstSegment();
121                 ResourceItem resourceItem = (ResourceItem)treeSelection.getLastSegment();
122 
123                 mCurrentResource = resourceItem.getXmlString(resourceType, false /* system */);
124             }
125         }
126     }
127 
128     @Override
createDialogArea(Composite parent)129     protected Control createDialogArea(Composite parent) {
130         Composite top = (Composite)super.createDialogArea(parent);
131 
132         // create the standard message area
133         createMessageArea(top);
134 
135         // create the filtered tree
136         createFilteredTree(top);
137 
138         // setup the initial selection
139         if (mCurrentResource != null) {
140             setupInitialSelection();
141         }
142 
143         // create the "New Resource" button
144         createNewResButtons(top);
145 
146         Composite workaround = PropertyFactory.addWorkaround(top);
147         if (workaround != null) {
148             workaround.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
149         }
150 
151         return top;
152     }
153 
154     /**
155      * Creates the "New Resource" button.
156      * @param top the parent composite
157      */
createNewResButtons(Composite top)158     private void createNewResButtons(Composite top) {
159         mNewResButton = new Button(top, SWT.NONE);
160         mNewResButton.addSelectionListener(new OnNewResButtonSelected());
161         updateNewResButton();
162     }
163 
createFilteredTree(Composite parent)164     private void createFilteredTree(Composite parent) {
165         mFilteredTree = new FilteredTree(parent, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION,
166                 new PatternFilter());
167 
168         GridData data = new GridData();
169         data.widthHint = convertWidthInCharsToPixels(60);
170         data.heightHint = convertHeightInCharsToPixels(18);
171         data.grabExcessVerticalSpace = true;
172         data.grabExcessHorizontalSpace = true;
173         data.horizontalAlignment = GridData.FILL;
174         data.verticalAlignment = GridData.FILL;
175         mFilteredTree.setLayoutData(data);
176         mFilteredTree.setFont(parent.getFont());
177 
178         mTreeViewer = mFilteredTree.getViewer();
179         Tree tree = mTreeViewer.getTree();
180 
181         tree.addSelectionListener(new SelectionListener() {
182             @Override
183             public void widgetDefaultSelected(SelectionEvent e) {
184                 handleDoubleClick();
185             }
186 
187             @Override
188             public void widgetSelected(SelectionEvent e) {
189                 handleSelection();
190             }
191         });
192 
193         mTreeViewer.setLabelProvider(new ResourceLabelProvider());
194         mTreeViewer.setContentProvider(new ResourceContentProvider(false /* fullLevels */));
195         mTreeViewer.setInput(mProjectResources);
196     }
197 
handleSelection()198     protected void handleSelection() {
199         validateCurrentSelection();
200         updateNewResButton();
201 
202         if (mPreviewHelper != null) {
203             TreePath treeSelection = getSelection();
204             ResourceType type = null;
205             if (treeSelection != null && treeSelection.getSegmentCount() == 2) {
206                 Object segment = treeSelection.getSegment(0);
207                 if (segment instanceof ResourceType) {
208                     type = (ResourceType) segment;
209                     // Ensure that mCurrentResource is valid
210                     computeResult();
211                 }
212             }
213 
214             mPreviewHelper.updatePreview(type, mCurrentResource);
215         }
216     }
217 
handleDoubleClick()218     protected void handleDoubleClick() {
219         if (validateCurrentSelection()) {
220             buttonPressed(IDialogConstants.OK_ID);
221         }
222     }
223 
224     /**
225      * Returns the selected item in the tree as a {@link TreePath} object.
226      * @return the <code>TreePath</code> object or <code>null</code> if there was no selection.
227      */
getSelection()228     private TreePath getSelection() {
229         ISelection selection = mFilteredTree.getViewer().getSelection();
230         if (selection instanceof TreeSelection) {
231             TreeSelection treeSelection = (TreeSelection)selection;
232             TreePath[] treePaths = treeSelection.getPaths();
233 
234             // the selection mode is SWT.SINGLE, so we just get the first one.
235             if (treePaths.length > 0) {
236                 return treePaths[0];
237             }
238         }
239 
240         return null;
241     }
242 
validateCurrentSelection()243     private boolean validateCurrentSelection() {
244         TreePath treeSelection = getSelection();
245 
246         IStatus status;
247         if (treeSelection != null) {
248             if (treeSelection.getSegmentCount() == 2) {
249                 status = new Status(IStatus.OK, AdtPlugin.PLUGIN_ID,
250                         IStatus.OK, "", //$NON-NLS-1$
251                         null);
252             } else {
253                 status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
254                         IStatus.ERROR, "You must select a Resource Item",
255                         null);
256             }
257         } else {
258             status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
259                     IStatus.ERROR, "", //$NON-NLS-1$
260                     null);
261         }
262 
263         updateStatus(status);
264 
265         return status.isOK();
266     }
267 
268     /**
269      * Updates the new res button when the list selection changes.
270      * The name of the button changes depending on the resource.
271      */
updateNewResButton()272     private void updateNewResButton() {
273         ResourceType type = getSelectedResourceType();
274 
275         // We only support adding new strings right now
276         mNewResButton.setEnabled(type == ResourceType.STRING);
277 
278         String title = String.format("New %1$s...",
279                 type == null ? "Resource" : type.getDisplayName());
280         mNewResButton.setText(title);
281         mNewResButton.pack();
282     }
283 
284     /**
285      * Callback invoked when the mNewResButton is selected by the user.
286      */
287     private class OnNewResButtonSelected extends SelectionAdapter {
288         @Override
widgetSelected(SelectionEvent e)289          public void widgetSelected(SelectionEvent e) {
290              super.widgetSelected(e);
291 
292              ResourceType type = getSelectedResourceType();
293 
294              // We currently only support strings
295              if (type == ResourceType.STRING) {
296 
297                  ExtractStringRefactoring ref = new ExtractStringRefactoring(
298                          mProject, true /*enforceNew*/);
299                  RefactoringWizard wizard = new ExtractStringWizard(ref, mProject);
300                  RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
301                  try {
302                      IWorkbench w = PlatformUI.getWorkbench();
303                      if (op.run(w.getDisplay().getActiveShell(), wizard.getDefaultPageTitle()) ==
304                              IDialogConstants.OK_ID) {
305                          mTreeViewer.refresh();
306 
307                          // select it if possible
308                          setupInitialSelection(type, ref.getXmlStringId());
309                      }
310                  } catch (InterruptedException ex) {
311                      // Interrupted. Pass.
312                  }
313              }
314          }
315      }
316 
317     /**
318      * Returns the {@link ResourceType} of the selected element, if any.
319      * Returns null if nothing suitable is selected.
320      */
getSelectedResourceType()321     private ResourceType getSelectedResourceType() {
322         ResourceType type = null;
323 
324         TreePath selection = getSelection();
325         if (selection != null && selection.getSegmentCount() > 0) {
326             Object first = selection.getFirstSegment();
327             if (first instanceof ResourceType) {
328                 type = (ResourceType) first;
329             }
330         }
331         return type;
332     }
333 
334     /**
335      * Sets up the initial selection.
336      * <p/>
337      * This parses {@link #mCurrentResource} to find out the resource type and the resource name.
338      */
setupInitialSelection()339     private void setupInitialSelection() {
340         // checks the inline id pattern first as it's more restrictive than the other one.
341         Matcher m = sInlineIdResourcePattern.matcher(mCurrentResource);
342         if (m.matches()) {
343             // get the matching name
344             String resourceName = m.group(1);
345 
346             // setup initial selection
347             setupInitialSelection(ResourceType.ID, resourceName);
348         } else {
349             // attempts the inline id pattern
350             m = sResourcePattern.matcher(mCurrentResource);
351             if (m.matches()) {
352                 // get the resource type.
353                 ResourceType resourceType = ResourceType.getEnum(m.group(1));
354                 if (resourceType != null) {
355                     // get the matching name
356                     String resourceName = m.group(2);
357 
358                     // setup initial selection
359                     setupInitialSelection(resourceType, resourceName);
360                 }
361             }
362         }
363     }
364 
365     /**
366      * Sets up the initial selection based on a {@link ResourceType} and a resource name.
367      * @param resourceType the resource type.
368      * @param resourceName the resource name.
369      */
setupInitialSelection(ResourceType resourceType, String resourceName)370     private void setupInitialSelection(ResourceType resourceType, String resourceName) {
371         // get all the resources of this type
372         Collection<ResourceItem> resourceItems =
373                 mProjectResources.getResourceItemsOfType(resourceType);
374 
375         for (ResourceItem resourceItem : resourceItems) {
376             if (resourceName.equals(resourceItem.getName())) {
377                 // name of the resource match, we select it,
378                 TreePath treePath = new TreePath(new Object[] { resourceType, resourceItem });
379                 mFilteredTree.getViewer().setSelection(
380                         new TreeSelection(treePath),
381                         true /*reveal*/);
382 
383                 // and we're done.
384                 return;
385             }
386         }
387 
388         // if we get here, the resource type is valid, but the resource is missing.
389         // we select and expand the resource type element.
390         TreePath treePath = new TreePath(new Object[] { resourceType });
391         mFilteredTree.getViewer().setSelection(
392                 new TreeSelection(treePath),
393                 true /*reveal*/);
394         mFilteredTree.getViewer().setExpandedState(resourceType, true /* expanded */);
395     }
396 }
397