com.android.ide.eclipse.adt.internal.ui.ResourceChooser.java Source code

Java tutorial

Introduction

Here is the source code for com.android.ide.eclipse.adt.internal.ui.ResourceChooser.java

Source

/*
 * Copyright (C) 2007 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 static com.android.SdkConstants.ANDROID_PREFIX;
import static com.android.SdkConstants.PREFIX_RESOURCE_REF;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.resources.ResourceItem;
import com.android.ide.common.resources.ResourceRepository;
import com.android.ide.common.resources.ResourceResolver;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtUtils;
import com.android.ide.eclipse.adt.internal.assetstudio.OpenCreateAssetSetWizardAction;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
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.ide.eclipse.adt.internal.resources.ResourceHelper;
import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.resources.ResourceType;
import com.android.utils.Pair;
import com.google.common.collect.Maps;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.window.Window;
import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
import org.eclipse.ui.dialogs.SelectionStatusDialog;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * A dialog to let the user select a resource based on a resource type.
 */
public class ResourceChooser extends AbstractElementListSelectionDialog implements ModifyListener {
    /** The return code from the dialog for the user choosing "Clear" */
    public static final int CLEAR_RETURN_CODE = -5;
    /** The dialog button ID for the user choosing "Clear" */
    private static final int CLEAR_BUTTON_ID = CLEAR_RETURN_CODE;

    private Pattern mProjectResourcePattern;
    private ResourceType mResourceType;
    private final List<ResourceRepository> mProjectResources;
    private final ResourceRepository mFrameworkResources;
    private Pattern mSystemResourcePattern;
    private Button mProjectButton;
    private Button mSystemButton;
    private Button mNewButton;
    private String mCurrentResource;
    private final IProject mProject;
    private IInputValidator mInputValidator;

    /** Helper object used to draw previews for drawables and colors. */
    private ResourcePreviewHelper mPreviewHelper;

    /**
     * Textfield for editing the actual returned value, updated when selection
     * changes. Only shown if {@link #mShowValueText} is true.
     */
    private Text mEditValueText;

    /**
     * Whether the {@link #mEditValueText} textfield should be shown when the dialog is created.
     */
    private boolean mShowValueText;

    /**
     * Flag indicating whether it's the first time {@link #handleSelectionChanged()} is called.
     * This is used to filter out the first selection event, always called by the superclass
     * when the widget is created, to distinguish between "the dialog was created" and
     * "the user clicked on a selection result", since only the latter should wipe out the
     * manual user edit shown in the value text.
     */
    private boolean mFirstSelect = true;

    /**
     * Label used to show the resolved value in the resource chooser. Only shown
     * if the {@link #mResourceResolver} field is set.
     */
    private Label mResolvedLabel;

    /** Resource resolver used to show actual values for resources selected. (Optional). */
    private ResourceResolver mResourceResolver;

    /**
     * Creates a Resource Chooser dialog.
     * @param project Project being worked on
     * @param type The type of the resource to choose
     * @param projectResources The repository for the project
     * @param frameworkResources The Framework resource repository
     * @param parent the parent shell
     */
    private ResourceChooser(@NonNull IProject project, @NonNull ResourceType type,
            @NonNull List<ResourceRepository> projectResources, @Nullable ResourceRepository frameworkResources,
            @NonNull Shell parent) {
        super(parent, new ResourceLabelProvider());
        mProject = project;

        mResourceType = type;
        mProjectResources = projectResources;
        mFrameworkResources = frameworkResources;

        mProjectResourcePattern = Pattern.compile(PREFIX_RESOURCE_REF + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$

        mSystemResourcePattern = Pattern.compile(ANDROID_PREFIX + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$

        setTitle("Resource Chooser");
        setMessage(String.format("Choose a %1$s resource", mResourceType.getDisplayName().toLowerCase(Locale.US)));
    }

    /**
     * Creates a new {@link ResourceChooser}
     *
     * @param editor the associated layout editor
     * @param type the resource type to choose
     * @return a new {@link ResourceChooser}
     */
    @NonNull
    public static ResourceChooser create(@NonNull GraphicalEditorPart editor, @NonNull ResourceType type) {
        IProject project = editor.getProject();
        Shell parent = editor.getCanvasControl().getShell();
        AndroidTargetData targetData = editor.getEditorDelegate().getEditor().getTargetData();
        ResourceChooser chooser = create(project, type, targetData, parent);

        // When editing Strings, allow editing the value text directly. When we
        // get inline editing support (where values entered directly into the
        // textual widget are translated automatically into a resource) this can
        // go away.
        if (type == ResourceType.STRING) {
            chooser.setResourceResolver(editor.getResourceResolver());
            chooser.setShowValueText(true);
        } else if (type == ResourceType.DIMEN || type == ResourceType.INTEGER) {
            chooser.setResourceResolver(editor.getResourceResolver());
        }

        chooser.setPreviewHelper(new ResourcePreviewHelper(chooser, editor));
        return chooser;
    }

    /**
     * Creates a new {@link ResourceChooser}
     *
     * @param project the associated project
     * @param type the resource type to choose
     * @param targetData the associated framework target data
     * @param parent the target shell
     * @return a new {@link ResourceChooser}
     */
    @NonNull
    public static ResourceChooser create(@NonNull IProject project, @NonNull ResourceType type,
            @Nullable AndroidTargetData targetData, @NonNull Shell parent) {
        ResourceManager manager = ResourceManager.getInstance();

        List<ResourceRepository> projectResources = new ArrayList<ResourceRepository>();
        ProjectResources resources = manager.getProjectResources(project);
        projectResources.add(resources);

        // Add in library project resources
        ProjectState projectState = Sdk.getProjectState(project);
        if (projectState != null) {
            List<IProject> libraries = projectState.getFullLibraryProjects();
            if (libraries != null && !libraries.isEmpty()) {
                for (IProject library : libraries) {
                    projectResources.add(manager.getProjectResources(library));
                }
            }
        }

        ResourceRepository frameworkResources = targetData != null ? targetData.getFrameworkResources() : null;
        return new ResourceChooser(project, type, projectResources, frameworkResources, parent);
    }

    /**
     * Sets whether this dialog should show the value field as a separate text
     * value (and take the resulting value of the dialog from this text field
     * rather than from the selection)
     *
     * @param showValueText if true, show the value text field
     * @return this, for constructor chaining
     */
    public ResourceChooser setShowValueText(boolean showValueText) {
        mShowValueText = showValueText;

        return this;
    }

    /**
     * Sets the resource resolver to use to show resolved values for the current
     * selection
     *
     * @param resourceResolver the resource resolver to use
     * @return this, for constructor chaining
     */
    public ResourceChooser setResourceResolver(ResourceResolver resourceResolver) {
        mResourceResolver = resourceResolver;

        return this;
    }

    /**
     * Sets the {@link ResourcePreviewHelper} to use to preview drawable
     * resources, if any
     *
     * @param previewHelper the helper to use
     * @return this, for constructor chaining
     */
    public ResourceChooser setPreviewHelper(ResourcePreviewHelper previewHelper) {
        mPreviewHelper = previewHelper;

        return this;
    }

    /**
     * Sets the initial dialog size
     *
     * @param width the initial width
     * @param height the initial height
     * @return this, for constructor chaining
     */
    public ResourceChooser setInitialSize(int width, int height) {
        setSize(width, height);

        return this;
    }

    @Override
    public void create() {
        super.create();

        if (mShowValueText) {
            mEditValueText.selectAll();
            mEditValueText.setFocus();
        }
    }

    @Override
    protected void createButtonsForButtonBar(Composite parent) {
        createButton(parent, CLEAR_BUTTON_ID, "Clear", false /*defaultButton*/);
        super.createButtonsForButtonBar(parent);
    }

    @Override
    protected void buttonPressed(int buttonId) {
        super.buttonPressed(buttonId);

        if (buttonId == CLEAR_BUTTON_ID) {
            assert CLEAR_RETURN_CODE != Window.OK && CLEAR_RETURN_CODE != Window.CANCEL;
            setReturnCode(CLEAR_RETURN_CODE);
            close();
        }
    }

    /**
     * Sets the currently selected item
     *
     * @param resource the resource url for the currently selected item
     * @return this, for constructor chaining
     */
    public ResourceChooser setCurrentResource(@Nullable String resource) {
        mCurrentResource = resource;

        if (mShowValueText && mEditValueText != null) {
            mEditValueText.setText(resource);
        }

        return this;
    }

    /**
     * Returns the currently selected url
     *
     * @return the currently selected url
     */
    @Nullable
    public String getCurrentResource() {
        return mCurrentResource;
    }

    /**
     * Sets the input validator to use, if any
     *
     * @param inputValidator the validator
     * @return this, for constructor chaining
     */
    public ResourceChooser setInputValidator(@Nullable IInputValidator inputValidator) {
        mInputValidator = inputValidator;

        return this;
    }

    @Override
    protected void computeResult() {
        if (mShowValueText) {
            mCurrentResource = mEditValueText.getText();
            if (mCurrentResource.length() == 0) {
                mCurrentResource = null;
            }
            return;
        }

        computeResultFromSelection();
    }

    private void computeResultFromSelection() {
        if (getSelectionIndex() == -1) {
            mCurrentResource = null;
            return;
        }

        Object[] elements = getSelectedElements();
        if (elements.length == 1 && elements[0] instanceof ResourceItem) {
            ResourceItem item = (ResourceItem) elements[0];

            mCurrentResource = item.getXmlString(mResourceType, mSystemButton.getSelection());

            if (mInputValidator != null && mInputValidator.isValid(mCurrentResource) != null) {
                mCurrentResource = null;
            }
        }
    }

    @Override
    protected Control createDialogArea(Composite parent) {
        Composite top = (Composite) super.createDialogArea(parent);

        createMessageArea(top);

        createButtons(top);
        createFilterText(top);
        createFilteredList(top);

        // create the "New Resource" button
        createNewResButtons(top);

        // Optionally create the value text field, if {@link #mShowValueText} is true
        createValueField(top);

        setupResourceList();
        selectResourceString(mCurrentResource);

        return top;
    }

    /**
     * Creates the radio button to switch between project and system resources.
     * @param top the parent composite
     */
    private void createButtons(Composite top) {
        mProjectButton = new Button(top, SWT.RADIO);
        mProjectButton.setText("Project Resources");
        mProjectButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                super.widgetSelected(e);
                if (mProjectButton.getSelection()) {
                    // Clear selection before changing the list contents. This works around
                    // a bug in the superclass where switching to the framework resources,
                    // choosing one of the last resources, then switching to the project
                    // resources would cause an exception when calling getSelection() because
                    // selection state doesn't get cleared when we set new contents on
                    // the filtered list.
                    fFilteredList.setSelection(new int[0]);
                    setupResourceList();
                    updateNewButton(false /*isSystem*/);
                    updateValue();
                }
            }
        });
        mSystemButton = new Button(top, SWT.RADIO);
        mSystemButton.setText("System Resources");
        mSystemButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                super.widgetSelected(e);
                if (mSystemButton.getSelection()) {
                    fFilteredList.setSelection(new int[0]);
                    setupResourceList();
                    updateNewButton(true /*isSystem*/);
                    updateValue();
                }
            }
        });
        if (mFrameworkResources == null) {
            mSystemButton.setVisible(false);
        }
    }

    /**
     * Creates the "New Resource" button.
     * @param top the parent composite
     */
    private void createNewResButtons(Composite top) {
        mNewButton = new Button(top, SWT.NONE);

        String title = String.format("New %1$s...", mResourceType.getDisplayName());
        if (mResourceType == ResourceType.DRAWABLE) {
            title = "Create New Icon...";
        }
        mNewButton.setText(title);

        mNewButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                super.widgetSelected(e);

                if (mResourceType == ResourceType.STRING) {
                    // Special case: Use Extract String refactoring wizard UI
                    String newName = createNewString();
                    selectAddedItem(newName);
                } else if (mResourceType == ResourceType.DRAWABLE) {
                    // Special case: Use the "Create Icon Set" wizard
                    OpenCreateAssetSetWizardAction action = new OpenCreateAssetSetWizardAction(mProject);
                    action.run();
                    List<IResource> files = action.getCreatedFiles();
                    if (files != null && files.size() > 0) {
                        String newName = AdtUtils.stripAllExtensions(files.get(0).getName());
                        // Recompute the "current resource" to select the new id
                        ResourceItem[] items = setupResourceList();
                        selectItemName(newName, items);
                    }
                } else {
                    if (ResourceHelper.isValueBasedResourceType(mResourceType)) {
                        String newName = createNewValue(mResourceType);
                        if (newName != null) {
                            selectAddedItem(newName);
                        }
                    } else {
                        String newName = createNewFile(mResourceType);
                        if (newName != null) {
                            selectAddedItem(newName);
                        }
                    }
                }
            }

            private void selectAddedItem(@NonNull String newName) {
                // Recompute the "current resource" to select the new id
                ResourceItem[] items = setupResourceList();

                // Ensure that the name is in the list. There's a delay after
                // an item is added (until the builder runs and processes the delta)
                // so if it's not in the list, add it
                boolean found = false;
                for (ResourceItem item : items) {
                    if (newName.equals(item.getName())) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    ResourceItem[] newItems = new ResourceItem[items.length + 1];
                    System.arraycopy(items, 0, newItems, 0, items.length);
                    newItems[items.length] = new ResourceItem(newName);
                    items = newItems;
                    Arrays.sort(items);
                    setListElements(items);
                    fFilteredList.setEnabled(newItems.length > 0);
                }

                selectItemName(newName, items);
            }
        });
    }

    /**
     * Creates the value text field.
     *
     * @param top the parent composite
     */
    private void createValueField(Composite top) {
        if (mShowValueText) {
            mEditValueText = new Text(top, SWT.BORDER);
            if (mCurrentResource != null) {
                mEditValueText.setText(mCurrentResource);
            }
            mEditValueText.addModifyListener(this);

            GridData data = new GridData();
            data.grabExcessVerticalSpace = false;
            data.grabExcessHorizontalSpace = true;
            data.horizontalAlignment = GridData.FILL;
            data.verticalAlignment = GridData.BEGINNING;
            mEditValueText.setLayoutData(data);
            mEditValueText.setFont(top.getFont());
        }

        if (mResourceResolver != null) {
            mResolvedLabel = new Label(top, SWT.NONE);
            GridData data = new GridData();
            data.grabExcessVerticalSpace = false;
            data.grabExcessHorizontalSpace = true;
            data.horizontalAlignment = GridData.FILL;
            data.verticalAlignment = GridData.BEGINNING;
            mResolvedLabel.setLayoutData(data);
        }

        Composite workaround = PropertyFactory.addWorkaround(top);
        if (workaround != null) {
            workaround.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
        }
    }

    private void updateResolvedLabel() {
        if (mResourceResolver == null) {
            return;
        }

        String v = null;
        if (mCurrentResource != null) {
            v = mCurrentResource;
            if (mCurrentResource.startsWith(PREFIX_RESOURCE_REF)) {
                ResourceValue value = mResourceResolver.findResValue(mCurrentResource, false);
                if (value != null) {
                    v = value.getValue();
                }
            }
        }

        if (v == null) {
            v = "";
        }

        mResolvedLabel.setText(String.format("Resolved Value: %1$s", v));
    }

    @Override
    protected void handleSelectionChanged() {
        super.handleSelectionChanged();
        if (mInputValidator != null) {
            Object[] elements = getSelectedElements();
            if (elements.length == 1 && elements[0] instanceof ResourceItem) {
                ResourceItem item = (ResourceItem) elements[0];
                String current = item.getXmlString(mResourceType, mSystemButton.getSelection());
                String error = mInputValidator.isValid(current);
                IStatus status;
                if (error != null) {
                    status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, error);
                } else {
                    status = new Status(IStatus.OK, AdtPlugin.PLUGIN_ID, null);
                }
                updateStatus(status);
            }
        }

        updateValue();
    }

    private void updateValue() {
        if (mPreviewHelper != null) {
            computeResult();
            mPreviewHelper.updatePreview(mResourceType, mCurrentResource);
        }

        if (mShowValueText) {
            if (mFirstSelect) {
                mFirstSelect = false;
                mEditValueText.selectAll();
            } else {
                computeResultFromSelection();
                mEditValueText.setText(mCurrentResource != null ? mCurrentResource : "");
            }
        }

        if (mResourceResolver != null) {
            if (!mShowValueText) {
                computeResultFromSelection();
            }
            updateResolvedLabel();
        }
    }

    @Nullable
    private String createNewFile(ResourceType type) {
        // Show a name/value dialog entering the key name and the value
        Shell shell = AdtPlugin.getShell();
        if (shell == null) {
            return null;
        }

        ResourceNameValidator validator = ResourceNameValidator.create(true /*allowXmlExtension*/, mProject,
                mResourceType);
        InputDialog d = new InputDialog(AdtPlugin.getShell(), "Enter name", // title
                "Enter name", "", //$NON-NLS-2$
                validator);
        if (d.open() == Window.OK) {
            String name = d.getValue().trim();
            if (name.length() == 0) {
                return null;
            }

            Pair<IFile, IRegion> resource = ResourceHelper.createResource(mProject, type, name, null);
            if (resource != null) {
                return name;
            }
        }

        return null;
    }

    @Nullable
    private String createNewValue(ResourceType type) {
        // Show a name/value dialog entering the key name and the value
        Shell shell = AdtPlugin.getShell();
        if (shell == null) {
            return null;
        }
        NameValueDialog dialog = new NameValueDialog(shell, getFilter());
        if (dialog.open() != Window.OK) {
            return null;
        }

        String name = dialog.getName();
        String value = dialog.getValue();
        if (name.length() == 0 || value.length() == 0) {
            return null;
        }

        Pair<IFile, IRegion> resource = ResourceHelper.createResource(mProject, type, name, value);
        if (resource != null) {
            return name;
        }

        return null;
    }

    private String createNewString() {
        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) {
                return ref.getXmlStringId();
            }
        } catch (InterruptedException ex) {
            // Interrupted. Pass.
        }

        return null;
    }

    /**
     * Setups the current list.
     */
    private ResourceItem[] setupResourceList() {
        Collection<ResourceItem> items = null;
        if (mProjectButton.getSelection()) {
            if (mProjectResources.size() == 1) {
                items = mProjectResources.get(0).getResourceItemsOfType(mResourceType);
            } else {
                Map<String, ResourceItem> merged = Maps.newHashMapWithExpectedSize(200);
                for (ResourceRepository repository : mProjectResources) {
                    for (ResourceItem item : repository.getResourceItemsOfType(mResourceType)) {
                        if (!merged.containsKey(item.getName())) {
                            merged.put(item.getName(), item);
                        }
                    }
                }
                items = merged.values();
            }
        } else if (mSystemButton.getSelection()) {
            items = mFrameworkResources.getResourceItemsOfType(mResourceType);
        }

        if (items == null) {
            items = Collections.emptyList();
        }

        ResourceItem[] arrayItems = items.toArray(new ResourceItem[items.size()]);

        // sort the array
        Arrays.sort(arrayItems);

        setListElements(arrayItems);
        fFilteredList.setEnabled(arrayItems.length > 0);

        return arrayItems;
    }

    /**
     * Select an item by its name, if possible.
     */
    private void selectItemName(String itemName, ResourceItem[] items) {
        if (itemName == null || items == null) {
            return;
        }

        for (ResourceItem item : items) {
            if (itemName.equals(item.getName())) {
                setSelection(new Object[] { item });
                break;
            }
        }
    }

    /**
     * Select an item by its full resource string.
     * This also selects between project and system repository based on the resource string.
     */
    private void selectResourceString(String resourceString) {
        boolean isSystem = false;
        String itemName = null;

        if (resourceString != null) {
            // Is this a system resource?
            // If not a system resource or if they are not available, this will be a project res.
            Matcher m = mSystemResourcePattern.matcher(resourceString);
            if (m.matches()) {
                itemName = m.group(1);
                isSystem = true;
            }

            if (!isSystem && itemName == null) {
                // Try to match project resource name
                m = mProjectResourcePattern.matcher(resourceString);
                if (m.matches()) {
                    itemName = m.group(1);
                }
            }
        }

        // Update the repository selection
        mProjectButton.setSelection(!isSystem);
        mSystemButton.setSelection(isSystem);
        updateNewButton(isSystem);

        // Update the list
        ResourceItem[] items = setupResourceList();

        // If we have a selection name, select it
        if (itemName != null) {
            selectItemName(itemName, items);
        }
    }

    private void updateNewButton(boolean isSystem) {
        mNewButton.setEnabled(!isSystem && ResourceHelper.canCreateResourceType(mResourceType));
    }

    // ---- Implements ModifyListener ----

    @Override
    public void modifyText(ModifyEvent e) {
        if (e.getSource() == mEditValueText && mResourceResolver != null) {
            mCurrentResource = mEditValueText.getText();

            if (mCurrentResource.startsWith(PREFIX_RESOURCE_REF)) {
                if (mProjectResourcePattern.matcher(mCurrentResource).matches()
                        || mSystemResourcePattern.matcher(mCurrentResource).matches()) {
                    updateResolvedLabel();
                }
            } else {
                updateResolvedLabel();
            }
        }
    }

    /** Dialog asking for a Name/Value pair */
    private class NameValueDialog extends SelectionStatusDialog implements Listener {
        private org.eclipse.swt.widgets.Text mNameText;
        private org.eclipse.swt.widgets.Text mValueText;
        private String mInitialName;
        private String mName;
        private String mValue;
        private ResourceNameValidator mValidator;

        public NameValueDialog(Shell parent, String initialName) {
            super(parent);
            mInitialName = initialName;
        }

        @Override
        protected Control createDialogArea(Composite parent) {
            Composite container = new Composite(parent, SWT.NONE);
            container.setLayout(new GridLayout(2, false));
            GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
            // Wide enough to accommodate the error label
            gridData.widthHint = 500;
            container.setLayoutData(gridData);

            Label nameLabel = new Label(container, SWT.NONE);
            nameLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
            nameLabel.setText("Name:");

            mNameText = new org.eclipse.swt.widgets.Text(container, SWT.BORDER);
            mNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
            if (mInitialName != null) {
                mNameText.setText(mInitialName);
                mNameText.selectAll();
            }

            Label valueLabel = new Label(container, SWT.NONE);
            valueLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
            valueLabel.setText("Value:");

            mValueText = new org.eclipse.swt.widgets.Text(container, SWT.BORDER);
            mValueText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

            mNameText.addListener(SWT.Modify, this);
            mValueText.addListener(SWT.Modify, this);

            validate();

            return container;
        }

        @Override
        protected void computeResult() {
            mName = mNameText.getText().trim();
            mValue = mValueText.getText().trim();
        }

        private String getName() {
            return mName;
        }

        private String getValue() {
            return mValue;
        }

        @Override
        public void handleEvent(Event event) {
            validate();
        }

        private void validate() {
            IStatus status;
            computeResult();
            if (mName.length() == 0) {
                status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "Enter a name");
            } else if (mValue.length() == 0) {
                status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "Enter a value");
            } else {
                if (mValidator == null) {
                    mValidator = ResourceNameValidator.create(false, mProject, mResourceType);
                }
                String error = mValidator.isValid(mName);
                if (error != null) {
                    status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, error);
                } else {
                    status = new Status(IStatus.OK, AdtPlugin.PLUGIN_ID, null);
                }
            }
            updateStatus(status);
        }
    }

    /**
     * Open the resource chooser for the given type, associated with the given
     * editor
     *
     * @param graphicalEditor the editor associated with the resource to be
     *            chosen (used to find the associated Android target to be used
     *            for framework resources etc)
     * @param type the resource type to be chosen
     * @param currentValue the current value, or null
     * @param validator a validator to be used, or null
     * @return the chosen resource, null if cancelled and "" if value should be
     *         cleared
     */
    public static String chooseResource(@NonNull GraphicalEditorPart graphicalEditor, @NonNull ResourceType type,
            String currentValue, IInputValidator validator) {
        ResourceChooser chooser = create(graphicalEditor, type).setCurrentResource(currentValue);
        if (validator != null) {
            // Ensure wide enough to accommodate validator error message
            chooser.setSize(85, 10);
            chooser.setInputValidator(validator);
        }
        int result = chooser.open();
        if (result == ResourceChooser.CLEAR_RETURN_CODE) {
            return ""; //$NON-NLS-1$
        } else if (result == Window.OK) {
            return chooser.getCurrentResource();
        }

        return null;
    }
}