| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Eclipse Public License, Version 1.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.eclipse.org/org/documents/epl-v10.php |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ide.eclipse.adt.internal.ui; |
| |
| import com.android.ide.common.resources.ResourceItem; |
| import com.android.ide.common.resources.ResourceRepository; |
| import com.android.ide.eclipse.adt.AdtPlugin; |
| import com.android.ide.eclipse.adt.internal.editors.layout.properties.PropertyFactory; |
| import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring; |
| import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard; |
| import com.android.resources.ResourceType; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jface.dialogs.DialogSettings; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.IDialogSettings; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.TreePath; |
| import org.eclipse.jface.viewers.TreeSelection; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.ltk.ui.refactoring.RefactoringWizard; |
| import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.dialogs.FilteredTree; |
| import org.eclipse.ui.dialogs.PatternFilter; |
| import org.eclipse.ui.dialogs.SelectionStatusDialog; |
| |
| import java.util.Collection; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * A dialog to let the user choose a reference to a resource. |
| * |
| */ |
| public class ReferenceChooserDialog extends SelectionStatusDialog { |
| |
| private static Pattern sResourcePattern = Pattern.compile("@(.*)/(.+)"); //$NON-NLS-1$ |
| private static Pattern sInlineIdResourcePattern = Pattern.compile("@\\+id/(.+)"); //$NON-NLS-1$ |
| |
| private static IDialogSettings sDialogSettings = new DialogSettings(""); |
| |
| private ResourceRepository mProjectResources; |
| private String mCurrentResource; |
| private FilteredTree mFilteredTree; |
| private Button mNewResButton; |
| private final IProject mProject; |
| private TreeViewer mTreeViewer; |
| private ResourcePreviewHelper mPreviewHelper; |
| |
| /** |
| * @param project |
| * @param parent |
| */ |
| public ReferenceChooserDialog(IProject project, ResourceRepository projectResources, |
| Shell parent) { |
| super(parent); |
| mProject = project; |
| mProjectResources = projectResources; |
| |
| int shellStyle = getShellStyle(); |
| setShellStyle(shellStyle | SWT.MAX | SWT.RESIZE); |
| |
| setTitle("Reference Chooser"); |
| setMessage(String.format("Choose a resource")); |
| |
| setDialogBoundsSettings(sDialogSettings, getDialogBoundsStrategy()); |
| } |
| |
| public void setPreviewHelper(ResourcePreviewHelper previewHelper) { |
| mPreviewHelper = previewHelper; |
| } |
| |
| public void setCurrentResource(String resource) { |
| mCurrentResource = resource; |
| } |
| |
| public String getCurrentResource() { |
| return mCurrentResource; |
| } |
| |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult() |
| */ |
| @Override |
| protected void computeResult() { |
| // get the selection |
| TreePath treeSelection = getSelection(); |
| if (treeSelection != null) { |
| if (treeSelection.getSegmentCount() == 2) { |
| // get the resource type and the resource item |
| ResourceType resourceType = (ResourceType)treeSelection.getFirstSegment(); |
| ResourceItem resourceItem = (ResourceItem)treeSelection.getLastSegment(); |
| |
| mCurrentResource = resourceItem.getXmlString(resourceType, false /* system */); |
| } |
| } |
| } |
| |
| @Override |
| protected Control createDialogArea(Composite parent) { |
| Composite top = (Composite)super.createDialogArea(parent); |
| |
| // create the standard message area |
| createMessageArea(top); |
| |
| // create the filtered tree |
| createFilteredTree(top); |
| |
| // setup the initial selection |
| if (mCurrentResource != null) { |
| setupInitialSelection(); |
| } |
| |
| // create the "New Resource" button |
| createNewResButtons(top); |
| |
| Composite workaround = PropertyFactory.addWorkaround(top); |
| if (workaround != null) { |
| workaround.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1)); |
| } |
| |
| return top; |
| } |
| |
| /** |
| * Creates the "New Resource" button. |
| * @param top the parent composite |
| */ |
| private void createNewResButtons(Composite top) { |
| mNewResButton = new Button(top, SWT.NONE); |
| mNewResButton.addSelectionListener(new OnNewResButtonSelected()); |
| updateNewResButton(); |
| } |
| |
| private void createFilteredTree(Composite parent) { |
| mFilteredTree = new FilteredTree(parent, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION, |
| new PatternFilter()); |
| |
| GridData data = new GridData(); |
| data.widthHint = convertWidthInCharsToPixels(60); |
| data.heightHint = convertHeightInCharsToPixels(18); |
| data.grabExcessVerticalSpace = true; |
| data.grabExcessHorizontalSpace = true; |
| data.horizontalAlignment = GridData.FILL; |
| data.verticalAlignment = GridData.FILL; |
| mFilteredTree.setLayoutData(data); |
| mFilteredTree.setFont(parent.getFont()); |
| |
| mTreeViewer = mFilteredTree.getViewer(); |
| Tree tree = mTreeViewer.getTree(); |
| |
| tree.addSelectionListener(new SelectionListener() { |
| @Override |
| public void widgetDefaultSelected(SelectionEvent e) { |
| handleDoubleClick(); |
| } |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| handleSelection(); |
| } |
| }); |
| |
| mTreeViewer.setLabelProvider(new ResourceLabelProvider()); |
| mTreeViewer.setContentProvider(new ResourceContentProvider(false /* fullLevels */)); |
| mTreeViewer.setInput(mProjectResources); |
| } |
| |
| protected void handleSelection() { |
| validateCurrentSelection(); |
| updateNewResButton(); |
| |
| if (mPreviewHelper != null) { |
| TreePath treeSelection = getSelection(); |
| ResourceType type = null; |
| if (treeSelection != null && treeSelection.getSegmentCount() == 2) { |
| Object segment = treeSelection.getSegment(0); |
| if (segment instanceof ResourceType) { |
| type = (ResourceType) segment; |
| // Ensure that mCurrentResource is valid |
| computeResult(); |
| } |
| } |
| |
| mPreviewHelper.updatePreview(type, mCurrentResource); |
| } |
| } |
| |
| protected void handleDoubleClick() { |
| if (validateCurrentSelection()) { |
| buttonPressed(IDialogConstants.OK_ID); |
| } |
| } |
| |
| /** |
| * Returns the selected item in the tree as a {@link TreePath} object. |
| * @return the <code>TreePath</code> object or <code>null</code> if there was no selection. |
| */ |
| private TreePath getSelection() { |
| ISelection selection = mFilteredTree.getViewer().getSelection(); |
| if (selection instanceof TreeSelection) { |
| TreeSelection treeSelection = (TreeSelection)selection; |
| TreePath[] treePaths = treeSelection.getPaths(); |
| |
| // the selection mode is SWT.SINGLE, so we just get the first one. |
| if (treePaths.length > 0) { |
| return treePaths[0]; |
| } |
| } |
| |
| return null; |
| } |
| |
| private boolean validateCurrentSelection() { |
| TreePath treeSelection = getSelection(); |
| |
| IStatus status; |
| if (treeSelection != null) { |
| if (treeSelection.getSegmentCount() == 2) { |
| status = new Status(IStatus.OK, AdtPlugin.PLUGIN_ID, |
| IStatus.OK, "", //$NON-NLS-1$ |
| null); |
| } else { |
| status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, |
| IStatus.ERROR, "You must select a Resource Item", |
| null); |
| } |
| } else { |
| status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, |
| IStatus.ERROR, "", //$NON-NLS-1$ |
| null); |
| } |
| |
| updateStatus(status); |
| |
| return status.isOK(); |
| } |
| |
| /** |
| * Updates the new res button when the list selection changes. |
| * The name of the button changes depending on the resource. |
| */ |
| private void updateNewResButton() { |
| ResourceType type = getSelectedResourceType(); |
| |
| // We only support adding new strings right now |
| mNewResButton.setEnabled(type == ResourceType.STRING); |
| |
| String title = String.format("New %1$s...", |
| type == null ? "Resource" : type.getDisplayName()); |
| mNewResButton.setText(title); |
| mNewResButton.pack(); |
| } |
| |
| /** |
| * Callback invoked when the mNewResButton is selected by the user. |
| */ |
| private class OnNewResButtonSelected extends SelectionAdapter { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| super.widgetSelected(e); |
| |
| ResourceType type = getSelectedResourceType(); |
| |
| // We currently only support strings |
| if (type == ResourceType.STRING) { |
| |
| ExtractStringRefactoring ref = new ExtractStringRefactoring( |
| mProject, true /*enforceNew*/); |
| RefactoringWizard wizard = new ExtractStringWizard(ref, mProject); |
| RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); |
| try { |
| IWorkbench w = PlatformUI.getWorkbench(); |
| if (op.run(w.getDisplay().getActiveShell(), wizard.getDefaultPageTitle()) == |
| IDialogConstants.OK_ID) { |
| mTreeViewer.refresh(); |
| |
| // select it if possible |
| setupInitialSelection(type, ref.getXmlStringId()); |
| } |
| } catch (InterruptedException ex) { |
| // Interrupted. Pass. |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns the {@link ResourceType} of the selected element, if any. |
| * Returns null if nothing suitable is selected. |
| */ |
| private ResourceType getSelectedResourceType() { |
| ResourceType type = null; |
| |
| TreePath selection = getSelection(); |
| if (selection != null && selection.getSegmentCount() > 0) { |
| Object first = selection.getFirstSegment(); |
| if (first instanceof ResourceType) { |
| type = (ResourceType) first; |
| } |
| } |
| return type; |
| } |
| |
| /** |
| * Sets up the initial selection. |
| * <p/> |
| * This parses {@link #mCurrentResource} to find out the resource type and the resource name. |
| */ |
| private void setupInitialSelection() { |
| // checks the inline id pattern first as it's more restrictive than the other one. |
| Matcher m = sInlineIdResourcePattern.matcher(mCurrentResource); |
| if (m.matches()) { |
| // get the matching name |
| String resourceName = m.group(1); |
| |
| // setup initial selection |
| setupInitialSelection(ResourceType.ID, resourceName); |
| } else { |
| // attempts the inline id pattern |
| m = sResourcePattern.matcher(mCurrentResource); |
| if (m.matches()) { |
| // get the resource type. |
| ResourceType resourceType = ResourceType.getEnum(m.group(1)); |
| if (resourceType != null) { |
| // get the matching name |
| String resourceName = m.group(2); |
| |
| // setup initial selection |
| setupInitialSelection(resourceType, resourceName); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Sets up the initial selection based on a {@link ResourceType} and a resource name. |
| * @param resourceType the resource type. |
| * @param resourceName the resource name. |
| */ |
| private void setupInitialSelection(ResourceType resourceType, String resourceName) { |
| // get all the resources of this type |
| Collection<ResourceItem> resourceItems = |
| mProjectResources.getResourceItemsOfType(resourceType); |
| |
| for (ResourceItem resourceItem : resourceItems) { |
| if (resourceName.equals(resourceItem.getName())) { |
| // name of the resource match, we select it, |
| TreePath treePath = new TreePath(new Object[] { resourceType, resourceItem }); |
| mFilteredTree.getViewer().setSelection( |
| new TreeSelection(treePath), |
| true /*reveal*/); |
| |
| // and we're done. |
| return; |
| } |
| } |
| |
| // if we get here, the resource type is valid, but the resource is missing. |
| // we select and expand the resource type element. |
| TreePath treePath = new TreePath(new Object[] { resourceType }); |
| mFilteredTree.getViewer().setSelection( |
| new TreeSelection(treePath), |
| true /*reveal*/); |
| mFilteredTree.getViewer().setExpandedState(resourceType, true /* expanded */); |
| } |
| } |