blob: 6c628658adf7bf253a080fd8503b08d1ea1d412b [file] [log] [blame]
/*
* 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 */);
}
}