ADT/GLE: Fix config selector to not select a config that has a better match than the current file.

When replacing a file with another one (because of a user "open action")
it is possible the config selector will find the current config compatible,
even though the previous layout was a better for match for it.

The config selector now attemps to find a config for which the new
file is the best match.

Change-Id: I7d794c2a8b9a90a120970049cb402f9ee84f8749
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidEditor.java
index 3cfa50f..9a1430b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidEditor.java
@@ -103,6 +103,9 @@
      * SDK location change or a project target change */
     private TargetChangeListener mTargetListener = null;
 
+    /** flag set during page creation */
+    private boolean mIsCreatingPage = false;
+
     /**
      * Creates a form editor.
      * <p/>The editor will setup a {@link ITargetChangeListener} and call
@@ -190,10 +193,19 @@
      * Creates the page for the Android Editors
      */
     protected void createAndroidPages() {
+        mIsCreatingPage = true;
         createFormPages();
         createTextEditor();
 
         createUndoRedoActions();
+        mIsCreatingPage = false;
+    }
+
+    /**
+     * Returns whether the editor is currently creating its pages.
+     */
+    public boolean isCreatingPages() {
+        return mIsCreatingPage;
     }
 
     /**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
index cfb3e1c..85427a9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
@@ -29,6 +29,10 @@
 import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier.Density;
 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier.ScreenOrientation;
 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
+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.LayoutDevice;
 import com.android.ide.eclipse.adt.internal.sdk.LayoutDeviceManager;
@@ -40,6 +44,10 @@
 import com.android.layoutlib.api.IStyleResourceValue;
 import com.android.sdklib.IAndroidTarget;
 
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IStatus;
 import org.eclipse.draw2d.geometry.Rectangle;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.SelectionAdapter;
@@ -101,9 +109,6 @@
     private int mPlatformThemeCount = 0;
     private boolean mDisableUpdates = false;
 
-    /** The {@link FolderConfiguration} representing the state of the UI controls */
-    private final FolderConfiguration mCurrentConfig = new FolderConfiguration();
-
     private List<LayoutDevice> mDeviceList;
 
     private final ArrayList<ResourceQualifier[] > mLocaleList =
@@ -120,16 +125,25 @@
      */
     private LayoutDevice mCurrentDevice;
 
-    /** The config listener given to the constructor. Never null. */
-    private final IConfigListener mListener;
-
-    private FolderConfiguration mEditedConfig;
-    private IAndroidTarget mTarget;
-
     private SelectionState mCurrentState = null;
 
     private boolean mSdkChanged = false;
 
+    /** The config listener given to the constructor. Never null. */
+    private final IConfigListener mListener;
+
+    /** The {@link FolderConfiguration} representing the state of the UI controls */
+    private final FolderConfiguration mCurrentConfig = new FolderConfiguration();
+
+    /** The file being edited */
+    private IFile mEditedFile;
+    /** The {@link ProjectResources} for the edited file's project */
+    private ProjectResources mResources;
+    /** The target of the project of the file being edited. */
+    private IAndroidTarget mTarget;
+    /** The {@link FolderConfiguration} being edited. */
+    private FolderConfiguration mEditedConfig;
+
     /**
      * Interface implemented by the part which owns a {@link ConfigurationComposite}.
      * This notifies the owners when the configuration change.
@@ -356,15 +370,14 @@
      * The state of the selection of the various combos will be initialized to default values that
      * are compatible with the opened file.
      *
-     * @param fileConfig The {@link FolderConfiguration} of the opened file.
-     * @param target the {@link IAndroidTarget} of the file's project.
+     * @param file the file being opened
      *
      * @see #replaceFile(FolderConfiguration)
      * @see #changeFileOnNewConfig(FolderConfiguration)
      */
-    public void openFile(FolderConfiguration fileConfig, IAndroidTarget target) {
-        mTarget = target;
-        mEditedConfig = fileConfig;
+    public void openFile(IFile file) {
+        mEditedFile = file;
+        IProject iProject = mEditedFile.getProject();
 
         mDisableUpdates = true; // we do not want to trigger onXXXChange when setting
                                 // new values in the widgets.
@@ -375,9 +388,18 @@
             // init the devices since the SDK is loaded.
             initDevices();
 
-            LoadStatus targetStatus = Sdk.getCurrent().checkAndLoadTargetData(target, null);
+            Sdk currentSdk = Sdk.getCurrent();
+            if (currentSdk != null) {
+                mTarget = currentSdk.getTarget(iProject);
+            }
+
+            LoadStatus targetStatus = Sdk.getCurrent().checkAndLoadTargetData(mTarget, null);
 
             if (targetStatus == LoadStatus.LOADED) {
+                mResources = ResourceManager.getInstance().getProjectResources(iProject);
+                ResourceFolder resFolder = mResources.getResourceFolder((IFolder)file.getParent());
+                mEditedConfig = resFolder.getConfiguration();
+
                 // update the themes and locales.
                 updateThemes();
                 updateLocales();
@@ -389,18 +411,14 @@
                     setClippingSupport(bridge.apiLevel >= 4);
                 }
 
-                // attempt to find a device that can display this particular config.
-                findAndSetCompatibleConfig(fileConfig);
-
-                // find a locale matching this file config
-                findAndSetCompatibleLocale(fileConfig.getLanguageQualifier(),
-                        fileConfig.getRegionQualifier());
+                // attempt to find a device/locale that can display this particular config.
+                findAndSetCompatibleConfig(false /*favorCurrentConfig*/);
 
                 // compute the final current config
                 computeCurrentConfig();
 
                 // update the string showing the config value
-                updateConfigDisplay(fileConfig);
+                updateConfigDisplay(mEditedConfig);
 
                 saveState();
             }
@@ -409,27 +427,31 @@
         mDisableUpdates = false;
     }
 
-
     /**
      * Replaces the UI with a given file configuration. This is meant to answer the user
      * Explicitly opening a different version of the same layout from the Package Explorer.
      * <p/>This attempts to keep the current config, but may change it if it's not compatible.
      * <p/>This will NOT trigger a redraw event (will not call
      * {@link IConfigListener#onConfigurationChange()}.)
-     *
+     * @param file the file being opened.
      * @param fileConfig The {@link FolderConfiguration} of the opened file.
      * @param target the {@link IAndroidTarget} of the file's project.
      *
      * @see #replaceFile(FolderConfiguration)
      */
-    public void replaceFile(FolderConfiguration fileConfig) {
+    public void replaceFile(IFile file) {
         // if there is no previous selection, revert to default mode.
         if (mCurrentDevice == null) {
-            openFile(fileConfig, mTarget);
+            openFile(file);
             return;
         }
 
-        mEditedConfig = fileConfig;
+        mEditedFile = file;
+        IProject iProject = mEditedFile.getProject();
+        mResources = ResourceManager.getInstance().getProjectResources(iProject);
+
+        ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);
+        mEditedConfig = resFolder.getConfiguration();
 
         mDisableUpdates = true; // we do not want to trigger onXXXChange when setting
         // new values in the widgets.
@@ -443,17 +465,13 @@
 
                 // update the current config selection to make sure it's
                 // compatible with the new file
-                adaptConfigSelectionToNewConfig(fileConfig);
-
-                // find a locale matching this file config
-                findAndSetCompatibleLocale(fileConfig.getLanguageQualifier(),
-                        fileConfig.getRegionQualifier());
+                adaptConfigSelection();
 
                 // compute the final current config
                 computeCurrentConfig();
 
                 // update the string showing the config value
-                updateConfigDisplay(fileConfig);
+                updateConfigDisplay(mEditedConfig);
 
                 saveState();
             }
@@ -464,18 +482,22 @@
 
     /**
      * Updates the UI with a new file that was opened in response to a config change.
-     * @param fileConfig the file being opened.
+     * @param file the file being opened.
      *
      * @see #openFile(FolderConfiguration, IAndroidTarget)
      * @see #replaceFile(FolderConfiguration)
      */
-    public void changeFileOnNewConfig(FolderConfiguration fileConfig) {
-        // there really isn't much to do since the combos were set by the user, and we don't want
-        // to touch them.
-        mEditedConfig = fileConfig;
+    public void changeFileOnNewConfig(IFile file) {
+        mEditedFile = file;
+        IProject iProject = mEditedFile.getProject();
+        mResources = ResourceManager.getInstance().getProjectResources(iProject);
 
-        // update the string showing the config value
-        updateConfigDisplay(fileConfig);
+        ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);
+        mEditedConfig = resFolder.getConfiguration();
+
+        // All that's needed is to update the string showing the config value
+        // (since the config combo were chosen by the user).
+        updateConfigDisplay(mEditedConfig);
     }
 
     /**
@@ -508,10 +530,22 @@
         if (mCurrentState == null) {
             // this means the file was opened before the target finished loaded.
             // This is basically an initial call to openFile that's delayed.
-            openFile(mEditedConfig, mTarget);
+            openFile(mEditedFile);
             return;
         }
 
+        // update the resource and config if they are not present
+        if (mResources == null) {
+            mResources = ResourceManager.getInstance().getProjectResources(
+                    mEditedFile.getProject());
+        }
+
+        if (mEditedConfig == null) {
+            ResourceFolder resFolder = mResources.getResourceFolder(
+                    (IFolder)mEditedFile.getParent());
+            mEditedConfig = resFolder.getConfiguration();
+        }
+
         mDisableUpdates = true; // we do not want to trigger onXXXChange when setting
                                 // new values in the widgets.
 
@@ -524,9 +558,9 @@
         // The former means the devices/configs are still there, the latter means they've
         // been reloaded (in #onSdkLoaded).
         if (mSdkChanged) {
-            findAndSetCompatibleConfig(mEditedConfig);
+            findAndSetCompatibleConfig(false /*favorCurrentConfig*/);
         } else {
-            adaptConfigSelectionToNewConfig(mEditedConfig);
+            adaptConfigSelection();
         }
 
         // update the clipping state
@@ -542,84 +576,171 @@
     }
 
     /**
-     * Finds a device/config that can display the given config.
+     * Finds a device/config that can display {@link #mEditedConfig}.
      * <p/>Once found the device and config combos are set to the config.
-     * <p/>If there is no compatible comfiguration, a custom one is created.
-     * @param fileConfig
+     * <p/>If there is no compatible configuration, a custom one is created.
+     * @param favorCurrentConfig if true, and no best match is found, don't change
+     * the current config. This must only be true if the current config is compatible.
      */
-    private void findAndSetCompatibleConfig(FolderConfiguration fileConfig) {
-        LayoutDevice deviceMatch = null;
-        String configMatchName = null;
+    private void findAndSetCompatibleConfig(boolean favorCurrentConfig) {
+        LayoutDevice anyDeviceMatch = null; // a compatible device/config/locale
+        String anyConfigMatchName = null;
+        int anyLocaleIndex = -1;
+
+        LayoutDevice bestDeviceMatch = null; // an actual best match
+        String bestConfigMatchName = null;
+        int bestLocaleIndex = -1;
+
+        FolderConfiguration testConfig = new FolderConfiguration();
 
         mainloop: for (LayoutDevice device : mDeviceList) {
             for (Entry<String, FolderConfiguration> entry :
                     device.getConfigs().entrySet()) {
-                if (fileConfig.isMatchFor(entry.getValue())) {
-                    // this is what we want.
-                    deviceMatch = device;
-                    configMatchName = entry.getKey();
-                    break mainloop;
+                testConfig.set(entry.getValue());
+
+                // look on the locales.
+                for (int i = 0 ; i < mLocaleList.size() ; i++) {
+                    ResourceQualifier[] locale = mLocaleList.get(i);
+
+                    // update the test config with the locale qualifiers
+                    testConfig.setLanguageQualifier((LanguageQualifier)locale[LOCALE_LANG]);
+                    testConfig.setRegionQualifier((RegionQualifier)locale[LOCALE_REGION]);
+
+                    if (mEditedConfig.isMatchFor(testConfig)) {
+                        // this is a basic match. record it in case we don't find a match
+                        // where the edited file is a best config.
+                        if (anyDeviceMatch == null) {
+                            anyDeviceMatch = device;
+                            anyConfigMatchName = entry.getKey();
+                            anyLocaleIndex = i;
+                        }
+
+                        if (isCurrentFileBestMatchFor(testConfig)) {
+                            // this is what we want.
+                            bestDeviceMatch = device;
+                            bestConfigMatchName = entry.getKey();
+                            bestLocaleIndex = i;
+                            break mainloop;
+                        }
+                    }
                 }
             }
         }
 
-        if (deviceMatch == null) {
-            // TODO: there is no device/config able to display the layout, create one.
-            // For the base config values, we'll take the first device and config,
-            // and replace whatever qualifier required by the layout file.
+        if (bestDeviceMatch == null) {
+            if (favorCurrentConfig) {
+                // quick check
+                if (mEditedConfig.isMatchFor(mCurrentConfig) == false) {
+                    AdtPlugin.log(IStatus.ERROR,
+                            "favorCurrentConfig can only be true if the current config is compatible");
+                }
+
+                // just display the warning
+                AdtPlugin.printErrorToConsole(mEditedFile.getProject(),
+                        String.format(
+                                "'%1$s' is not a best match for any device/locale combination.",
+                                mEditedConfig.toDisplayString()),
+                        String.format(
+                                "Displaying it with '%1$s'",
+                                mCurrentConfig.toDisplayString()));
+            } else if (anyDeviceMatch != null) {
+                // select the device anyway.
+                selectDevice(mCurrentDevice = anyDeviceMatch);
+                fillConfigCombo(anyConfigMatchName);
+                mLocaleCombo.select(anyLocaleIndex);
+
+                // TODO: display a better warning!
+                computeCurrentConfig();
+                AdtPlugin.printErrorToConsole(mEditedFile.getProject(),
+                        String.format(
+                                "'%1$s' is not a best match for any device/locale combination.",
+                                mEditedConfig.toDisplayString()),
+                        String.format(
+                                "Displaying it with '%1$s'",
+                                mCurrentConfig.toDisplayString()));
+
+            } else {
+                // TODO: there is no device/config able to display the layout, create one.
+                // For the base config values, we'll take the first device and config,
+                // and replace whatever qualifier required by the layout file.
+            }
         } else {
-            selectDevice(mCurrentDevice = deviceMatch);
-            fillConfigCombo(configMatchName);
+            selectDevice(mCurrentDevice = bestDeviceMatch);
+            fillConfigCombo(bestConfigMatchName);
+            mLocaleCombo.select(bestLocaleIndex);
         }
     }
 
     /**
-     * Adapts the current device/config selection so that it's compatible with a given config.
+     * Adapts the current device/config selection so that it's compatible with
+     * {@link #mEditedConfig}.
      * <p/>If the current selection is compatible, nothing is changed.
      * <p/>If it's not compatible, configs from the current devices are tested.
      * <p/>If none are compatible, it reverts to
      * {@link #findAndSetCompatibleConfig(FolderConfiguration)}
-     * @param fileConfig
      */
-    private void adaptConfigSelectionToNewConfig(FolderConfiguration fileConfig) {
+    private void adaptConfigSelection() {
         // check the device config (ie sans locale)
         boolean needConfigChange = true; // if still true, we need to find another config.
+        boolean currentConfigIsCompatible = false;
         int configIndex = mDeviceConfigCombo.getSelectionIndex();
         if (configIndex != -1) {
             String configName = mDeviceConfigCombo.getItem(configIndex);
             FolderConfiguration currentConfig = mCurrentDevice.getConfigs().get(configName);
-            if (fileConfig.isMatchFor(currentConfig)) {
-                needConfigChange = false;
+            if (mEditedConfig.isMatchFor(currentConfig)) {
+                currentConfigIsCompatible = true; // current config is compatible
+                if (isCurrentFileBestMatchFor(currentConfig)) {
+                    needConfigChange = false;
+                }
             }
         }
 
         if (needConfigChange) {
+            // if the current config/locale isn't a correct match, then
+            // look for another config/locale in the same device.
+            FolderConfiguration testConfig = new FolderConfiguration();
+
             // first look in the current device.
             String matchName = null;
+            int localeIndex = -1;
             Map<String, FolderConfiguration> configs = mCurrentDevice.getConfigs();
-            for (Entry<String, FolderConfiguration> entry : configs.entrySet()) {
-                if (fileConfig.isMatchFor(entry.getValue())) {
-                    matchName = entry.getKey();
-                    break;
+            mainloop: for (Entry<String, FolderConfiguration> entry : configs.entrySet()) {
+                testConfig.set(entry.getValue());
+
+                // loop on the locales.
+                for (int i = 0 ; i < mLocaleList.size() ; i++) {
+                    ResourceQualifier[] locale = mLocaleList.get(i);
+
+                    // update the test config with the locale qualifiers
+                    testConfig.setLanguageQualifier((LanguageQualifier)locale[LOCALE_LANG]);
+                    testConfig.setRegionQualifier((RegionQualifier)locale[LOCALE_REGION]);
+
+                    if (mEditedConfig.isMatchFor(testConfig) &&
+                            isCurrentFileBestMatchFor(testConfig)) {
+                        matchName = entry.getKey();
+                        localeIndex = i;
+                        break mainloop;
+                    }
                 }
             }
 
             if (matchName != null) {
                 selectConfig(matchName);
+                mLocaleCombo.select(localeIndex);
             } else {
-                // attempt to find a device that can display this particular config.
-                findAndSetCompatibleConfig(fileConfig);
+                // no match in current device with any config/locale
+                // attempt to find another device that can display this particular config.
+                findAndSetCompatibleConfig(currentConfigIsCompatible);
             }
         }
     }
 
-
     /**
      * Finds a locale matching the config from a file.
      * @param language the language qualifier or null if none is set.
      * @param region the region qualifier or null if none is set.
      */
-    private void findAndSetCompatibleLocale(ResourceQualifier language, ResourceQualifier region) {
+    private void setLocaleCombo(ResourceQualifier language, ResourceQualifier region) {
         // find the locale match. Since the locale list is based on the content of the
         // project resources there must be an exact match.
         // The only trick is that the region could be null in the fileConfig but in our
@@ -751,7 +872,9 @@
         });
 
         if (mCurrentState != null && mCurrentState.locale != null) {
-            findAndSetCompatibleLocale(mCurrentState.locale[LOCALE_LANG],
+            // FIXME: this may fails if the layout was deleted (and was the last one to have that local.
+            // (we have other problem in this case though)
+            setLocaleCombo(mCurrentState.locale[LOCALE_LANG],
                     mCurrentState.locale[LOCALE_REGION]);
         } else {
             mLocaleCombo.select(0);
@@ -874,6 +997,10 @@
 
     // ---- getters for the config selection values ----
 
+    public FolderConfiguration getEditedConfig() {
+        return mEditedConfig;
+    }
+
     public FolderConfiguration getCurrentConfig() {
         return mCurrentConfig;
     }
@@ -1146,11 +1273,11 @@
 
             // reset the UI as if it was just a replacement file, since we can keep
             // the current device (and possibly config).
-            adaptConfigSelectionToNewConfig(mEditedConfig);
+            adaptConfigSelection();
 
         } else {
             // find a new device/config to match the current file.
-            findAndSetCompatibleConfig(mEditedConfig);
+            findAndSetCompatibleConfig(false /*favorCurrentConfig*/);
         }
 
         mDisableUpdates = false;
@@ -1403,5 +1530,28 @@
         mCreateButton.setEnabled(mEditedConfig.equals(mCurrentConfig) == false);
     }
 
+    /**
+     * Checks whether the current edited file is the best match for a given config.
+     * <p/>
+     * This tests against other versions of the same layout in the project.
+     * <p/>
+     * The given config must be compatible with the current edited file.
+     * @param config the config to test.
+     * @return true if the current edited file is the best match in the project for the
+     * given config.
+     */
+    private boolean isCurrentFileBestMatchFor(FolderConfiguration config) {
+        ResourceFile match = mResources.getMatchingFile(mEditedFile.getName(),
+                ResourceFolderType.LAYOUT, config);
+
+        if (match != null) {
+            return match.getFile().equals(mEditedFile);
+        } else {
+            // if we stop here that means the current file is not even a match!
+            AdtPlugin.log(IStatus.ERROR, "Current file is not a match for the given config.");
+        }
+
+        return false;
+    }
 }
 
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle1/GraphicalLayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle1/GraphicalLayoutEditor.java
index 4bfd589..0fb008b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle1/GraphicalLayoutEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle1/GraphicalLayoutEditor.java
@@ -44,7 +44,6 @@
 import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;
-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
@@ -141,9 +140,6 @@
 
     private PaletteRoot mPaletteRoot;
 
-    /** The {@link FolderConfiguration} being edited. */
-    private FolderConfiguration mEditedConfig;
-
     private Map<String, Map<String, IResourceValue>> mConfiguredFrameworkRes;
     private Map<String, Map<String, IResourceValue>> mConfiguredProjectRes;
     private ProjectCallback mProjectCallback;
@@ -595,17 +591,7 @@
      */
     public void openFile(IFile file) {
         mEditedFile = file;
-
-        ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);
-        mEditedConfig = resFolder.getConfiguration();
-
-        IAndroidTarget target = null;
-        Sdk currentSdk = Sdk.getCurrent();
-        if (currentSdk != null) {
-            target = currentSdk.getTarget(mEditedFile.getProject());
-        }
-
-        mConfigComposite.openFile(mEditedConfig, target);
+        mConfigComposite.openFile(mEditedFile);
     }
 
     /**
@@ -616,12 +602,7 @@
         resetInput();
 
         mEditedFile = file;
-
-        ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);
-        mEditedConfig = resFolder.getConfiguration();
-
-        mConfigComposite.replaceFile(mEditedConfig);
-        onConfigurationChange();
+        mConfigComposite.replaceFile(mEditedFile);
     }
 
     /**
@@ -633,12 +614,7 @@
         resetInput();
 
         mEditedFile = file;
-
-        ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);
-        mEditedConfig = resFolder.getConfiguration();
-
-        mConfigComposite.changeFileOnNewConfig(mEditedConfig);
-        onConfigurationChange();
+        mConfigComposite.changeFileOnNewConfig(mEditedFile);
     }
 
     public void onTargetChange() {
@@ -817,55 +793,69 @@
     public void onConfigurationChange() {
         mConfiguredFrameworkRes = mConfiguredProjectRes = null;
 
-        if (mEditedFile == null || mEditedConfig == null) {
+        if (mEditedFile == null || mConfigComposite.getEditedConfig() == null) {
             return;
         }
 
-        // get the resources of the file's project.
-        ProjectResources resources = ResourceManager.getInstance().getProjectResources(
-                mEditedFile.getProject());
-
-        // from the resources, look for a matching file
-        ResourceFile match = null;
-        if (resources != null) {
-            match = resources.getMatchingFile(mEditedFile.getName(),
-                                              ResourceFolderType.LAYOUT,
-                                              mConfigComposite.getCurrentConfig());
-        }
-
-        if (match != null) {
-            if (match.getFile().equals(mEditedFile) == false) {
-                try {
-                    // tell the editor that the next replacement file is due to a config change.
-                    mLayoutEditor.setNewFileOnConfigChange(true);
-
-                    // ask the IDE to open the replacement file.
-                    IDE.openEditor(
-                            getSite().getWorkbenchWindow().getActivePage(),
-                            match.getFile().getIFile());
-
-                    // we're done!
-                    return;
-                } catch (PartInitException e) {
-                    // FIXME: do something!
-                }
-            }
-
-            // at this point, we have not opened a new file.
-
-            // Even though the layout doesn't change, the config changed, and referenced
-            // resources need to be updated.
+        // Before doing the normal process, test for the following case.
+        // - the editor is being opened (or reset for a new input)
+        // - the file being opened is not the best match for any possible configuration
+        // - another random compatible config was chosen in the config composite.
+        // The result is that match will not be the file being edited, but because this is not
+        // due to a config change, we should not trigger opening the actual best match (also,
+        // because the editor is still opening the MatchingStrategy woudln't answer true
+        // and the best match file would open in a different editor).
+        // So the solution is that if the editor is being created, we just call recomputeLayout
+        // without looking for a better matching layout file.
+        if (mLayoutEditor.isCreatingPages()) {
             recomputeLayout();
         } else {
-            // display the error.
-            FolderConfiguration currentConfig = mConfigComposite.getCurrentConfig();
-            String message = String.format(
-                    "No resources match the configuration\n \n\t%1$s\n \nChange the configuration or create:\n \n\tres/%2$s/%3$s\n \nYou can also click the 'Create' button above.",
-                    currentConfig.toDisplayString(),
-                    currentConfig.getFolderName(ResourceFolderType.LAYOUT,
-                            Sdk.getCurrent().getTarget(mEditedFile.getProject())),
-                    mEditedFile.getName());
-            showErrorInEditor(message);
+            // get the resources of the file's project.
+            ProjectResources resources = ResourceManager.getInstance().getProjectResources(
+                    mEditedFile.getProject());
+
+            // from the resources, look for a matching file
+            ResourceFile match = null;
+            if (resources != null) {
+                match = resources.getMatchingFile(mEditedFile.getName(),
+                                                  ResourceFolderType.LAYOUT,
+                                                  mConfigComposite.getCurrentConfig());
+            }
+
+            if (match != null) {
+                if (match.getFile().equals(mEditedFile) == false) {
+                    try {
+                        // tell the editor that the next replacement file is due to a config change.
+                        mLayoutEditor.setNewFileOnConfigChange(true);
+
+                        // ask the IDE to open the replacement file.
+                        IDE.openEditor(
+                                getSite().getWorkbenchWindow().getActivePage(),
+                                match.getFile().getIFile());
+
+                        // we're done!
+                        return;
+                    } catch (PartInitException e) {
+                        // FIXME: do something!
+                    }
+                }
+
+                // at this point, we have not opened a new file.
+
+                // Even though the layout doesn't change, the config changed, and referenced
+                // resources need to be updated.
+                recomputeLayout();
+            } else {
+                // display the error.
+                FolderConfiguration currentConfig = mConfigComposite.getCurrentConfig();
+                String message = String.format(
+                        "No resources match the configuration\n \n\t%1$s\n \nChange the configuration or create:\n \n\tres/%2$s/%3$s\n \nYou can also click the 'Create' button above.",
+                        currentConfig.toDisplayString(),
+                        currentConfig.getFolderName(ResourceFolderType.LAYOUT,
+                                Sdk.getCurrent().getTarget(mEditedFile.getProject())),
+                        mEditedFile.getName());
+                showErrorInEditor(message);
+            }
         }
     }
 
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
index 5745b40..26d4206 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
@@ -36,7 +36,6 @@
 import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;

 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;

 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFile;

-import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;

 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType;

 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;

 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;

@@ -137,9 +136,6 @@
 

     private StyledText mErrorLabel;

 

-    /** The {@link FolderConfiguration} being edited. */

-    private FolderConfiguration mEditedConfig;

-

     private Map<String, Map<String, IResourceValue>> mConfiguredFrameworkRes;

     private Map<String, Map<String, IResourceValue>> mConfiguredProjectRes;

     private ProjectCallback mProjectCallback;

@@ -340,54 +336,69 @@
         public void onConfigurationChange() {

             mConfiguredFrameworkRes = mConfiguredProjectRes = null;

 

-            if (mEditedFile == null || mEditedConfig == null) {

+            if (mEditedFile == null || mConfigComposite.getEditedConfig() == null) {

                 return;

             }

 

-            // get the resources of the file's project.

-            ProjectResources resources = ResourceManager.getInstance().getProjectResources(

-                    mEditedFile.getProject());

-

-            // from the resources, look for a matching file

-            ResourceFile match = null;

-            if (resources != null) {

-                match = resources.getMatchingFile(mEditedFile.getName(),

-                                                  ResourceFolderType.LAYOUT,

-                                                  mConfigComposite.getCurrentConfig());

-            }

-

-            if (match != null) {

-                if (match.getFile().equals(mEditedFile) == false) {

-                    try {

-                        // tell the editor that the next replacement file is due to a config change.

-                        mLayoutEditor.setNewFileOnConfigChange(true);

-

-                        // ask the IDE to open the replacement file.

-                        IDE.openEditor(

-                                getSite().getWorkbenchWindow().getActivePage(),

-                                match.getFile().getIFile());

-

-                        // we're done!

-                        return;

-                    } catch (PartInitException e) {

-                        // FIXME: do something!

-                    }

-                }

-

-                // at this point, we have not opened a new file.

-

-                // Even though the layout doesn't change, the config changed, and referenced

-                // resources need to be updated.

+            // Before doing the normal process, test for the following case.

+            // - the editor is being opened (or reset for a new input)

+            // - the file being opened is not the best match for any possible configuration

+            // - another random compatible config was chosen in the config composite.

+            // The result is that 'match' will not be the file being edited, but because this is not

+            // due to a config change, we should not trigger opening the actual best match (also,

+            // because the editor is still opening the MatchingStrategy woudln't answer true

+            // and the best match file would open in a different editor).

+            // So the solution is that if the editor is being created, we just call recomputeLayout

+            // without looking for a better matching layout file.

+            if (mLayoutEditor.isCreatingPages()) {

                 recomputeLayout();

             } else {

-                // display the error.

-                FolderConfiguration currentConfig = mConfigComposite.getCurrentConfig();

-                displayError(

-                        "No resources match the configuration\n \n\t%1$s\n \nChange the configuration or create:\n \n\tres/%2$s/%3$s\n \nYou can also click the 'Create' button above.",

-                        currentConfig.toDisplayString(),

-                        currentConfig.getFolderName(ResourceFolderType.LAYOUT,

-                                Sdk.getCurrent().getTarget(mEditedFile.getProject())),

-                        mEditedFile.getName());

+                // get the resources of the file's project.

+                ProjectResources resources = ResourceManager.getInstance().getProjectResources(

+                        mEditedFile.getProject());

+

+                // from the resources, look for a matching file

+                ResourceFile match = null;

+                if (resources != null) {

+                    match = resources.getMatchingFile(mEditedFile.getName(),

+                                                      ResourceFolderType.LAYOUT,

+                                                      mConfigComposite.getCurrentConfig());

+                }

+

+                if (match != null) {

+                    if (match.getFile().equals(mEditedFile) == false) {

+                        try {

+                            // tell the editor that the next replacement file is due to a config

+                            // change.

+                            mLayoutEditor.setNewFileOnConfigChange(true);

+

+                            // ask the IDE to open the replacement file.

+                            IDE.openEditor(

+                                    getSite().getWorkbenchWindow().getActivePage(),

+                                    match.getFile().getIFile());

+

+                            // we're done!

+                            return;

+                        } catch (PartInitException e) {

+                            // FIXME: do something!

+                        }

+                    }

+

+                    // at this point, we have not opened a new file.

+

+                    // Even though the layout doesn't change, the config changed, and referenced

+                    // resources need to be updated.

+                    recomputeLayout();

+                } else {

+                    // display the error.

+                    FolderConfiguration currentConfig = mConfigComposite.getCurrentConfig();

+                    displayError(

+                            "No resources match the configuration\n \n\t%1$s\n \nChange the configuration or create:\n \n\tres/%2$s/%3$s\n \nYou can also click the 'Create' button above.",

+                            currentConfig.toDisplayString(),

+                            currentConfig.getFolderName(ResourceFolderType.LAYOUT,

+                                    Sdk.getCurrent().getTarget(mEditedFile.getProject())),

+                            mEditedFile.getName());

+                }

             }

         }

 

@@ -726,17 +737,7 @@
      */

     public void openFile(IFile file) {

         mEditedFile = file;

-

-        ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);

-        mEditedConfig = resFolder.getConfiguration();

-

-        IAndroidTarget target = null;

-        Sdk currentSdk = Sdk.getCurrent();

-        if (currentSdk != null) {

-            target = currentSdk.getTarget(mEditedFile.getProject());

-        }

-

-        mConfigComposite.openFile(mEditedConfig, target);

+        mConfigComposite.openFile(mEditedFile);

 

         if (mReloadListener == null) {

             mReloadListener = new ReloadListener();

@@ -754,11 +755,7 @@
      */

     public void replaceFile(IFile file) {

         mEditedFile = file;

-

-        ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);

-        mEditedConfig = resFolder.getConfiguration();

-

-        mConfigComposite.replaceFile(mEditedConfig);

+        mConfigComposite.replaceFile(mEditedFile);

     }

 

     /**

@@ -768,15 +765,9 @@
      */

     public void changeFileOnNewConfig(IFile file) {

         mEditedFile = file;

-

-        ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);

-        mEditedConfig = resFolder.getConfiguration();

-

-        mConfigComposite.changeFileOnNewConfig(mEditedConfig);

+        mConfigComposite.changeFileOnNewConfig(mEditedFile);

     }

 

-

-

     public void onTargetChange() {

         mConfigComposite.onTargetChange();

         mConfigListener.onConfigurationChange();

diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java
index 2148068..61ffb6f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/GlobalProjectMonitor.java
@@ -240,8 +240,10 @@
         if (sThis != null) {
             ws.removeResourceChangeListener(sThis);
 
-            sThis.mFileListeners.clear();
-            sThis.mProjectListeners.clear();
+            synchronized (sThis) {
+                sThis.mFileListeners.clear();
+                sThis.mProjectListeners.clear();
+            }
         }
     }
 
@@ -349,7 +351,7 @@
     /**
      * Processes the workspace resource change events.
      */
-    public void resourceChanged(IResourceChangeEvent event) {
+    public synchronized void resourceChanged(IResourceChangeEvent event) {
         // notify the event listeners of a start.
         for (IResourceEventListener listener : mEventListeners) {
             listener.resourceChangeEventStart();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java
index cc7c39f..143b644 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ResourceManager.java
@@ -16,6 +16,7 @@
 
 package com.android.ide.eclipse.adt.internal.resources.manager;
 
+import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.AndroidConstants;
 import com.android.ide.eclipse.adt.internal.resources.ResourceType;
 import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
@@ -127,7 +128,9 @@
      * @param listener the listener to be added.
      */
     public void addListener(IResourceListener listener) {
-        mListeners.add(listener);
+        synchronized (mListeners) {
+            mListeners.add(listener);
+        }
     }
 
     /**
@@ -135,7 +138,9 @@
      * @param listener the listener to be removed.
      */
     public void removeListener(IResource listener) {
-        mListeners.remove(listener);
+        synchronized (mListeners) {
+            mListeners.remove(listener);
+        }
     }
 
     /**
@@ -617,14 +622,28 @@
 
     private void notifyListenerOnFolderChange(IProject project, ResourceFolder folder,
             int eventType) {
-        for (IResourceListener listener : mListeners) {
-            listener.folderChanged(project, folder, eventType);
+        synchronized (mListeners) {
+            for (IResourceListener listener : mListeners) {
+                try {
+                    listener.folderChanged(project, folder, eventType);
+                } catch (Throwable t) {
+                    AdtPlugin.log(t,
+                            "Failed to execute ResourceManager.IResouceListener.folderChanged()"); //$NON-NLS-1$
+                }
+            }
         }
     }
 
     private void notifyListenerOnFileChange(IProject project, ResourceFile file, int eventType) {
-        for (IResourceListener listener : mListeners) {
-            listener.fileChanged(project, file, eventType);
+        synchronized (mListeners) {
+            for (IResourceListener listener : mListeners) {
+                try {
+                    listener.fileChanged(project, file, eventType);
+                } catch (Throwable t) {
+                    AdtPlugin.log(t,
+                            "Failed to execute ResourceManager.IResouceListener.fileChanged()"); //$NON-NLS-1$
+                }
+            }
         }
     }
 
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
index c11f4b8..f52a069 100644
--- 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
@@ -647,6 +647,7 @@
             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() {
                 public void modifyText(ModifyEvent e) {
@@ -712,6 +713,7 @@
             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() {
                 public void modifyText(ModifyEvent e) {
@@ -1399,11 +1401,13 @@
             };
 
             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);
@@ -1457,6 +1461,7 @@
             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() {
                 public void modifyText(ModifyEvent e) {