aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java1989
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/EclipseUiHelper.java64
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/IUpdateWizardDialog.java30
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java229
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java396
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java958
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java124
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java353
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java142
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourcePreviewHelper.java195
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/WizardDialogEx.java46
11 files changed, 4526 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java
new file mode 100644
index 000000000..44f90822a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java
@@ -0,0 +1,1989 @@
+/*
+ * 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.SdkConstants;
+import com.android.ide.common.resources.LocaleManager;
+import com.android.ide.common.resources.configuration.CountryCodeQualifier;
+import com.android.ide.common.resources.configuration.DensityQualifier;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
+import com.android.ide.common.resources.configuration.LayoutDirectionQualifier;
+import com.android.ide.common.resources.configuration.LocaleQualifier;
+import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
+import com.android.ide.common.resources.configuration.NavigationStateQualifier;
+import com.android.ide.common.resources.configuration.NetworkCodeQualifier;
+import com.android.ide.common.resources.configuration.NightModeQualifier;
+import com.android.ide.common.resources.configuration.ResourceQualifier;
+import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
+import com.android.ide.common.resources.configuration.ScreenHeightQualifier;
+import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
+import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
+import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
+import com.android.ide.common.resources.configuration.ScreenWidthQualifier;
+import com.android.ide.common.resources.configuration.SmallestScreenWidthQualifier;
+import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
+import com.android.ide.common.resources.configuration.TouchScreenQualifier;
+import com.android.ide.common.resources.configuration.UiModeQualifier;
+import com.android.ide.common.resources.configuration.VersionQualifier;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
+import com.android.resources.Density;
+import com.android.resources.Keyboard;
+import com.android.resources.KeyboardState;
+import com.android.resources.LayoutDirection;
+import com.android.resources.Navigation;
+import com.android.resources.NavigationState;
+import com.android.resources.NightMode;
+import com.android.resources.ResourceEnum;
+import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRatio;
+import com.android.resources.ScreenSize;
+import com.android.resources.TouchScreen;
+import com.android.resources.UiMode;
+
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StackLayout;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+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.events.SelectionListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Custom UI widget to let user build a Folder configuration.
+ * <p/>
+ * To use this, instantiate somewhere in the UI and then:
+ * <ul>
+ * <li>Use {@link #setConfiguration(String)} or {@link #setConfiguration(FolderConfiguration)}.
+ * <li>Retrieve the configuration using {@link #getConfiguration(FolderConfiguration)}.
+ * </ul>
+ */
+public class ConfigurationSelector extends Composite {
+
+ public static final int WIDTH_HINT = 600;
+ public static final int HEIGHT_HINT = 250;
+
+ private Runnable mOnChangeListener;
+
+ private TableViewer mFullTableViewer;
+ private TableViewer mSelectionTableViewer;
+ private Button mAddButton;
+ private Button mRemoveButton;
+ private StackLayout mStackLayout;
+
+ private boolean mOnRefresh = false;
+
+ private final FolderConfiguration mBaseConfiguration = new FolderConfiguration();
+ private final FolderConfiguration mSelectedConfiguration = new FolderConfiguration();
+
+ private final HashMap<Class<? extends ResourceQualifier>, QualifierEditBase> mUiMap =
+ new HashMap<Class<? extends ResourceQualifier>, QualifierEditBase>();
+ private final SelectorMode mMode;
+ private Composite mQualifierEditParent;
+ private IQualifierFilter mQualifierFilter;
+
+ /**
+ * Basic of {@link VerifyListener} to only accept digits.
+ */
+ private static class DigitVerifier implements VerifyListener {
+ @Override
+ public void verifyText(VerifyEvent e) {
+ // check for digit only.
+ for (int i = 0 ; i < e.text.length(); i++) {
+ char letter = e.text.charAt(i);
+ if (letter < '0' || letter > '9') {
+ e.doit = false;
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Implementation of {@link VerifyListener} for Country Code qualifiers.
+ */
+ public static class MobileCodeVerifier extends DigitVerifier {
+ @Override
+ public void verifyText(VerifyEvent e) {
+ super.verifyText(e);
+
+ // basic tests passed?
+ if (e.doit) {
+ // check the max 3 digits.
+ if (e.text.length() - e.end + e.start +
+ ((Text)e.getSource()).getText().length() > 3) {
+ e.doit = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Implementation of {@link VerifyListener} for the Language and Region qualifiers.
+ */
+ public static class LanguageRegionVerifier implements VerifyListener {
+ @Override
+ public void verifyText(VerifyEvent e) {
+ // check for length
+ if (e.text.length() - e.end + e.start + ((Combo)e.getSource()).getText().length() > 6) {
+ e.doit = false;
+ return;
+ }
+
+ // check for lower case only.
+ for (int i = 0 ; i < e.text.length(); i++) {
+ char letter = e.text.charAt(i);
+ if (letter == '-') {
+ if (i+e.start != 2) {
+ e.doit = false;
+ return;
+ } else {
+ continue;
+ }
+ }
+ if (i+e.start == 3 && letter != 'r') {
+ e.doit = false;
+ return;
+ }
+ if ((letter < 'a' || letter > 'z') && (letter < 'A' || letter > 'Z')) {
+ e.doit = false;
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Implementation of {@link VerifyListener} for the Density qualifier.
+ */
+ public static class DensityVerifier extends DigitVerifier { }
+
+ /**
+ * Implementation of {@link VerifyListener} for the Screen Dimension qualifier.
+ */
+ public static class DimensionVerifier extends DigitVerifier { }
+
+ /**
+ * Enum for the state of the configuration being created.
+ */
+ public enum ConfigurationState {
+ OK, INVALID_CONFIG, REGION_WITHOUT_LANGUAGE;
+ }
+
+ /**
+ * Behavior mode for the Selector.
+ *
+ * @see #DEFAULT
+ * @see #DEVICE_ONLY
+ * @see #CONFIG_ONLY
+ */
+ public enum SelectorMode {
+ /** the default mode */
+ DEFAULT,
+ /** mode forcing the qualifier values to be valid on a device.
+ * For instance {@link Density#NODPI} is a valid qualifier for a resource configuration but
+ * this is not valid on a device */
+ DEVICE_ONLY,
+ /** mode where only the specific config can be edited. The user can only select
+ * which non-empty qualifier to select. */
+ CONFIG_ONLY;
+ }
+
+ /**
+ * A filter for {@link ResourceQualifier}.
+ * @see ConfigurationSelector#setQualifierFilter(IQualifierFilter)
+ */
+ public interface IQualifierFilter {
+ /**
+ * Returns true of the qualifier is accepted.
+ */
+ boolean accept(ResourceQualifier qualifier);
+ }
+
+ /**
+ * Creates the selector.
+ * <p/>
+ * The {@link SelectorMode} changes the behavior of the selector depending on what is being
+ * edited (a device config, a resource config, a given configuration).
+ *
+ * @param parent the composite parent.
+ * @param mode the mode for the selector.
+ */
+ public ConfigurationSelector(Composite parent, SelectorMode mode) {
+ super(parent, SWT.NONE);
+
+ mMode = mode;
+ mBaseConfiguration.createDefault();
+
+ GridLayout gl = new GridLayout(4, false);
+ gl.marginWidth = gl.marginHeight = 0;
+ setLayout(gl);
+
+ // first column is the first table
+ final Table fullTable = new Table(this, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
+ fullTable.setLayoutData(new GridData(GridData.FILL_BOTH));
+ fullTable.setHeaderVisible(true);
+ fullTable.setLinesVisible(true);
+
+ // create the column
+ final TableColumn fullTableColumn = new TableColumn(fullTable, SWT.LEFT);
+ // set the header
+ fullTableColumn.setText("Available Qualifiers");
+
+ fullTable.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Rectangle r = fullTable.getClientArea();
+ fullTableColumn.setWidth(r.width);
+ }
+ });
+
+ mFullTableViewer = new TableViewer(fullTable);
+ mFullTableViewer.setContentProvider(new QualifierContentProvider());
+ // the label provider must return the value of the label only if the mode is
+ // CONFIG_ONLY
+ mFullTableViewer.setLabelProvider(new QualifierLabelProvider(
+ mMode == SelectorMode.CONFIG_ONLY));
+ mFullTableViewer.setInput(mBaseConfiguration);
+ mFullTableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ ISelection selection = event.getSelection();
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection structSelection = (IStructuredSelection)selection;
+ Object first = structSelection.getFirstElement();
+
+ if (first instanceof ResourceQualifier) {
+ mAddButton.setEnabled(true);
+ return;
+ }
+ }
+
+ mAddButton.setEnabled(false);
+ }
+ });
+
+ // 2nd column is the left/right arrow button
+ Composite buttonComposite = new Composite(this, SWT.NONE);
+ gl = new GridLayout(1, false);
+ gl.marginWidth = gl.marginHeight = 0;
+ buttonComposite.setLayout(gl);
+ buttonComposite.setLayoutData(new GridData(GridData.FILL_VERTICAL));
+
+ new Composite(buttonComposite, SWT.NONE);
+ mAddButton = new Button(buttonComposite, SWT.BORDER | SWT.PUSH);
+ mAddButton.setText("->");
+ mAddButton.setEnabled(false);
+ mAddButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection selection =
+ (IStructuredSelection)mFullTableViewer.getSelection();
+
+ Object first = selection.getFirstElement();
+ if (first instanceof ResourceQualifier) {
+ ResourceQualifier qualifier = (ResourceQualifier)first;
+
+ mBaseConfiguration.removeQualifier(qualifier);
+ mSelectedConfiguration.addQualifier(qualifier);
+
+ mFullTableViewer.refresh();
+ mSelectionTableViewer.refresh();
+ mSelectionTableViewer.setSelection(new StructuredSelection(qualifier), true);
+
+ onChange(false /* keepSelection */);
+ }
+ }
+ });
+
+ mRemoveButton = new Button(buttonComposite, SWT.BORDER | SWT.PUSH);
+ mRemoveButton.setText("<-");
+ mRemoveButton.setEnabled(false);
+ mRemoveButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection selection =
+ (IStructuredSelection)mSelectionTableViewer.getSelection();
+
+ Object first = selection.getFirstElement();
+ if (first instanceof ResourceQualifier) {
+ ResourceQualifier qualifier = (ResourceQualifier)first;
+
+ mSelectedConfiguration.removeQualifier(qualifier);
+ mBaseConfiguration.addQualifier(qualifier);
+
+ mFullTableViewer.refresh();
+ mSelectionTableViewer.refresh();
+
+ onChange(false /* keepSelection */);
+ }
+ }
+ });
+
+ // 3rd column is the selected config table
+ final Table selectionTable = new Table(this, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
+ selectionTable.setLayoutData(new GridData(GridData.FILL_BOTH));
+ selectionTable.setHeaderVisible(true);
+ selectionTable.setLinesVisible(true);
+
+ // create the column
+ final TableColumn selectionTableColumn = new TableColumn(selectionTable, SWT.LEFT);
+ // set the header
+ selectionTableColumn.setText("Chosen Qualifiers");
+
+ selectionTable.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Rectangle r = selectionTable.getClientArea();
+ selectionTableColumn.setWidth(r.width);
+ }
+ });
+ mSelectionTableViewer = new TableViewer(selectionTable);
+ mSelectionTableViewer.setContentProvider(new QualifierContentProvider());
+ // always show the qualifier value in this case.
+ mSelectionTableViewer.setLabelProvider(new QualifierLabelProvider(
+ true /* showQualifierValue */));
+ mSelectionTableViewer.setInput(mSelectedConfiguration);
+ mSelectionTableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ // ignore selection changes during resfreshes in some cases.
+ if (mOnRefresh) {
+ return;
+ }
+
+ ISelection selection = event.getSelection();
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection structSelection = (IStructuredSelection)selection;
+
+ if (structSelection.isEmpty() == false) {
+ Object first = structSelection.getFirstElement();
+
+ if (first instanceof ResourceQualifier) {
+ mRemoveButton.setEnabled(true);
+
+ if (mMode != SelectorMode.CONFIG_ONLY) {
+ QualifierEditBase composite = mUiMap.get(first.getClass());
+
+ if (composite != null) {
+ composite.setQualifier((ResourceQualifier)first);
+ }
+
+ mStackLayout.topControl = composite;
+ mQualifierEditParent.layout();
+ }
+
+ return;
+ }
+ } else {
+ if (mMode != SelectorMode.CONFIG_ONLY) {
+ mStackLayout.topControl = null;
+ mQualifierEditParent.layout();
+ }
+ }
+ }
+
+ mRemoveButton.setEnabled(false);
+ }
+ });
+
+ if (mMode != SelectorMode.CONFIG_ONLY) {
+ // 4th column is the detail of the selected qualifier
+ mQualifierEditParent = new Composite(this, SWT.NONE);
+ mQualifierEditParent.setLayout(mStackLayout = new StackLayout());
+ mQualifierEditParent.setLayoutData(new GridData(GridData.FILL_VERTICAL));
+
+ // create the UI for all the qualifiers, and associate them to the
+ // ResourceQualifer class.
+ mUiMap.put(CountryCodeQualifier.class, new MCCEdit(mQualifierEditParent));
+ mUiMap.put(NetworkCodeQualifier.class, new MNCEdit(mQualifierEditParent));
+ mUiMap.put(LocaleQualifier.class, new LocaleEdit(mQualifierEditParent));
+ mUiMap.put(LayoutDirectionQualifier.class,
+ new LayoutDirectionEdit(mQualifierEditParent));
+ mUiMap.put(SmallestScreenWidthQualifier.class,
+ new SmallestScreenWidthEdit(mQualifierEditParent));
+ mUiMap.put(ScreenWidthQualifier.class, new ScreenWidthEdit(mQualifierEditParent));
+ mUiMap.put(ScreenHeightQualifier.class, new ScreenHeightEdit(mQualifierEditParent));
+ mUiMap.put(ScreenSizeQualifier.class, new ScreenSizeEdit(mQualifierEditParent));
+ mUiMap.put(ScreenRatioQualifier.class, new ScreenRatioEdit(mQualifierEditParent));
+ mUiMap.put(ScreenOrientationQualifier.class, new OrientationEdit(mQualifierEditParent));
+ mUiMap.put(UiModeQualifier.class, new UiModeEdit(mQualifierEditParent));
+ mUiMap.put(NightModeQualifier.class, new NightModeEdit(mQualifierEditParent));
+ mUiMap.put(DensityQualifier.class, new DensityEdit(mQualifierEditParent));
+ mUiMap.put(TouchScreenQualifier.class, new TouchEdit(mQualifierEditParent));
+ mUiMap.put(KeyboardStateQualifier.class, new KeyboardEdit(mQualifierEditParent));
+ mUiMap.put(TextInputMethodQualifier.class, new TextInputEdit(mQualifierEditParent));
+ mUiMap.put(NavigationStateQualifier.class,
+ new NavigationStateEdit(mQualifierEditParent));
+ mUiMap.put(NavigationMethodQualifier.class, new NavigationEdit(mQualifierEditParent));
+ mUiMap.put(ScreenDimensionQualifier.class,
+ new ScreenDimensionEdit(mQualifierEditParent));
+ mUiMap.put(VersionQualifier.class, new VersionEdit(mQualifierEditParent));
+ }
+ }
+
+ /**
+ * Sets a {@link IQualifierFilter}. If non null, this will restrict the qualifiers that
+ * can be chosen.
+ * @param filter the filter to set.
+ */
+ public void setQualifierFilter(IQualifierFilter filter) {
+ mQualifierFilter = filter;
+ }
+
+ /**
+ * Sets a listener to be notified when the configuration changes.
+ * @param listener A {@link Runnable} whose <code>run()</code> method is called when the
+ * configuration is changed. The method is called from the UI thread.
+ */
+ public void setOnChangeListener(Runnable listener) {
+ mOnChangeListener = listener;
+ }
+
+ /**
+ * Initialize the UI with a given {@link FolderConfiguration}. This must
+ * be called from the UI thread.
+ * @param config The configuration.
+ */
+ public void setConfiguration(FolderConfiguration config) {
+
+ if (mMode != SelectorMode.CONFIG_ONLY) {
+ mSelectedConfiguration.set(config, true /*nonFakeValuesOnly*/);
+
+ // create the base config, which is the default config minus the qualifiers
+ // in SelectedConfiguration
+ mBaseConfiguration.substract(mSelectedConfiguration);
+ } else {
+ // set the base config to the edited config.
+ // reset the config to be empty
+ mBaseConfiguration.reset();
+ mBaseConfiguration.set(config, true /*nonFakeValuesOnly*/);
+ }
+
+ mSelectionTableViewer.refresh();
+ mFullTableViewer.refresh();
+ }
+
+ /**
+ * Initialize the UI with the configuration represented by a resource folder name.
+ * This must be called from the UI thread.
+ *
+ * @param folderSegments the segments of the folder name,
+ * split using {@link FolderConfiguration#QUALIFIER_SEP}.
+ * @return true if success, or false if the folder name is not a valid name.
+ */
+ public boolean setConfiguration(String[] folderSegments) {
+ FolderConfiguration config = FolderConfiguration.getConfig(folderSegments);
+
+ if (config == null) {
+ return false;
+ }
+
+ setConfiguration(config);
+
+ return true;
+ }
+
+ /**
+ * Initialize the UI with the configuration represented by a resource folder name.
+ * This must be called from the UI thread.
+ * @param folderName the name of the folder.
+ * @return true if success, or false if the folder name is not a valid name.
+ */
+ public boolean setConfiguration(String folderName) {
+ // split the name of the folder in segments.
+ String[] folderSegments = folderName.split(SdkConstants.RES_QUALIFIER_SEP);
+
+ return setConfiguration(folderSegments);
+ }
+
+ /**
+ * Gets the configuration as setup by the widget.
+ * @param config the {@link FolderConfiguration} object to be filled with the information
+ * from the UI.
+ */
+ public void getConfiguration(FolderConfiguration config) {
+ config.set(mSelectedConfiguration);
+ }
+
+ /**
+ * Returns the state of the configuration being edited/created.
+ */
+ public ConfigurationState getState() {
+ if (mSelectedConfiguration.getInvalidQualifier() != null) {
+ return ConfigurationState.INVALID_CONFIG;
+ }
+
+ return ConfigurationState.OK;
+ }
+
+ /**
+ * Returns the first invalid qualifier of the configuration being edited/created,
+ * or <code>null<code> if they are all valid (or if none exists).
+ * <p/>If {@link #getState()} return {@link ConfigurationState#INVALID_CONFIG} then this will
+ * not return <code>null</code>.
+ */
+ public ResourceQualifier getInvalidQualifier() {
+ return mSelectedConfiguration.getInvalidQualifier();
+ }
+
+ /**
+ * Handle changes in the configuration.
+ * @param keepSelection if <code>true</code> attemps to avoid triggering selection change in
+ * {@link #mSelectedConfiguration}.
+ */
+ private void onChange(boolean keepSelection) {
+ ISelection selection = null;
+ if (keepSelection) {
+ mOnRefresh = true;
+ selection = mSelectionTableViewer.getSelection();
+ }
+
+ mSelectionTableViewer.refresh(true);
+
+ if (keepSelection) {
+ mSelectionTableViewer.setSelection(selection);
+ mOnRefresh = false;
+ }
+
+ if (mOnChangeListener != null) {
+ mOnChangeListener.run();
+ }
+ }
+
+ private void fillCombo(Combo combo, ResourceEnum[] resEnums) {
+ for (ResourceEnum resEnum : resEnums) {
+ // only add the enum if:
+ // not in device mode OR (device mode is true and) it's a valid device value.
+ // Also, always ignore fake values.
+ if ((mMode == SelectorMode.DEFAULT || resEnum.isValidValueForDevice()) &&
+ resEnum.isFakeValue() == false) {
+ combo.add(resEnum.getShortDisplayValue());
+ }
+ }
+ }
+
+ /**
+ * Content provider around a {@link FolderConfiguration}.
+ */
+ private class QualifierContentProvider implements IStructuredContentProvider {
+
+ private FolderConfiguration mInput;
+
+ public QualifierContentProvider() {
+ }
+
+ @Override
+ public void dispose() {
+ // pass
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ // default easy case
+ if (mQualifierFilter == null) {
+ return mInput.getQualifiers();
+ }
+
+ // in this case we have to compute the list
+ ArrayList<ResourceQualifier> list = new ArrayList<ResourceQualifier>();
+ for (ResourceQualifier qual : mInput.getQualifiers()) {
+ if (mQualifierFilter.accept(qual)) {
+ list.add(qual);
+ }
+ }
+
+ return list.toArray();
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ mInput = null;
+ if (newInput instanceof FolderConfiguration) {
+ mInput = (FolderConfiguration)newInput;
+ }
+ }
+ }
+
+ /**
+ * Label provider for {@link ResourceQualifier} objects.
+ */
+ private static class QualifierLabelProvider implements ITableLabelProvider {
+
+ private final boolean mShowQualifierValue;
+
+ public QualifierLabelProvider(boolean showQualifierValue) {
+ mShowQualifierValue = showQualifierValue;
+ }
+
+ @Override
+ public String getColumnText(Object element, int columnIndex) {
+ // only one column, so we can ignore columnIndex
+ if (element instanceof ResourceQualifier) {
+ if (mShowQualifierValue) {
+ String value = ((ResourceQualifier)element).getShortDisplayValue();
+ if (value == null || value.length() == 0) {
+ return String.format("%1$s (?)",
+ ((ResourceQualifier)element).getShortName());
+ } else {
+ return value;
+ }
+
+ } else {
+ return ((ResourceQualifier)element).getShortName();
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public Image getColumnImage(Object element, int columnIndex) {
+ // only one column, so we can ignore columnIndex
+ if (element instanceof ResourceQualifier) {
+ return ResourceHelper.getIcon(((ResourceQualifier)element).getClass());
+ }
+
+ return null;
+ }
+
+ @Override
+ public void addListener(ILabelProviderListener listener) {
+ // pass
+ }
+
+ @Override
+ public void dispose() {
+ // pass
+ }
+
+ @Override
+ public boolean isLabelProperty(Object element, String property) {
+ // pass
+ return false;
+ }
+
+ @Override
+ public void removeListener(ILabelProviderListener listener) {
+ // pass
+ }
+ }
+
+ /**
+ * Base class for Edit widget for {@link ResourceQualifier}.
+ */
+ private abstract static class QualifierEditBase extends Composite {
+
+ public QualifierEditBase(Composite parent, String title) {
+ super(parent, SWT.NONE);
+ setLayout(new GridLayout(1, false));
+
+ new Label(this, SWT.NONE).setText(title);
+ }
+
+ public abstract void setQualifier(ResourceQualifier qualifier);
+ }
+
+ /**
+ * Edit widget for {@link CountryCodeQualifier}.
+ */
+ private class MCCEdit extends QualifierEditBase {
+
+ private final Text mText;
+
+ public MCCEdit(Composite parent) {
+ super(parent, CountryCodeQualifier.NAME);
+
+ mText = new Text(this, SWT.BORDER);
+ mText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mText.addVerifyListener(new MobileCodeVerifier());
+ mText.addModifyListener(new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ onTextChange();
+ }
+ });
+
+ mText.addFocusListener(new FocusAdapter() {
+ @Override
+ public void focusLost(FocusEvent e) {
+ onTextChange();
+ }
+ });
+
+ new Label(this, SWT.NONE).setText("(3 digit code)");
+ }
+
+ private void onTextChange() {
+ String value = mText.getText();
+
+ if (value.length() == 0) {
+ // empty string, means a qualifier with no value.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setCountryCodeQualifier(new CountryCodeQualifier());
+ } else {
+ try {
+ CountryCodeQualifier qualifier = CountryCodeQualifier.getQualifier(
+ CountryCodeQualifier.getFolderSegment(Integer.parseInt(value)));
+ if (qualifier != null) {
+ mSelectedConfiguration.setCountryCodeQualifier(qualifier);
+ } else {
+ // Failure! Looks like the value is wrong
+ // (for instance not exactly 3 digits).
+ mSelectedConfiguration.setCountryCodeQualifier(new CountryCodeQualifier());
+ }
+ } catch (NumberFormatException nfe) {
+ // Looks like the code is not a number. This should not happen since the text
+ // field has a VerifyListener that prevents it.
+ mSelectedConfiguration.setCountryCodeQualifier(new CountryCodeQualifier());
+ }
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ CountryCodeQualifier q = (CountryCodeQualifier)qualifier;
+
+ mText.setText(Integer.toString(q.getCode()));
+ }
+ }
+
+ /**
+ * Edit widget for {@link NetworkCodeQualifier}.
+ */
+ private class MNCEdit extends QualifierEditBase {
+ private final Text mText;
+
+ public MNCEdit(Composite parent) {
+ super(parent, NetworkCodeQualifier.NAME);
+
+ mText = new Text(this, SWT.BORDER);
+ mText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mText.addVerifyListener(new MobileCodeVerifier());
+ mText.addModifyListener(new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ onTextChange();
+ }
+ });
+ mText.addFocusListener(new FocusAdapter() {
+ @Override
+ public void focusLost(FocusEvent e) {
+ onTextChange();
+ }
+ });
+
+ new Label(this, SWT.NONE).setText("(1-3 digit code)");
+ }
+
+ private void onTextChange() {
+ String value = mText.getText();
+
+ if (value.length() == 0) {
+ // empty string, means a qualifier with no value.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setNetworkCodeQualifier(new NetworkCodeQualifier());
+ } else {
+ try {
+ NetworkCodeQualifier qualifier = NetworkCodeQualifier.getQualifier(
+ NetworkCodeQualifier.getFolderSegment(Integer.parseInt(value)));
+ if (qualifier != null) {
+ mSelectedConfiguration.setNetworkCodeQualifier(qualifier);
+ } else {
+ // Failure! Looks like the value is wrong
+ // (for instance not exactly 3 digits).
+ mSelectedConfiguration.setNetworkCodeQualifier(new NetworkCodeQualifier());
+ }
+ } catch (NumberFormatException nfe) {
+ // Looks like the code is not a number. This should not happen since the text
+ // field has a VerifyListener that prevents it.
+ mSelectedConfiguration.setNetworkCodeQualifier(new NetworkCodeQualifier());
+ }
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ NetworkCodeQualifier q = (NetworkCodeQualifier)qualifier;
+
+ mText.setText(Integer.toString(q.getCode()));
+ }
+ }
+
+ /**
+ * Edit widget for {@link LanguageQualifier}.
+ */
+ private class LocaleEdit extends QualifierEditBase {
+ private final Combo mLanguage;
+ private final Label mName;
+
+ public LocaleEdit(Composite parent) {
+ super(parent, LocaleQualifier.NAME);
+
+ mLanguage = new Combo(this, SWT.DROP_DOWN);
+ List<String> codes = LocaleManager.getLanguageCodes();
+ String[] items = codes.toArray(new String[codes.size()]);
+ Arrays.sort(items);
+ mLanguage.setItems(items);
+
+ mLanguage.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mLanguage.addVerifyListener(new LanguageRegionVerifier());
+ mLanguage.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onLanguageChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onLanguageChange();
+ }
+ });
+ mLanguage.addModifyListener(new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ onLanguageChange();
+ }
+ });
+
+ new Label(this, SWT.NONE).setText("(2 letter code or language-rRegion)");
+
+ mName = new Label(this, SWT.NONE);
+ mName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ }
+
+ private void onLanguageChange() {
+ // update the current config
+ String value = mLanguage.getText();
+
+ String newName = "";
+ if (value.length() == 2) {
+ String name = LocaleManager.getLanguageName(value.toLowerCase(Locale.US));
+ if (name != null) {
+ newName = name;
+ }
+ }
+ mName.setText(newName);
+
+ if (value.length() == 0) {
+ // empty string, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setLocaleQualifier(new LocaleQualifier());
+ } else {
+ LocaleQualifier qualifier = LocaleQualifier.getQualifier(value);
+ if (qualifier != null) {
+ mSelectedConfiguration.setLocaleQualifier(qualifier);
+ } else {
+ // Failure! Looks like the value is wrong (for instance a one letter string).
+ mSelectedConfiguration.setLocaleQualifier(new LocaleQualifier());
+ }
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ LocaleQualifier q = (LocaleQualifier)qualifier;
+
+ String value = q.getValue();
+ if (value != null) {
+ mLanguage.setText(value);
+ }
+ }
+ }
+
+ /**
+ * Edit widget for {@link LayoutDirectionQualifier}.
+ */
+ private class LayoutDirectionEdit extends QualifierEditBase {
+
+ private final Combo mDirection;
+
+ public LayoutDirectionEdit(Composite parent) {
+ super(parent, LayoutDirectionQualifier.NAME);
+
+ mDirection = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fillCombo(mDirection, LayoutDirection.values());
+
+ mDirection.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mDirection.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onDirectionChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onDirectionChange();
+ }
+ });
+ }
+
+ protected void onDirectionChange() {
+ // update the current config
+ int index = mDirection.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setLayoutDirectionQualifier(new LayoutDirectionQualifier(
+ LayoutDirection.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setLayoutDirectionQualifier(
+ new LayoutDirectionQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ LayoutDirectionQualifier q = (LayoutDirectionQualifier)qualifier;
+
+ LayoutDirection value = q.getValue();
+ if (value == null) {
+ mDirection.clearSelection();
+ } else {
+ mDirection.select(LayoutDirection.getIndex(value));
+ }
+ }
+ }
+
+
+ /**
+ * Edit widget for {@link SmallestScreenWidthQualifier}.
+ */
+ private class SmallestScreenWidthEdit extends QualifierEditBase {
+
+ private final Text mSize;
+
+ public SmallestScreenWidthEdit(Composite parent) {
+ super(parent, SmallestScreenWidthQualifier.NAME);
+
+ ModifyListener modifyListener = new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ onSizeChange();
+ }
+ };
+
+ FocusAdapter focusListener = new FocusAdapter() {
+ @Override
+ public void focusLost(FocusEvent e) {
+ onSizeChange();
+ }
+ };
+
+ mSize = new Text(this, SWT.BORDER);
+ mSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mSize.addVerifyListener(new DimensionVerifier());
+ mSize.addModifyListener(modifyListener);
+ mSize.addFocusListener(focusListener);
+ }
+
+ private void onSizeChange() {
+ // update the current config
+ String size = mSize.getText();
+
+ if (size.length() == 0) {
+ // if one of the strings is empty, reset to no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setSmallestScreenWidthQualifier(
+ new SmallestScreenWidthQualifier());
+ } else {
+ SmallestScreenWidthQualifier qualifier = SmallestScreenWidthQualifier.getQualifier(
+ size);
+
+ if (qualifier != null) {
+ mSelectedConfiguration.setSmallestScreenWidthQualifier(qualifier);
+ } else {
+ // Failure! Looks like the value is wrong, reset the qualifier
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setSmallestScreenWidthQualifier(
+ new SmallestScreenWidthQualifier());
+ }
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ SmallestScreenWidthQualifier q = (SmallestScreenWidthQualifier)qualifier;
+
+ mSize.setText(Integer.toString(q.getValue()));
+ }
+ }
+
+ /**
+ * Edit widget for {@link ScreenWidthQualifier}.
+ */
+ private class ScreenWidthEdit extends QualifierEditBase {
+
+ private final Text mSize;
+
+ public ScreenWidthEdit(Composite parent) {
+ super(parent, ScreenWidthQualifier.NAME);
+
+ ModifyListener modifyListener = new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ onSizeChange();
+ }
+ };
+
+ FocusAdapter focusListener = new FocusAdapter() {
+ @Override
+ public void focusLost(FocusEvent e) {
+ onSizeChange();
+ }
+ };
+
+ mSize = new Text(this, SWT.BORDER);
+ mSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mSize.addVerifyListener(new DimensionVerifier());
+ mSize.addModifyListener(modifyListener);
+ mSize.addFocusListener(focusListener);
+ }
+
+ private void onSizeChange() {
+ // update the current config
+ String size = mSize.getText();
+
+ if (size.length() == 0) {
+ // if one of the strings is empty, reset to no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setScreenWidthQualifier(new ScreenWidthQualifier());
+ } else {
+ ScreenWidthQualifier qualifier = ScreenWidthQualifier.getQualifier(size);
+
+ if (qualifier != null) {
+ mSelectedConfiguration.setScreenWidthQualifier(qualifier);
+ } else {
+ // Failure! Looks like the value is wrong, reset the qualifier
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setScreenWidthQualifier(
+ new ScreenWidthQualifier());
+ }
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ ScreenWidthQualifier q = (ScreenWidthQualifier)qualifier;
+
+ mSize.setText(Integer.toString(q.getValue()));
+ }
+ }
+
+ /**
+ * Edit widget for {@link ScreenHeightQualifier}.
+ */
+ private class ScreenHeightEdit extends QualifierEditBase {
+
+ private final Text mSize;
+
+ public ScreenHeightEdit(Composite parent) {
+ super(parent, ScreenHeightQualifier.NAME);
+
+ ModifyListener modifyListener = new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ onSizeChange();
+ }
+ };
+
+ FocusAdapter focusListener = new FocusAdapter() {
+ @Override
+ public void focusLost(FocusEvent e) {
+ onSizeChange();
+ }
+ };
+
+ mSize = new Text(this, SWT.BORDER);
+ mSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mSize.addVerifyListener(new DimensionVerifier());
+ mSize.addModifyListener(modifyListener);
+ mSize.addFocusListener(focusListener);
+ }
+
+ private void onSizeChange() {
+ // update the current config
+ String size = mSize.getText();
+
+ if (size.length() == 0) {
+ // if one of the strings is empty, reset to no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setScreenHeightQualifier(new ScreenHeightQualifier());
+ } else {
+ ScreenHeightQualifier qualifier = ScreenHeightQualifier.getQualifier(size);
+
+ if (qualifier != null) {
+ mSelectedConfiguration.setScreenHeightQualifier(qualifier);
+ } else {
+ // Failure! Looks like the value is wrong, reset the qualifier
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setScreenHeightQualifier(
+ new ScreenHeightQualifier());
+ }
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ ScreenHeightQualifier q = (ScreenHeightQualifier)qualifier;
+
+ mSize.setText(Integer.toString(q.getValue()));
+ }
+ }
+
+
+ /**
+ * Edit widget for {@link ScreenSizeQualifier}.
+ */
+ private class ScreenSizeEdit extends QualifierEditBase {
+
+ private final Combo mSize;
+
+ public ScreenSizeEdit(Composite parent) {
+ super(parent, ScreenSizeQualifier.NAME);
+
+ mSize = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fillCombo(mSize, ScreenSize.values());
+
+ mSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mSize.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onScreenSizeChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onScreenSizeChange();
+ }
+ });
+ }
+
+ protected void onScreenSizeChange() {
+ // update the current config
+ int index = mSize.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setScreenSizeQualifier(new ScreenSizeQualifier(
+ ScreenSize.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setScreenSizeQualifier(
+ new ScreenSizeQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ ScreenSizeQualifier q = (ScreenSizeQualifier)qualifier;
+
+ ScreenSize value = q.getValue();
+ if (value == null) {
+ mSize.clearSelection();
+ } else {
+ mSize.select(ScreenSize.getIndex(value));
+ }
+ }
+ }
+
+ /**
+ * Edit widget for {@link ScreenRatioQualifier}.
+ */
+ private class ScreenRatioEdit extends QualifierEditBase {
+
+ private final Combo mRatio;
+
+ public ScreenRatioEdit(Composite parent) {
+ super(parent, ScreenRatioQualifier.NAME);
+
+ mRatio = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fillCombo(mRatio, ScreenRatio.values());
+
+ mRatio.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mRatio.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onScreenRatioChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onScreenRatioChange();
+ }
+ });
+ }
+
+ protected void onScreenRatioChange() {
+ // update the current config
+ int index = mRatio.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setScreenRatioQualifier(new ScreenRatioQualifier(
+ ScreenRatio.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setScreenRatioQualifier(
+ new ScreenRatioQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ ScreenRatioQualifier q = (ScreenRatioQualifier)qualifier;
+
+ ScreenRatio value = q.getValue();
+ if (value == null) {
+ mRatio.clearSelection();
+ } else {
+ mRatio.select(ScreenRatio.getIndex(value));
+ }
+ }
+ }
+
+ /**
+ * Edit widget for {@link ScreenOrientationQualifier}.
+ */
+ private class OrientationEdit extends QualifierEditBase {
+
+ private final Combo mOrientation;
+
+ public OrientationEdit(Composite parent) {
+ super(parent, ScreenOrientationQualifier.NAME);
+
+ mOrientation = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fillCombo(mOrientation, ScreenOrientation.values());
+
+ mOrientation.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mOrientation.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onOrientationChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onOrientationChange();
+ }
+ });
+ }
+
+ protected void onOrientationChange() {
+ // update the current config
+ int index = mOrientation.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setScreenOrientationQualifier(new ScreenOrientationQualifier(
+ ScreenOrientation.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setScreenOrientationQualifier(
+ new ScreenOrientationQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ ScreenOrientationQualifier q = (ScreenOrientationQualifier)qualifier;
+
+ ScreenOrientation value = q.getValue();
+ if (value == null) {
+ mOrientation.clearSelection();
+ } else {
+ mOrientation.select(ScreenOrientation.getIndex(value));
+ }
+ }
+ }
+
+ /**
+ * Edit widget for {@link DockModeQualifier}.
+ */
+ private class UiModeEdit extends QualifierEditBase {
+
+ private final Combo mUiMode;
+
+ public UiModeEdit(Composite parent) {
+ super(parent, UiModeQualifier.NAME);
+
+ mUiMode = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fillCombo(mUiMode, UiMode.values());
+
+ mUiMode.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mUiMode.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onDockModeChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onDockModeChange();
+ }
+ });
+ }
+
+ protected void onDockModeChange() {
+ // update the current config
+ int index = mUiMode.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setUiModeQualifier(
+ new UiModeQualifier(UiMode.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setUiModeQualifier(new UiModeQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ UiModeQualifier q = (UiModeQualifier)qualifier;
+
+ UiMode value = q.getValue();
+ if (value == null) {
+ mUiMode.clearSelection();
+ } else {
+ mUiMode.select(UiMode.getIndex(value));
+ }
+ }
+ }
+
+ /**
+ * Edit widget for {@link NightModeQualifier}.
+ */
+ private class NightModeEdit extends QualifierEditBase {
+
+ private final Combo mNightMode;
+
+ public NightModeEdit(Composite parent) {
+ super(parent, NightModeQualifier.NAME);
+
+ mNightMode = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fillCombo(mNightMode, NightMode.values());
+
+ mNightMode.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mNightMode.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onNightModeChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onNightModeChange();
+ }
+ });
+ }
+
+ protected void onNightModeChange() {
+ // update the current config
+ int index = mNightMode.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setNightModeQualifier(
+ new NightModeQualifier(NightMode.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setNightModeQualifier(new NightModeQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ NightModeQualifier q = (NightModeQualifier)qualifier;
+
+ NightMode value = q.getValue();
+ if (value == null) {
+ mNightMode.clearSelection();
+ } else {
+ mNightMode.select(NightMode.getIndex(value));
+ }
+ }
+ }
+
+
+ /**
+ * Edit widget for {@link DensityQualifier}.
+ */
+ private class DensityEdit extends QualifierEditBase {
+ private final Combo mDensity;
+
+ public DensityEdit(Composite parent) {
+ super(parent, DensityQualifier.NAME);
+
+ mDensity = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fillCombo(mDensity, Density.values());
+
+ mDensity.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mDensity.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onDensityChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onDensityChange();
+ }
+ });
+ }
+
+ private void onDensityChange() {
+ // update the current config
+ int index = mDensity.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setDensityQualifier(new DensityQualifier(
+ Density.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setDensityQualifier(
+ new DensityQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ DensityQualifier q = (DensityQualifier)qualifier;
+
+ Density value = q.getValue();
+ if (value == null) {
+ mDensity.clearSelection();
+ } else {
+ mDensity.select(Density.getIndex(value));
+ }
+ }
+ }
+
+ /**
+ * Edit widget for {@link TouchScreenQualifier}.
+ */
+ private class TouchEdit extends QualifierEditBase {
+
+ private final Combo mTouchScreen;
+
+ public TouchEdit(Composite parent) {
+ super(parent, TouchScreenQualifier.NAME);
+
+ mTouchScreen = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fillCombo(mTouchScreen, TouchScreen.values());
+
+ mTouchScreen.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mTouchScreen.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onTouchChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onTouchChange();
+ }
+ });
+ }
+
+ protected void onTouchChange() {
+ // update the current config
+ int index = mTouchScreen.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setTouchTypeQualifier(new TouchScreenQualifier(
+ TouchScreen.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setTouchTypeQualifier(new TouchScreenQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ TouchScreenQualifier q = (TouchScreenQualifier)qualifier;
+
+ TouchScreen value = q.getValue();
+ if (value == null) {
+ mTouchScreen.clearSelection();
+ } else {
+ mTouchScreen.select(TouchScreen.getIndex(value));
+ }
+ }
+ }
+
+ /**
+ * Edit widget for {@link KeyboardStateQualifier}.
+ */
+ private class KeyboardEdit extends QualifierEditBase {
+
+ private final Combo mKeyboardState;
+
+ public KeyboardEdit(Composite parent) {
+ super(parent, KeyboardStateQualifier.NAME);
+
+ mKeyboardState = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fillCombo(mKeyboardState, KeyboardState.values());
+
+ mKeyboardState.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mKeyboardState.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onKeyboardChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onKeyboardChange();
+ }
+ });
+ }
+
+ protected void onKeyboardChange() {
+ // update the current config
+ int index = mKeyboardState.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setKeyboardStateQualifier(new KeyboardStateQualifier(
+ KeyboardState.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setKeyboardStateQualifier(
+ new KeyboardStateQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ KeyboardStateQualifier q = (KeyboardStateQualifier)qualifier;
+
+ KeyboardState value = q.getValue();
+ if (value == null) {
+ mKeyboardState.clearSelection();
+ } else {
+ mKeyboardState.select(KeyboardState.getIndex(value));
+ }
+ }
+ }
+
+ /**
+ * Edit widget for {@link TextInputMethodQualifier}.
+ */
+ private class TextInputEdit extends QualifierEditBase {
+
+ private final Combo mTextInput;
+
+ public TextInputEdit(Composite parent) {
+ super(parent, TextInputMethodQualifier.NAME);
+
+ mTextInput = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fillCombo(mTextInput, Keyboard.values());
+
+ mTextInput.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mTextInput.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onTextInputChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onTextInputChange();
+ }
+ });
+ }
+
+ protected void onTextInputChange() {
+ // update the current config
+ int index = mTextInput.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setTextInputMethodQualifier(new TextInputMethodQualifier(
+ Keyboard.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setTextInputMethodQualifier(
+ new TextInputMethodQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ TextInputMethodQualifier q = (TextInputMethodQualifier)qualifier;
+
+ Keyboard value = q.getValue();
+ if (value == null) {
+ mTextInput.clearSelection();
+ } else {
+ mTextInput.select(Keyboard.getIndex(value));
+ }
+ }
+ }
+
+ /**
+ * Edit widget for {@link NavigationStateQualifier}.
+ */
+ private class NavigationStateEdit extends QualifierEditBase {
+
+ private final Combo mNavigationState;
+
+ public NavigationStateEdit(Composite parent) {
+ super(parent, NavigationStateQualifier.NAME);
+
+ mNavigationState = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fillCombo(mNavigationState, NavigationState.values());
+
+ mNavigationState.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mNavigationState.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onNavigationChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onNavigationChange();
+ }
+ });
+ }
+
+ protected void onNavigationChange() {
+ // update the current config
+ int index = mNavigationState.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setNavigationStateQualifier(
+ new NavigationStateQualifier(NavigationState.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setNavigationStateQualifier(new NavigationStateQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ NavigationStateQualifier q = (NavigationStateQualifier)qualifier;
+
+ NavigationState value = q.getValue();
+ if (value == null) {
+ mNavigationState.clearSelection();
+ } else {
+ mNavigationState.select(NavigationState.getIndex(value));
+ }
+ }
+ }
+
+
+ /**
+ * Edit widget for {@link NavigationMethodQualifier}.
+ */
+ private class NavigationEdit extends QualifierEditBase {
+
+ private final Combo mNavigation;
+
+ public NavigationEdit(Composite parent) {
+ super(parent, NavigationMethodQualifier.NAME);
+
+ mNavigation = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ fillCombo(mNavigation, Navigation.values());
+
+ mNavigation.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mNavigation.addSelectionListener(new SelectionListener() {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ onNavigationChange();
+ }
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onNavigationChange();
+ }
+ });
+ }
+
+ protected void onNavigationChange() {
+ // update the current config
+ int index = mNavigation.getSelectionIndex();
+
+ if (index != -1) {
+ mSelectedConfiguration.setNavigationMethodQualifier(new NavigationMethodQualifier(
+ Navigation.getByIndex(index)));
+ } else {
+ // empty selection, means no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setNavigationMethodQualifier(
+ new NavigationMethodQualifier());
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ NavigationMethodQualifier q = (NavigationMethodQualifier)qualifier;
+
+ Navigation value = q.getValue();
+ if (value == null) {
+ mNavigation.clearSelection();
+ } else {
+ mNavigation.select(Navigation.getIndex(value));
+ }
+ }
+ }
+
+ /**
+ * Edit widget for {@link ScreenDimensionQualifier}.
+ */
+ private class ScreenDimensionEdit extends QualifierEditBase {
+
+ private final Text mSize1;
+ private final Text mSize2;
+
+ public ScreenDimensionEdit(Composite parent) {
+ super(parent, ScreenDimensionQualifier.NAME);
+
+ ModifyListener modifyListener = new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ onSizeChange();
+ }
+ };
+
+ FocusAdapter focusListener = new FocusAdapter() {
+ @Override
+ public void focusLost(FocusEvent e) {
+ onSizeChange();
+ }
+ };
+
+ mSize1 = new Text(this, SWT.BORDER);
+ mSize1.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mSize1.addVerifyListener(new DimensionVerifier());
+ mSize1.addModifyListener(modifyListener);
+ mSize1.addFocusListener(focusListener);
+
+ mSize2 = new Text(this, SWT.BORDER);
+ mSize2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mSize2.addVerifyListener(new DimensionVerifier());
+ mSize2.addModifyListener(modifyListener);
+ mSize2.addFocusListener(focusListener);
+ }
+
+ private void onSizeChange() {
+ // update the current config
+ String size1 = mSize1.getText();
+ String size2 = mSize2.getText();
+
+ if (size1.length() == 0 || size2.length() == 0) {
+ // if one of the strings is empty, reset to no qualifier.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setScreenDimensionQualifier(new ScreenDimensionQualifier());
+ } else {
+ ScreenDimensionQualifier qualifier = ScreenDimensionQualifier.getQualifier(size1,
+ size2);
+
+ if (qualifier != null) {
+ mSelectedConfiguration.setScreenDimensionQualifier(qualifier);
+ } else {
+ // Failure! Looks like the value is wrong, reset the qualifier
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setScreenDimensionQualifier(
+ new ScreenDimensionQualifier());
+ }
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ ScreenDimensionQualifier q = (ScreenDimensionQualifier)qualifier;
+
+ mSize1.setText(Integer.toString(q.getValue1()));
+ mSize2.setText(Integer.toString(q.getValue2()));
+ }
+ }
+
+ /**
+ * Edit widget for {@link VersionQualifier}.
+ */
+ private class VersionEdit extends QualifierEditBase {
+ private final Text mText;
+
+ public VersionEdit(Composite parent) {
+ super(parent, VersionQualifier.NAME);
+
+ mText = new Text(this, SWT.BORDER);
+ mText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mText.addVerifyListener(new MobileCodeVerifier());
+ mText.addModifyListener(new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ onVersionChange();
+ }
+ });
+ mText.addFocusListener(new FocusAdapter() {
+ @Override
+ public void focusLost(FocusEvent e) {
+ onVersionChange();
+ }
+ });
+
+ new Label(this, SWT.NONE).setText("(Platform API level)");
+ }
+
+ private void onVersionChange() {
+ String value = mText.getText();
+
+ if (value.length() == 0) {
+ // empty string, means a qualifier with no value.
+ // Since the qualifier classes are immutable, and we don't want to
+ // remove the qualifier from the configuration, we create a new default one.
+ mSelectedConfiguration.setVersionQualifier(new VersionQualifier());
+ } else {
+ try {
+ VersionQualifier qualifier = VersionQualifier.getQualifier(
+ VersionQualifier.getFolderSegment(Integer.parseInt(value)));
+ if (qualifier != null) {
+ mSelectedConfiguration.setVersionQualifier(qualifier);
+ } else {
+ // Failure! Looks like the value is wrong
+ mSelectedConfiguration.setVersionQualifier(new VersionQualifier());
+ }
+ } catch (NumberFormatException nfe) {
+ // Looks like the code is not a number. This should not happen since the text
+ // field has a VerifyListener that prevents it.
+ mSelectedConfiguration.setVersionQualifier(new VersionQualifier());
+ }
+ }
+
+ // notify of change
+ onChange(true /* keepSelection */);
+ }
+
+ @Override
+ public void setQualifier(ResourceQualifier qualifier) {
+ VersionQualifier q = (VersionQualifier)qualifier;
+
+ mText.setText(Integer.toString(q.getVersion()));
+ }
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/EclipseUiHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/EclipseUiHelper.java
new file mode 100644
index 000000000..f0fd23135
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/EclipseUiHelper.java
@@ -0,0 +1,64 @@
+/*
+ * 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 org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Helpers for Eclipse UI related stuff.
+ */
+public final class EclipseUiHelper {
+
+ /** View Id for the default Eclipse Content Outline view. */
+ public static final String CONTENT_OUTLINE_VIEW_ID = "org.eclipse.ui.views.ContentOutline";
+ /** View Id for the default Eclipse Property Sheet view. */
+ public static final String PROPERTY_SHEET_VIEW_ID = "org.eclipse.ui.views.PropertySheet";
+
+ /** This class never gets instantiated. */
+ private EclipseUiHelper() {
+ }
+
+ /**
+ * Shows the corresponding view.
+ * <p/>
+ * Silently fails in case of error.
+ *
+ * @param viewId One of {@link #CONTENT_OUTLINE_VIEW_ID}, {@link #PROPERTY_SHEET_VIEW_ID}.
+ * @param activate True to force activate (i.e. takes focus), false to just make visible (i.e.
+ * does not steal focus.)
+ */
+ public static void showView(String viewId, boolean activate) {
+ IWorkbenchWindow win = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (win != null) {
+ IWorkbenchPage page = win.getActivePage();
+ if (page != null) {
+ try {
+ IViewPart part = page.showView(viewId,
+ null /* secondaryId */,
+ activate ? IWorkbenchPage.VIEW_ACTIVATE : IWorkbenchPage.VIEW_VISIBLE);
+ } catch (PartInitException e) {
+ // ignore
+ }
+ }
+ }
+
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/IUpdateWizardDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/IUpdateWizardDialog.java
new file mode 100755
index 000000000..c49b58953
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/IUpdateWizardDialog.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2009 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 org.eclipse.jface.wizard.WizardDialog;
+
+
+/**
+ * An interface that enables a client to update {@link WizardDialog} after its creation.
+ */
+public interface IUpdateWizardDialog {
+ /**
+ * Invoked after {@link WizardDialog#create()} to let the caller update the dialog.
+ */
+ public void updateWizardDialog(WizardDialogEx dialog);
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java
new file mode 100644
index 000000000..c2a5f71b2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/MarginChooser.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2011 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.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.resources.ResourceType;
+
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+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.dialogs.SelectionStatusDialog;
+
+/**
+ * Dialog for choosing margins
+ */
+public class MarginChooser extends SelectionStatusDialog implements Listener {
+ private GraphicalEditorPart mEditor;
+ private AndroidTargetData mTargetData;
+ private Text mLeftField;
+ private Text mRightField;
+ private Text mTopField;
+ private Text mBottomField;
+ private Text mAllField;
+ private String mInitialAll;
+ private String mInitialLeft;
+ private String mInitialRight;
+ private String mInitialTop;
+ private String mInitialBottom;
+ private Label mErrorLabel;
+ private String[] mMargins;
+
+ // Client data key for resource buttons pointing to the associated text field
+ private final static String PROP_TEXTFIELD = "textField"; //$NON-NLS-1$
+
+ /**
+ * Constructs a new margin chooser dialog.
+ *
+ * @param parent parent widget
+ * @param editor associated layout editor
+ * @param targetData current SDK target
+ * @param all current value for the all margins attribute
+ * @param left current value for the left margin
+ * @param right current value for the right margin
+ * @param top current value for the top margin
+ * @param bottom current value for the bottom margin
+ */
+ public MarginChooser(Shell parent, GraphicalEditorPart editor, AndroidTargetData targetData, String all,
+ String left, String right, String top, String bottom) {
+ super(parent);
+ setTitle("Edit Margins");
+ mEditor = editor;
+ mTargetData = targetData;
+ mInitialAll = all;
+ mInitialLeft = left;
+ mInitialRight = right;
+ mInitialTop = top;
+ mInitialBottom = bottom;
+ }
+
+ @SuppressWarnings("unused") // SWT constructors have side effects, "new Label" is not unused.
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite container = new Composite(parent, SWT.NONE);
+ container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+
+ container.setLayout(new GridLayout(3, false));
+
+ Label allLabel = new Label(container, SWT.NONE);
+ allLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ allLabel.setText("All:");
+
+ mAllField = new Text(container, SWT.BORDER | SWT.LEFT);
+ mAllField.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mAllField.setText(mInitialAll != null ? mInitialAll : ""); //$NON-NLS-1$
+
+ Button allButton = new Button(container, SWT.NONE);
+ allButton.setText("Resource...");
+ allButton.setData(PROP_TEXTFIELD, mAllField);
+
+ Label label = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL);
+ label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 3, 1));
+
+ Label leftLabel = new Label(container, SWT.NONE);
+ leftLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ leftLabel.setText("Left:");
+
+ mLeftField = new Text(container, SWT.BORDER | SWT.LEFT);
+ mLeftField.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mLeftField.setText(mInitialLeft != null ? mInitialLeft : ""); //$NON-NLS-1$
+
+ Button leftButton = new Button(container, SWT.NONE);
+ leftButton.setText("Resource...");
+ leftButton.setData(PROP_TEXTFIELD, mLeftField);
+
+ Label rightLabel = new Label(container, SWT.NONE);
+ rightLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ rightLabel.setText("Right:");
+
+ mRightField = new Text(container, SWT.BORDER | SWT.LEFT);
+ mRightField.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mRightField.setText(mInitialRight != null ? mInitialRight : ""); //$NON-NLS-1$
+
+ Button rightButton = new Button(container, SWT.NONE);
+ rightButton.setText("Resource...");
+ rightButton.setData(PROP_TEXTFIELD, mRightField);
+
+ Label topLabel = new Label(container, SWT.NONE);
+ topLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ topLabel.setText("Top:");
+
+ mTopField = new Text(container, SWT.BORDER | SWT.LEFT);
+ mTopField.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mTopField.setText(mInitialTop != null ? mInitialTop : ""); //$NON-NLS-1$
+
+ Button topButton = new Button(container, SWT.NONE);
+ topButton.setText("Resource...");
+ topButton.setData(PROP_TEXTFIELD, mTopField);
+
+ Label bottomLabel = new Label(container, SWT.NONE);
+ bottomLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ bottomLabel.setText("Bottom:");
+
+ mBottomField = new Text(container, SWT.BORDER | SWT.LEFT);
+ mBottomField.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mBottomField.setText(mInitialBottom != null ? mInitialBottom : ""); //$NON-NLS-1$
+
+ Button bottomButton = new Button(container, SWT.NONE);
+ bottomButton.setText("Resource...");
+ bottomButton.setData(PROP_TEXTFIELD, mBottomField);
+
+ allButton.addListener(SWT.Selection, this);
+ leftButton.addListener(SWT.Selection, this);
+ rightButton.addListener(SWT.Selection, this);
+ topButton.addListener(SWT.Selection, this);
+ bottomButton.addListener(SWT.Selection, this);
+
+ mAllField.addListener(SWT.Modify, this);
+ mLeftField.addListener(SWT.Modify, this);
+ mRightField.addListener(SWT.Modify, this);
+ mTopField.addListener(SWT.Modify, this);
+ mBottomField.addListener(SWT.Modify, this);
+
+ new Label(container, SWT.NONE);
+ mErrorLabel = new Label(container, SWT.WRAP);
+ mErrorLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+ mErrorLabel.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_RED));
+ return container;
+ }
+
+ @Override
+ protected void computeResult() {
+ mMargins = new String[] {
+ mAllField.getText().trim(),
+ mLeftField.getText().trim(), mRightField.getText().trim(),
+ mTopField.getText().trim(), mBottomField.getText().trim()
+ };
+ }
+
+ /**
+ * Returns the margins in the order all, left, right, top, bottom
+ *
+ * @return the margins in the order all, left, right, top, bottom, never
+ * null
+ */
+ public String[] getMargins() {
+ return mMargins;
+ }
+
+ @Override
+ public void handleEvent(Event event) {
+ if (event.type == SWT.Modify) {
+ // Text field modification -- warn about non-dip numbers
+ if (event.widget instanceof Text) {
+ Text text = (Text) event.widget;
+ String input = text.getText().trim();
+ boolean isNumber = false;
+ try {
+ if (Integer.parseInt(input) > 0) {
+ isNumber = true;
+ }
+ } catch (NumberFormatException nufe) {
+ // Users are allowed to enter non-numbers here, not an error
+ }
+ if (isNumber) {
+ String message = String.format("Hint: Use \"%1$sdp\" instead", input);
+ mErrorLabel.setText(message);
+ } else {
+ mErrorLabel.setText("");
+ }
+ }
+ } else if (event.type == SWT.Selection) {
+ // Button pressed - open resource chooser
+ if (event.widget instanceof Button) {
+ Button button = (Button) event.widget;
+ Text text = (Text) button.getData(PROP_TEXTFIELD);
+
+ // Open a resource chooser dialog for specified resource type.
+ ResourceChooser chooser = ResourceChooser.create(mEditor, ResourceType.DIMEN)
+ .setCurrentResource(text.getText().trim());
+ if (chooser.open() == Window.OK) {
+ text.setText(chooser.getCurrentResource());
+ }
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java
new file mode 100644
index 000000000..6c628658a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ReferenceChooserDialog.java
@@ -0,0 +1,396 @@
+/*
+ * 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 */);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
new file mode 100644
index 000000000..ce828cf2a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
@@ -0,0 +1,958 @@
+/*
+ * 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-1$
+ 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;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java
new file mode 100644
index 000000000..d26dfaf4a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceContentProvider.java
@@ -0,0 +1,124 @@
+/*
+ * 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 com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.resources.ResourceType;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Content provider for the Resource Explorer TreeView.
+ * Each level of the tree is represented by a different class.
+ * <ul>
+ * <li>{@link ResourceType}. This represents the list of existing Resource Type present
+ * in the resources. This can be matched to the subclasses inside the class <code>R</code>
+ * </li>
+ * <ul>
+ * <li>{@link ResourceItem}. This represents one resource (which can existing in various alternate
+ * versions). This is similar to the resource Ids defined as <code>R.sometype.id</code>.
+ * </li>
+ * <ul>
+ * <li>{@link ResourceFile}. (optional) This represents a particular version of the
+ * {@link ResourceItem}. It is displayed as a list of resource qualifier.
+ * </li>
+ * </ul>
+ * </ul>
+ * </ul>
+ *
+ * @see ResourceLabelProvider
+ */
+public class ResourceContentProvider implements ITreeContentProvider {
+
+ /**
+ * The current ProjectResources being displayed.
+ */
+ private ResourceRepository mResources;
+
+ private boolean mFullLevels;
+
+ /**
+ * Constructs a new content providers for resource display.
+ * @param fullLevels if <code>true</code> the content provider will suppport all 3 levels. If
+ * <code>false</code>, only two levels are provided.
+ */
+ public ResourceContentProvider(boolean fullLevels) {
+ mFullLevels = fullLevels;
+ }
+
+ @Override
+ public Object[] getChildren(Object parentElement) {
+ if (parentElement instanceof ResourceType) {
+ Object[] array = mResources.getResourceItemsOfType(
+ (ResourceType)parentElement).toArray();
+ Arrays.sort(array);
+ return array;
+ } else if (mFullLevels && parentElement instanceof ResourceItem) {
+ return ((ResourceItem)parentElement).getSourceFileArray();
+ }
+ return null;
+ }
+
+ @Override
+ public Object getParent(Object element) {
+ // pass
+ return null;
+ }
+
+ @Override
+ public boolean hasChildren(Object element) {
+ if (element instanceof ResourceType) {
+ return mResources.hasResourcesOfType((ResourceType)element);
+ } else if (mFullLevels && element instanceof ResourceItem) {
+ return ((ResourceItem)element).hasAlternates();
+ }
+ return false;
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ if (inputElement instanceof ResourceRepository) {
+ if ((ResourceRepository)inputElement == mResources) {
+ // get the top level resources.
+ List<ResourceType> types = mResources.getAvailableResourceTypes();
+ Collections.sort(types);
+ return types.toArray();
+ }
+ }
+
+ return new Object[0];
+ }
+
+ @Override
+ public void dispose() {
+ // pass
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ if (newInput instanceof ResourceRepository) {
+ mResources = (ResourceRepository)newInput;
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java
new file mode 100644
index 000000000..f48423c84
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceExplorerView.java
@@ -0,0 +1,353 @@
+/*
+ * 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 com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IResourceEventListener;
+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.io.IFileWrapper;
+import com.android.io.IAbstractFile;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.part.ViewPart;
+
+import java.util.Iterator;
+
+/**
+ * Resource Explorer View.
+ * <p/>
+ * This contains a basic Tree view, and uses a TreeViewer to handle the data.
+ * <p/>
+ * The view listener to change in selection in the workbench, and update to show the resource
+ * of the project of the current selected item (either item in the package explorer, or of the
+ * current editor).
+ *
+ * @see ResourceContentProvider
+ */
+public class ResourceExplorerView extends ViewPart implements ISelectionListener,
+ IResourceEventListener {
+
+ // Note: keep using the obsolete SdkConstants.EDITORS_NAMESPACE (which used
+ // to be the Editors Plugin ID) to keep existing preferences functional.
+ private final static String PREFS_COLUMN_RES =
+ AdtConstants.EDITORS_NAMESPACE + "ResourceExplorer.Col1"; //$NON-NLS-1$
+ private final static String PREFS_COLUMN_2 =
+ AdtConstants.EDITORS_NAMESPACE + "ResourceExplorer.Col2"; //$NON-NLS-1$
+
+ private Tree mTree;
+ private TreeViewer mTreeViewer;
+
+ private IProject mCurrentProject;
+
+ public ResourceExplorerView() {
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ mTree = new Tree(parent, SWT.SINGLE | SWT.VIRTUAL);
+ mTree.setLayoutData(new GridData(GridData.FILL_BOTH));
+ mTree.setHeaderVisible(true);
+ mTree.setLinesVisible(true);
+
+ final IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
+
+ // create 2 columns. The main one with the resources, and an "info" column.
+ createTreeColumn(mTree, "Resources", SWT.LEFT,
+ "abcdefghijklmnopqrstuvwxz", -1, PREFS_COLUMN_RES, store); //$NON-NLS-1$
+ createTreeColumn(mTree, "Info", SWT.LEFT,
+ "0123456789", -1, PREFS_COLUMN_2, store); //$NON-NLS-1$
+
+ // create the jface wrapper
+ mTreeViewer = new TreeViewer(mTree);
+
+ mTreeViewer.setContentProvider(new ResourceContentProvider(true /* fullLevels */));
+ mTreeViewer.setLabelProvider(new ResourceLabelProvider());
+
+ // listen to selection change in the workbench.
+ IWorkbenchPage page = getSite().getPage();
+
+ page.addSelectionListener(this);
+
+ // init with current selection
+ selectionChanged(getSite().getPart(), page.getSelection());
+
+ // add support for double click.
+ mTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
+ @Override
+ public void doubleClick(DoubleClickEvent event) {
+ ISelection sel = event.getSelection();
+
+ if (sel instanceof IStructuredSelection) {
+ IStructuredSelection selection = (IStructuredSelection) sel;
+
+ if (selection.size() == 1) {
+ Object element = selection.getFirstElement();
+
+ // if it's a resourceFile, we directly open it.
+ if (element instanceof ResourceFile) {
+ try {
+ IAbstractFile iAbstractFile = ((ResourceFile)element).getFile();
+ if (iAbstractFile instanceof IFileWrapper) {
+ IDE.openEditor(getSite().getWorkbenchWindow().getActivePage(),
+ ((IFileWrapper)iAbstractFile).getIFile());
+ }
+ } catch (PartInitException e) {
+ }
+ } else if (element instanceof ResourceItem) {
+ // if it's a ResourceItem, we open the first file, but only if
+ // there's no alternate files.
+ ResourceItem item = (ResourceItem)element;
+
+ if (item.isEditableDirectly()) {
+ ResourceFile[] files = item.getSourceFileArray();
+ if (files[0] != null) {
+ try {
+ IAbstractFile iAbstractFile = files[0].getFile();
+ if (iAbstractFile instanceof IFileWrapper) {
+ IDE.openEditor(
+ getSite().getWorkbenchWindow().getActivePage(),
+ ((IFileWrapper)iAbstractFile).getIFile());
+ }
+ } catch (PartInitException e) {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+
+ // set up the resource manager to send us resource change notification
+ AdtPlugin.getDefault().getResourceMonitor().addResourceEventListener(this);
+ }
+
+ @Override
+ public void dispose() {
+ AdtPlugin.getDefault().getResourceMonitor().removeResourceEventListener(this);
+
+ super.dispose();
+ }
+
+ @Override
+ public void setFocus() {
+ mTree.setFocus();
+ }
+
+ /**
+ * Processes a new selection.
+ */
+ @Override
+ public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+ // first we test if the part is an editor.
+ if (part instanceof IEditorPart) {
+ // if it is, we check if it's a file editor.
+ IEditorInput input = ((IEditorPart)part).getEditorInput();
+
+ if (input instanceof IFileEditorInput) {
+ // from the file editor we can get the IFile object, and from it, the IProject.
+ IFile file = ((IFileEditorInput)input).getFile();
+
+ // get the file project
+ IProject project = file.getProject();
+
+ handleProjectSelection(project);
+ }
+ } else if (selection instanceof IStructuredSelection) {
+ // if it's not an editor, we look for structured selection.
+ for (Iterator<?> it = ((IStructuredSelection) selection).iterator();
+ it.hasNext();) {
+ Object element = it.next();
+ IProject project = null;
+
+ // if we are in the navigator or package explorer, the selection could contain a
+ // IResource object.
+ if (element instanceof IResource) {
+ project = ((IResource) element).getProject();
+ } else if (element instanceof IJavaElement) {
+ // if we are in the package explorer on a java element, we handle that too.
+ IJavaElement javaElement = (IJavaElement)element;
+ IJavaProject javaProject = javaElement.getJavaProject();
+ if (javaProject != null) {
+ project = javaProject.getProject();
+ }
+ } else if (element instanceof IAdaptable) {
+ // finally we try to get a project object from IAdaptable.
+ project = (IProject) ((IAdaptable) element)
+ .getAdapter(IProject.class);
+ }
+
+ // if we found a project, handle it, and return.
+ if (project != null) {
+ if (handleProjectSelection(project)) {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles a project selection.
+ * @param project the new selected project
+ * @return true if the project could be processed.
+ */
+ private boolean handleProjectSelection(IProject project) {
+ try {
+ // if it's an android project, then we get its resources, and feed them
+ // to the tree viewer.
+ if (project.hasNature(AdtConstants.NATURE_DEFAULT)) {
+ if (mCurrentProject != project) {
+ ProjectResources projRes = ResourceManager.getInstance().getProjectResources(
+ project);
+ if (projRes != null) {
+ mTreeViewer.setInput(projRes);
+ mCurrentProject = project;
+ return true;
+ }
+ }
+ }
+ } catch (CoreException e) {
+ }
+
+ return false;
+ }
+
+ /**
+ * Create a TreeColumn with the specified parameters. If a
+ * <code>PreferenceStore</code> object and a preference entry name String
+ * object are provided then the column will listen to change in its width
+ * and update the preference store accordingly.
+ *
+ * @param parent The Table parent object
+ * @param header The header string
+ * @param style The column style
+ * @param sample_text A sample text to figure out column width if preference
+ * value is missing
+ * @param fixedSize a fixed size. If != -1 the column is non resizable
+ * @param pref_name The preference entry name for column width
+ * @param prefs The preference store
+ */
+ public void createTreeColumn(Tree parent, String header, int style,
+ String sample_text, int fixedSize, final String pref_name,
+ final IPreferenceStore prefs) {
+
+ // create the column
+ TreeColumn col = new TreeColumn(parent, style);
+
+ if (fixedSize != -1) {
+ col.setWidth(fixedSize);
+ col.setResizable(false);
+ } else {
+ // if there is no pref store or the entry is missing, we use the sample
+ // text and pack the column.
+ // Otherwise we just read the width from the prefs and apply it.
+ if (prefs == null || prefs.contains(pref_name) == false) {
+ col.setText(sample_text);
+ col.pack();
+
+ // init the prefs store with the current value
+ if (prefs != null) {
+ prefs.setValue(pref_name, col.getWidth());
+ }
+ } else {
+ col.setWidth(prefs.getInt(pref_name));
+ }
+
+ // if there is a pref store and a pref entry name, then we setup a
+ // listener to catch column resize to put the new width value into the store.
+ if (prefs != null && pref_name != null) {
+ col.addControlListener(new ControlListener() {
+ @Override
+ public void controlMoved(ControlEvent e) {
+ }
+
+ @Override
+ public void controlResized(ControlEvent e) {
+ // get the new width
+ int w = ((TreeColumn)e.widget).getWidth();
+
+ // store in pref store
+ prefs.setValue(pref_name, w);
+ }
+ });
+ }
+ }
+
+ // set the header
+ col.setText(header);
+ }
+
+ /**
+ * Processes a start in a resource event change.
+ */
+ @Override
+ public void resourceChangeEventStart() {
+ // pass
+ }
+
+ /**
+ * Processes the end of a resource change event.
+ */
+ @Override
+ public void resourceChangeEventEnd() {
+ try {
+ mTree.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (mTree.isDisposed() == false) {
+ mTreeViewer.refresh();
+ }
+ }
+ });
+ } catch (SWTException e) {
+ // display is disposed. nothing to do.
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java
new file mode 100644
index 000000000..5bf8615d8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceLabelProvider.java
@@ -0,0 +1,142 @@
+/*
+ * 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 com.android.ide.common.resources.ResourceFile;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.resources.ResourceType;
+
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Label provider for the Resource Explorer TreeView.
+ * Each level of the tree is represented by a different class.
+ * <ul>
+ * <li>{@link ResourceType}. This represents the list of existing Resource Type present
+ * in the resources. This can be matched to the subclasses inside the class <code>R</code>
+ * </li>
+ * <ul>
+ * <li>{@link ResourceItem}. This represents one resource. The actual type can be
+ * {@link ConfigurableResourceItem} (which can exist in various alternate versions),
+ * or {@link IdResourceItem}.
+ * This is similar to the resource Ids defined as <code>R.sometype.id</code>.
+ * </li>
+ * <ul>
+ * <li>{@link ResourceFile}. This represents a particular version of the {@link ResourceItem}.
+ * It is displayed as a list of resource qualifier.
+ * </li>
+ * </ul>
+ * </ul>
+ * </ul>
+ *
+ * @see ResourceContentProvider
+ */
+public class ResourceLabelProvider implements ILabelProvider, ITableLabelProvider {
+ private Image mWarningImage;
+
+ public ResourceLabelProvider() {
+ mWarningImage = PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(
+ ISharedImages.IMG_OBJS_WARN_TSK).createImage();
+ }
+
+ /**
+ * @see #getColumnImage(Object, int)
+ */
+ @Override
+ public Image getImage(Object element) {
+ // pass
+ return null;
+ }
+
+ /**
+ * @see #getColumnText(Object, int)
+ */
+ @Override
+ public String getText(Object element) {
+ return getColumnText(element, 0);
+ }
+
+ @Override
+ public void addListener(ILabelProviderListener listener) {
+ // pass
+ }
+
+ @Override
+ public void dispose() {
+ mWarningImage.dispose();
+ }
+
+ @Override
+ public boolean isLabelProperty(Object element, String property) {
+ return false;
+ }
+
+ @Override
+ public void removeListener(ILabelProviderListener listener) {
+ // pass
+ }
+
+ @Override
+ public Image getColumnImage(Object element, int columnIndex) {
+ if (columnIndex == 1) {
+ if (element instanceof ResourceItem) {
+ ResourceItem item = (ResourceItem)element;
+ if (item.hasDefault() == false) {
+ return mWarningImage;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getColumnText(Object element, int columnIndex) {
+ switch (columnIndex) {
+ case 0:
+ if (element instanceof ResourceType) {
+ return ((ResourceType)element).getDisplayName();
+ } else if (element instanceof ResourceItem) {
+ return ((ResourceItem)element).getName();
+ } else if (element instanceof ResourceFile) {
+ return ((ResourceFile)element).getFolder().getConfiguration().toDisplayString();
+ }
+ break;
+ case 1:
+ if (element instanceof ResourceItem) {
+ ResourceItem item = (ResourceItem)element;
+ if (item.isDeclaredInline()) {
+ return "Declared inline";
+ } else {
+ int count = item.getAlternateCount();
+ if (count > 0) {
+ if (item.hasDefault()) {
+ count++;
+ }
+ return String.format("%1$d version(s)", count);
+ }
+ }
+ }
+ return null;
+ }
+ return null;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourcePreviewHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourcePreviewHelper.java
new file mode 100644
index 000000000..afd1df92d
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourcePreviewHelper.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2011 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.DOT_9PNG;
+import static com.android.utils.SdkUtils.endsWithIgnoreCase;
+
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.resources.ResourceResolver;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageControl;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderService;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtUtils;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
+import com.android.resources.ResourceType;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.DialogTray;
+import org.eclipse.jface.dialogs.TrayDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+/**
+ * The {@link ResourcePreviewHelper} provides help to {@link TrayDialog} resource choosers
+ * where some resources (such as drawables and colors) are previewed in the tray area.
+ */
+public class ResourcePreviewHelper {
+ /**
+ * The width of the preview rendering
+ * <p>
+ * TODO: Make the preview rendering resize if the tray area is resized
+ */
+ private static final int WIDTH = 100;
+ /** The height of the preview rendering */
+ private static final int HEIGHT = 100;
+
+ private final GraphicalEditorPart mEditor;
+ private final TrayDialog mTrayDialog;
+
+ private boolean mShowingPreview;
+ private DialogTray mPreviewTray;
+ private ImageControl mPreviewImageControl;
+
+ /**
+ * Constructs a new {@link ResourcePreviewHelper}.
+ * <p>
+ * TODO: Add support for performing previews without an associated graphical editor,
+ * such as previewing icons from the manifest form editor; just pick default
+ * configuration settings in that case.
+ *
+ * @param trayDialog the associated tray-capable dialog
+ * @param editor a graphical editor. This is currently needed in order to provide
+ * configuration data for the rendering.
+ */
+ public ResourcePreviewHelper(TrayDialog trayDialog, GraphicalEditorPart editor) {
+ this.mTrayDialog = trayDialog;
+ this.mEditor = editor;
+ }
+
+ /**
+ * Updates the preview based on the current selection and resource type, possibly
+ * hiding or opening the tray in the process.
+ *
+ * @param type the resource type for the selected resource
+ * @param resource the full resource url
+ */
+ public void updatePreview(ResourceType type, String resource) {
+ boolean showPreview = type == ResourceType.DRAWABLE || type == ResourceType.COLOR;
+ if (showPreview) {
+ if (mPreviewTray == null) {
+ mPreviewTray = new DialogTray() {
+ @Override
+ protected Control createContents(Composite parent) {
+ // This creates a centered image control
+ Composite panel = new Composite(parent, SWT.NONE);
+ panel.setLayout(new GridLayout(3, false));
+ Label dummy1 = new Label(panel, SWT.NONE);
+ dummy1.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true, 1, 1));
+ mPreviewImageControl = new ImageControl(panel, SWT.NONE, SwtUtils
+ .createEmptyImage(parent.getDisplay(), WIDTH, HEIGHT));
+ GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1);
+ gd.widthHint = WIDTH;
+ gd.heightHint = HEIGHT;
+ mPreviewImageControl.setLayoutData(gd);
+ Label dummy2 = new Label(panel, SWT.NONE);
+ dummy2.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true, 1, 1));
+
+ return panel;
+ }
+
+ };
+ }
+
+ if (!mShowingPreview) {
+ mTrayDialog.openTray(mPreviewTray);
+ }
+
+ BufferedImage image = null;
+ try {
+ if (type == ResourceType.COLOR) {
+ ResourceResolver resources = mEditor.getResourceResolver();
+ ResourceValue value = resources.findResValue(resource, false);
+ if (value != null) {
+ RGB color = ResourceHelper.resolveColor(resources, value);
+ if (color != null) {
+ image = ImageUtils.createColoredImage(WIDTH, HEIGHT, color);
+ }
+ }
+ } else {
+ assert type == ResourceType.DRAWABLE;
+
+ ResourceResolver resources = mEditor.getResourceResolver();
+ ResourceValue drawable = resources.findResValue(resource, false);
+ if (drawable != null) {
+ String path = drawable.getValue();
+
+ // Special-case image files (other than 9-patch files) and render these
+ // directly, in order to provide proper aspect ratio handling and
+ // to handle scaling to show the full contents:
+ if (ImageUtils.hasImageExtension(path)
+ && !endsWithIgnoreCase(path, DOT_9PNG)) {
+ File file = new File(path);
+ if (file.exists()) {
+ try {
+ image = ImageIO.read(file);
+ int width = image.getWidth();
+ int height = image.getHeight();
+ if (width > WIDTH || height > HEIGHT) {
+ double xScale = WIDTH / (double) width;
+ double yScale = HEIGHT / (double) height;
+ double scale = Math.min(xScale, yScale);
+ image = ImageUtils.scale(image, scale, scale);
+ }
+ } catch (IOException e) {
+ AdtPlugin.log(e, "Can't read preview image %1$s", path);
+ }
+ }
+ }
+
+ if (image == null) {
+ RenderService renderService = RenderService.create(mEditor);
+ renderService.setOverrideRenderSize(WIDTH, HEIGHT);
+ image = renderService.renderDrawable(drawable);
+ }
+ }
+ }
+ } catch (Throwable t) {
+ // Some kind of rendering error occurred. However, we don't want to use
+ // AdtPlugin.log(t, "Can't generate preview for %1$s", resource);
+ // because if it's a severe type of error (such as an InternalError shown
+ // in issue #18623) then a dialog will pop up and interfere with the
+ // preview, so just log a warning (unfortunately without the trace) instead.
+ AdtPlugin.log(IStatus.WARNING, "Can't generate preview for %1$s", resource);
+ }
+
+ Display display = mEditor.getSite().getShell().getDisplay();
+ if (image != null) {
+ mPreviewImageControl.setImage(SwtUtils.convertToSwt(display, image, true, -1));
+ } else {
+ mPreviewImageControl.setImage(SwtUtils.createEmptyImage(display, WIDTH, HEIGHT));
+ }
+ mPreviewImageControl.redraw();
+ } else if (mPreviewTray != null && mShowingPreview) {
+ mTrayDialog.closeTray();
+ }
+ mShowingPreview = showPreview;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/WizardDialogEx.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/WizardDialogEx.java
new file mode 100755
index 000000000..ee1ac9725
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/WizardDialogEx.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 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 org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * A {@link WizardDialog} that gives access to some inner controls.
+ */
+public final class WizardDialogEx extends WizardDialog {
+
+ /**
+ * @see WizardDialog#WizardDialog(Shell, IWizard)
+ */
+ public WizardDialogEx(Shell parentShell, IWizard newWizard) {
+ super(parentShell, newWizard);
+ }
+
+ /**
+ * Returns the cancel button.
+ * <p/>
+ * Note: there is already a protected, deprecated method that does the same thing.
+ * To avoid overriding a deprecated method, the name as be changed to ...Ex.
+ */
+ public Button getCancelButtonEx() {
+ return getButton(IDialogConstants.CANCEL_ID);
+ }
+}