diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration')
18 files changed, 0 insertions, 7566 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ActivityMenuListener.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ActivityMenuListener.java deleted file mode 100644 index 36cd0fbbb..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ActivityMenuListener.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; -import com.android.ide.eclipse.adt.internal.resources.ResourceHelper; - -import org.eclipse.core.resources.IProject; -import org.eclipse.jdt.ui.ISharedImages; -import org.eclipse.jdt.ui.JavaUI; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Menu; -import org.eclipse.swt.widgets.MenuItem; -import org.eclipse.swt.widgets.ToolItem; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * The {@linkplain ActivityMenuListener} class is responsible for - * generating the activity menu in the {@link ConfigurationChooser}. - */ -class ActivityMenuListener extends SelectionAdapter { - private static final int ACTION_OPEN_ACTIVITY = 1; - private static final int ACTION_SELECT_ACTIVITY = 2; - - private final ConfigurationChooser mConfigChooser; - private final int mAction; - private final String mFqcn; - - ActivityMenuListener( - @NonNull ConfigurationChooser configChooser, - int action, - @Nullable String fqcn) { - mConfigChooser = configChooser; - mAction = action; - mFqcn = fqcn; - } - - @Override - public void widgetSelected(SelectionEvent e) { - switch (mAction) { - case ACTION_OPEN_ACTIVITY: { - Configuration configuration = mConfigChooser.getConfiguration(); - String fqcn = configuration.getActivity(); - AdtPlugin.openJavaClass(mConfigChooser.getProject(), fqcn); - break; - } - case ACTION_SELECT_ACTIVITY: { - mConfigChooser.selectActivity(mFqcn); - mConfigChooser.onSelectActivity(); - break; - } - default: assert false : mAction; - } - } - - static void show(ConfigurationChooser chooser, ToolItem combo) { - // TODO: Allow using fragments here as well? - Menu menu = new Menu(chooser.getShell(), SWT.POP_UP); - ISharedImages sharedImages = JavaUI.getSharedImages(); - Configuration configuration = chooser.getConfiguration(); - String current = configuration.getActivity(); - - if (current != null) { - MenuItem item = new MenuItem(menu, SWT.PUSH); - String label = ConfigurationChooser.getActivityLabel(current, true); - item.setText( String.format("Open %1$s...", label)); - Image image = sharedImages.getImage(ISharedImages.IMG_OBJS_CUNIT); - item.setImage(image); - item.addSelectionListener( - new ActivityMenuListener(chooser, ACTION_OPEN_ACTIVITY, null)); - - @SuppressWarnings("unused") - MenuItem separator = new MenuItem(menu, SWT.SEPARATOR); - } - - IProject project = chooser.getProject(); - Image image = sharedImages.getImage(ISharedImages.IMG_OBJS_CLASS); - - // Add activities found to be relevant to this layout - String layoutName = ResourceHelper.getLayoutName(chooser.getEditedFile()); - String pkg = ManifestInfo.get(project).getPackage(); - List<String> preferred = ManifestInfo.guessActivities(project, layoutName, pkg); - current = addActivities(chooser, menu, current, image, preferred); - - // Add all activities - List<String> activities = ManifestInfo.getProjectActivities(project); - if (preferred.size() > 0) { - // Filter out the activities we've already listed above - List<String> filtered = new ArrayList<String>(activities.size()); - Set<String> remove = new HashSet<String>(preferred); - for (String fqcn : activities) { - if (!remove.contains(fqcn)) { - filtered.add(fqcn); - } - } - activities = filtered; - } - - if (activities.size() > 0) { - if (preferred.size() > 0) { - @SuppressWarnings("unused") - MenuItem separator = new MenuItem(menu, SWT.SEPARATOR); - } - - addActivities(chooser, menu, current, image, activities); - } - - Rectangle bounds = combo.getBounds(); - Point location = new Point(bounds.x, bounds.y + bounds.height); - location = combo.getParent().toDisplay(location); - menu.setLocation(location.x, location.y); - menu.setVisible(true); - } - - private static String addActivities(ConfigurationChooser chooser, Menu menu, String current, - Image image, List<String> activities) { - for (final String fqcn : activities) { - String title = ConfigurationChooser.getActivityLabel(fqcn, false); - MenuItem item = new MenuItem(menu, SWT.CHECK); - item.setText(title); - item.setImage(image); - - boolean selected = title.equals(current); - if (selected) { - item.setSelection(true); - current = null; // Only show the first occurrence as selected - // such that we don't show it selected again in the full activity list - } - - item.addSelectionListener(new ActivityMenuListener(chooser, - ACTION_SELECT_ACTIVITY, fqcn)); - } - - return current; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/Configuration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/Configuration.java deleted file mode 100644 index c4253cddf..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/Configuration.java +++ /dev/null @@ -1,1091 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX; -import static com.android.SdkConstants.PREFIX_RESOURCE_REF; -import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.rendering.LayoutLibrary; -import com.android.ide.common.rendering.api.Capability; -import com.android.ide.common.resources.ResourceFolder; -import com.android.ide.common.resources.ResourceRepository; -import com.android.ide.common.resources.configuration.DensityQualifier; -import com.android.ide.common.resources.configuration.DeviceConfigHelper; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.LayoutDirectionQualifier; -import com.android.ide.common.resources.configuration.LocaleQualifier; -import com.android.ide.common.resources.configuration.NightModeQualifier; -import com.android.ide.common.resources.configuration.ScreenSizeQualifier; -import com.android.ide.common.resources.configuration.UiModeQualifier; -import com.android.ide.common.resources.configuration.VersionQualifier; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderService; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo.ActivityAttributes; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; -import com.android.ide.eclipse.adt.internal.resources.ResourceHelper; -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.Sdk; -import com.android.resources.Density; -import com.android.resources.LayoutDirection; -import com.android.resources.NightMode; -import com.android.resources.ScreenSize; -import com.android.resources.UiMode; -import com.android.sdklib.AndroidVersion; -import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.devices.Device; -import com.android.sdklib.devices.State; -import com.android.utils.Pair; -import com.google.common.base.Objects; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.QualifiedName; - -import java.util.List; - -/** - * A {@linkplain Configuration} is a selection of device, orientation, theme, - * etc for use when rendering a layout. - */ -public class Configuration { - /** The {@link FolderConfiguration} in change flags or override flags */ - public static final int CFG_FOLDER = 1 << 0; - /** The {@link Device} in change flags or override flags */ - public static final int CFG_DEVICE = 1 << 1; - /** The {@link State} in change flags or override flags */ - public static final int CFG_DEVICE_STATE = 1 << 2; - /** The theme in change flags or override flags */ - public static final int CFG_THEME = 1 << 3; - /** The locale in change flags or override flags */ - public static final int CFG_LOCALE = 1 << 4; - /** The rendering {@link IAndroidTarget} in change flags or override flags */ - public static final int CFG_TARGET = 1 << 5; - /** The {@link NightMode} in change flags or override flags */ - public static final int CFG_NIGHT_MODE = 1 << 6; - /** The {@link UiMode} in change flags or override flags */ - public static final int CFG_UI_MODE = 1 << 7; - /** The {@link UiMode} in change flags or override flags */ - public static final int CFG_ACTIVITY = 1 << 8; - - /** References all attributes */ - public static final int MASK_ALL = 0xFFFF; - - /** Attributes which affect which best-layout-file selection */ - public static final int MASK_FILE_ATTRS = - CFG_DEVICE|CFG_DEVICE_STATE|CFG_LOCALE|CFG_TARGET|CFG_NIGHT_MODE|CFG_UI_MODE; - - /** Attributes which affect rendering appearance */ - public static final int MASK_RENDERING = MASK_FILE_ATTRS|CFG_THEME; - - /** - * Setting name for project-wide setting controlling rendering target and locale which - * is shared for all files - */ - public final static QualifiedName NAME_RENDER_STATE = - new QualifiedName(AdtPlugin.PLUGIN_ID, "render"); //$NON-NLS-1$ - - private final static String MARKER_FRAMEWORK = "-"; //$NON-NLS-1$ - private final static String MARKER_PROJECT = "+"; //$NON-NLS-1$ - private final static String SEP = ":"; //$NON-NLS-1$ - private final static String SEP_LOCALE = "-"; //$NON-NLS-1$ - - @NonNull - protected ConfigurationChooser mConfigChooser; - - /** The {@link FolderConfiguration} representing the state of the UI controls */ - @NonNull - protected final FolderConfiguration mFullConfig = new FolderConfiguration(); - - /** The {@link FolderConfiguration} being edited. */ - @Nullable - protected FolderConfiguration mEditedConfig; - - /** The target of the project of the file being edited. */ - @Nullable - private IAndroidTarget mTarget; - - /** The theme style to render with */ - @Nullable - private String mTheme; - - /** The device to render with */ - @Nullable - private Device mDevice; - - /** The device state */ - @Nullable - private State mState; - - /** - * The activity associated with the layout. This is just a cached value of - * the true value stored on the layout. - */ - @Nullable - private String mActivity; - - /** The locale to use for this configuration */ - @NonNull - private Locale mLocale = Locale.ANY; - - /** UI mode */ - @NonNull - private UiMode mUiMode = UiMode.NORMAL; - - /** Night mode */ - @NonNull - private NightMode mNightMode = NightMode.NOTNIGHT; - - /** The display name */ - private String mDisplayName; - - /** - * Creates a new {@linkplain Configuration} - * - * @param chooser the associated chooser - */ - protected Configuration(@NonNull ConfigurationChooser chooser) { - mConfigChooser = chooser; - } - - /** - * Sets the associated configuration chooser - * - * @param chooser the chooser - */ - void setChooser(@NonNull ConfigurationChooser chooser) { - // TODO: We should get rid of the binding between configurations - // and configuration choosers. This is currently needed because - // the choosers contain vital data such as the set of available - // rendering targets, the set of available locales etc, which - // also doesn't belong inside the configuration but is needed by it. - mConfigChooser = chooser; - } - - /** - * Gets the associated configuration chooser - * - * @return the chooser - */ - @NonNull - ConfigurationChooser getChooser() { - return mConfigChooser; - } - - /** - * Creates a new {@linkplain Configuration} - * - * @param chooser the associated chooser - * @return a new configuration - */ - @NonNull - public static Configuration create(@NonNull ConfigurationChooser chooser) { - return new Configuration(chooser); - } - - /** - * Creates a configuration suitable for the given file - * - * @param base the base configuration to base the file configuration off of - * @param file the file to look up a configuration for - * @return a suitable configuration - */ - @NonNull - public static Configuration create( - @NonNull Configuration base, - @NonNull IFile file) { - Configuration configuration = copy(base); - ConfigurationChooser chooser = base.getChooser(); - ProjectResources resources = chooser.getResources(); - ConfigurationMatcher matcher = new ConfigurationMatcher(chooser, configuration, file, - resources, false); - - ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file); - configuration.mEditedConfig = new FolderConfiguration(); - configuration.mEditedConfig.set(resFolder.getConfiguration()); - - matcher.adaptConfigSelection(true /*needBestMatch*/); - configuration.syncFolderConfig(); - - return configuration; - } - - /** - * Creates a new {@linkplain Configuration} that is a copy from a different configuration - * - * @param original the original to copy from - * @return a new configuration copied from the original - */ - @NonNull - public static Configuration copy(@NonNull Configuration original) { - Configuration copy = create(original.mConfigChooser); - copy.mFullConfig.set(original.mFullConfig); - if (original.mEditedConfig != null) { - copy.mEditedConfig = new FolderConfiguration(); - copy.mEditedConfig.set(original.mEditedConfig); - } - copy.mTarget = original.getTarget(); - copy.mTheme = original.getTheme(); - copy.mDevice = original.getDevice(); - copy.mState = original.getDeviceState(); - copy.mActivity = original.getActivity(); - copy.mLocale = original.getLocale(); - copy.mUiMode = original.getUiMode(); - copy.mNightMode = original.getNightMode(); - copy.mDisplayName = original.getDisplayName(); - - return copy; - } - - /** - * Returns the associated activity - * - * @return the activity - */ - @Nullable - public String getActivity() { - return mActivity; - } - - /** - * Returns the chosen device. - * - * @return the chosen device - */ - @Nullable - public Device getDevice() { - return mDevice; - } - - /** - * Returns the chosen device state - * - * @return the device state - */ - @Nullable - public State getDeviceState() { - return mState; - } - - /** - * Returns the chosen locale - * - * @return the locale - */ - @NonNull - public Locale getLocale() { - return mLocale; - } - - /** - * Returns the UI mode - * - * @return the UI mode - */ - @NonNull - public UiMode getUiMode() { - return mUiMode; - } - - /** - * Returns the day/night mode - * - * @return the night mode - */ - @NonNull - public NightMode getNightMode() { - return mNightMode; - } - - /** - * Returns the current theme style - * - * @return the theme style - */ - @Nullable - public String getTheme() { - return mTheme; - } - - /** - * Returns the rendering target - * - * @return the target - */ - @Nullable - public IAndroidTarget getTarget() { - return mTarget; - } - - /** - * Returns the display name to show for this configuration - * - * @return the display name, or null if none has been assigned - */ - @Nullable - public String getDisplayName() { - return mDisplayName; - } - - /** - * Returns whether the configuration's theme is a project theme. - * <p/> - * The returned value is meaningless if {@link #getTheme()} returns - * <code>null</code>. - * - * @return true for project a theme, false for a framework theme - */ - public boolean isProjectTheme() { - String theme = getTheme(); - if (theme != null) { - assert theme.startsWith(STYLE_RESOURCE_PREFIX) - || theme.startsWith(ANDROID_STYLE_RESOURCE_PREFIX); - - return ResourceHelper.isProjectStyle(theme); - } - - return false; - } - - /** - * Returns true if the current layout is locale-specific - * - * @return if this configuration represents a locale-specific layout - */ - public boolean isLocaleSpecificLayout() { - return mEditedConfig == null || mEditedConfig.getLocaleQualifier() != null; - } - - /** - * Returns the full, complete {@link FolderConfiguration} - * - * @return the full configuration - */ - @NonNull - public FolderConfiguration getFullConfig() { - return mFullConfig; - } - - /** - * Copies the full, complete {@link FolderConfiguration} into the given - * folder config instance. - * - * @param dest the {@link FolderConfiguration} instance to copy into - */ - public void copyFullConfig(FolderConfiguration dest) { - dest.set(mFullConfig); - } - - /** - * Returns the edited {@link FolderConfiguration} (this is not a full - * configuration, so you can think of it as the "constraints" used by the - * {@link ConfigurationMatcher} to produce a full configuration. - * - * @return the constraints configuration - */ - @NonNull - public FolderConfiguration getEditedConfig() { - return mEditedConfig; - } - - /** - * Sets the edited {@link FolderConfiguration} (this is not a full - * configuration, so you can think of it as the "constraints" used by the - * {@link ConfigurationMatcher} to produce a full configuration. - * - * @param editedConfig the constraints configuration - */ - public void setEditedConfig(@NonNull FolderConfiguration editedConfig) { - mEditedConfig = editedConfig; - } - - /** - * Sets the associated activity - * - * @param activity the activity - */ - public void setActivity(String activity) { - mActivity = activity; - } - - /** - * Sets the device - * - * @param device the device - * @param skipSync if true, don't sync folder configuration (typically because - * you are going to set other configuration parameters and you'll call - * {@link #syncFolderConfig()} once at the end) - */ - public void setDevice(Device device, boolean skipSync) { - mDevice = device; - - if (!skipSync) { - syncFolderConfig(); - } - } - - /** - * Sets the device state - * - * @param state the device state - * @param skipSync if true, don't sync folder configuration (typically because - * you are going to set other configuration parameters and you'll call - * {@link #syncFolderConfig()} once at the end) - */ - public void setDeviceState(State state, boolean skipSync) { - mState = state; - - if (!skipSync) { - syncFolderConfig(); - } - } - - /** - * Sets the locale - * - * @param locale the locale - * @param skipSync if true, don't sync folder configuration (typically because - * you are going to set other configuration parameters and you'll call - * {@link #syncFolderConfig()} once at the end) - */ - public void setLocale(@NonNull Locale locale, boolean skipSync) { - mLocale = locale; - - if (!skipSync) { - syncFolderConfig(); - } - } - - /** - * Sets the rendering target - * - * @param target rendering target - * @param skipSync if true, don't sync folder configuration (typically because - * you are going to set other configuration parameters and you'll call - * {@link #syncFolderConfig()} once at the end) - */ - public void setTarget(IAndroidTarget target, boolean skipSync) { - mTarget = target; - - if (!skipSync) { - syncFolderConfig(); - } - } - - /** - * Sets the display name to be shown for this configuration. - * - * @param displayName the new display name - */ - public void setDisplayName(@Nullable String displayName) { - mDisplayName = displayName; - } - - /** - * Sets the night mode - * - * @param night the night mode - * @param skipSync if true, don't sync folder configuration (typically because - * you are going to set other configuration parameters and you'll call - * {@link #syncFolderConfig()} once at the end) - */ - public void setNightMode(@NonNull NightMode night, boolean skipSync) { - mNightMode = night; - - if (!skipSync) { - syncFolderConfig(); - } - } - - /** - * Sets the UI mode - * - * @param uiMode the UI mode - * @param skipSync if true, don't sync folder configuration (typically because - * you are going to set other configuration parameters and you'll call - * {@link #syncFolderConfig()} once at the end) - */ - public void setUiMode(@NonNull UiMode uiMode, boolean skipSync) { - mUiMode = uiMode; - - if (!skipSync) { - syncFolderConfig(); - } - } - - /** - * Sets the theme style - * - * @param theme the theme - */ - public void setTheme(String theme) { - mTheme = theme; - checkThemePrefix(); - } - - /** - * Updates the folder configuration such that it reflects changes in - * configuration state such as the device orientation, the UI mode, the - * rendering target, etc. - */ - public void syncFolderConfig() { - Device device = getDevice(); - if (device == null) { - return; - } - - // get the device config from the device/state combos. - FolderConfiguration config = DeviceConfigHelper.getFolderConfig(getDeviceState()); - - // replace the config with the one from the device - mFullConfig.set(config); - - // sync the selected locale - Locale locale = getLocale(); - mFullConfig.setLocaleQualifier(locale.qualifier); - if (!locale.hasLanguage()) { - // Avoid getting the layout library if the locale doesn't have any language. - mFullConfig.setLayoutDirectionQualifier( - new LayoutDirectionQualifier(LayoutDirection.LTR)); - } else { - Sdk currentSdk = Sdk.getCurrent(); - if (currentSdk != null) { - AndroidTargetData targetData = currentSdk.getTargetData(getTarget()); - if (targetData != null) { - LayoutLibrary layoutLib = targetData.getLayoutLibrary(); - if (layoutLib != null) { - if (layoutLib.isRtl(locale.toLocaleId())) { - mFullConfig.setLayoutDirectionQualifier( - new LayoutDirectionQualifier(LayoutDirection.RTL)); - } else { - mFullConfig.setLayoutDirectionQualifier( - new LayoutDirectionQualifier(LayoutDirection.LTR)); - } - } - } - } - } - - // Replace the UiMode with the selected one, if one is selected - UiMode uiMode = getUiMode(); - if (uiMode != null) { - mFullConfig.setUiModeQualifier(new UiModeQualifier(uiMode)); - } - - // Replace the NightMode with the selected one, if one is selected - NightMode nightMode = getNightMode(); - if (nightMode != null) { - mFullConfig.setNightModeQualifier(new NightModeQualifier(nightMode)); - } - - // replace the API level by the selection of the combo - IAndroidTarget target = getTarget(); - if (target == null && mConfigChooser != null) { - target = mConfigChooser.getProjectTarget(); - } - if (target != null) { - int apiLevel = target.getVersion().getApiLevel(); - mFullConfig.setVersionQualifier(new VersionQualifier(apiLevel)); - } - } - - /** - * Creates a string suitable for persistence, which can be initialized back - * to a configuration via {@link #initialize(String)} - * - * @return a persistent string - */ - @NonNull - public String toPersistentString() { - StringBuilder sb = new StringBuilder(32); - Device device = getDevice(); - if (device != null) { - sb.append(device.getName()); - sb.append(SEP); - State state = getDeviceState(); - if (state != null) { - sb.append(state.getName()); - } - sb.append(SEP); - Locale locale = getLocale(); - if (isLocaleSpecificLayout() && locale != null && locale.qualifier.hasLanguage()) { - // locale[0]/[1] can be null sometimes when starting Eclipse - sb.append(locale.qualifier.getLanguage()); - sb.append(SEP_LOCALE); - if (locale.qualifier.hasRegion()) { - sb.append(locale.qualifier.getRegion()); - } - } - sb.append(SEP); - // Need to escape the theme: if we write the full theme style, then - // we can end up with ":"'s in the string (as in @android:style/Theme) which - // can be mistaken for {@link #SEP}. Instead use {@link #MARKER_FRAMEWORK}. - String theme = getTheme(); - if (theme != null) { - String themeName = ResourceHelper.styleToTheme(theme); - if (theme.startsWith(STYLE_RESOURCE_PREFIX)) { - sb.append(MARKER_PROJECT); - } else if (theme.startsWith(ANDROID_STYLE_RESOURCE_PREFIX)) { - sb.append(MARKER_FRAMEWORK); - } - sb.append(themeName); - } - sb.append(SEP); - UiMode uiMode = getUiMode(); - if (uiMode != null) { - sb.append(uiMode.getResourceValue()); - } - sb.append(SEP); - NightMode nightMode = getNightMode(); - if (nightMode != null) { - sb.append(nightMode.getResourceValue()); - } - sb.append(SEP); - - // We used to store the render target here in R9. Leave a marker - // to ensure that we don't reuse this slot; add new extra fields after it. - sb.append(SEP); - String activity = getActivity(); - if (activity != null) { - sb.append(activity); - } - } - - return sb.toString(); - } - - /** Returns the preferred theme, or null */ - @Nullable - String computePreferredTheme() { - IProject project = mConfigChooser.getProject(); - ManifestInfo manifest = ManifestInfo.get(project); - - // Look up the screen size for the current state - ScreenSize screenSize = null; - Device device = getDevice(); - if (device != null) { - List<State> states = device.getAllStates(); - for (State state : states) { - FolderConfiguration folderConfig = DeviceConfigHelper.getFolderConfig(state); - if (folderConfig != null) { - ScreenSizeQualifier qualifier = folderConfig.getScreenSizeQualifier(); - screenSize = qualifier.getValue(); - break; - } - } - } - - // Look up the default/fallback theme to use for this project (which - // depends on the screen size when no particular theme is specified - // in the manifest) - String defaultTheme = manifest.getDefaultTheme(getTarget(), screenSize); - - String preferred = defaultTheme; - if (getTheme() == null) { - // If we are rendering a layout in included context, pick the theme - // from the outer layout instead - - String activity = getActivity(); - if (activity != null) { - ActivityAttributes attributes = manifest.getActivityAttributes(activity); - if (attributes != null) { - preferred = attributes.getTheme(); - } - } - if (preferred == null) { - preferred = defaultTheme; - } - setTheme(preferred); - } - - return preferred; - } - - private void checkThemePrefix() { - if (mTheme != null && !mTheme.startsWith(PREFIX_RESOURCE_REF)) { - if (mTheme.isEmpty()) { - computePreferredTheme(); - return; - } - ResourceRepository frameworkRes = mConfigChooser.getClient().getFrameworkResources(); - if (frameworkRes != null - && frameworkRes.hasResourceItem(ANDROID_STYLE_RESOURCE_PREFIX + mTheme)) { - mTheme = ANDROID_STYLE_RESOURCE_PREFIX + mTheme; - } else { - mTheme = STYLE_RESOURCE_PREFIX + mTheme; - } - } - } - - /** - * Initializes a string previously created with - * {@link #toPersistentString()} - * - * @param data the string to initialize back from - * @return true if the configuration was initialized - */ - boolean initialize(String data) { - String[] values = data.split(SEP); - if (values.length >= 6 && values.length <= 8) { - for (Device d : mConfigChooser.getDevices()) { - if (d.getName().equals(values[0])) { - mDevice = d; - String stateName = null; - FolderConfiguration config = null; - if (!values[1].isEmpty() && !values[1].equals("null")) { //$NON-NLS-1$ - stateName = values[1]; - config = DeviceConfigHelper.getFolderConfig(mDevice, stateName); - } else if (mDevice.getAllStates().size() > 0) { - State first = mDevice.getAllStates().get(0); - stateName = first.getName(); - config = DeviceConfigHelper.getFolderConfig(first); - } - mState = getState(mDevice, stateName); - if (config != null) { - // Load locale. Note that this can get overwritten by the - // project-wide settings read below. - LocaleQualifier locale = Locale.ANY_QUALIFIER; - String locales[] = values[2].split(SEP_LOCALE); - if (locales.length >= 2 && locales[0].length() > 0 - && !LocaleQualifier.FAKE_VALUE.equals(locales[0])) { - String language = locales[0]; - String region = locales[1]; - if (region.length() > 0 && !LocaleQualifier.FAKE_VALUE.equals(region)) { - locale = LocaleQualifier.getQualifier(language + "-r" + region); - } else { - locale = new LocaleQualifier(language); - } - mLocale = Locale.create(locale); - } - - // Decode the theme name: See {@link #getData} - mTheme = values[3]; - if (mTheme.startsWith(MARKER_FRAMEWORK)) { - mTheme = ANDROID_STYLE_RESOURCE_PREFIX - + mTheme.substring(MARKER_FRAMEWORK.length()); - } else if (mTheme.startsWith(MARKER_PROJECT)) { - mTheme = STYLE_RESOURCE_PREFIX - + mTheme.substring(MARKER_PROJECT.length()); - } else { - checkThemePrefix(); - } - - mUiMode = UiMode.getEnum(values[4]); - if (mUiMode == null) { - mUiMode = UiMode.NORMAL; - } - mNightMode = NightMode.getEnum(values[5]); - if (mNightMode == null) { - mNightMode = NightMode.NOTNIGHT; - } - - // element 7/values[6]: used to store render target in R9. - // No longer stored here. If adding more data, make - // sure you leave 7 alone. - - Pair<Locale, IAndroidTarget> pair = loadRenderState(mConfigChooser); - if (pair != null) { - // We only use the "global" setting - if (!isLocaleSpecificLayout()) { - mLocale = pair.getFirst(); - } - mTarget = pair.getSecond(); - } - - if (values.length == 8) { - mActivity = values[7]; - } - - return true; - } - } - } - } - - return false; - } - - /** - * Loads the render state (the locale and the render target, which are shared among - * all the layouts meaning that changing it in one will change it in all) and returns - * the current project-wide locale and render target to be used. - * - * @param chooser the {@link ConfigurationChooser} providing information about - * loaded targets - * @return a pair of a locale and a render target - */ - @Nullable - static Pair<Locale, IAndroidTarget> loadRenderState(ConfigurationChooser chooser) { - IProject project = chooser.getProject(); - if (project == null || !project.isAccessible()) { - return null; - } - - try { - String data = project.getPersistentProperty(NAME_RENDER_STATE); - if (data != null) { - Locale locale = Locale.ANY; - IAndroidTarget target = null; - - String[] values = data.split(SEP); - if (values.length == 2) { - - LocaleQualifier qualifier = Locale.ANY_QUALIFIER; - String locales[] = values[0].split(SEP_LOCALE); - if (locales.length >= 2 && locales[0].length() > 0 - && !LocaleQualifier.FAKE_VALUE.equals(locales[0])) { - String language = locales[0]; - String region = locales[1]; - if (region.length() > 0 && !LocaleQualifier.FAKE_VALUE.equals(region)) { - locale = Locale.create(LocaleQualifier.getQualifier(language + "-r" + region)); - } else { - locale = Locale.create(new LocaleQualifier(language)); - } - } else { - locale = Locale.ANY; - } - if (AdtPrefs.getPrefs().isAutoPickRenderTarget()) { - target = ConfigurationMatcher.findDefaultRenderTarget(chooser); - } else { - String targetString = values[1]; - target = stringToTarget(chooser, targetString); - // See if we should "correct" the rendering target to a - // better version. If you're using a pre-release version - // of the render target, and a final release is - // available and installed, we should switch to that - // one instead. - if (target != null) { - AndroidVersion version = target.getVersion(); - List<IAndroidTarget> targetList = chooser.getTargetList(); - if (version.getCodename() != null && targetList != null) { - int targetApiLevel = version.getApiLevel() + 1; - for (IAndroidTarget t : targetList) { - if (t.getVersion().getApiLevel() == targetApiLevel - && t.isPlatform()) { - target = t; - break; - } - } - } - } else { - target = ConfigurationMatcher.findDefaultRenderTarget(chooser); - } - } - } - - return Pair.of(locale, target); - } - - return Pair.of(Locale.ANY, ConfigurationMatcher.findDefaultRenderTarget(chooser)); - } catch (CoreException e) { - AdtPlugin.log(e, null); - } - - return null; - } - - /** - * Saves the render state (the current locale and render target settings) into the - * project wide settings storage - */ - void saveRenderState() { - IProject project = mConfigChooser.getProject(); - if (project == null) { - return; - } - try { - // Generate a persistent string from locale+target - StringBuilder sb = new StringBuilder(32); - Locale locale = getLocale(); - if (locale != null) { - // locale[0]/[1] can be null sometimes when starting Eclipse - sb.append(locale.qualifier.getLanguage()); - sb.append(SEP_LOCALE); - if (locale.qualifier.hasRegion()) { - sb.append(locale.qualifier.getRegion()); - } - } - sb.append(SEP); - IAndroidTarget target = getTarget(); - if (target != null) { - sb.append(targetToString(target)); - sb.append(SEP); - } - - project.setPersistentProperty(NAME_RENDER_STATE, sb.toString()); - } catch (CoreException e) { - AdtPlugin.log(e, null); - } - } - - /** - * Returns a String id to represent an {@link IAndroidTarget} which can be translated - * back to an {@link IAndroidTarget} by the matching {@link #stringToTarget}. The id - * will never contain the {@link #SEP} character. - * - * @param target the target to return an id for - * @return an id for the given target; never null - */ - @NonNull - public static String targetToString(@NonNull IAndroidTarget target) { - return target.getFullName().replace(SEP, ""); //$NON-NLS-1$ - } - - /** - * Returns an {@link IAndroidTarget} that corresponds to the given id that was - * originally returned by {@link #targetToString}. May be null, if the platform is no - * longer available, or if the platform list has not yet been initialized. - * - * @param chooser the {@link ConfigurationChooser} providing information about - * loaded targets - * @param id the id that corresponds to the desired platform - * @return an {@link IAndroidTarget} that matches the given id, or null - */ - @Nullable - public static IAndroidTarget stringToTarget( - @NonNull ConfigurationChooser chooser, - @NonNull String id) { - List<IAndroidTarget> targetList = chooser.getTargetList(); - if (targetList != null && targetList.size() > 0) { - for (IAndroidTarget target : targetList) { - if (id.equals(targetToString(target))) { - return target; - } - } - } - - return null; - } - - /** - * Returns an {@link IAndroidTarget} that corresponds to the given id that was - * originally returned by {@link #targetToString}. May be null, if the platform is no - * longer available, or if the platform list has not yet been initialized. - * - * @param id the id that corresponds to the desired platform - * @return an {@link IAndroidTarget} that matches the given id, or null - */ - @Nullable - public static IAndroidTarget stringToTarget( - @NonNull String id) { - Sdk currentSdk = Sdk.getCurrent(); - if (currentSdk != null) { - IAndroidTarget[] targets = currentSdk.getTargets(); - for (IAndroidTarget target : targets) { - if (id.equals(targetToString(target))) { - return target; - } - } - } - - return null; - } - - /** - * Returns the {@link State} by the given name for the given {@link Device} - * - * @param device the device - * @param name the name of the state - */ - @Nullable - static State getState(@Nullable Device device, @Nullable String name) { - if (device == null) { - return null; - } else if (name != null) { - State state = device.getState(name); - if (state != null) { - return state; - } - } - - return device.getDefaultState(); - } - - /** - * Returns the currently selected {@link Density}. This is guaranteed to be non null. - * - * @return the density - */ - @NonNull - public Density getDensity() { - if (mFullConfig != null) { - DensityQualifier qual = mFullConfig.getDensityQualifier(); - if (qual != null) { - // just a sanity check - Density d = qual.getValue(); - if (d != Density.NODPI) { - return d; - } - } - } - - // no config? return medium as the default density. - return Density.MEDIUM; - } - - /** - * Get the next cyclical state after the given state - * - * @param from the state to start with - * @return the following state following - */ - @Nullable - public State getNextDeviceState(@Nullable State from) { - Device device = getDevice(); - if (device == null) { - return null; - } - List<State> states = device.getAllStates(); - for (int i = 0; i < states.size(); i++) { - if (states.get(i) == from) { - return states.get((i + 1) % states.size()); - } - } - - return null; - } - - /** - * Returns true if this configuration supports the given rendering - * capability - * - * @param capability the capability to check - * @return true if the capability is supported - */ - public boolean supports(Capability capability) { - IAndroidTarget target = getTarget(); - if (target != null) { - return RenderService.supports(target, capability); - } - - return false; - } - - @Override - public String toString() { - return Objects.toStringHelper(this.getClass()) - .add("display", getDisplayName()) //$NON-NLS-1$ - .add("persistent", toPersistentString()) //$NON-NLS-1$ - .toString(); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java deleted file mode 100644 index 009b8646c..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationChooser.java +++ /dev/null @@ -1,2096 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX; -import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX; -import static com.android.SdkConstants.ATTR_CONTEXT; -import static com.android.SdkConstants.PREFIX_RESOURCE_REF; -import static com.android.SdkConstants.RES_QUALIFIER_SEP; -import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX; -import static com.android.SdkConstants.TOOLS_URI; -import static com.android.ide.eclipse.adt.AdtUtils.isUiThread; -import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_DEVICE; -import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_DEVICE_STATE; -import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_FOLDER; -import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_LOCALE; -import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_TARGET; -import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_THEME; -import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.MASK_ALL; -import static com.google.common.base.Objects.equal; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.rendering.api.StyleResourceValue; -import com.android.ide.common.resources.LocaleManager; -import com.android.ide.common.resources.ResourceFile; -import com.android.ide.common.resources.ResourceFolder; -import com.android.ide.common.resources.ResourceRepository; -import com.android.ide.common.resources.configuration.DeviceConfigHelper; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.LocaleQualifier; -import com.android.ide.common.resources.configuration.ResourceQualifier; -import com.android.ide.common.sdk.LoadStatus; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlDelegate; -import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutCanvas; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo.ActivityAttributes; -import com.android.ide.eclipse.adt.internal.resources.ResourceHelper; -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.Sdk; -import com.android.resources.ResourceType; -import com.android.resources.ScreenOrientation; -import com.android.sdklib.AndroidVersion; -import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.devices.Device; -import com.android.sdklib.devices.DeviceManager; -import com.android.sdklib.devices.DeviceManager.DevicesChangedListener; -import com.android.sdklib.devices.State; -import com.android.utils.Pair; -import com.google.common.base.Objects; -import com.google.common.base.Strings; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.ToolBar; -import org.eclipse.swt.widgets.ToolItem; -import org.eclipse.ui.IEditorPart; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.SortedSet; - -/** - * The {@linkplain ConfigurationChooser} allows the user to pick a - * {@link Configuration} by configuring various constraints. - */ -public class ConfigurationChooser extends Composite - implements DevicesChangedListener, DisposeListener { - private static final String ICON_SQUARE = "square"; //$NON-NLS-1$ - private static final String ICON_LANDSCAPE = "landscape"; //$NON-NLS-1$ - private static final String ICON_PORTRAIT = "portrait"; //$NON-NLS-1$ - private static final String ICON_LANDSCAPE_FLIP = "flip_landscape";//$NON-NLS-1$ - private static final String ICON_PORTRAIT_FLIP = "flip_portrait";//$NON-NLS-1$ - private static final String ICON_DISPLAY = "display"; //$NON-NLS-1$ - private static final String ICON_THEMES = "themes"; //$NON-NLS-1$ - private static final String ICON_ACTIVITY = "activity"; //$NON-NLS-1$ - - /** The configuration state associated with this editor */ - private @NonNull Configuration mConfiguration = Configuration.create(this); - - /** Serialized state to use when initializing the configuration after the SDK is loaded */ - private String mInitialState; - - /** The client of the configuration editor */ - private final ConfigurationClient mClient; - - /** Counter for programmatic UI changes: if greater than 0, we're within a call */ - private int mDisableUpdates = 0; - - /** List of available devices */ - private Collection<Device> mDevices = Collections.emptyList(); - - /** List of available targets */ - private final List<IAndroidTarget> mTargetList = new ArrayList<IAndroidTarget>(); - - /** List of available themes */ - private final List<String> mThemeList = new ArrayList<String>(); - - /** List of available locales */ - private final List<Locale > mLocaleList = new ArrayList<Locale>(); - - /** 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 mProjectTarget; - - /** Dropdown for configurations */ - private ToolItem mConfigCombo; - - /** Dropdown for devices */ - private ToolItem mDeviceCombo; - - /** Dropdown for device states */ - private ToolItem mOrientationCombo; - - /** Dropdown for themes */ - private ToolItem mThemeCombo; - - /** Dropdown for locales */ - private ToolItem mLocaleCombo; - - /** Dropdown for activities */ - private ToolItem mActivityCombo; - - /** Dropdown for rendering targets */ - private ToolItem mTargetCombo; - - /** Whether the SDK has changed since the last model reload; if so we must reload targets */ - private boolean mSdkChanged = true; - - /** - * Creates a new {@linkplain ConfigurationChooser} and adds it to the - * parent. The method also receives custom buttons to set into the - * configuration composite. The list is organized as an array of arrays. - * Each array represents a group of buttons thematically grouped together. - * - * @param client the client embedding this configuration chooser - * @param parent The parent composite. - * @param initialState The initial state (serialized form) to use for the - * configuration - */ - public ConfigurationChooser( - @NonNull ConfigurationClient client, - Composite parent, - @Nullable String initialState) { - super(parent, SWT.NONE); - mClient = client; - - setVisible(false); // Delayed until the targets are loaded - - mInitialState = initialState; - setLayout(new GridLayout(1, false)); - - IconFactory icons = IconFactory.getInstance(); - - // TODO: Consider switching to a CoolBar instead - ToolBar toolBar = new ToolBar(this, SWT.WRAP | SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL); - toolBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - - mConfigCombo = new ToolItem(toolBar, SWT.DROP_DOWN ); - mConfigCombo.setImage(icons.getIcon("android_file")); //$NON-NLS-1$ - mConfigCombo.setToolTipText("Configuration to render this layout with in Eclipse"); - - @SuppressWarnings("unused") - ToolItem separator2 = new ToolItem(toolBar, SWT.SEPARATOR); - - mDeviceCombo = new ToolItem(toolBar, SWT.DROP_DOWN); - mDeviceCombo.setImage(icons.getIcon(ICON_DISPLAY)); - - @SuppressWarnings("unused") - ToolItem separator3 = new ToolItem(toolBar, SWT.SEPARATOR); - - mOrientationCombo = new ToolItem(toolBar, SWT.DROP_DOWN); - mOrientationCombo.setImage(icons.getIcon(ICON_PORTRAIT)); - mOrientationCombo.setToolTipText("Go to next state"); - - @SuppressWarnings("unused") - ToolItem separator4 = new ToolItem(toolBar, SWT.SEPARATOR); - - mThemeCombo = new ToolItem(toolBar, SWT.DROP_DOWN); - mThemeCombo.setImage(icons.getIcon(ICON_THEMES)); - - @SuppressWarnings("unused") - ToolItem separator5 = new ToolItem(toolBar, SWT.SEPARATOR); - - mActivityCombo = new ToolItem(toolBar, SWT.DROP_DOWN); - mActivityCombo.setToolTipText("Associated activity or fragment providing context"); - // The JDT class icon is lopsided, presumably because they've left room in the - // bottom right corner for badges (for static, final etc). Unfortunately, this - // means that the icon looks out of place when sitting close to the language globe - // icon, the theme icon, etc so that it looks vertically misaligned: - //mActivityCombo.setImage(JavaUI.getSharedImages().getImage(ISharedImages.IMG_OBJS_CLASS)); - // ...so use one that is centered instead: - mActivityCombo.setImage(icons.getIcon(ICON_ACTIVITY)); - - @SuppressWarnings("unused") - ToolItem separator6 = new ToolItem(toolBar, SWT.SEPARATOR); - - //ToolBar rightToolBar = new ToolBar(this, SWT.WRAP | SWT.FLAT | SWT.RIGHT | SWT.HORIZONTAL); - //rightToolBar.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1)); - ToolBar rightToolBar = toolBar; - - mLocaleCombo = new ToolItem(rightToolBar, SWT.DROP_DOWN); - mLocaleCombo.setImage(FlagManager.getGlobeIcon()); - mLocaleCombo.setToolTipText("Locale to use when rendering layouts in Eclipse"); - - @SuppressWarnings("unused") - ToolItem separator7 = new ToolItem(rightToolBar, SWT.SEPARATOR); - - mTargetCombo = new ToolItem(rightToolBar, SWT.DROP_DOWN); - mTargetCombo.setImage(AdtPlugin.getAndroidLogo()); - mTargetCombo.setToolTipText("Android version to use when rendering layouts in Eclipse"); - - SelectionListener listener = new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - Object source = e.getSource(); - - if (source == mConfigCombo) { - ConfigurationMenuListener.show(ConfigurationChooser.this, mConfigCombo); - } else if (source == mActivityCombo) { - ActivityMenuListener.show(ConfigurationChooser.this, mActivityCombo); - } else if (source == mLocaleCombo) { - LocaleMenuListener.show(ConfigurationChooser.this, mLocaleCombo); - } else if (source == mDeviceCombo) { - DeviceMenuListener.show(ConfigurationChooser.this, mDeviceCombo); - } else if (source == mTargetCombo) { - TargetMenuListener.show(ConfigurationChooser.this, mTargetCombo); - } else if (source == mThemeCombo) { - ThemeMenuAction.showThemeMenu(ConfigurationChooser.this, mThemeCombo, - mThemeList); - } else if (source == mOrientationCombo) { - if (e.detail == SWT.ARROW) { - OrientationMenuAction.showMenu(ConfigurationChooser.this, - mOrientationCombo); - } else { - gotoNextState(); - } - } - } - }; - mConfigCombo.addSelectionListener(listener); - mActivityCombo.addSelectionListener(listener); - mLocaleCombo.addSelectionListener(listener); - mDeviceCombo.addSelectionListener(listener); - mTargetCombo.addSelectionListener(listener); - mThemeCombo.addSelectionListener(listener); - mOrientationCombo.addSelectionListener(listener); - - addDisposeListener(this); - - initDevices(); - initTargets(); - } - - /** - * Returns the edited file - * - * @return the file - */ - @Nullable - public IFile getEditedFile() { - return mEditedFile; - } - - /** - * Returns the project of the edited file - * - * @return the project - */ - @Nullable - public IProject getProject() { - if (mEditedFile != null) { - return mEditedFile.getProject(); - } else { - return null; - } - } - - ConfigurationClient getClient() { - return mClient; - } - - /** - * Returns the project resources for the project being configured by this - * chooser - * - * @return the project resources - */ - @Nullable - public ProjectResources getResources() { - return mResources; - } - - /** - * Returns the full, complete {@link FolderConfiguration} - * - * @return the full configuration - */ - public FolderConfiguration getFullConfiguration() { - return mConfiguration.getFullConfig(); - } - - /** - * Returns the project target - * - * @return the project target - */ - public IAndroidTarget getProjectTarget() { - return mProjectTarget; - } - - /** - * Returns the configuration being edited by this {@linkplain ConfigurationChooser} - * - * @return the configuration - */ - public Configuration getConfiguration() { - return mConfiguration; - } - - /** - * Returns the list of locales - * @return a list of {@link ResourceQualifier} pairs - */ - @NonNull - public List<Locale> getLocaleList() { - return mLocaleList; - } - - /** - * Returns the list of available devices - * - * @return a list of {@link Device} objects - */ - @NonNull - public Collection<Device> getDevices() { - return mDevices; - } - - /** - * Returns the list of available render targets - * - * @return a list of {@link IAndroidTarget} objects - */ - @NonNull - public List<IAndroidTarget> getTargetList() { - return mTargetList; - } - - // ---- Configuration State Lookup ---- - - /** - * Returns the rendering target to be used - * - * @return the target - */ - @NonNull - public IAndroidTarget getTarget() { - IAndroidTarget target = mConfiguration.getTarget(); - if (target == null) { - target = mProjectTarget; - } - - return target; - } - - /** - * Returns the current device string, or null if no device is selected - * - * @return the device name, or null - */ - @Nullable - public String getDeviceName() { - Device device = mConfiguration.getDevice(); - if (device != null) { - return device.getName(); - } - - return null; - } - - /** - * Returns the current theme, or null if none has been selected - * - * @return the theme name, or null - */ - @Nullable - public String getThemeName() { - String theme = mConfiguration.getTheme(); - if (theme != null) { - theme = ResourceHelper.styleToTheme(theme); - } - - return theme; - } - - /** Move to the next device state, changing the icon if it changes orientation */ - private void gotoNextState() { - State state = mConfiguration.getDeviceState(); - State flipped = mConfiguration.getNextDeviceState(state); - if (flipped != state) { - selectDeviceState(flipped); - onDeviceConfigChange(); - } - } - - // ---- Implements DisposeListener ---- - - @Override - public void widgetDisposed(DisposeEvent e) { - dispose(); - } - - @Override - public void dispose() { - if (!isDisposed()) { - super.dispose(); - - final Sdk sdk = Sdk.getCurrent(); - if (sdk != null) { - DeviceManager manager = sdk.getDeviceManager(); - manager.unregisterListener(this); - } - } - } - - // ---- Init and reset/reload methods ---- - - /** - * Sets the reference to the file being edited. - * <p/>The UI is initialized in {@link #onXmlModelLoaded()} which is called as the XML model is - * loaded (or reloaded as the SDK/target changes). - * - * @param file the file being opened - * - * @see #onXmlModelLoaded() - * @see #replaceFile(IFile) - * @see #changeFileOnNewConfig(IFile) - */ - public void setFile(IFile file) { - mEditedFile = file; - ensureInitialized(); - } - - /** - * 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 or - * not the best match - * @param file the file being opened. - */ - public void replaceFile(IFile file) { - // if there is no previous selection, revert to default mode. - if (mConfiguration.getDevice() == null) { - setFile(file); // onTargetChanged will be called later. - return; - } - - setFile(file); - IProject project = mEditedFile.getProject(); - mResources = ResourceManager.getInstance().getProjectResources(project); - - ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file); - mConfiguration.setEditedConfig(resFolder.getConfiguration()); - - mDisableUpdates++; // we do not want to trigger onXXXChange when setting - // new values in the widgets. - - try { - // only attempt to do anything if the SDK and targets are loaded. - LoadStatus sdkStatus = AdtPlugin.getDefault().getSdkLoadStatus(); - - if (sdkStatus == LoadStatus.LOADED) { - setVisible(true); - - LoadStatus targetStatus = Sdk.getCurrent().checkAndLoadTargetData(mProjectTarget, - null /*project*/); - - if (targetStatus == LoadStatus.LOADED) { - - // update the current config selection to make sure it's - // compatible with the new file - ConfigurationMatcher matcher = new ConfigurationMatcher(this); - matcher.adaptConfigSelection(true /*needBestMatch*/); - mConfiguration.syncFolderConfig(); - - // update the string showing the config value - selectConfiguration(mConfiguration.getEditedConfig()); - updateActivity(); - } - } else if (sdkStatus == LoadStatus.FAILED) { - setVisible(true); - } - } finally { - mDisableUpdates--; - } - } - - /** - * Updates the UI with a new file that was opened in response to a config change. - * @param file the file being opened. - * - * @see #replaceFile(IFile) - */ - public void changeFileOnNewConfig(IFile file) { - setFile(file); - IProject project = mEditedFile.getProject(); - mResources = ResourceManager.getInstance().getProjectResources(project); - - ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file); - FolderConfiguration config = resFolder.getConfiguration(); - mConfiguration.setEditedConfig(config); - - // All that's needed is to update the string showing the config value - // (since the config combo settings chosen by the user). - selectConfiguration(config); - } - - /** - * Resets the configuration chooser to reflect the given file configuration. This is - * intended to be used by the "Show Included In" functionality where the user has - * picked a non-default configuration (such as a particular landscape layout) and the - * configuration chooser must be switched to a landscape layout. This method will - * trigger a model change. - * <p> - * This will NOT trigger a redraw event! - * <p> - * FIXME: We are currently setting the configuration file to be the configuration for - * the "outer" (the including) file, rather than the inner file, which is the file the - * user is actually editing. We need to refine this, possibly with a way for the user - * to choose which configuration they are editing. And in particular, we should be - * filtering the configuration chooser to only show options in the outer configuration - * that are compatible with the inner included file. - * - * @param file the file to be configured - */ - public void resetConfigFor(IFile file) { - setFile(file); - - IFolder parent = (IFolder) mEditedFile.getParent(); - ResourceFolder resFolder = mResources.getResourceFolder(parent); - if (resFolder != null) { - mConfiguration.setEditedConfig(resFolder.getConfiguration()); - } else { - FolderConfiguration config = FolderConfiguration.getConfig( - parent.getName().split(RES_QUALIFIER_SEP)); - if (config != null) { - mConfiguration.setEditedConfig(config); - } else { - mConfiguration.setEditedConfig(new FolderConfiguration()); - } - } - - onXmlModelLoaded(); - } - - - /** - * Sets the current configuration to match the given folder configuration, - * the given theme name, the given device and device state. - * - * @param configuration new folder configuration to use - */ - public void setConfiguration(@NonNull Configuration configuration) { - if (mClient != null) { - mClient.aboutToChange(MASK_ALL); - } - - Configuration oldConfiguration = mConfiguration; - mConfiguration = configuration; - mConfiguration.setChooser(this); - - selectTheme(configuration.getTheme()); - selectLocale(configuration.getLocale()); - selectDevice(configuration.getDevice()); - selectDeviceState(configuration.getDeviceState()); - selectTarget(configuration.getTarget()); - selectActivity(configuration.getActivity()); - - // This may be a second refresh after triggered by theme above - if (mClient != null) { - LayoutCanvas canvas = mClient.getCanvas(); - if (canvas != null) { - assert mConfiguration != oldConfiguration; - canvas.getPreviewManager().updateChooserConfig(oldConfiguration, mConfiguration); - } - - boolean accepted = mClient.changed(MASK_ALL); - if (!accepted) { - configuration = oldConfiguration; - selectTheme(configuration.getTheme()); - selectLocale(configuration.getLocale()); - selectDevice(configuration.getDevice()); - selectDeviceState(configuration.getDeviceState()); - selectTarget(configuration.getTarget()); - selectActivity(configuration.getActivity()); - if (canvas != null && mConfiguration != oldConfiguration) { - canvas.getPreviewManager().updateChooserConfig(mConfiguration, - oldConfiguration); - } - return; - } else { - int changed = 0; - if (!equal(oldConfiguration.getTheme(), mConfiguration.getTheme())) { - changed |= CFG_THEME; - } - if (!equal(oldConfiguration.getDevice(), mConfiguration.getDevice())) { - changed |= CFG_DEVICE | CFG_DEVICE_STATE; - } - if (changed != 0) { - syncToVariations(changed, mEditedFile, mConfiguration, false, true); - } - } - } - - saveConstraints(); - } - - /** - * Responds to the event that the basic SDK information finished loading. - * @param target the possibly new target object associated with the file being edited (in case - * the SDK path was changed). - */ - public void onSdkLoaded(IAndroidTarget target) { - // a change to the SDK means that we need to check for new/removed devices. - mSdkChanged = true; - - // store the new target. - mProjectTarget = target; - - mDisableUpdates++; // we do not want to trigger onXXXChange when setting - // new values in the widgets. - try { - updateDevices(); - updateTargets(); - ensureInitialized(); - } finally { - mDisableUpdates--; - } - } - - /** - * Responds to the XML model being loaded, either the first time or when the - * Target/SDK changes. - * <p> - * This initializes the UI, either with the first compatible configuration - * found, or it will attempt to restore a configuration if one is found to - * have been saved in the file persistent storage. - * <p> - * If the SDK or target are not loaded, nothing will happen (but the method - * must be called back when they are.) - * <p> - * The method automatically handles being called the first time after editor - * creation, or being called after during SDK/Target changes (as long as - * {@link #onSdkLoaded(IAndroidTarget)} is properly called). - * - * @return the target data for the rendering target used to render the - * layout - * - * @see #saveConstraints() - * @see #onSdkLoaded(IAndroidTarget) - */ - public AndroidTargetData onXmlModelLoaded() { - AndroidTargetData targetData = null; - - // only attempt to do anything if the SDK and targets are loaded. - LoadStatus sdkStatus = AdtPlugin.getDefault().getSdkLoadStatus(); - if (sdkStatus == LoadStatus.LOADED) { - mDisableUpdates++; // we do not want to trigger onXXXChange when setting - - try { - // init the devices if needed (new SDK or first time going through here) - if (mSdkChanged) { - updateDevices(); - updateTargets(); - ensureInitialized(); - mSdkChanged = false; - } - - IProject project = mEditedFile.getProject(); - - Sdk currentSdk = Sdk.getCurrent(); - if (currentSdk != null) { - mProjectTarget = currentSdk.getTarget(project); - } - - LoadStatus targetStatus = LoadStatus.FAILED; - if (mProjectTarget != null) { - targetStatus = Sdk.getCurrent().checkAndLoadTargetData(mProjectTarget, null); - updateTargets(); - ensureInitialized(); - } - - if (targetStatus == LoadStatus.LOADED) { - setVisible(true); - if (mResources == null) { - mResources = ResourceManager.getInstance().getProjectResources(project); - } - if (mConfiguration.getEditedConfig() == null) { - IFolder parent = (IFolder) mEditedFile.getParent(); - ResourceFolder resFolder = mResources.getResourceFolder(parent); - if (resFolder != null) { - mConfiguration.setEditedConfig(resFolder.getConfiguration()); - } else { - FolderConfiguration config = FolderConfiguration.getConfig( - parent.getName().split(RES_QUALIFIER_SEP)); - if (config != null) { - mConfiguration.setEditedConfig(config); - } else { - mConfiguration.setEditedConfig(new FolderConfiguration()); - } - } - } - - targetData = Sdk.getCurrent().getTargetData(mProjectTarget); - - // get the file stored state - ensureInitialized(); - boolean loadedConfigData = mConfiguration.getDevice() != null && - mConfiguration.getDeviceState() != null; - - // Load locale list. This must be run after we initialize the - // configuration above, since it attempts to sync the UI with - // the value loaded into the configuration. - updateLocales(); - - // If the current state was loaded from the persistent storage, we update the - // UI with it and then try to adapt it (which will handle incompatible - // configuration). - // Otherwise, just look for the first compatible configuration. - ConfigurationMatcher matcher = new ConfigurationMatcher(this); - if (loadedConfigData) { - // first make sure we have the config to adapt - selectDevice(mConfiguration.getDevice()); - selectDeviceState(mConfiguration.getDeviceState()); - mConfiguration.syncFolderConfig(); - - matcher.adaptConfigSelection(false); - - IAndroidTarget target = mConfiguration.getTarget(); - selectTarget(target); - targetData = Sdk.getCurrent().getTargetData(target); - } else { - matcher.findAndSetCompatibleConfig(false); - - // Default to modern layout lib - IAndroidTarget target = ConfigurationMatcher.findDefaultRenderTarget(this); - if (target != null) { - targetData = Sdk.getCurrent().getTargetData(target); - selectTarget(target); - mConfiguration.setTarget(target, true); - } - } - - // Update activity: This is done before updateThemes() since - // the themes selection can depend on the currently selected activity - // (e.g. when there are manifest registrations for the theme to use - // for a given activity) - updateActivity(); - - // Update themes. This is done after updating the devices above, - // since we want to look at the chosen device size to decide - // what the default theme (for example, with Honeycomb we choose - // Holo as the default theme but only if the screen size is XLARGE - // (and of course only if the manifest does not specify another - // default theme). - updateThemes(); - - // update the string showing the config value - selectConfiguration(mConfiguration.getEditedConfig()); - - // compute the final current config - mConfiguration.syncFolderConfig(); - } else if (targetStatus == LoadStatus.FAILED) { - setVisible(true); - } - } finally { - mDisableUpdates--; - } - } - - return targetData; - } - - /** - * This is a temporary workaround for a infrequently happening bug; apparently - * there are cases where the configuration chooser isn't shown - */ - public void ensureVisible() { - if (!isVisible()) { - LoadStatus sdkStatus = AdtPlugin.getDefault().getSdkLoadStatus(); - if (sdkStatus == LoadStatus.LOADED) { - onXmlModelLoaded(); - } - } - } - - /** - * An alternate layout for this layout has been created. This means that the - * current layout may no longer be a best fit. However, since we support multiple - * layouts being open at the same time, we need to adjust the current configuration - * back to something where this layout <b>is</b> a best match. - */ - public void onAlternateLayoutCreated() { - IFile best = ConfigurationMatcher.getBestFileMatch(this); - if (best != null && !best.equals(mEditedFile)) { - ConfigurationMatcher matcher = new ConfigurationMatcher(this); - matcher.adaptConfigSelection(true /*needBestMatch*/); - mConfiguration.syncFolderConfig(); - if (mClient != null) { - mClient.changed(MASK_ALL); - } - } - } - - /** - * Loads the list of {@link Device}s and inits the UI with it. - */ - private void initDevices() { - final Sdk sdk = Sdk.getCurrent(); - if (sdk != null) { - DeviceManager manager = sdk.getDeviceManager(); - // This method can be called more than once, so avoid duplicate entries - manager.unregisterListener(this); - manager.registerListener(this); - mDevices = manager.getDevices(DeviceManager.ALL_DEVICES); - } else { - mDevices = new ArrayList<Device>(); - } - } - - /** - * Loads the list of {@link IAndroidTarget} and inits the UI with it. - */ - private boolean initTargets() { - mTargetList.clear(); - - Sdk currentSdk = Sdk.getCurrent(); - if (currentSdk != null) { - IAndroidTarget[] targets = currentSdk.getTargets(); - for (int i = 0 ; i < targets.length; i++) { - if (targets[i].hasRenderingLibrary()) { - mTargetList.add(targets[i]); - } - } - - return true; - } - - return false; - } - - /** Ensures that the configuration has been initialized */ - public void ensureInitialized() { - if (mConfiguration.getDevice() == null && mEditedFile != null) { - String data = ConfigurationDescription.getDescription(mEditedFile); - if (mInitialState != null) { - data = mInitialState; - mInitialState = null; - } - if (data != null) { - mConfiguration.initialize(data); - mConfiguration.syncFolderConfig(); - } - } - } - - private void updateDevices() { - if (mDevices.size() == 0) { - initDevices(); - } - } - - private void updateTargets() { - if (mTargetList.size() == 0) { - if (!initTargets()) { - return; - } - } - - IAndroidTarget renderingTarget = mConfiguration.getTarget(); - - IAndroidTarget match = null; - for (IAndroidTarget target : mTargetList) { - if (renderingTarget != null) { - // use equals because the rendering could be from a previous SDK, so - // it may not be the same instance. - if (renderingTarget.equals(target)) { - match = target; - } - } else if (mProjectTarget == target) { - match = target; - } - - } - - if (match == null) { - // the rendering target is the same as the project. - renderingTarget = mProjectTarget; - } else { - // set the rendering target to the new object. - renderingTarget = match; - } - - mConfiguration.setTarget(renderingTarget, true); - selectTarget(renderingTarget); - } - - /** Update the toolbar whenever a label has changed, to not only - * cause the layout in the current toolbar to update, but to possibly - * wrap the toolbars and update the layout of the surrounding area. - */ - private void resizeToolBar() { - Point size = getSize(); - Point newSize = computeSize(size.x, SWT.DEFAULT, true); - setSize(newSize); - Composite parent = getParent(); - parent.layout(); - parent.redraw(); - } - - - Image getOrientationIcon(ScreenOrientation orientation, boolean flip) { - IconFactory icons = IconFactory.getInstance(); - switch (orientation) { - case LANDSCAPE: - return icons.getIcon(flip ? ICON_LANDSCAPE_FLIP : ICON_LANDSCAPE); - case SQUARE: - return icons.getIcon(ICON_SQUARE); - case PORTRAIT: - default: - return icons.getIcon(flip ? ICON_PORTRAIT_FLIP : ICON_PORTRAIT); - } - } - - ImageDescriptor getOrientationImage(ScreenOrientation orientation, boolean flip) { - IconFactory icons = IconFactory.getInstance(); - switch (orientation) { - case LANDSCAPE: - return icons.getImageDescriptor(flip ? ICON_LANDSCAPE_FLIP : ICON_LANDSCAPE); - case SQUARE: - return icons.getImageDescriptor(ICON_SQUARE); - case PORTRAIT: - default: - return icons.getImageDescriptor(flip ? ICON_PORTRAIT_FLIP : ICON_PORTRAIT); - } - } - - @NonNull - ScreenOrientation getOrientation(State state) { - FolderConfiguration config = DeviceConfigHelper.getFolderConfig(state); - ScreenOrientation orientation = null; - if (config != null && config.getScreenOrientationQualifier() != null) { - orientation = config.getScreenOrientationQualifier().getValue(); - } - - if (orientation == null) { - orientation = ScreenOrientation.PORTRAIT; - } - - return orientation; - } - - /** - * Stores the current config selection into the edited file such that we can - * bring it back the next time this layout is opened. - */ - public void saveConstraints() { - String description = mConfiguration.toPersistentString(); - if (description != null && !description.isEmpty()) { - ConfigurationDescription.setDescription(mEditedFile, description); - } - } - - // ---- Setting the current UI state ---- - - void selectDeviceState(@Nullable State state) { - assert isUiThread(); - try { - mDisableUpdates++; - mOrientationCombo.setData(state); - - State nextState = mConfiguration.getNextDeviceState(state); - mOrientationCombo.setImage(getOrientationIcon(getOrientation(state), - nextState != state)); - } finally { - mDisableUpdates--; - } - } - - void selectTarget(IAndroidTarget target) { - assert isUiThread(); - try { - mDisableUpdates++; - mTargetCombo.setData(target); - String label = getRenderingTargetLabel(target, true); - mTargetCombo.setText(label); - resizeToolBar(); - } finally { - mDisableUpdates--; - } - } - - /** - * Selects a given {@link Device} in the device combo, if it is found. - * @param device the device to select - * @return true if the device was found. - */ - boolean selectDevice(@Nullable Device device) { - assert isUiThread(); - try { - mDisableUpdates++; - mDeviceCombo.setData(device); - if (device != null) { - mDeviceCombo.setText(getDeviceLabel(device, true)); - } else { - mDeviceCombo.setText("Device"); - } - resizeToolBar(); - } finally { - mDisableUpdates--; - } - - return false; - } - - void selectActivity(@Nullable String fqcn) { - assert isUiThread(); - try { - mDisableUpdates++; - if (fqcn != null) { - mActivityCombo.setData(fqcn); - String label = getActivityLabel(fqcn, true); - mActivityCombo.setText(label); - } else { - mActivityCombo.setText("(Select)"); - } - resizeToolBar(); - } finally { - mDisableUpdates--; - } - } - - void selectTheme(@Nullable String theme) { - assert isUiThread(); - try { - mDisableUpdates++; - assert theme == null || theme.startsWith(STYLE_RESOURCE_PREFIX) - || theme.startsWith(ANDROID_STYLE_RESOURCE_PREFIX) : theme; - mThemeCombo.setData(theme); - if (theme != null) { - mThemeCombo.setText(getThemeLabel(theme, true)); - } else { - // FIXME eclipse claims this is dead code. - mThemeCombo.setText("(Set Theme)"); - } - resizeToolBar(); - } finally { - mDisableUpdates--; - } - } - - void selectLocale(@Nullable Locale locale) { - assert isUiThread(); - try { - mDisableUpdates++; - mLocaleCombo.setData(locale); - String label = Strings.nullToEmpty(getLocaleLabel(this, locale, true)); - mLocaleCombo.setText(label); - - Image image = getFlagImage(locale); - mLocaleCombo.setImage(image); - - resizeToolBar(); - } finally { - mDisableUpdates--; - } - } - - @NonNull - Image getFlagImage(@Nullable Locale locale) { - if (locale != null) { - return locale.getFlagImage(); - } - - return FlagManager.getGlobeIcon(); - } - - private void selectConfiguration(FolderConfiguration fileConfig) { - /* For now, don't show any text in the configuration combo, use just an - icon. This has the advantage that the configuration contents don't - shift around, so you can for example click back and forth between - portrait and landscape without the icon moving under the mouse. - If this works well, remove this whole method post ADT 21. - assert isUiThread(); - try { - String current = mEditedFile.getParent().getName(); - if (current.equals(FD_RES_LAYOUT)) { - current = "default"; - } - - // Pretty things up a bit - //if (current == null || current.equals("default")) { - // current = "Default Configuration"; - //} - mConfigCombo.setText(current); - resizeToolBar(); - } finally { - mDisableUpdates--; - } - */ - } - - /** - * 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. - * @return true if there was a change in the combobox as a result of - * applying the locale - */ - private boolean setLocale(@Nullable Locale locale) { - boolean changed = !Objects.equal(mConfiguration.getLocale(), locale); - selectLocale(locale); - - return changed; - } - - // ---- Creating UI labels ---- - - /** - * Returns a suitable label to use to display the given activity - * - * @param fqcn the activity class to look up a label for - * @param brief if true, generate a brief label (suitable for a toolbar - * button), otherwise a fuller name (suitable for a menu item) - * @return the label - */ - public static String getActivityLabel(String fqcn, boolean brief) { - if (brief) { - String label = fqcn; - int packageIndex = label.lastIndexOf('.'); - if (packageIndex != -1) { - label = label.substring(packageIndex + 1); - } - int innerClass = label.lastIndexOf('$'); - if (innerClass != -1) { - label = label.substring(innerClass + 1); - } - - // Also strip out the "Activity" or "Fragment" common suffix - // if this is a long name - if (label.endsWith("Activity") && label.length() > 8 + 12) { // 12 chars + 8 in suffix - label = label.substring(0, label.length() - 8); - } else if (label.endsWith("Fragment") && label.length() > 8 + 12) { - label = label.substring(0, label.length() - 8); - } - - return label; - } - - return fqcn; - } - - /** - * Returns a suitable label to use to display the given theme - * - * @param theme the theme to produce a label for - * @param brief if true, generate a brief label (suitable for a toolbar - * button), otherwise a fuller name (suitable for a menu item) - * @return the label - */ - public static String getThemeLabel(String theme, boolean brief) { - theme = ResourceHelper.styleToTheme(theme); - - if (brief) { - int index = theme.lastIndexOf('.'); - if (index < theme.length() - 1) { - return theme.substring(index + 1); - } - } - return theme; - } - - /** - * Returns a suitable label to use to display the given rendering target - * - * @param target the target to produce a label for - * @param brief if true, generate a brief label (suitable for a toolbar - * button), otherwise a fuller name (suitable for a menu item) - * @return the label - */ - public static String getRenderingTargetLabel(IAndroidTarget target, boolean brief) { - if (target == null) { - return "<null>"; - } - - AndroidVersion version = target.getVersion(); - - if (brief) { - if (target.isPlatform()) { - return Integer.toString(version.getApiLevel()); - } else { - return target.getName() + ':' + Integer.toString(version.getApiLevel()); - } - } - - String label = String.format("API %1$d: %2$s", - version.getApiLevel(), - target.getShortClasspathName()); - - return label; - } - - /** - * Returns a suitable label to use to display the given device - * - * @param device the device to produce a label for - * @param brief if true, generate a brief label (suitable for a toolbar - * button), otherwise a fuller name (suitable for a menu item) - * @return the label - */ - public static String getDeviceLabel(@Nullable Device device, boolean brief) { - if (device == null) { - return ""; - } - String name = device.getName(); - - if (brief) { - // Produce a really brief summary of the device name, suitable for - // use in the narrow space available in the toolbar for example - int nexus = name.indexOf("Nexus"); //$NON-NLS-1$ - if (nexus != -1) { - int begin = name.indexOf('('); - if (begin != -1) { - begin++; - int end = name.indexOf(')', begin); - if (end != -1) { - return name.substring(begin, end).trim(); - } - } - } - } - - return name; - } - - /** - * Returns a suitable label to use to display the given locale - * - * @param chooser the chooser, if known - * @param locale the locale to look up a label for - * @param brief if true, generate a brief label (suitable for a toolbar - * button), otherwise a fuller name (suitable for a menu item) - * @return the label - */ - @Nullable - public static String getLocaleLabel( - @Nullable ConfigurationChooser chooser, - @Nullable Locale locale, - boolean brief) { - if (locale == null) { - return null; - } - - if (!locale.hasLanguage()) { - if (brief) { - // Just use the icon - return ""; - } - - boolean hasLocale = false; - ResourceRepository projectRes = chooser != null ? chooser.mClient.getProjectResources() - : null; - if (projectRes != null) { - hasLocale = projectRes.getLanguages().size() > 0; - } - - if (hasLocale) { - return "Other"; - } else { - return "Any"; - } - } - - String languageCode = locale.qualifier.getLanguage(); - String languageName = LocaleManager.getLanguageName(languageCode); - - if (!locale.hasRegion()) { - // TODO: Make the region string use "Other" instead of "Any" if - // there is more than one region for a given language - //if (regions.size() > 0) { - // return String.format("%1$s / Other", language); - //} else { - // return String.format("%1$s / Any", language); - //} - if (!brief && languageName != null) { - return String.format("%1$s (%2$s)", languageName, languageCode); - } else { - return languageCode; - } - } else { - String regionCode = locale.qualifier.getRegion(); - if (!brief && languageName != null) { - String regionName = LocaleManager.getRegionName(regionCode); - if (regionName != null) { - return String.format("%1$s (%2$s) in %3$s (%4$s)", languageName, languageCode, - regionName, regionCode); - } - return String.format("%1$s (%2$s) in %3$s", languageName, languageCode, - regionCode); - } - return String.format("%1$s / %2$s", languageCode, regionCode); - } - } - - // ---- Implements DevicesChangedListener ---- - - @Override - public void onDevicesChanged() { - final Sdk sdk = Sdk.getCurrent(); - if (sdk != null) { - mDevices = sdk.getDeviceManager().getDevices(DeviceManager.ALL_DEVICES); - } else { - mDevices = new ArrayList<Device>(); - } - } - - // ---- Reacting to UI changes ---- - - /** - * Called when the selection of the device combo changes. - */ - void onDeviceChange() { - // because changing the content of a combo triggers a change event, respect the - // mDisableUpdates flag - if (mDisableUpdates > 0) { - return; - } - - // Attempt to preserve the device state - String stateName = null; - Device prevDevice = mConfiguration.getDevice(); - State prevState = mConfiguration.getDeviceState(); - Device device = (Device) mDeviceCombo.getData(); - if (prevDevice != null && prevState != null && device != null) { - // get the previous config, so that we can look for a close match - FolderConfiguration oldConfig = DeviceConfigHelper.getFolderConfig(prevState); - if (oldConfig != null) { - stateName = ConfigurationMatcher.getClosestMatch(oldConfig, device.getAllStates()); - } - } - mConfiguration.setDevice(device, true); - State newState = Configuration.getState(device, stateName); - mConfiguration.setDeviceState(newState, true); - selectDeviceState(newState); - mConfiguration.syncFolderConfig(); - - // Notify - IFile file = mEditedFile; - boolean accepted = mClient.changed(CFG_DEVICE | CFG_DEVICE_STATE); - if (!accepted) { - mConfiguration.setDevice(prevDevice, true); - mConfiguration.setDeviceState(prevState, true); - mConfiguration.syncFolderConfig(); - selectDevice(prevDevice); - selectDeviceState(prevState); - return; - } else { - syncToVariations(CFG_DEVICE | CFG_DEVICE_STATE, file, mConfiguration, false, true); - } - - saveConstraints(); - } - - /** - * Synchronizes changes to the given attributes (indicated by the mask - * referencing the {@code CFG_} configuration attribute bit flags in - * {@link Configuration} to the layout variations of the given updated file. - * - * @param flags the attributes which were updated - * @param updatedFile the file which was updated - * @param base the base configuration to base the chooser off of - * @param includeSelf whether the updated file itself should be updated - * @param async whether the updates should be performed asynchronously - */ - public void syncToVariations( - final int flags, - final @NonNull IFile updatedFile, - final @NonNull Configuration base, - final boolean includeSelf, - boolean async) { - if (async) { - getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - doSyncToVariations(flags, updatedFile, includeSelf, base); - } - }); - } else { - doSyncToVariations(flags, updatedFile, includeSelf, base); - } - } - - private void doSyncToVariations(int flags, IFile updatedFile, boolean includeSelf, - Configuration base) { - // Synchronize the given changes to other configurations as well - List<IFile> files = AdtUtils.getResourceVariations(updatedFile, includeSelf); - for (IFile file : files) { - Configuration configuration = Configuration.create(base, file); - configuration.setTheme(base.getTheme()); - configuration.setActivity(base.getActivity()); - Collection<IEditorPart> editors = AdtUtils.findEditorsFor(file, false); - boolean found = false; - for (IEditorPart editor : editors) { - if (editor instanceof CommonXmlEditor) { - CommonXmlDelegate delegate = ((CommonXmlEditor) editor).getDelegate(); - if (delegate instanceof LayoutEditorDelegate) { - editor = ((LayoutEditorDelegate) delegate).getGraphicalEditor(); - } - } - if (editor instanceof GraphicalEditorPart) { - ConfigurationChooser chooser = - ((GraphicalEditorPart) editor).getConfigurationChooser(); - chooser.setConfiguration(configuration); - found = true; - } - } - if (!found) { - // Just update the file persistence - String description = configuration.toPersistentString(); - ConfigurationDescription.setDescription(file, description); - } - } - } - - /** - * Called when the device config selection changes. - */ - void onDeviceConfigChange() { - // because changing the content of a combo triggers a change event, respect the - // mDisableUpdates flag - if (mDisableUpdates > 0) { - return; - } - - State prev = mConfiguration.getDeviceState(); - State state = (State) mOrientationCombo.getData(); - mConfiguration.setDeviceState(state, false); - - if (mClient != null) { - boolean accepted = mClient.changed(CFG_DEVICE | CFG_DEVICE_STATE); - if (!accepted) { - mConfiguration.setDeviceState(prev, false); - selectDeviceState(prev); - return; - } - } - - saveConstraints(); - } - - /** - * Call back for language combo selection - */ - void onLocaleChange() { - // because mLocaleList triggers onLocaleChange at each modification, the filling - // of the combo with data will trigger notifications, and we don't want that. - if (mDisableUpdates > 0) { - return; - } - - Locale prev = mConfiguration.getLocale(); - Locale locale = (Locale) mLocaleCombo.getData(); - if (locale == null) { - locale = Locale.ANY; - } - mConfiguration.setLocale(locale, false); - - if (mClient != null) { - boolean accepted = mClient.changed(CFG_LOCALE); - if (!accepted) { - mConfiguration.setLocale(prev, false); - selectLocale(prev); - } - } - - // Store locale project-wide setting - mConfiguration.saveRenderState(); - } - - - void onThemeChange() { - if (mDisableUpdates > 0) { - return; - } - - String prev = mConfiguration.getTheme(); - mConfiguration.setTheme((String) mThemeCombo.getData()); - - if (mClient != null) { - boolean accepted = mClient.changed(CFG_THEME); - if (!accepted) { - mConfiguration.setTheme(prev); - selectTheme(prev); - return; - } else { - syncToVariations(CFG_DEVICE|CFG_DEVICE_STATE, mEditedFile, mConfiguration, - false, true); - } - } - - saveConstraints(); - } - - void notifyFolderConfigChanged() { - if (mDisableUpdates > 0 || mClient == null) { - return; - } - - if (mClient.changed(CFG_FOLDER)) { - saveConstraints(); - } - } - - void onSelectActivity() { - if (mDisableUpdates > 0) { - return; - } - - String activity = (String) mActivityCombo.getData(); - mConfiguration.setActivity(activity); - - if (activity == null) { - return; - } - - // See if there is a default theme assigned to this activity, and if so, use it - ManifestInfo manifest = ManifestInfo.get(mEditedFile.getProject()); - String preferred = null; - ActivityAttributes attributes = manifest.getActivityAttributes(activity); - if (attributes != null) { - preferred = attributes.getTheme(); - } - if (preferred != null && !Objects.equal(preferred, mConfiguration.getTheme())) { - // Yes, switch to it - selectTheme(preferred); - onThemeChange(); - } - - // Persist in XML - if (mClient != null) { - mClient.setActivity(activity); - } - - saveConstraints(); - } - - /** - * Call back for api level combo selection - */ - void onRenderingTargetChange() { - // because mApiCombo triggers onApiLevelChange at each modification, the filling - // of the combo with data will trigger notifications, and we don't want that. - if (mDisableUpdates > 0) { - return; - } - - IAndroidTarget prevTarget = mConfiguration.getTarget(); - String prevTheme = mConfiguration.getTheme(); - - int changeFlags = 0; - - // tell the listener a new rendering target is being set. Need to do this before updating - // mRenderingTarget. - if (prevTarget != null) { - changeFlags |= CFG_TARGET; - mClient.aboutToChange(changeFlags); - } - - IAndroidTarget target = (IAndroidTarget) mTargetCombo.getData(); - mConfiguration.setTarget(target, true); - - // force a theme update to reflect the new rendering target. - // This must be done after computeCurrentConfig since it'll depend on the currentConfig - // to figure out the theme list. - String oldTheme = mConfiguration.getTheme(); - updateThemes(); - // updateThemes may change the theme (based on theme availability in the new rendering - // target) so mark theme change if necessary - if (!Objects.equal(oldTheme, mConfiguration.getTheme())) { - changeFlags |= CFG_THEME; - } - - if (target != null) { - changeFlags |= CFG_TARGET; - changeFlags |= CFG_FOLDER; // In case we added a -vNN qualifier - } - - // Store project-wide render-target setting - mConfiguration.saveRenderState(); - - mConfiguration.syncFolderConfig(); - - if (mClient != null) { - boolean accepted = mClient.changed(changeFlags); - if (!accepted) { - mConfiguration.setTarget(prevTarget, true); - mConfiguration.setTheme(prevTheme); - mConfiguration.syncFolderConfig(); - selectTheme(prevTheme); - selectTarget(prevTarget); - } - } - } - - /** - * Syncs this configuration to the project wide locale and render target settings. The - * locale may ignore the project-wide setting if it is a locale-specific - * configuration. - * - * @return true if one or both of the toggles were changed, false if there were no - * changes - */ - public boolean syncRenderState() { - if (mConfiguration.getEditedConfig() == null) { - // Startup; ignore - return false; - } - - boolean renderTargetChanged = false; - - // When a page is re-activated, force the toggles to reflect the current project - // state - - Pair<Locale, IAndroidTarget> pair = Configuration.loadRenderState(this); - - int changeFlags = 0; - // Only sync the locale if this layout is not already a locale-specific layout! - if (pair != null && !mConfiguration.isLocaleSpecificLayout()) { - Locale locale = pair.getFirst(); - if (locale != null) { - boolean localeChanged = setLocale(locale); - if (localeChanged) { - changeFlags |= CFG_LOCALE; - } - } else { - locale = Locale.ANY; - } - mConfiguration.setLocale(locale, true); - } - - // Sync render target - IAndroidTarget configurationTarget = mConfiguration.getTarget(); - IAndroidTarget target = pair != null ? pair.getSecond() : configurationTarget; - if (target != null && configurationTarget != target) { - if (mClient != null && configurationTarget != null) { - changeFlags |= CFG_TARGET; - mClient.aboutToChange(changeFlags); - } - - mConfiguration.setTarget(target, true); - selectTarget(target); - renderTargetChanged = true; - } - - // Neither locale nor render target changed: nothing to do - if (changeFlags == 0) { - return false; - } - - // Update the locale and/or the render target. This code contains a logical - // merge of the onRenderingTargetChange() and onLocaleChange() methods, combined - // such that we don't duplicate work. - - // Compute the new configuration; we want to do this both for locale changes - // and for render targets. - mConfiguration.syncFolderConfig(); - changeFlags |= CFG_FOLDER; // in case we added/remove a -v<NN> qualifier - - if (renderTargetChanged) { - // force a theme update to reflect the new rendering target. - // This must be done after computeCurrentConfig since it'll depend on the currentConfig - // to figure out the theme list. - updateThemes(); - } - - if (mClient != null) { - mClient.changed(changeFlags); - } - - return true; - } - - // ---- Populate data structures with themes, locales, etc ---- - - /** - * Updates the internal list of themes. - */ - private void updateThemes() { - if (mClient == null) { - return; // can't do anything without it. - } - - ResourceRepository frameworkRes = mClient.getFrameworkResources( - mConfiguration.getTarget()); - - mDisableUpdates++; - - try { - if (mEditedFile != null) { - String theme = mConfiguration.getTheme(); - if (theme == null || theme.isEmpty() || mClient.getIncludedWithin() != null) { - mConfiguration.setTheme(null); - mConfiguration.computePreferredTheme(); - } - assert mConfiguration.getTheme() != null; - } - - mThemeList.clear(); - - ArrayList<String> themes = new ArrayList<String>(); - ResourceRepository projectRes = mClient.getProjectResources(); - // in cases where the opened file is not linked to a project, this could be null. - if (projectRes != null) { - // get the configured resources for the project - Map<ResourceType, Map<String, ResourceValue>> configuredProjectRes = - mClient.getConfiguredProjectResources(); - - if (configuredProjectRes != null) { - // get the styles. - Map<String, ResourceValue> styleMap = configuredProjectRes.get( - ResourceType.STYLE); - - if (styleMap != null) { - // collect the themes out of all the styles, ie styles that extend, - // directly or indirectly a platform theme. - for (ResourceValue value : styleMap.values()) { - if (isTheme(value, styleMap, null)) { - String theme = value.getName(); - themes.add(theme); - } - } - - Collections.sort(themes); - - for (String theme : themes) { - if (!theme.startsWith(PREFIX_RESOURCE_REF)) { - theme = STYLE_RESOURCE_PREFIX + theme; - } - mThemeList.add(theme); - } - } - } - themes.clear(); - } - - // get the themes, and languages from the Framework. - if (frameworkRes != null) { - // get the configured resources for the framework - Map<ResourceType, Map<String, ResourceValue>> frameworResources = - frameworkRes.getConfiguredResources(mConfiguration.getFullConfig()); - - if (frameworResources != null) { - // get the styles. - Map<String, ResourceValue> styles = frameworResources.get(ResourceType.STYLE); - - // collect the themes out of all the styles. - for (ResourceValue value : styles.values()) { - String name = value.getName(); - if (name.startsWith("Theme.") || name.equals("Theme")) { //$NON-NLS-1$ //$NON-NLS-2$ - themes.add(value.getName()); - } - } - - // sort them and add them to the combo - Collections.sort(themes); - - for (String theme : themes) { - if (!theme.startsWith(PREFIX_RESOURCE_REF)) { - theme = ANDROID_STYLE_RESOURCE_PREFIX + theme; - } - mThemeList.add(theme); - } - - themes.clear(); - } - } - - // Migration: In the past we didn't store the style prefix in the settings; - // this meant we might lose track of whether the theme is a project style - // or a framework style. For now we need to migrate. Search through the - // theme list until we have a match - String theme = mConfiguration.getTheme(); - if (theme != null && !theme.startsWith(PREFIX_RESOURCE_REF)) { - String projectStyle = STYLE_RESOURCE_PREFIX + theme; - String frameworkStyle = ANDROID_STYLE_RESOURCE_PREFIX + theme; - for (String t : mThemeList) { - if (t.equals(projectStyle)) { - mConfiguration.setTheme(projectStyle); - break; - } else if (t.equals(frameworkStyle)) { - mConfiguration.setTheme(frameworkStyle); - break; - } - } - if (!theme.startsWith(PREFIX_RESOURCE_REF)) { - // Arbitrary guess - if (theme.startsWith("Theme.")) { - theme = ANDROID_STYLE_RESOURCE_PREFIX + theme; - } else { - theme = STYLE_RESOURCE_PREFIX + theme; - } - } - } - - // TODO: Handle the case where you have a theme persisted that isn't available?? - // We could look up mConfiguration.theme and make sure it appears in the list! And if - // not, picking one. - selectTheme(mConfiguration.getTheme()); - } finally { - mDisableUpdates--; - } - } - - private void updateActivity() { - if (mEditedFile != null) { - String preferred = getPreferredActivity(mEditedFile); - selectActivity(preferred); - } - } - - /** - * Updates the locale combo. - * This must be called from the UI thread. - */ - public void updateLocales() { - if (mClient == null) { - return; // can't do anything w/o it. - } - - mDisableUpdates++; - - try { - mLocaleList.clear(); - - SortedSet<String> languages = null; - - // get the languages from the project. - ResourceRepository projectRes = mClient.getProjectResources(); - - // in cases where the opened file is not linked to a project, this could be null. - if (projectRes != null) { - // now get the languages from the project. - languages = projectRes.getLanguages(); - - for (String language : languages) { - // find the matching regions and add them - SortedSet<String> regions = projectRes.getRegions(language); - for (String region : regions) { - LocaleQualifier locale = LocaleQualifier.getQualifier(language + "-r" + region); - if (locale != null) { - mLocaleList.add(Locale.create(locale)); - } - } - - // now the entry for the other regions the language alone - // create a region qualifier that will never be matched by qualified resources. - LocaleQualifier locale = new LocaleQualifier(language); - mLocaleList.add(Locale.create(locale)); - } - } - - // create language/region qualifier that will never be matched by qualified resources. - mLocaleList.add(Locale.ANY); - - Locale locale = mConfiguration.getLocale(); - setLocale(locale); - } finally { - mDisableUpdates--; - } - } - - @Nullable - private String getPreferredActivity(@NonNull IFile file) { - // Store/restore the activity context in the config state to help with - // performance if for some reason we can't write it into the XML file and to - // avoid having to open the model below - if (mConfiguration.getActivity() != null) { - return mConfiguration.getActivity(); - } - - IProject project = file.getProject(); - - // Look up from XML file - Document document = DomUtilities.getDocument(file); - if (document != null) { - Element element = document.getDocumentElement(); - if (element != null) { - String activity = element.getAttributeNS(TOOLS_URI, ATTR_CONTEXT); - if (activity != null && !activity.isEmpty()) { - if (activity.startsWith(".") || activity.indexOf('.') == -1) { //$NON-NLS-1$ - ManifestInfo manifest = ManifestInfo.get(project); - String pkg = manifest.getPackage(); - if (!pkg.isEmpty()) { - if (activity.startsWith(".")) { //$NON-NLS-1$ - activity = pkg + activity; - } else { - activity = activity + '.' + pkg; - } - } - } - - mConfiguration.setActivity(activity); - saveConstraints(); - return activity; - } - } - } - - // No, not available there: try to infer it from the code index - String includedIn = null; - Reference includedWithin = mClient.getIncludedWithin(); - if (mClient != null && includedWithin != null) { - includedIn = includedWithin.getName(); - } - - ManifestInfo manifest = ManifestInfo.get(project); - String pkg = manifest.getPackage(); - String layoutName = ResourceHelper.getLayoutName(mEditedFile); - - // If we are rendering a layout in included context, pick the theme - // from the outer layout instead - if (includedIn != null) { - layoutName = includedIn; - } - - String activity = ManifestInfo.guessActivity(project, layoutName, pkg); - - if (activity == null) { - List<String> activities = ManifestInfo.getProjectActivities(project); - if (activities.size() == 1) { - activity = activities.get(0); - } - } - - if (activity != null) { - mConfiguration.setActivity(activity); - saveConstraints(); - return activity; - } - - // TODO: Do anything else, such as pick the first activity found? - // Or just leave some default label instead? - // Also, figure out what to store in the mState so I don't keep trying - - return null; - } - - /** - * Returns whether the given <var>style</var> is a theme. - * This is done by making sure the parent is a theme. - * @param value the style to check - * @param styleMap the map of styles for the current project. Key is the style name. - * @param seen the map of styles we have already processed (or null if not yet - * initialized). Only the keys are significant (since there is no IdentityHashSet). - * @return True if the given <var>style</var> is a theme. - */ - private static boolean isTheme(ResourceValue value, Map<String, ResourceValue> styleMap, - IdentityHashMap<ResourceValue, Boolean> seen) { - if (value instanceof StyleResourceValue) { - StyleResourceValue style = (StyleResourceValue)value; - - boolean frameworkStyle = false; - String parentStyle = style.getParentStyle(); - if (parentStyle == null) { - // if there is no specified parent style we look an implied one. - // For instance 'Theme.light' is implied child style of 'Theme', - // and 'Theme.light.fullscreen' is implied child style of 'Theme.light' - String name = style.getName(); - int index = name.lastIndexOf('.'); - if (index != -1) { - parentStyle = name.substring(0, index); - } - } else { - // remove the useless @ if it's there - if (parentStyle.startsWith("@")) { - parentStyle = parentStyle.substring(1); - } - - // check for framework identifier. - if (parentStyle.startsWith(ANDROID_NS_NAME_PREFIX)) { - frameworkStyle = true; - parentStyle = parentStyle.substring(ANDROID_NS_NAME_PREFIX.length()); - } - - // at this point we could have the format style/<name>. we want only the name - if (parentStyle.startsWith("style/")) { - parentStyle = parentStyle.substring("style/".length()); - } - } - - if (parentStyle != null) { - if (frameworkStyle) { - // if the parent is a framework style, it has to be 'Theme' or 'Theme.*' - return parentStyle.equals("Theme") || parentStyle.startsWith("Theme."); - } else { - // if it's a project style, we check this is a theme. - ResourceValue parentValue = styleMap.get(parentStyle); - - // also prevent stack overflow in case the dev mistakenly declared - // the parent of the style as the style itself. - if (parentValue != null && !parentValue.equals(value)) { - if (seen == null) { - seen = new IdentityHashMap<ResourceValue, Boolean>(); - seen.put(value, Boolean.TRUE); - } else if (seen.containsKey(parentValue)) { - return false; - } - seen.put(parentValue, Boolean.TRUE); - return isTheme(parentValue, styleMap, seen); - } - } - } - } - - return false; - } - - /** - * Returns true if this configuration chooser represents the best match for - * the given file - * - * @param file the file to test - * @param config the config to test - * @return true if the given config is the best match for the given file - */ - public boolean isBestMatchFor(IFile file, FolderConfiguration config) { - ResourceFile match = mResources.getMatchingFile(mEditedFile.getName(), - ResourceType.LAYOUT, config); - if (match != null) { - return match.getFile().equals(mEditedFile); - } - - return false; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationClient.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationClient.java deleted file mode 100644 index 3df2feda3..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationClient.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.ResourceRepository; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutCanvas; -import com.android.resources.ResourceType; -import com.android.sdklib.IAndroidTarget; - -import java.util.Map; - -/** - * Interface implemented by clients who embed a {@link ConfigurationChooser}. - */ -public interface ConfigurationClient { - /** - * The configuration is about to be changed. - * - * @param flags details about what changed; consult the {@code CFG_} flags - * in {@link Configuration} such as - * {@link Configuration#CFG_DEVICE}, - * {@link Configuration#CFG_LOCALE}, etc. - */ - void aboutToChange(int flags); - - /** - * The configuration has changed. If the client returns false, it means that - * the change was rejected. This typically means that changing the - * configuration in this particular way makes a configuration which has a - * better file match than the current client's file, so it will open that - * file to edit the new configuration -- and the current configuration - * should go back to editing the state prior to this change. - * - * @param flags details about what changed; consult the {@code CFG_} flags - * such as {@link Configuration#CFG_DEVICE}, - * {@link Configuration#CFG_LOCALE}, etc. - * @return true if the change was accepted, false if it was rejected. - */ - boolean changed(int flags); - - /** - * Compute the project resources - * - * @return the project resources as a {@link ResourceRepository} - */ - @Nullable - ResourceRepository getProjectResources(); - - /** - * Compute the framework resources - * - * @return the project resources as a {@link ResourceRepository} - */ - @Nullable - ResourceRepository getFrameworkResources(); - - /** - * Compute the framework resources for the given Android API target - * - * @param target the target to look up framework resources for - * @return the project resources as a {@link ResourceRepository} - */ - @Nullable - ResourceRepository getFrameworkResources(@Nullable IAndroidTarget target); - - /** - * Returns the configured project resources for the current file and - * configuration - * - * @return resource type maps to names to resource values - */ - @NonNull - Map<ResourceType, Map<String, ResourceValue>> getConfiguredProjectResources(); - - /** - * Returns the configured framework resources for the current file and - * configuration - * - * @return resource type maps to names to resource values - */ - @NonNull - Map<ResourceType, Map<String, ResourceValue>> getConfiguredFrameworkResources(); - - /** - * If the current layout is an included layout rendered within an outer layout, - * returns the outer layout. - * - * @return the outer including layout, or null - */ - @Nullable - Reference getIncludedWithin(); - - /** - * Called when the "Create" button is clicked. - */ - void createConfigFile(); - - /** - * Called when an associated activity is picked - * - * @param fqcn the fully qualified class name for the associated activity context - */ - void setActivity(@NonNull String fqcn); - - /** - * Returns the associated layout canvas, if any - * - * @return the canvas, if any - */ - @Nullable - LayoutCanvas getCanvas(); -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationDescription.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationDescription.java deleted file mode 100644 index 956ac1839..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationDescription.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_THEME; -import static com.android.SdkConstants.PREFIX_RESOURCE_REF; -import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.resources.ResourceRepository; -import com.android.ide.common.resources.configuration.DeviceConfigHelper; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.LocaleQualifier; -import com.android.ide.common.resources.configuration.ScreenSizeQualifier; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo.ActivityAttributes; -import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.resources.NightMode; -import com.android.resources.ResourceFolderType; -import com.android.resources.ScreenSize; -import com.android.resources.UiMode; -import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.devices.Device; -import com.android.sdklib.devices.State; -import com.google.common.base.Splitter; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.QualifiedName; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import java.util.Collection; -import java.util.List; - -/** A description of a configuration, used for persistence */ -public class ConfigurationDescription { - private static final String TAG_PREVIEWS = "previews"; //$NON-NLS-1$ - private static final String TAG_PREVIEW = "preview"; //$NON-NLS-1$ - private static final String ATTR_TARGET = "target"; //$NON-NLS-1$ - private static final String ATTR_CONFIG = "config"; //$NON-NLS-1$ - private static final String ATTR_LOCALE = "locale"; //$NON-NLS-1$ - private static final String ATTR_ACTIVITY = "activity"; //$NON-NLS-1$ - private static final String ATTR_DEVICE = "device"; //$NON-NLS-1$ - private static final String ATTR_STATE = "devicestate"; //$NON-NLS-1$ - private static final String ATTR_UIMODE = "ui"; //$NON-NLS-1$ - private static final String ATTR_NIGHTMODE = "night"; //$NON-NLS-1$ - private final static String SEP_LOCALE = "-"; //$NON-NLS-1$ - - /** - * Settings name for file-specific configuration preferences, such as which theme or - * device to render the current layout with - */ - public final static QualifiedName NAME_CONFIG_STATE = - new QualifiedName(AdtPlugin.PLUGIN_ID, "state");//$NON-NLS-1$ - - /** The project corresponding to this configuration's description */ - public final IProject project; - - /** The display name */ - public String displayName; - - /** The theme */ - public String theme; - - /** The target */ - public IAndroidTarget target; - - /** The display name */ - public FolderConfiguration folder; - - /** The locale */ - public Locale locale = Locale.ANY; - - /** The device */ - public Device device; - - /** The device state */ - public State state; - - /** The activity */ - public String activity; - - /** UI mode */ - @NonNull - public UiMode uiMode = UiMode.NORMAL; - - /** Night mode */ - @NonNull - public NightMode nightMode = NightMode.NOTNIGHT; - - private ConfigurationDescription(@Nullable IProject project) { - this.project = project; - } - - /** - * Returns the persistent configuration description from the given file - * - * @param file the file to look up a description from - * @return the description or null if never written - */ - @Nullable - public static String getDescription(@NonNull IFile file) { - return AdtPlugin.getFileProperty(file, NAME_CONFIG_STATE); - } - - /** - * Sets the persistent configuration description data for the given file - * - * @param file the file to associate the description with - * @param description the description - */ - public static void setDescription(@NonNull IFile file, @NonNull String description) { - AdtPlugin.setFileProperty(file, NAME_CONFIG_STATE, description); - } - - /** - * Creates a description from a given configuration - * - * @param project the project for this configuration's description - * @param configuration the configuration to describe - * @return a new configuration - */ - public static ConfigurationDescription fromConfiguration( - @Nullable IProject project, - @NonNull Configuration configuration) { - ConfigurationDescription description = new ConfigurationDescription(project); - description.displayName = configuration.getDisplayName(); - description.theme = configuration.getTheme(); - description.target = configuration.getTarget(); - description.folder = new FolderConfiguration(); - description.folder.set(configuration.getFullConfig()); - description.locale = configuration.getLocale(); - description.device = configuration.getDevice(); - description.state = configuration.getDeviceState(); - description.activity = configuration.getActivity(); - return description; - } - - /** - * Initializes a string previously created with - * {@link #toXml(Document)} - * - * @param project the project for this configuration's description - * @param element the element to read back from - * @param deviceList list of available devices - * @return true if the configuration was initialized - */ - @Nullable - public static ConfigurationDescription fromXml( - @Nullable IProject project, - @NonNull Element element, - @NonNull Collection<Device> deviceList) { - ConfigurationDescription description = new ConfigurationDescription(project); - - if (!TAG_PREVIEW.equals(element.getTagName())) { - return null; - } - - String displayName = element.getAttribute(ATTR_NAME); - if (!displayName.isEmpty()) { - description.displayName = displayName; - } - - String config = element.getAttribute(ATTR_CONFIG); - Iterable<String> segments = Splitter.on('-').split(config); - description.folder = FolderConfiguration.getConfig(segments); - - String theme = element.getAttribute(ATTR_THEME); - if (!theme.isEmpty()) { - description.theme = theme; - } - - String targetId = element.getAttribute(ATTR_TARGET); - if (!targetId.isEmpty()) { - IAndroidTarget target = Configuration.stringToTarget(targetId); - description.target = target; - } - - String localeString = element.getAttribute(ATTR_LOCALE); - if (!localeString.isEmpty()) { - // Load locale. Note that this can get overwritten by the - // project-wide settings read below. - String locales[] = localeString.split(SEP_LOCALE); - if (locales[0].length() > 0 && !LocaleQualifier.FAKE_VALUE.equals(locales[0])) { - String language = locales[0]; - if (locales.length >= 2 && locales[1].length() > 0 && !LocaleQualifier.FAKE_VALUE.equals(locales[1])) { - description.locale = Locale.create(LocaleQualifier.getQualifier(language + "-r" + locales[1])); - } else { - description.locale = Locale.create(new LocaleQualifier(language)); - } - } else { - description.locale = Locale.ANY; - } - - - } - - String activity = element.getAttribute(ATTR_ACTIVITY); - if (activity.isEmpty()) { - activity = null; - } - - String deviceString = element.getAttribute(ATTR_DEVICE); - if (!deviceString.isEmpty()) { - for (Device d : deviceList) { - if (d.getName().equals(deviceString)) { - description.device = d; - String stateName = element.getAttribute(ATTR_STATE); - if (stateName.isEmpty() || stateName.equals("null")) { - description.state = Configuration.getState(d, stateName); - } else if (d.getAllStates().size() > 0) { - description.state = d.getAllStates().get(0); - } - break; - } - } - } - - String uiModeString = element.getAttribute(ATTR_UIMODE); - if (!uiModeString.isEmpty()) { - description.uiMode = UiMode.getEnum(uiModeString); - if (description.uiMode == null) { - description.uiMode = UiMode.NORMAL; - } - } - - String nightModeString = element.getAttribute(ATTR_NIGHTMODE); - if (!nightModeString.isEmpty()) { - description.nightMode = NightMode.getEnum(nightModeString); - if (description.nightMode == null) { - description.nightMode = NightMode.NOTNIGHT; - } - } - - - // Should I really be storing the FULL configuration? Might be trouble if - // you bring a different device - - return description; - } - - /** - * Write this description into the given document as a new element. - * - * @param document the document to add the description to - * @return the newly inserted element - */ - @NonNull - public Element toXml(Document document) { - Element element = document.createElement(TAG_PREVIEW); - - element.setAttribute(ATTR_NAME, displayName); - FolderConfiguration fullConfig = folder; - String folderName = fullConfig.getFolderName(ResourceFolderType.LAYOUT); - element.setAttribute(ATTR_CONFIG, folderName); - if (theme != null) { - element.setAttribute(ATTR_THEME, theme); - } - if (target != null) { - element.setAttribute(ATTR_TARGET, Configuration.targetToString(target)); - } - - if (locale != null && (locale.hasLanguage() || locale.hasRegion())) { - String value; - if (locale.hasRegion()) { - value = locale.qualifier.getLanguage() + SEP_LOCALE + locale.qualifier.getRegion(); - } else { - value = locale.qualifier.getLanguage(); - } - element.setAttribute(ATTR_LOCALE, value); - } - - if (device != null) { - element.setAttribute(ATTR_DEVICE, device.getName()); - if (state != null) { - element.setAttribute(ATTR_STATE, state.getName()); - } - } - - if (activity != null) { - element.setAttribute(ATTR_ACTIVITY, activity); - } - - if (uiMode != null && uiMode != UiMode.NORMAL) { - element.setAttribute(ATTR_UIMODE, uiMode.getResourceValue()); - } - - if (nightMode != null && nightMode != NightMode.NOTNIGHT) { - element.setAttribute(ATTR_NIGHTMODE, nightMode.getResourceValue()); - } - - Element parent = document.getDocumentElement(); - if (parent == null) { - parent = document.createElement(TAG_PREVIEWS); - document.appendChild(parent); - } - parent.appendChild(element); - - return element; - } - - /** Returns the preferred theme, or null */ - @Nullable - String computePreferredTheme() { - if (project == null) { - return "Theme"; - } - ManifestInfo manifest = ManifestInfo.get(project); - - // Look up the screen size for the current state - ScreenSize screenSize = null; - if (device != null) { - List<State> states = device.getAllStates(); - for (State s : states) { - FolderConfiguration folderConfig = DeviceConfigHelper.getFolderConfig(s); - if (folderConfig != null) { - ScreenSizeQualifier qualifier = folderConfig.getScreenSizeQualifier(); - screenSize = qualifier.getValue(); - break; - } - } - } - - // Look up the default/fallback theme to use for this project (which - // depends on the screen size when no particular theme is specified - // in the manifest) - String defaultTheme = manifest.getDefaultTheme(target, screenSize); - - String preferred = defaultTheme; - if (theme == null) { - // If we are rendering a layout in included context, pick the theme - // from the outer layout instead - - if (activity != null) { - ActivityAttributes attributes = manifest.getActivityAttributes(activity); - if (attributes != null) { - preferred = attributes.getTheme(); - } - } - if (preferred == null) { - preferred = defaultTheme; - } - theme = preferred; - } - - return preferred; - } - - private void checkThemePrefix() { - if (theme != null && !theme.startsWith(PREFIX_RESOURCE_REF)) { - if (theme.isEmpty()) { - computePreferredTheme(); - return; - } - - if (target != null) { - Sdk sdk = Sdk.getCurrent(); - if (sdk != null) { - AndroidTargetData data = sdk.getTargetData(target); - - if (data != null) { - ResourceRepository resources = data.getFrameworkResources(); - if (resources != null - && resources.hasResourceItem(ANDROID_STYLE_RESOURCE_PREFIX + theme)) { - theme = ANDROID_STYLE_RESOURCE_PREFIX + theme; - return; - } - } - } - } - - theme = STYLE_RESOURCE_PREFIX + theme; - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationMatcher.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationMatcher.java deleted file mode 100644 index 9724d4015..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationMatcher.java +++ /dev/null @@ -1,843 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.resources.ResourceFile; -import com.android.ide.common.resources.configuration.DensityQualifier; -import com.android.ide.common.resources.configuration.DeviceConfigHelper; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.LocaleQualifier; -import com.android.ide.common.resources.configuration.NightModeQualifier; -import com.android.ide.common.resources.configuration.ResourceQualifier; -import com.android.ide.common.resources.configuration.ScreenOrientationQualifier; -import com.android.ide.common.resources.configuration.ScreenSizeQualifier; -import com.android.ide.common.resources.configuration.UiModeQualifier; -import com.android.ide.common.resources.configuration.VersionQualifier; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -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.Sdk; -import com.android.ide.eclipse.adt.io.IFileWrapper; -import com.android.resources.Density; -import com.android.resources.NightMode; -import com.android.resources.ResourceType; -import com.android.resources.ScreenOrientation; -import com.android.resources.ScreenSize; -import com.android.resources.UiMode; -import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.devices.Device; -import com.android.sdklib.devices.State; -import com.android.sdklib.repository.PkgProps; -import com.android.utils.Pair; -import com.android.utils.SparseIntArray; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.ui.IEditorPart; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * Produces matches for configurations - * <p> - * See algorithm described here: - * http://developer.android.com/guide/topics/resources/providing-resources.html - */ -public class ConfigurationMatcher { - private static final boolean PREFER_RECENT_RENDER_TARGETS = true; - - private final ConfigurationChooser mConfigChooser; - private final Configuration mConfiguration; - private final IFile mEditedFile; - private final ProjectResources mResources; - private final boolean mUpdateUi; - - ConfigurationMatcher(ConfigurationChooser chooser) { - this(chooser, chooser.getConfiguration(), chooser.getEditedFile(), - chooser.getResources(), true); - } - - ConfigurationMatcher( - @NonNull ConfigurationChooser chooser, - @NonNull Configuration configuration, - @Nullable IFile editedFile, - @Nullable ProjectResources resources, - boolean updateUi) { - mConfigChooser = chooser; - mConfiguration = configuration; - mEditedFile = editedFile; - mResources = resources; - mUpdateUi = updateUi; - } - - // ---- Finding matching configurations ---- - - private static class ConfigBundle { - private final FolderConfiguration config; - private int localeIndex; - private int dockModeIndex; - private int nightModeIndex; - - private ConfigBundle() { - config = new FolderConfiguration(); - } - - private ConfigBundle(ConfigBundle bundle) { - config = new FolderConfiguration(); - config.set(bundle.config); - localeIndex = bundle.localeIndex; - dockModeIndex = bundle.dockModeIndex; - nightModeIndex = bundle.nightModeIndex; - } - } - - private static class ConfigMatch { - final FolderConfiguration testConfig; - final Device device; - final State state; - final ConfigBundle bundle; - - public ConfigMatch(@NonNull FolderConfiguration testConfig, @NonNull Device device, - @NonNull State state, @NonNull ConfigBundle bundle) { - this.testConfig = testConfig; - this.device = device; - this.state = state; - this.bundle = bundle; - } - - @Override - public String toString() { - return device.getName() + " - " + state.getName(); - } - } - - /** - * 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. - */ - public boolean isCurrentFileBestMatchFor(FolderConfiguration config) { - ResourceFile match = mResources.getMatchingFile(mEditedFile.getName(), - ResourceType.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; - } - - /** - * Adapts the current device/config selection so that it's compatible with - * the configuration. - * <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(boolean)} - */ - void adaptConfigSelection(boolean needBestMatch) { - // check the device config (ie sans locale) - boolean needConfigChange = true; // if still true, we need to find another config. - boolean currentConfigIsCompatible = false; - State selectedState = mConfiguration.getDeviceState(); - FolderConfiguration editedConfig = mConfiguration.getEditedConfig(); - if (selectedState != null) { - FolderConfiguration currentConfig = DeviceConfigHelper.getFolderConfig(selectedState); - if (currentConfig != null && editedConfig.isMatchFor(currentConfig)) { - currentConfigIsCompatible = true; // current config is compatible - if (!needBestMatch || isCurrentFileBestMatchFor(currentConfig)) { - needConfigChange = false; - } - } - } - - if (needConfigChange) { - List<Locale> localeList = mConfigChooser.getLocaleList(); - - // if the current state/locale isn't a correct match, then - // look for another state/locale in the same device. - FolderConfiguration testConfig = new FolderConfiguration(); - - // first look in the current device. - State matchState = null; - int localeIndex = -1; - Device device = mConfiguration.getDevice(); - if (device != null) { - mainloop: for (State state : device.getAllStates()) { - testConfig.set(DeviceConfigHelper.getFolderConfig(state)); - - // loop on the locales. - for (int i = 0 ; i < localeList.size() ; i++) { - Locale locale = localeList.get(i); - - // update the test config with the locale qualifiers - testConfig.setLocaleQualifier(locale.qualifier); - - - if (editedConfig.isMatchFor(testConfig) && - isCurrentFileBestMatchFor(testConfig)) { - matchState = state; - localeIndex = i; - break mainloop; - } - } - } - } - - if (matchState != null) { - mConfiguration.setDeviceState(matchState, true); - Locale locale = localeList.get(localeIndex); - mConfiguration.setLocale(locale, true); - if (mUpdateUi) { - mConfigChooser.selectDeviceState(matchState); - mConfigChooser.selectLocale(locale); - } - mConfiguration.syncFolderConfig(); - } else { - // no match in current device with any state/locale - // attempt to find another device that can display this - // particular state. - findAndSetCompatibleConfig(currentConfigIsCompatible); - } - } - } - - /** - * Finds a device/config that can display a configuration. - * <p> - * Once found the device and config combos are set to the config. - * <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. - */ - void findAndSetCompatibleConfig(boolean favorCurrentConfig) { - List<Locale> localeList = mConfigChooser.getLocaleList(); - Collection<Device> devices = mConfigChooser.getDevices(); - FolderConfiguration editedConfig = mConfiguration.getEditedConfig(); - FolderConfiguration currentConfig = mConfiguration.getFullConfig(); - - // list of compatible device/state/locale - List<ConfigMatch> anyMatches = new ArrayList<ConfigMatch>(); - - // list of actual best match (ie the file is a best match for the - // device/state) - List<ConfigMatch> bestMatches = new ArrayList<ConfigMatch>(); - - // get a locale that match the host locale roughly (may not be exact match on the region.) - int localeHostMatch = getLocaleMatch(); - - // build a list of combinations of non standard qualifiers to add to each device's - // qualifier set when testing for a match. - // These qualifiers are: locale, night-mode, car dock. - List<ConfigBundle> configBundles = new ArrayList<ConfigBundle>(200); - - // If the edited file has locales, then we have to select a matching locale from - // the list. - // However, if it doesn't, we don't randomly take the first locale, we take one - // matching the current host locale (making sure it actually exist in the project) - int start, max; - if (editedConfig.getLocaleQualifier() != null || localeHostMatch == -1) { - // add all the locales - start = 0; - max = localeList.size(); - } else { - // only add the locale host match - start = localeHostMatch; - max = localeHostMatch + 1; // test is < - } - - for (int i = start ; i < max ; i++) { - Locale l = localeList.get(i); - - ConfigBundle bundle = new ConfigBundle(); - bundle.config.setLocaleQualifier(l.qualifier); - - bundle.localeIndex = i; - configBundles.add(bundle); - } - - // add the dock mode to the bundle combinations. - addDockModeToBundles(configBundles); - - // add the night mode to the bundle combinations. - addNightModeToBundles(configBundles); - - addRenderTargetToBundles(configBundles); - - for (Device device : devices) { - for (State state : device.getAllStates()) { - - // loop on the list of config bundles to create full - // configurations. - FolderConfiguration stateConfig = DeviceConfigHelper.getFolderConfig(state); - for (ConfigBundle bundle : configBundles) { - // create a new config with device config - FolderConfiguration testConfig = new FolderConfiguration(); - testConfig.set(stateConfig); - - // add on top of it, the extra qualifiers from the bundle - testConfig.add(bundle.config); - - if (editedConfig.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. - anyMatches.add(new ConfigMatch(testConfig, device, state, bundle)); - - if (isCurrentFileBestMatchFor(testConfig)) { - // this is what we want. - bestMatches.add(new ConfigMatch(testConfig, device, state, bundle)); - } - } - } - } - } - - if (bestMatches.size() == 0) { - if (favorCurrentConfig) { - // quick check - if (!editedConfig.isMatchFor(currentConfig)) { - 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.", - editedConfig.toDisplayString()), - String.format( - "Displaying it with '%1$s'", - currentConfig.toDisplayString())); - } else if (anyMatches.size() > 0) { - // select the best device anyway. - ConfigMatch match = selectConfigMatch(anyMatches); - mConfiguration.setDevice(match.device, true); - mConfiguration.setDeviceState(match.state, true); - mConfiguration.setLocale(localeList.get(match.bundle.localeIndex), true); - mConfiguration.setUiMode(UiMode.getByIndex(match.bundle.dockModeIndex), true); - mConfiguration.setNightMode(NightMode.getByIndex(match.bundle.nightModeIndex), - true); - - if (mUpdateUi) { - mConfigChooser.selectDevice(mConfiguration.getDevice()); - mConfigChooser.selectDeviceState(mConfiguration.getDeviceState()); - mConfigChooser.selectLocale(mConfiguration.getLocale()); - } - - mConfiguration.syncFolderConfig(); - - // TODO: display a better warning! - AdtPlugin.printErrorToConsole(mEditedFile.getProject(), - String.format( - "'%1$s' is not a best match for any device/locale combination.", - editedConfig.toDisplayString()), - String.format( - "Displaying it with '%1$s' which is compatible, but will " + - "actually be displayed with another more specific version of " + - "the layout.", - currentConfig.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 state, - // and replace whatever qualifier required by the layout file. - } - } else { - ConfigMatch match = selectConfigMatch(bestMatches); - mConfiguration.setDevice(match.device, true); - mConfiguration.setDeviceState(match.state, true); - mConfiguration.setLocale(localeList.get(match.bundle.localeIndex), true); - mConfiguration.setUiMode(UiMode.getByIndex(match.bundle.dockModeIndex), true); - mConfiguration.setNightMode(NightMode.getByIndex(match.bundle.nightModeIndex), true); - - mConfiguration.syncFolderConfig(); - - if (mUpdateUi) { - mConfigChooser.selectDevice(mConfiguration.getDevice()); - mConfigChooser.selectDeviceState(mConfiguration.getDeviceState()); - mConfigChooser.selectLocale(mConfiguration.getLocale()); - } - } - } - - private void addRenderTargetToBundles(List<ConfigBundle> configBundles) { - Pair<Locale, IAndroidTarget> state = Configuration.loadRenderState(mConfigChooser); - if (state != null) { - IAndroidTarget target = state.getSecond(); - if (target != null) { - int apiLevel = target.getVersion().getApiLevel(); - for (ConfigBundle bundle : configBundles) { - bundle.config.setVersionQualifier( - new VersionQualifier(apiLevel)); - } - } - } - } - - private void addDockModeToBundles(List<ConfigBundle> addConfig) { - ArrayList<ConfigBundle> list = new ArrayList<ConfigBundle>(); - - // loop on each item and for each, add all variations of the dock modes - for (ConfigBundle bundle : addConfig) { - int index = 0; - for (UiMode mode : UiMode.values()) { - ConfigBundle b = new ConfigBundle(bundle); - b.config.setUiModeQualifier(new UiModeQualifier(mode)); - b.dockModeIndex = index++; - list.add(b); - } - } - - addConfig.clear(); - addConfig.addAll(list); - } - - private void addNightModeToBundles(List<ConfigBundle> addConfig) { - ArrayList<ConfigBundle> list = new ArrayList<ConfigBundle>(); - - // loop on each item and for each, add all variations of the night modes - for (ConfigBundle bundle : addConfig) { - int index = 0; - for (NightMode mode : NightMode.values()) { - ConfigBundle b = new ConfigBundle(bundle); - b.config.setNightModeQualifier(new NightModeQualifier(mode)); - b.nightModeIndex = index++; - list.add(b); - } - } - - addConfig.clear(); - addConfig.addAll(list); - } - - private int getLocaleMatch() { - java.util.Locale defaultLocale = java.util.Locale.getDefault(); - if (defaultLocale != null) { - String currentLanguage = defaultLocale.getLanguage(); - String currentRegion = defaultLocale.getCountry(); - - List<Locale> localeList = mConfigChooser.getLocaleList(); - final int count = localeList.size(); - for (int l = 0; l < count; l++) { - Locale locale = localeList.get(l); - LocaleQualifier qualifier = locale.qualifier; - - // there's always a ##/Other or ##/Any (which is the same, the region - // contains FAKE_REGION_VALUE). If we don't find a perfect region match - // we take the fake region. Since it's last in the list, this makes the - // test easy. - if (qualifier.getLanguage().equals(currentLanguage) && - (qualifier.getRegion() == null || qualifier.getRegion().equals(currentRegion))) { - return l; - } - } - - // if no locale match the current local locale, it's likely that it is - // the default one which is the last one. - return count - 1; - } - - return -1; - } - - private ConfigMatch selectConfigMatch(List<ConfigMatch> matches) { - // API 11-13: look for a x-large device - Comparator<ConfigMatch> comparator = null; - Sdk sdk = Sdk.getCurrent(); - if (sdk != null) { - IAndroidTarget projectTarget = sdk.getTarget(mEditedFile.getProject()); - if (projectTarget != null) { - int apiLevel = projectTarget.getVersion().getApiLevel(); - if (apiLevel >= 11 && apiLevel < 14) { - // TODO: Maybe check the compatible-screen tag in the manifest to figure out - // what kind of device should be used for display. - comparator = new TabletConfigComparator(); - } - } - } - if (comparator == null) { - // lets look for a high density device - comparator = new PhoneConfigComparator(); - } - Collections.sort(matches, comparator); - - // Look at the currently active editor to see if it's a layout editor, and if so, - // look up its configuration and if the configuration is in our match list, - // use it. This means we "preserve" the current configuration when you open - // new layouts. - IEditorPart activeEditor = AdtUtils.getActiveEditor(); - LayoutEditorDelegate delegate = LayoutEditorDelegate.fromEditor(activeEditor); - if (delegate != null - // (Only do this when the two files are in the same project) - && delegate.getEditor().getProject() == mEditedFile.getProject()) { - FolderConfiguration configuration = delegate.getGraphicalEditor().getConfiguration(); - if (configuration != null) { - for (ConfigMatch match : matches) { - if (configuration.equals(match.testConfig)) { - return match; - } - } - } - } - - // the list has been sorted so that the first item is the best config - return matches.get(0); - } - - /** Return the default render target to use, or null if no strong preference */ - @Nullable - static IAndroidTarget findDefaultRenderTarget(ConfigurationChooser chooser) { - if (PREFER_RECENT_RENDER_TARGETS) { - // Use the most recent target - List<IAndroidTarget> targetList = chooser.getTargetList(); - if (!targetList.isEmpty()) { - return targetList.get(targetList.size() - 1); - } - } - - IProject project = chooser.getProject(); - // Default to layoutlib version 5 - Sdk current = Sdk.getCurrent(); - if (current != null) { - IAndroidTarget projectTarget = current.getTarget(project); - int minProjectApi = Integer.MAX_VALUE; - if (projectTarget != null) { - if (!projectTarget.isPlatform() && projectTarget.hasRenderingLibrary()) { - // Renderable non-platform targets are all going to be adequate (they - // will have at least version 5 of layoutlib) so use the project - // target as the render target. - return projectTarget; - } - - if (projectTarget.getVersion().isPreview() - && projectTarget.hasRenderingLibrary()) { - // If the project target is a preview version, then just use it - return projectTarget; - } - - minProjectApi = projectTarget.getVersion().getApiLevel(); - } - - // We want to pick a render target that contains at least version 5 (and - // preferably version 6) of the layout library. To do this, we go through the - // targets and pick the -smallest- API level that is both simultaneously at - // least as big as the project API level, and supports layoutlib level 5+. - IAndroidTarget best = null; - int bestApiLevel = Integer.MAX_VALUE; - - for (IAndroidTarget target : current.getTargets()) { - // Non-platform targets are not chosen as the default render target - if (!target.isPlatform()) { - continue; - } - - int apiLevel = target.getVersion().getApiLevel(); - - // Ignore targets that have a lower API level than the minimum project - // API level: - if (apiLevel < minProjectApi) { - continue; - } - - // Look up the layout lib API level. This property is new so it will only - // be defined for version 6 or higher, which means non-null is adequate - // to see if this target is eligible: - String property = target.getProperty(PkgProps.LAYOUTLIB_API); - // In addition, Android 3.0 with API level 11 had version 5.0 which is adequate: - if (property != null || apiLevel >= 11) { - if (apiLevel < bestApiLevel) { - bestApiLevel = apiLevel; - best = target; - } - } - } - - return best; - } - - return null; - } - - /** - * Attempts to find a close state among a list - * - * @param oldConfig the reference config. - * @param states the list of states to search through - * @return the name of the closest state match, or possibly null if no states are compatible - * (this can only happen if the states don't have a single qualifier that is the same). - */ - @Nullable - static String getClosestMatch(@NonNull FolderConfiguration oldConfig, - @NonNull List<State> states) { - - // create 2 lists as we're going to go through one and put the - // candidates in the other. - List<State> list1 = new ArrayList<State>(states.size()); - List<State> list2 = new ArrayList<State>(states.size()); - - list1.addAll(states); - - final int count = FolderConfiguration.getQualifierCount(); - for (int i = 0 ; i < count ; i++) { - // compute the new candidate list by only taking states that have - // the same i-th qualifier as the old state - for (State s : list1) { - ResourceQualifier oldQualifier = oldConfig.getQualifier(i); - - FolderConfiguration folderConfig = DeviceConfigHelper.getFolderConfig(s); - ResourceQualifier newQualifier = - folderConfig != null ? folderConfig.getQualifier(i) : null; - - if (oldQualifier == null) { - if (newQualifier == null) { - list2.add(s); - } - } else if (oldQualifier.equals(newQualifier)) { - list2.add(s); - } - } - - // at any moment if the new candidate list contains only one match, its name - // is returned. - if (list2.size() == 1) { - return list2.get(0).getName(); - } - - // if the list is empty, then all the new states failed. It is considered ok, and - // we move to the next qualifier anyway. This way, if a qualifier is different for - // all new states it is simply ignored. - if (list2.size() != 0) { - // move the candidates back into list1. - list1.clear(); - list1.addAll(list2); - list2.clear(); - } - } - - // the only way to reach this point is if there's an exact match. - // (if there are more than one, then there's a duplicate state and it doesn't matter, - // we take the first one). - if (list1.size() > 0) { - return list1.get(0).getName(); - } - - return null; - } - - /** - * Returns the layout {@link IFile} which best matches the configuration - * selected in the given configuration chooser. - * - * @param chooser the associated configuration chooser holding project state - * @return the file which best matches the settings - */ - @Nullable - public static IFile getBestFileMatch(ConfigurationChooser chooser) { - // get the resources of the file's project. - ResourceManager manager = ResourceManager.getInstance(); - ProjectResources resources = manager.getProjectResources(chooser.getProject()); - if (resources == null) { - return null; - } - - // From the resources, look for a matching file - IFile editedFile = chooser.getEditedFile(); - if (editedFile == null) { - return null; - } - String name = editedFile.getName(); - FolderConfiguration config = chooser.getConfiguration().getFullConfig(); - ResourceFile match = resources.getMatchingFile(name, ResourceType.LAYOUT, config); - - if (match != null) { - // In Eclipse, the match's file is always an instance of IFileWrapper - return ((IFileWrapper) match.getFile()).getIFile(); - } - - return null; - } - - /** - * Note: this comparator imposes orderings that are inconsistent with equals. - */ - private static class TabletConfigComparator implements Comparator<ConfigMatch> { - @Override - public int compare(ConfigMatch o1, ConfigMatch o2) { - FolderConfiguration config1 = o1 != null ? o1.testConfig : null; - FolderConfiguration config2 = o2 != null ? o2.testConfig : null; - if (config1 == null) { - if (config2 == null) { - return 0; - } else { - return -1; - } - } else if (config2 == null) { - return 1; - } - - ScreenSizeQualifier size1 = config1.getScreenSizeQualifier(); - ScreenSizeQualifier size2 = config2.getScreenSizeQualifier(); - ScreenSize ss1 = size1 != null ? size1.getValue() : ScreenSize.NORMAL; - ScreenSize ss2 = size2 != null ? size2.getValue() : ScreenSize.NORMAL; - - // X-LARGE is better than all others (which are considered identical) - // if both X-LARGE, then LANDSCAPE is better than all others (which are identical) - - if (ss1 == ScreenSize.XLARGE) { - if (ss2 == ScreenSize.XLARGE) { - ScreenOrientationQualifier orientation1 = - config1.getScreenOrientationQualifier(); - ScreenOrientation so1 = orientation1.getValue(); - if (so1 == null) { - so1 = ScreenOrientation.PORTRAIT; - } - ScreenOrientationQualifier orientation2 = - config2.getScreenOrientationQualifier(); - ScreenOrientation so2 = orientation2.getValue(); - if (so2 == null) { - so2 = ScreenOrientation.PORTRAIT; - } - - if (so1 == ScreenOrientation.LANDSCAPE) { - if (so2 == ScreenOrientation.LANDSCAPE) { - return 0; - } else { - return -1; - } - } else if (so2 == ScreenOrientation.LANDSCAPE) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - } else if (ss2 == ScreenSize.XLARGE) { - return 1; - } else { - return 0; - } - } - } - - /** - * Note: this comparator imposes orderings that are inconsistent with equals. - */ - private static class PhoneConfigComparator implements Comparator<ConfigMatch> { - - private final SparseIntArray mDensitySort = new SparseIntArray(4); - - public PhoneConfigComparator() { - // put the sort order for the density. - mDensitySort.put(Density.HIGH.getDpiValue(), 1); - mDensitySort.put(Density.MEDIUM.getDpiValue(), 2); - mDensitySort.put(Density.XHIGH.getDpiValue(), 3); - mDensitySort.put(Density.LOW.getDpiValue(), 4); - } - - @Override - public int compare(ConfigMatch o1, ConfigMatch o2) { - FolderConfiguration config1 = o1 != null ? o1.testConfig : null; - FolderConfiguration config2 = o2 != null ? o2.testConfig : null; - if (config1 == null) { - if (config2 == null) { - return 0; - } else { - return -1; - } - } else if (config2 == null) { - return 1; - } - - int dpi1 = Density.DEFAULT_DENSITY; - int dpi2 = Density.DEFAULT_DENSITY; - - DensityQualifier dpiQualifier1 = config1.getDensityQualifier(); - if (dpiQualifier1 != null) { - Density value = dpiQualifier1.getValue(); - dpi1 = value != null ? value.getDpiValue() : Density.DEFAULT_DENSITY; - } - dpi1 = mDensitySort.get(dpi1, 100 /* valueIfKeyNotFound*/); - - DensityQualifier dpiQualifier2 = config2.getDensityQualifier(); - if (dpiQualifier2 != null) { - Density value = dpiQualifier2.getValue(); - dpi2 = value != null ? value.getDpiValue() : Density.DEFAULT_DENSITY; - } - dpi2 = mDensitySort.get(dpi2, 100 /* valueIfKeyNotFound*/); - - if (dpi1 == dpi2) { - // portrait is better - ScreenOrientation so1 = ScreenOrientation.PORTRAIT; - ScreenOrientationQualifier orientationQualifier1 = - config1.getScreenOrientationQualifier(); - if (orientationQualifier1 != null) { - so1 = orientationQualifier1.getValue(); - if (so1 == null) { - so1 = ScreenOrientation.PORTRAIT; - } - } - ScreenOrientation so2 = ScreenOrientation.PORTRAIT; - ScreenOrientationQualifier orientationQualifier2 = - config2.getScreenOrientationQualifier(); - if (orientationQualifier2 != null) { - so2 = orientationQualifier2.getValue(); - if (so2 == null) { - so2 = ScreenOrientation.PORTRAIT; - } - } - - if (so1 == ScreenOrientation.PORTRAIT) { - if (so2 == ScreenOrientation.PORTRAIT) { - return 0; - } else { - return -1; - } - } else if (so2 == ScreenOrientation.PORTRAIT) { - return 1; - } else { - return 0; - } - } - - return dpi1 - dpi2; - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationMenuListener.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationMenuListener.java deleted file mode 100644 index a791c63f8..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationMenuListener.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.CUSTOM; -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.DEFAULT; -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.INCLUDES; -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.LOCALES; -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.NONE; -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.SCREENS; -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.VARIATIONS; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.resources.ResourceFolder; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutCanvas; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewManager; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; -import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Menu; -import org.eclipse.swt.widgets.MenuItem; -import org.eclipse.swt.widgets.ToolItem; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.PartInitException; - -import java.util.List; - -/** - * The {@linkplain ConfigurationMenuListener} class is responsible for - * generating the configuration menu in the {@link ConfigurationChooser}. - */ -class ConfigurationMenuListener extends SelectionAdapter { - private static final String ICON_NEW_CONFIG = "newConfig"; //$NON-NLS-1$ - private static final int ACTION_SELECT_CONFIG = 1; - private static final int ACTION_CREATE_CONFIG_FILE = 2; - private static final int ACTION_ADD = 3; - private static final int ACTION_DELETE_ALL = 4; - private static final int ACTION_PREVIEW_MODE = 5; - - private final ConfigurationChooser mConfigChooser; - private final int mAction; - private final IFile mResource; - private final RenderPreviewMode mMode; - - ConfigurationMenuListener( - @NonNull ConfigurationChooser configChooser, - int action, - @Nullable IFile resource, - @Nullable RenderPreviewMode mode) { - mConfigChooser = configChooser; - mAction = action; - mResource = resource; - mMode = mode; - } - - @Override - public void widgetSelected(SelectionEvent e) { - switch (mAction) { - case ACTION_SELECT_CONFIG: { - try { - AdtPlugin.openFile(mResource, null, false); - } catch (PartInitException ex) { - AdtPlugin.log(ex, null); - } - return; - } - case ACTION_CREATE_CONFIG_FILE: { - ConfigurationClient client = mConfigChooser.getClient(); - if (client != null) { - client.createConfigFile(); - } - return; - } - } - - IEditorPart activeEditor = AdtUtils.getActiveEditor(); - LayoutEditorDelegate delegate = LayoutEditorDelegate.fromEditor(activeEditor); - IFile editedFile = mConfigChooser.getEditedFile(); - - if (delegate == null || editedFile == null) { - return; - } - // (Only do this when the two files are in the same project) - IProject project = delegate.getEditor().getProject(); - if (project == null || - !project.equals(editedFile.getProject())) { - return; - } - LayoutCanvas canvas = delegate.getGraphicalEditor().getCanvasControl(); - RenderPreviewManager previewManager = canvas.getPreviewManager(); - - switch (mAction) { - case ACTION_ADD: { - previewManager.addAsThumbnail(); - break; - } - case ACTION_PREVIEW_MODE: { - previewManager.selectMode(mMode); - break; - } - case ACTION_DELETE_ALL: { - previewManager.deleteManualPreviews(); - break; - } - default: assert false : mAction; - } - canvas.setFitScale(true /*onlyZoomOut*/, false /*allowZoomIn*/); - canvas.redraw(); - } - - static void show(ConfigurationChooser chooser, ToolItem combo) { - Menu menu = new Menu(chooser.getShell(), SWT.POP_UP); - RenderPreviewMode mode = AdtPrefs.getPrefs().getRenderPreviewMode(); - - // Configuration Previews - create(menu, "Add As Thumbnail...", - new ConfigurationMenuListener(chooser, ACTION_ADD, null, null), - SWT.PUSH, false); - if (mode == RenderPreviewMode.CUSTOM) { - MenuItem item = create(menu, "Delete All Thumbnails", - new ConfigurationMenuListener(chooser, ACTION_DELETE_ALL, null, null), - SWT.PUSH, false); - IEditorPart activeEditor = AdtUtils.getActiveEditor(); - LayoutEditorDelegate delegate = LayoutEditorDelegate.fromEditor(activeEditor); - if (delegate != null) { - LayoutCanvas canvas = delegate.getGraphicalEditor().getCanvasControl(); - RenderPreviewManager previewManager = canvas.getPreviewManager(); - if (!previewManager.hasManualPreviews()) { - item.setEnabled(false); - } - } - } - - @SuppressWarnings("unused") - MenuItem configSeparator = new MenuItem(menu, SWT.SEPARATOR); - - create(menu, "Preview Representative Sample", - new ConfigurationMenuListener(chooser, ACTION_PREVIEW_MODE, null, - DEFAULT), SWT.RADIO, mode == DEFAULT); - create(menu, "Preview All Screen Sizes", - new ConfigurationMenuListener(chooser, ACTION_PREVIEW_MODE, null, - SCREENS), SWT.RADIO, mode == SCREENS); - - MenuItem localeItem = create(menu, "Preview All Locales", - new ConfigurationMenuListener(chooser, ACTION_PREVIEW_MODE, null, - LOCALES), SWT.RADIO, mode == LOCALES); - if (chooser.getLocaleList().size() <= 1) { - localeItem.setEnabled(false); - } - - boolean canPreviewIncluded = false; - IProject project = chooser.getProject(); - if (project != null) { - IncludeFinder finder = IncludeFinder.get(project); - final List<Reference> includedBy = finder.getIncludedBy(chooser.getEditedFile()); - canPreviewIncluded = includedBy != null && !includedBy.isEmpty(); - } - //if (!graphicalEditor.renderingSupports(Capability.EMBEDDED_LAYOUT)) { - // canPreviewIncluded = false; - //} - MenuItem includedItem = create(menu, "Preview Included", - new ConfigurationMenuListener(chooser, ACTION_PREVIEW_MODE, null, - INCLUDES), SWT.RADIO, mode == INCLUDES); - if (!canPreviewIncluded) { - includedItem.setEnabled(false); - } - - IFile file = chooser.getEditedFile(); - List<IFile> variations = AdtUtils.getResourceVariations(file, true); - MenuItem variationsItem = create(menu, "Preview Layout Versions", - new ConfigurationMenuListener(chooser, ACTION_PREVIEW_MODE, null, - VARIATIONS), SWT.RADIO, mode == VARIATIONS); - if (variations.size() <= 1) { - variationsItem.setEnabled(false); - } - - create(menu, "Manual Previews", - new ConfigurationMenuListener(chooser, ACTION_PREVIEW_MODE, null, - CUSTOM), SWT.RADIO, mode == CUSTOM); - create(menu, "None", - new ConfigurationMenuListener(chooser, ACTION_PREVIEW_MODE, null, - NONE), SWT.RADIO, mode == NONE); - - if (variations.size() > 1) { - @SuppressWarnings("unused") - MenuItem separator = new MenuItem(menu, SWT.SEPARATOR); - - ResourceManager manager = ResourceManager.getInstance(); - for (final IFile resource : variations) { - IFolder parent = (IFolder) resource.getParent(); - ResourceFolder parentResource = manager.getResourceFolder(parent); - FolderConfiguration configuration = parentResource.getConfiguration(); - String title = configuration.toDisplayString(); - - MenuItem item = create(menu, title, - new ConfigurationMenuListener(chooser, ACTION_SELECT_CONFIG, - resource, null), - SWT.CHECK, false); - - if (file != null) { - boolean selected = file.equals(resource); - if (selected) { - item.setSelection(true); - item.setEnabled(false); - } - } - } - } - - Configuration configuration = chooser.getConfiguration(); - if (configuration.getEditedConfig() != null && - !configuration.getEditedConfig().equals(configuration.getFullConfig())) { - if (variations.size() > 0) { - @SuppressWarnings("unused") - MenuItem separator = new MenuItem(menu, SWT.SEPARATOR); - } - - // Add action for creating a new configuration - MenuItem item = create(menu, "Create New...", - new ConfigurationMenuListener(chooser, ACTION_CREATE_CONFIG_FILE, - null, null), - SWT.PUSH, false); - item.setImage(IconFactory.getInstance().getIcon(ICON_NEW_CONFIG)); - } - - Rectangle bounds = combo.getBounds(); - Point location = new Point(bounds.x, bounds.y + bounds.height); - location = combo.getParent().toDisplay(location); - menu.setLocation(location.x, location.y); - menu.setVisible(true); - } - - @NonNull - public static MenuItem create(@NonNull Menu menu, String title, - ConfigurationMenuListener listener, int style, boolean selected) { - MenuItem item = new MenuItem(menu, style); - item.setText(title); - item.addSelectionListener(listener); - if (selected) { - item.setSelection(true); - } - return item; - } - - @NonNull - static MenuItem addTogglePreviewModeAction( - @NonNull Menu menu, - @NonNull String title, - @NonNull ConfigurationChooser chooser, - @NonNull RenderPreviewMode mode) { - boolean selected = AdtPrefs.getPrefs().getRenderPreviewMode() == mode; - if (selected) { - mode = RenderPreviewMode.NONE; - } - return create(menu, title, - new ConfigurationMenuListener(chooser, ACTION_PREVIEW_MODE, null, mode), - SWT.CHECK, selected); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/DeviceMenuListener.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/DeviceMenuListener.java deleted file mode 100644 index 72910f9cc..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/DeviceMenuListener.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import static com.android.ide.common.rendering.HardwareConfigHelper.MANUFACTURER_GENERIC; -import static com.android.ide.common.rendering.HardwareConfigHelper.getGenericLabel; -import static com.android.ide.common.rendering.HardwareConfigHelper.getNexusLabel; -import static com.android.ide.common.rendering.HardwareConfigHelper.isGeneric; -import static com.android.ide.common.rendering.HardwareConfigHelper.isNexus; -import static com.android.ide.common.rendering.HardwareConfigHelper.sortNexusList; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.sdklib.devices.Device; -import com.android.sdklib.internal.avd.AvdInfo; -import com.android.sdklib.internal.avd.AvdManager; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Menu; -import org.eclipse.swt.widgets.MenuItem; -import org.eclipse.swt.widgets.ToolItem; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -/** - * The {@linkplain DeviceMenuListener} class is responsible for generating the device - * menu in the {@link ConfigurationChooser}. - */ -class DeviceMenuListener extends SelectionAdapter { - private final ConfigurationChooser mConfigChooser; - private final Device mDevice; - - DeviceMenuListener( - @NonNull ConfigurationChooser configChooser, - @Nullable Device device) { - mConfigChooser = configChooser; - mDevice = device; - } - - @Override - public void widgetSelected(SelectionEvent e) { - mConfigChooser.selectDevice(mDevice); - mConfigChooser.onDeviceChange(); - } - - static void show(final ConfigurationChooser chooser, ToolItem combo) { - Configuration configuration = chooser.getConfiguration(); - Device current = configuration.getDevice(); - Menu menu = new Menu(chooser.getShell(), SWT.POP_UP); - - Collection<Device> deviceCollection = chooser.getDevices(); - Sdk sdk = Sdk.getCurrent(); - if (sdk != null) { - AvdManager avdManager = sdk.getAvdManager(); - if (avdManager != null) { - boolean separatorNeeded = false; - AvdInfo[] avds = avdManager.getValidAvds(); - for (AvdInfo avd : avds) { - for (Device device : deviceCollection) { - if (device.getManufacturer().equals(avd.getDeviceManufacturer()) - && device.getName().equals(avd.getDeviceName())) { - separatorNeeded = true; - MenuItem item = new MenuItem(menu, SWT.CHECK); - item.setText(avd.getName()); - item.setSelection(current == device); - - item.addSelectionListener(new DeviceMenuListener(chooser, device)); - } - } - } - - if (separatorNeeded) { - @SuppressWarnings("unused") - MenuItem separator = new MenuItem(menu, SWT.SEPARATOR); - } - } - } - - // Group the devices by manufacturer, then put them in the menu. - // If we don't have anything but Nexus devices, group them together rather than - // make many manufacturer submenus. - boolean haveNexus = false; - boolean haveNonNexus = false; - if (!deviceCollection.isEmpty()) { - Map<String, List<Device>> manufacturers = new TreeMap<String, List<Device>>(); - for (Device device : deviceCollection) { - List<Device> devices; - if (isNexus(device)) { - haveNexus = true; - } else if (!isGeneric(device)) { - haveNonNexus = true; - } - if (manufacturers.containsKey(device.getManufacturer())) { - devices = manufacturers.get(device.getManufacturer()); - } else { - devices = new ArrayList<Device>(); - manufacturers.put(device.getManufacturer(), devices); - } - devices.add(device); - } - if (haveNonNexus) { - for (List<Device> devices : manufacturers.values()) { - Menu manufacturerMenu = menu; - if (manufacturers.size() > 1) { - MenuItem item = new MenuItem(menu, SWT.CASCADE); - item.setText(devices.get(0).getManufacturer()); - manufacturerMenu = new Menu(menu); - item.setMenu(manufacturerMenu); - } - for (final Device device : devices) { - MenuItem deviceItem = new MenuItem(manufacturerMenu, SWT.CHECK); - deviceItem.setText(getGenericLabel(device)); - deviceItem.setSelection(current == device); - deviceItem.addSelectionListener(new DeviceMenuListener(chooser, device)); - } - } - } else { - List<Device> nexus = new ArrayList<Device>(); - List<Device> generic = new ArrayList<Device>(); - if (haveNexus) { - // Nexus - for (List<Device> devices : manufacturers.values()) { - for (Device device : devices) { - if (isNexus(device)) { - if (device.getManufacturer().equals(MANUFACTURER_GENERIC)) { - generic.add(device); - } else { - nexus.add(device); - } - } else { - generic.add(device); - } - } - } - } - - if (!nexus.isEmpty()) { - sortNexusList(nexus); - for (final Device device : nexus) { - MenuItem item = new MenuItem(menu, SWT.CHECK); - item.setText(getNexusLabel(device)); - item.setSelection(current == device); - item.addSelectionListener(new DeviceMenuListener(chooser, device)); - } - - @SuppressWarnings("unused") - MenuItem separator = new MenuItem(menu, SWT.SEPARATOR); - } - - // Generate the generic menu. - Collections.reverse(generic); - for (final Device device : generic) { - MenuItem item = new MenuItem(menu, SWT.CHECK); - item.setText(getGenericLabel(device)); - item.setSelection(current == device); - item.addSelectionListener(new DeviceMenuListener(chooser, device)); - } - } - } - - @SuppressWarnings("unused") - MenuItem separator = new MenuItem(menu, SWT.SEPARATOR); - - ConfigurationMenuListener.addTogglePreviewModeAction(menu, - "Preview All Screens", chooser, RenderPreviewMode.SCREENS); - - - Rectangle bounds = combo.getBounds(); - Point location = new Point(bounds.x, bounds.y + bounds.height); - location = combo.getParent().toDisplay(location); - menu.setLocation(location.x, location.y); - menu.setVisible(true); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/FlagManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/FlagManager.java deleted file mode 100644 index 15623cf30..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/FlagManager.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.resources.LocaleManager; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.LocaleQualifier; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.google.common.collect.Maps; - -import org.eclipse.swt.graphics.Image; -import org.eclipse.wb.internal.core.DesignerPlugin; - -import java.util.Locale; -import java.util.Map; - -/** - * The {@linkplain FlagManager} provides access to flags for regions known - * to {@link LocaleManager}. It also contains some locale related display - * functions. - * <p> - * All the flag images came from the WindowBuilder subversion repository - * http://dev.eclipse.org/svnroot/tools/org.eclipse.windowbuilder/trunk (and in - * particular, a snapshot of revision 424). However, it appears that the icons - * are from http://www.famfamfam.com/lab/icons/flags/ which states that "these - * flag icons are available for free use for any purpose with no requirement for - * attribution." Adding the URL here such that we can check back occasionally - * and see if there are corrections or updates. Also note that the flag names - * are in ISO 3166-1 alpha-2 country codes. - */ -public class FlagManager { - private static final FlagManager sInstance = new FlagManager(); - - /** - * Returns the {@linkplain FlagManager} singleton - * - * @return the {@linkplain FlagManager} singleton, never null - */ - @NonNull - public static FlagManager get() { - return sInstance; - } - - /** Use the {@link #get()} factory method */ - private FlagManager() { - } - - /** Map from region to flag icon */ - private final Map<String, Image> mImageMap = Maps.newHashMap(); - - /** - * Returns the empty flag icon used to indicate an unknown country - * - * @return the globe icon used to indicate an unknown country - */ - public static Image getEmptyIcon() { - return DesignerPlugin.getImage("nls/flags/flag_empty.png"); //$NON-NLS-1$ - } - - /** - * Returns the globe icon used to indicate "any" language - * - * @return the globe icon used to indicate "any" language - */ - public static Image getGlobeIcon() { - return IconFactory.getInstance().getIcon("globe"); //$NON-NLS-1$ - } - - /** - * Returns the flag for the given language and region. - * - * @param language the language, or null (if null, region must not be null), - * the 2 letter language code (ISO 639-1), in lower case - * @param region the region, or null (if null, language must not be null), - * the 2 letter region code (ISO 3166-1 alpha-2), in upper case - * @return a suitable flag icon, or null - */ - @Nullable - public Image getFlag(@Nullable String language, @Nullable String region) { - assert region != null || language != null; - if (region == null || region.isEmpty()) { - // Look up the region for a given language - assert language != null; - - // Special cases where we have a dedicated flag available: - if (language.equals("ca")) { //$NON-NLS-1$ - return getIcon("catalonia"); //$NON-NLS-1$ - } - else if (language.equals("gd")) { //$NON-NLS-1$ - return getIcon("scotland"); //$NON-NLS-1$ - } - else if (language.equals("cy")) { //$NON-NLS-1$ - return getIcon("wales"); //$NON-NLS-1$ - } - - // Prefer the local registration of the current locale; even if - // for example the default locale for English is the US, if the current - // default locale is English, then use its associated country, which could - // for example be Australia. - Locale locale = Locale.getDefault(); - if (language.equals(locale.getLanguage())) { - Image flag = getFlag(locale.getCountry()); - if (flag != null) { - return flag; - } - } - - region = LocaleManager.getLanguageRegion(language); - } - - if (region == null || region.isEmpty()) { - // No country specified, and the language is for a country we - // don't have a flag for - return null; - } - - return getIcon(region); - } - - /** - * Returns the flag for the given language and region. - * - * @param language the language qualifier, or null (if null, region must not be null), - * @param region the region, or null (if null, language must not be null), - * @return a suitable flag icon, or null - */ - public Image getFlag(@Nullable LocaleQualifier locale) { - if (locale == null) { - return null; - } - String languageCode = locale.getLanguage(); - String regionCode = locale.getRegion(); - if (LocaleQualifier.FAKE_VALUE.equals(languageCode)) { - languageCode = null; - } - return getFlag(languageCode, regionCode); - } - - /** - * Returns a flag for a given resource folder name (such as - * {@code values-en-rUS}), or null - * - * @param folder the folder name - * @return a corresponding flag icon, or null if none was found - */ - @Nullable - public Image getFlagForFolderName(@NonNull String folder) { - FolderConfiguration configuration = FolderConfiguration.getConfigForFolder(folder); - if (configuration != null) { - return get().getFlag(configuration); - } - - return null; - } - - /** - * Returns the flag for the given folder - * - * @param configuration the folder configuration - * @return a suitable flag icon, or null - */ - @Nullable - public Image getFlag(@NonNull FolderConfiguration configuration) { - return getFlag(configuration.getLocaleQualifier()); - } - - - - /** - * Returns the flag for the given region. - * - * @param region the 2 letter region code (ISO 3166-1 alpha-2), in upper case - * @return a suitable flag icon, or null - */ - @Nullable - public Image getFlag(@NonNull String region) { - assert region.length() == 2 - && Character.isUpperCase(region.charAt(0)) - && Character.isUpperCase(region.charAt(1)) : region; - - return getIcon(region); - } - - private Image getIcon(@NonNull String base) { - Image flagImage = mImageMap.get(base); - if (flagImage == null) { - // TODO: Special case locale currently running on system such - // that the current country matches the current locale - if (mImageMap.containsKey(base)) { - // Already checked: there's just no image there - return null; - } - String flagFileName = base.toLowerCase(Locale.US) + ".png"; //$NON-NLS-1$ - flagImage = DesignerPlugin.getImage("nls/flags/" + flagFileName); //$NON-NLS-1$ - mImageMap.put(base, flagImage); - } - - return flagImage; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LayoutCreatorDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LayoutCreatorDialog.java deleted file mode 100644 index 97ff66845..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LayoutCreatorDialog.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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.editors.layout.configuration; - -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.ResourceQualifier; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector; -import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.ConfigurationState; -import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.SelectorMode; -import com.android.resources.ResourceFolderType; -import com.android.sdkuilib.ui.GridDialog; - -import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.jface.dialogs.IDialogConstants; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; - -/** - * Dialog to choose a non existing {@link FolderConfiguration}. - */ -public final class LayoutCreatorDialog extends GridDialog { - - private ConfigurationSelector mSelector; - private Composite mStatusComposite; - private Label mStatusLabel; - private Label mStatusImage; - - private final FolderConfiguration mConfig = new FolderConfiguration(); - private final String mFileName; - - /** - * Creates a dialog, and init the UI from a {@link FolderConfiguration}. - * @param parentShell the parent {@link Shell}. - * @param fileName the filename associated with the configuration - * @param config The starting configuration. - */ - public LayoutCreatorDialog(Shell parentShell, String fileName, FolderConfiguration config) { - super(parentShell, 1, false); - - mFileName = fileName; - - // FIXME: add some data to know what configurations already exist. - mConfig.set(config); - } - - @Override - public void createDialogContent(Composite parent) { - new Label(parent, SWT.NONE).setText( - String.format("Configuration for the alternate version of %1$s", mFileName)); - - mSelector = new ConfigurationSelector(parent, SelectorMode.CONFIG_ONLY); - mSelector.setConfiguration(mConfig); - - // because the ConfigSelector is running in CONFIG_ONLY mode, the current config - // displayed by it is not mConfig anymore, so get the current config. - mSelector.getConfiguration(mConfig); - - // parent's layout is a GridLayout as specified in the javadoc. - GridData gd = new GridData(); - gd.widthHint = ConfigurationSelector.WIDTH_HINT; - gd.heightHint = ConfigurationSelector.HEIGHT_HINT; - mSelector.setLayoutData(gd); - - // add a listener to check on the validity of the FolderConfiguration as - // they are built. - mSelector.setOnChangeListener(new Runnable() { - @Override - public void run() { - ConfigurationState state = mSelector.getState(); - - switch (state) { - case OK: - mSelector.getConfiguration(mConfig); - - resetStatus(); - mStatusImage.setImage(null); - getButton(IDialogConstants.OK_ID).setEnabled(true); - break; - case INVALID_CONFIG: - ResourceQualifier invalidQualifier = mSelector.getInvalidQualifier(); - mStatusLabel.setText(String.format( - "Invalid Configuration: %1$s has no filter set.", - invalidQualifier.getName())); - mStatusImage.setImage(IconFactory.getInstance().getIcon("warning")); //$NON-NLS-1$ - getButton(IDialogConstants.OK_ID).setEnabled(false); - break; - case REGION_WITHOUT_LANGUAGE: - mStatusLabel.setText( - "The Region qualifier requires the Language qualifier."); - mStatusImage.setImage(IconFactory.getInstance().getIcon("warning")); //$NON-NLS-1$ - getButton(IDialogConstants.OK_ID).setEnabled(false); - break; - } - - // need to relayout, because of the change in size in mErrorImage. - mStatusComposite.layout(); - } - }); - - mStatusComposite = new Composite(parent, SWT.NONE); - mStatusComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - GridLayout gl = new GridLayout(2, false); - mStatusComposite.setLayout(gl); - gl.marginHeight = gl.marginWidth = 0; - - mStatusImage = new Label(mStatusComposite, SWT.NONE); - mStatusLabel = new Label(mStatusComposite, SWT.NONE); - mStatusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - resetStatus(); - } - - /** - * Sets the edited configuration on the given configuration parameter - * - * @param config the configuration to apply the current edits to - */ - public void getConfiguration(FolderConfiguration config) { - config.set(mConfig); - } - - /** - * resets the status label to show the file that will be created. - */ - private void resetStatus() { - String displayString = Dialog.shortenText(String.format("New File: res/%1$s/%2$s", - mConfig.getFolderName(ResourceFolderType.LAYOUT), mFileName), - mStatusLabel); - mStatusLabel.setText(displayString); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/Locale.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/Locale.java deleted file mode 100644 index 6cb396394..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/Locale.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import static com.android.ide.common.resources.configuration.LocaleQualifier.FAKE_VALUE; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.LocaleQualifier; - -import org.eclipse.swt.graphics.Image; - -/** - * A language,region pair - */ -public class Locale { - /** - * A special marker region qualifier representing any region - */ - public static final LocaleQualifier ANY_QUALIFIER = new LocaleQualifier(FAKE_VALUE); - - /** - * A locale which matches any language and region - */ - public static final Locale ANY = new Locale(ANY_QUALIFIER); - - /** - * The locale qualifier, or {@link #ANY_QUALIFIER} if this locale matches - * any locale - */ - @NonNull - public final LocaleQualifier qualifier; - - /** - * Constructs a new {@linkplain Locale} matching a given language in a given - * locale. - * - * @param locale the locale - */ - private Locale(@NonNull - LocaleQualifier locale) { - qualifier = locale; - } - - /** - * Constructs a new {@linkplain Locale} matching a given language in a given - * specific locale. - * - * @param locale the locale - * @return a locale with the given locale - */ - @NonNull - public static Locale create(@NonNull - LocaleQualifier locale) { - return new Locale(locale); - } - - /** - * Constructs a new {@linkplain Locale} for the given folder configuration - * - * @param folder the folder configuration - * @return a locale with the given language and region - */ - public static Locale create(FolderConfiguration folder) { - LocaleQualifier locale = folder.getLocaleQualifier(); - if (locale == null) { - return ANY; - } else { - return new Locale(locale); - } - } - - /** - * Constructs a new {@linkplain Locale} for the given locale string, e.g. - * "zh", "en-rUS", or "b+eng+US". - * - * @param localeString the locale description - * @return the corresponding locale - */ - @NonNull - public static Locale create(@NonNull - String localeString) { - // Load locale. Note that this can get overwritten by the - // project-wide settings read below. - - LocaleQualifier qualifier = LocaleQualifier.getQualifier(localeString); - if (qualifier != null) { - return new Locale(qualifier); - } else { - return ANY; - } - } - - /** - * Returns a flag image to use for this locale - * - * @return a flag image, or a default globe icon - */ - @NonNull - public Image getFlagImage() { - String languageCode = qualifier.hasLanguage() ? qualifier.getLanguage() : null; - if (languageCode == null) { - return FlagManager.getGlobeIcon(); - } - String regionCode = hasRegion() ? qualifier.getRegion() : null; - FlagManager icons = FlagManager.get(); - Image image = icons.getFlag(languageCode, regionCode); - if (image != null) { - return image; - } else { - return FlagManager.getGlobeIcon(); - } - } - - /** - * Returns true if this locale specifies a specific language. This is true - * for all locales except {@link #ANY}. - * - * @return true if this locale specifies a specific language - */ - public boolean hasLanguage() { - return !qualifier.hasFakeValue(); - } - - /** - * Returns true if this locale specifies a specific region - * - * @return true if this locale specifies a region - */ - public boolean hasRegion() { - return qualifier.getRegion() != null && !FAKE_VALUE.equals(qualifier.getRegion()); - } - - /** - * Returns the locale formatted as language-region. If region is not set, - * language is returned. If language is not set, empty string is returned. - */ - public String toLocaleId() { - return qualifier == ANY_QUALIFIER ? "" : qualifier.getTag(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + qualifier.hashCode(); - return result; - } - - @Override - public boolean equals(@Nullable - Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Locale other = (Locale) obj; - if (!qualifier.equals(other.qualifier)) - return false; - return true; - } - - @Override - public String toString() { - return qualifier.getTag(); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LocaleMenuListener.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LocaleMenuListener.java deleted file mode 100644 index 2bc5417b0..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LocaleMenuListener.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode; -import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.AddTranslationDialog; - -import org.eclipse.core.resources.IProject; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Menu; -import org.eclipse.swt.widgets.MenuItem; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.ToolItem; - -import java.util.List; - -/** - * The {@linkplain LocaleMenuListener} class is responsible for generating the locale - * menu in the {@link ConfigurationChooser}. - */ -class LocaleMenuListener extends SelectionAdapter { - private static final int ACTION_SET_LOCALE = 1; - private static final int ACTION_ADD_TRANSLATION = 2; - - private final ConfigurationChooser mConfigChooser; - private final int mAction; - private final Locale mLocale; - - LocaleMenuListener( - @NonNull ConfigurationChooser configChooser, - int action, - @Nullable Locale locale) { - mConfigChooser = configChooser; - mAction = action; - mLocale = locale; - } - - @Override - public void widgetSelected(SelectionEvent e) { - switch (mAction) { - case ACTION_SET_LOCALE: { - mConfigChooser.selectLocale(mLocale); - mConfigChooser.onLocaleChange(); - break; - } - case ACTION_ADD_TRANSLATION: { - IProject project = mConfigChooser.getProject(); - Shell shell = mConfigChooser.getShell(); - AddTranslationDialog dialog = new AddTranslationDialog(shell, project); - dialog.open(); - break; - } - default: assert false : mAction; - } - } - - static void show(final ConfigurationChooser chooser, ToolItem combo) { - Menu menu = new Menu(chooser.getShell(), SWT.POP_UP); - Configuration configuration = chooser.getConfiguration(); - List<Locale> locales = chooser.getLocaleList(); - Locale current = configuration.getLocale(); - - for (Locale locale : locales) { - String title = ConfigurationChooser.getLocaleLabel(chooser, locale, false); - MenuItem item = new MenuItem(menu, SWT.CHECK); - item.setText(title); - Image image = locale.getFlagImage(); - item.setImage(image); - - boolean selected = current == locale; - if (selected) { - item.setSelection(true); - } - - LocaleMenuListener listener = new LocaleMenuListener(chooser, ACTION_SET_LOCALE, - locale); - item.addSelectionListener(listener); - } - - if (locales.size() > 1) { - @SuppressWarnings("unused") - MenuItem separator = new MenuItem(menu, SWT.SEPARATOR); - - ConfigurationMenuListener.addTogglePreviewModeAction(menu, - "Preview All Locales", chooser, RenderPreviewMode.LOCALES); - } - - @SuppressWarnings("unused") - MenuItem separator = new MenuItem(menu, SWT.SEPARATOR); - - MenuItem item = new MenuItem(menu, SWT.PUSH); - item.setText("Add New Translation..."); - LocaleMenuListener listener = new LocaleMenuListener(chooser, - ACTION_ADD_TRANSLATION, null); - item.addSelectionListener(listener); - - Rectangle bounds = combo.getBounds(); - Point location = new Point(bounds.x, bounds.y + bounds.height); - location = combo.getParent().toDisplay(location); - menu.setLocation(location.x, location.y); - menu.setVisible(true); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/NestedConfiguration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/NestedConfiguration.java deleted file mode 100644 index 50778e2f1..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/NestedConfiguration.java +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.resources.NightMode; -import com.android.resources.UiMode; -import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.devices.Device; -import com.android.sdklib.devices.State; -import com.google.common.base.Objects; - -/** - * An {@linkplain NestedConfiguration} is a {@link Configuration} which inherits - * all of its values from a different configuration, except for one or more - * attributes where it overrides a custom value. - * <p> - * Unlike a {@link VaryingConfiguration}, a {@linkplain NestedConfiguration} - * will always return the same overridden value, regardless of the inherited - * value. - * <p> - * For example, an {@linkplain NestedConfiguration} may fix the locale to always - * be "en", but otherwise inherit everything else. - */ -public class NestedConfiguration extends Configuration { - /** The configuration we are inheriting non-overridden values from */ - protected Configuration mParent; - - /** Bitmask of attributes to be overridden in this configuration */ - private int mOverride; - - /** - * Constructs a new {@linkplain NestedConfiguration}. - * Construct via - * - * @param chooser the associated chooser - * @param configuration the configuration to inherit from - */ - protected NestedConfiguration( - @NonNull ConfigurationChooser chooser, - @NonNull Configuration configuration) { - super(chooser); - mParent = configuration; - - mFullConfig.set(mParent.mFullConfig); - if (mParent.getEditedConfig() != null) { - mEditedConfig = new FolderConfiguration(); - mEditedConfig.set(mParent.mEditedConfig); - } - } - - /** - * Returns the override flags for this configuration. Corresponds to - * the {@code CFG_} flags in {@link ConfigurationClient}. - * - * @return the bitmask - */ - public int getOverrideFlags() { - return mOverride; - } - - /** - * Creates a new {@linkplain NestedConfiguration} that has the same overriding - * attributes as the given other {@linkplain NestedConfiguration}, and gets - * its values from the given {@linkplain Configuration}. - * - * @param other the configuration to copy overrides from - * @param values the configuration to copy values from - * @param parent the parent to tie the configuration to for inheriting values - * @return a new configuration - */ - @NonNull - public static NestedConfiguration create( - @NonNull NestedConfiguration other, - @NonNull Configuration values, - @NonNull Configuration parent) { - NestedConfiguration configuration = - new NestedConfiguration(other.mConfigChooser, parent); - initFrom(configuration, other, values, true /*sync*/); - return configuration; - } - - /** - * Initializes a new {@linkplain NestedConfiguration} with the overriding - * attributes as the given other {@linkplain NestedConfiguration}, and gets - * its values from the given {@linkplain Configuration}. - * - * @param configuration the configuration to initialize - * @param other the configuration to copy overrides from - * @param values the configuration to copy values from - * @param sync if true, sync the folder configuration from - */ - protected static void initFrom(NestedConfiguration configuration, - NestedConfiguration other, Configuration values, boolean sync) { - configuration.mOverride = other.mOverride; - configuration.setDisplayName(values.getDisplayName()); - configuration.setActivity(values.getActivity()); - - if (configuration.isOverridingLocale()) { - configuration.setLocale(values.getLocale(), true); - } - if (configuration.isOverridingTarget()) { - configuration.setTarget(values.getTarget(), true); - } - if (configuration.isOverridingDevice()) { - configuration.setDevice(values.getDevice(), true); - } - if (configuration.isOverridingDeviceState()) { - configuration.setDeviceState(values.getDeviceState(), true); - } - if (configuration.isOverridingNightMode()) { - configuration.setNightMode(values.getNightMode(), true); - } - if (configuration.isOverridingUiMode()) { - configuration.setUiMode(values.getUiMode(), true); - } - if (sync) { - configuration.syncFolderConfig(); - } - } - - /** - * Sets the parent configuration that this configuration is inheriting from. - * - * @param parent the parent configuration - */ - public void setParent(@NonNull Configuration parent) { - mParent = parent; - } - - /** - * Creates a new {@linkplain Configuration} which inherits values from the - * given parent {@linkplain Configuration}, possibly overriding some as - * well. - * - * @param chooser the associated chooser - * @param parent the configuration to inherit values from - * @return a new configuration - */ - @NonNull - public static NestedConfiguration create(@NonNull ConfigurationChooser chooser, - @NonNull Configuration parent) { - return new NestedConfiguration(chooser, parent); - } - - @Override - @Nullable - public String getTheme() { - // Never overridden: this is a static attribute of a layout, not something which - // varies by configuration or at runtime - return mParent.getTheme(); - } - - @Override - public void setTheme(String theme) { - // Never overridden - mParent.setTheme(theme); - } - - /** - * Sets whether the locale should be overridden by this configuration - * - * @param override if true, override the inherited value - */ - public void setOverrideLocale(boolean override) { - mOverride |= CFG_LOCALE; - } - - /** - * Returns true if the locale is overridden - * - * @return true if the locale is overridden - */ - public final boolean isOverridingLocale() { - return (mOverride & CFG_LOCALE) != 0; - } - - @Override - @NonNull - public Locale getLocale() { - if (isOverridingLocale()) { - return super.getLocale(); - } else { - return mParent.getLocale(); - } - } - - @Override - public void setLocale(@NonNull Locale locale, boolean skipSync) { - if (isOverridingLocale()) { - super.setLocale(locale, skipSync); - } else { - mParent.setLocale(locale, skipSync); - } - } - - /** - * Sets whether the rendering target should be overridden by this configuration - * - * @param override if true, override the inherited value - */ - public void setOverrideTarget(boolean override) { - mOverride |= CFG_TARGET; - } - - /** - * Returns true if the target is overridden - * - * @return true if the target is overridden - */ - public final boolean isOverridingTarget() { - return (mOverride & CFG_TARGET) != 0; - } - - @Override - @Nullable - public IAndroidTarget getTarget() { - if (isOverridingTarget()) { - return super.getTarget(); - } else { - return mParent.getTarget(); - } - } - - @Override - public void setTarget(IAndroidTarget target, boolean skipSync) { - if (isOverridingTarget()) { - super.setTarget(target, skipSync); - } else { - mParent.setTarget(target, skipSync); - } - } - - /** - * Sets whether the device should be overridden by this configuration - * - * @param override if true, override the inherited value - */ - public void setOverrideDevice(boolean override) { - mOverride |= CFG_DEVICE; - } - - /** - * Returns true if the device is overridden - * - * @return true if the device is overridden - */ - public final boolean isOverridingDevice() { - return (mOverride & CFG_DEVICE) != 0; - } - - @Override - @Nullable - public Device getDevice() { - if (isOverridingDevice()) { - return super.getDevice(); - } else { - return mParent.getDevice(); - } - } - - @Override - public void setDevice(Device device, boolean skipSync) { - if (isOverridingDevice()) { - super.setDevice(device, skipSync); - } else { - mParent.setDevice(device, skipSync); - } - } - - /** - * Sets whether the device state should be overridden by this configuration - * - * @param override if true, override the inherited value - */ - public void setOverrideDeviceState(boolean override) { - mOverride |= CFG_DEVICE_STATE; - } - - /** - * Returns true if the device state is overridden - * - * @return true if the device state is overridden - */ - public final boolean isOverridingDeviceState() { - return (mOverride & CFG_DEVICE_STATE) != 0; - } - - @Override - @Nullable - public State getDeviceState() { - if (isOverridingDeviceState()) { - return super.getDeviceState(); - } else { - State state = mParent.getDeviceState(); - if (isOverridingDevice()) { - // If the device differs, I need to look up a suitable equivalent state - // on our device - if (state != null) { - Device device = super.getDevice(); - if (device != null) { - return device.getState(state.getName()); - } - } - } - - return state; - } - } - - @Override - public void setDeviceState(State state, boolean skipSync) { - if (isOverridingDeviceState()) { - super.setDeviceState(state, skipSync); - } else { - if (isOverridingDevice()) { - Device device = super.getDevice(); - if (device != null) { - State equivalentState = device.getState(state.getName()); - if (equivalentState != null) { - state = equivalentState; - } - } - } - mParent.setDeviceState(state, skipSync); - } - } - - /** - * Sets whether the night mode should be overridden by this configuration - * - * @param override if true, override the inherited value - */ - public void setOverrideNightMode(boolean override) { - mOverride |= CFG_NIGHT_MODE; - } - - /** - * Returns true if the night mode is overridden - * - * @return true if the night mode is overridden - */ - public final boolean isOverridingNightMode() { - return (mOverride & CFG_NIGHT_MODE) != 0; - } - - @Override - @NonNull - public NightMode getNightMode() { - if (isOverridingNightMode()) { - return super.getNightMode(); - } else { - return mParent.getNightMode(); - } - } - - @Override - public void setNightMode(@NonNull NightMode night, boolean skipSync) { - if (isOverridingNightMode()) { - super.setNightMode(night, skipSync); - } else { - mParent.setNightMode(night, skipSync); - } - } - - /** - * Sets whether the UI mode should be overridden by this configuration - * - * @param override if true, override the inherited value - */ - public void setOverrideUiMode(boolean override) { - mOverride |= CFG_UI_MODE; - } - - /** - * Returns true if the UI mode is overridden - * - * @return true if the UI mode is overridden - */ - public final boolean isOverridingUiMode() { - return (mOverride & CFG_UI_MODE) != 0; - } - - @Override - @NonNull - public UiMode getUiMode() { - if (isOverridingUiMode()) { - return super.getUiMode(); - } else { - return mParent.getUiMode(); - } - } - - @Override - public void setUiMode(@NonNull UiMode uiMode, boolean skipSync) { - if (isOverridingUiMode()) { - super.setUiMode(uiMode, skipSync); - } else { - mParent.setUiMode(uiMode, skipSync); - } - } - - /** - * Returns the configuration this {@linkplain NestedConfiguration} is - * inheriting from - * - * @return the configuration this configuration is inheriting from - */ - @NonNull - public Configuration getParent() { - return mParent; - } - - @Override - @Nullable - public String getActivity() { - return mParent.getActivity(); - } - - @Override - public void setActivity(String activity) { - super.setActivity(activity); - } - - /** - * Returns a computed display name (ignoring the value stored by - * {@link #setDisplayName(String)}) by looking at the override flags - * and picking a suitable name. - * - * @return a suitable display name - */ - @Nullable - public String computeDisplayName() { - return computeDisplayName(mOverride, this); - } - - /** - * Computes a display name for the given configuration, using the given - * override flags (which correspond to the {@code CFG_} constants in - * {@link ConfigurationClient} - * - * @param flags the override bitmask - * @param configuration the configuration to fetch values from - * @return a suitable display name - */ - @Nullable - public static String computeDisplayName(int flags, @NonNull Configuration configuration) { - if ((flags & CFG_LOCALE) != 0) { - return ConfigurationChooser.getLocaleLabel(configuration.mConfigChooser, - configuration.getLocale(), false); - } - - if ((flags & CFG_TARGET) != 0) { - return ConfigurationChooser.getRenderingTargetLabel(configuration.getTarget(), false); - } - - if ((flags & CFG_DEVICE) != 0) { - return ConfigurationChooser.getDeviceLabel(configuration.getDevice(), true); - } - - if ((flags & CFG_DEVICE_STATE) != 0) { - State deviceState = configuration.getDeviceState(); - if (deviceState != null) { - return deviceState.getName(); - } - } - - if ((flags & CFG_NIGHT_MODE) != 0) { - return configuration.getNightMode().getLongDisplayValue(); - } - - if ((flags & CFG_UI_MODE) != 0) { - configuration.getUiMode().getLongDisplayValue(); - } - - return null; - } - - @Override - public String toString() { - return Objects.toStringHelper(this.getClass()) - .add("parent", mParent.getDisplayName()) //$NON-NLS-1$ - .add("display", getDisplayName()) //$NON-NLS-1$ - .add("overrideLocale", isOverridingLocale()) //$NON-NLS-1$ - .add("overrideTarget", isOverridingTarget()) //$NON-NLS-1$ - .add("overrideDevice", isOverridingDevice()) //$NON-NLS-1$ - .add("overrideDeviceState", isOverridingDeviceState()) //$NON-NLS-1$ - .add("persistent", toPersistentString()) //$NON-NLS-1$ - .toString(); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/OrientationMenuAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/OrientationMenuAction.java deleted file mode 100644 index 5cad29afc..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/OrientationMenuAction.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SubmenuAction; -import com.android.resources.NightMode; -import com.android.resources.ScreenOrientation; -import com.android.resources.UiMode; -import com.android.sdklib.devices.Device; -import com.android.sdklib.devices.State; - -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.ActionContributionItem; -import org.eclipse.jface.action.IAction; -import org.eclipse.jface.action.MenuManager; -import org.eclipse.jface.action.Separator; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Menu; -import org.eclipse.swt.widgets.ToolItem; - -import java.util.List; - -/** - * Action which creates a submenu that shows the available orientations as well - * as some related options for night mode and dock mode - */ -class OrientationMenuAction extends SubmenuAction { - // Constants used to indicate what type of menu is being shown, such that - // the submenus can lazily construct their contents - private static final int MENU_NIGHTMODE = 1; - private static final int MENU_UIMODE = 2; - - private final ConfigurationChooser mConfigChooser; - /** Type of menu; one of the constants {@link #MENU_NIGHTMODE} etc */ - private final int mType; - - OrientationMenuAction(int type, String title, ConfigurationChooser configuration) { - super(title); - mType = type; - mConfigChooser = configuration; - } - - static void showMenu(ConfigurationChooser configChooser, ToolItem combo) { - MenuManager manager = new MenuManager(); - - // Show toggles for all the available states - - Configuration configuration = configChooser.getConfiguration(); - Device device = configuration.getDevice(); - State current = configuration.getDeviceState(); - if (device != null) { - List<State> states = device.getAllStates(); - - if (states.size() > 1 && current != null) { - State flip = configuration.getNextDeviceState(current); - String flipName = flip != null ? flip.getName() : current.getName(); - manager.add(new DeviceConfigAction(configChooser, - String.format("Switch to %1$s", flipName), flip, false, true)); - manager.add(new Separator()); - } - - for (State config : states) { - manager.add(new DeviceConfigAction(configChooser, config.getName(), - config, config == current, false)); - } - manager.add(new Separator()); - } - manager.add(new OrientationMenuAction(MENU_UIMODE, "UI Mode", configChooser)); - manager.add(new Separator()); - manager.add(new OrientationMenuAction(MENU_NIGHTMODE, "Night Mode", configChooser)); - - Menu menu = manager.createContextMenu(configChooser.getShell()); - Rectangle bounds = combo.getBounds(); - Point location = new Point(bounds.x, bounds.y + bounds.height); - location = combo.getParent().toDisplay(location); - menu.setLocation(location.x, location.y); - menu.setVisible(true); - } - - @Override - protected void addMenuItems(Menu menu) { - switch (mType) { - case MENU_NIGHTMODE: { - NightMode selected = mConfigChooser.getConfiguration().getNightMode(); - for (NightMode mode : NightMode.values()) { - boolean checked = mode == selected; - SelectNightModeAction action = new SelectNightModeAction(mode, checked); - new ActionContributionItem(action).fill(menu, -1); - - } - break; - } - case MENU_UIMODE: { - UiMode selected = mConfigChooser.getConfiguration().getUiMode(); - for (UiMode mode : UiMode.values()) { - boolean checked = mode == selected; - SelectUiModeAction action = new SelectUiModeAction(mode, checked); - new ActionContributionItem(action).fill(menu, -1); - } - break; - } - } - } - - - private class SelectNightModeAction extends Action { - private final NightMode mMode; - - private SelectNightModeAction(NightMode mode, boolean checked) { - super(mode.getLongDisplayValue(), IAction.AS_RADIO_BUTTON); - mMode = mode; - if (checked) { - setChecked(true); - } - } - - @Override - public void run() { - Configuration configuration = mConfigChooser.getConfiguration(); - configuration.setNightMode(mMode, false); - mConfigChooser.notifyFolderConfigChanged(); - } - } - - private class SelectUiModeAction extends Action { - private final UiMode mMode; - - private SelectUiModeAction(UiMode mode, boolean checked) { - super(mode.getLongDisplayValue(), IAction.AS_RADIO_BUTTON); - mMode = mode; - if (checked) { - setChecked(true); - } - } - - @Override - public void run() { - Configuration configuration = mConfigChooser.getConfiguration(); - configuration.setUiMode(mMode, false); - } - } - - private static class DeviceConfigAction extends Action { - private final ConfigurationChooser mConfiguration; - private final State mState; - - private DeviceConfigAction(ConfigurationChooser configuration, String title, - State state, boolean checked, boolean flip) { - super(title, IAction.AS_RADIO_BUTTON); - mConfiguration = configuration; - mState = state; - if (checked) { - setChecked(true); - } - ScreenOrientation orientation = configuration.getOrientation(state); - setImageDescriptor(configuration.getOrientationImage(orientation, flip)); - } - - @Override - public void run() { - mConfiguration.selectDeviceState(mState); - mConfiguration.onDeviceConfigChange(); - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/SelectThemeAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/SelectThemeAction.java deleted file mode 100644 index d062849d1..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/SelectThemeAction.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX; -import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX; - -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.IAction; - -/** - * Action which brings up the "Create new XML File" wizard, pre-selected with the - * animation category - */ -class SelectThemeAction extends Action { - private final ConfigurationChooser mConfiguration; - private final String mTheme; - - public SelectThemeAction(ConfigurationChooser configuration, String title, String theme, - boolean selected) { - super(title, IAction.AS_RADIO_BUTTON); - assert theme.startsWith(STYLE_RESOURCE_PREFIX) - || theme.startsWith(ANDROID_STYLE_RESOURCE_PREFIX) : theme; - mConfiguration = configuration; - mTheme = theme; - if (selected) { - setChecked(selected); - } - } - - @Override - public void run() { - mConfiguration.selectTheme(mTheme); - mConfiguration.onThemeChange(); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/TargetMenuListener.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/TargetMenuListener.java deleted file mode 100644 index 71905f7c9..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/TargetMenuListener.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; -import com.android.sdklib.AndroidVersion; -import com.android.sdklib.IAndroidTarget; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Menu; -import org.eclipse.swt.widgets.MenuItem; -import org.eclipse.swt.widgets.ToolItem; - -import java.util.List; -import java.util.RandomAccess; - -/** - * The {@linkplain TargetMenuListener} class is responsible for - * generating the rendering target menu in the {@link ConfigurationChooser}. - */ -class TargetMenuListener extends SelectionAdapter { - private final ConfigurationChooser mConfigChooser; - private final IAndroidTarget mTarget; - private final boolean mPickBest; - - TargetMenuListener( - @NonNull ConfigurationChooser configChooser, - @Nullable IAndroidTarget target, - boolean pickBest) { - mConfigChooser = configChooser; - mTarget = target; - mPickBest = pickBest; - } - - @Override - public void widgetSelected(SelectionEvent e) { - IAndroidTarget target = mTarget; - AdtPrefs prefs = AdtPrefs.getPrefs(); - if (mPickBest) { - boolean autoPick = prefs.isAutoPickRenderTarget(); - autoPick = !autoPick; - prefs.setAutoPickRenderTarget(autoPick); - if (autoPick) { - target = ConfigurationMatcher.findDefaultRenderTarget(mConfigChooser); - } else { - // Turn it off, but keep current target until another one is chosen - return; - } - } else { - // Manually picked some other target: turn off auto-pick - prefs.setAutoPickRenderTarget(false); - } - mConfigChooser.selectTarget(target); - mConfigChooser.onRenderingTargetChange(); - } - - static void show(ConfigurationChooser chooser, ToolItem combo) { - Menu menu = new Menu(chooser.getShell(), SWT.POP_UP); - Configuration configuration = chooser.getConfiguration(); - IAndroidTarget current = configuration.getTarget(); - List<IAndroidTarget> targets = chooser.getTargetList(); - boolean haveRecent = false; - - MenuItem menuItem = new MenuItem(menu, SWT.CHECK); - menuItem.setText("Automatically Pick Best"); - menuItem.addSelectionListener(new TargetMenuListener(chooser, null, true)); - if (AdtPrefs.getPrefs().isAutoPickRenderTarget()) { - menuItem.setSelection(true); - } - - @SuppressWarnings("unused") - MenuItem separator = new MenuItem(menu, SWT.SEPARATOR); - - // Process in reverse order: most important targets first - assert targets instanceof RandomAccess; - for (int i = targets.size() - 1; i >= 0; i--) { - IAndroidTarget target = targets.get(i); - - AndroidVersion version = target.getVersion(); - if (version.getApiLevel() >= 7) { - haveRecent = true; - } else if (haveRecent) { - // Don't show ancient rendering targets; they're pretty broken - // (unless of course all you have are ancient targets) - break; - } - - String title = ConfigurationChooser.getRenderingTargetLabel(target, false); - MenuItem item = new MenuItem(menu, SWT.CHECK); - item.setText(title); - - boolean selected = current == target; - if (selected) { - item.setSelection(true); - } - - item.addSelectionListener(new TargetMenuListener(chooser, target, false)); - } - - Rectangle bounds = combo.getBounds(); - Point location = new Point(bounds.x, bounds.y + bounds.height); - location = combo.getParent().toDisplay(location); - menu.setLocation(location.x, location.y); - menu.setVisible(true); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ThemeMenuAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ThemeMenuAction.java deleted file mode 100644 index b1ce21d36..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ThemeMenuAction.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX; - -import com.android.ide.eclipse.adt.internal.editors.Hyperlinks; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SubmenuAction; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo.ActivityAttributes; -import com.android.ide.eclipse.adt.internal.resources.ResourceHelper; -import com.android.sdklib.IAndroidTarget; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.ActionContributionItem; -import org.eclipse.jface.action.IAction; -import org.eclipse.jface.action.MenuManager; -import org.eclipse.jface.action.Separator; -import org.eclipse.jface.text.hyperlink.IHyperlink; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Menu; -import org.eclipse.swt.widgets.ToolItem; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Action which creates a submenu displaying available themes - */ -class ThemeMenuAction extends SubmenuAction { - private static final String DEVICE_LIGHT_PREFIX = - ANDROID_STYLE_RESOURCE_PREFIX + "Theme.DeviceDefault.Light"; //$NON-NLS-1$ - private static final String HOLO_LIGHT_PREFIX = - ANDROID_STYLE_RESOURCE_PREFIX + "Theme.Holo.Light"; //$NON-NLS-1$ - private static final String DEVICE_PREFIX = - ANDROID_STYLE_RESOURCE_PREFIX + "Theme.DeviceDefault"; //$NON-NLS-1$ - private static final String HOLO_PREFIX = - ANDROID_STYLE_RESOURCE_PREFIX + "Theme.Holo"; //$NON-NLS-1$ - private static final String LIGHT_PREFIX = - ANDROID_STYLE_RESOURCE_PREFIX +"Theme.Light"; //$NON-NLS-1$ - private static final String THEME_PREFIX = - ANDROID_STYLE_RESOURCE_PREFIX +"Theme"; //$NON-NLS-1$ - - // Constants used to indicate what type of menu is being shown, such that - // the submenus can lazily construct their contents - private static final int MENU_MANIFEST = 1; - private static final int MENU_PROJECT = 2; - private static final int MENU_THEME = 3; - private static final int MENU_THEME_LIGHT = 4; - private static final int MENU_HOLO = 5; - private static final int MENU_HOLO_LIGHT = 6; - private static final int MENU_DEVICE = 7; - private static final int MENU_DEVICE_LIGHT = 8; - private static final int MENU_ALL = 9; - - private final ConfigurationChooser mConfigChooser; - private final List<String> mThemeList; - /** Type of menu; one of the constants {@link #MENU_ALL} etc */ - private final int mType; - - ThemeMenuAction(int type, String title, ConfigurationChooser configuration, - List<String> themeList) { - super(title); - mType = type; - mConfigChooser = configuration; - mThemeList = themeList; - } - - static void showThemeMenu(ConfigurationChooser configChooser, ToolItem combo, - List<String> themeList) { - MenuManager manager = new MenuManager(); - - // First show the currently selected theme (grayed out since you can't - // reselect it) - Configuration configuration = configChooser.getConfiguration(); - String currentTheme = configuration.getTheme(); - String currentName = null; - if (currentTheme != null) { - currentName = ResourceHelper.styleToTheme(currentTheme); - SelectThemeAction action = new SelectThemeAction(configChooser, - currentName, - currentTheme, - true /* selected */); - action.setEnabled(false); - manager.add(action); - manager.add(new Separator()); - } - - String preferred = configuration.computePreferredTheme(); - if (preferred != null && !preferred.equals(currentTheme)) { - manager.add(new SelectThemeAction(configChooser, - ResourceHelper.styleToTheme(preferred), - preferred, false /* selected */)); - manager.add(new Separator()); - } - - IAndroidTarget target = configuration.getTarget(); - int apiLevel = target != null ? target.getVersion().getApiLevel() : 1; - boolean hasHolo = apiLevel >= 11; // Honeycomb - boolean hasDeviceDefault = apiLevel >= 14; // ICS - - // TODO: Add variations of the current theme here, e.g. - // if you're using Theme.Holo, add Theme.Holo.Dialog, Theme.Holo.Panel, - // Theme.Holo.Wallpaper etc - - manager.add(new ThemeMenuAction(MENU_PROJECT, "Project Themes", - configChooser, themeList)); - manager.add(new ThemeMenuAction(MENU_MANIFEST, "Manifest Themes", - configChooser, themeList)); - - manager.add(new Separator()); - - if (hasHolo) { - manager.add(new ThemeMenuAction(MENU_HOLO, "Holo", - configChooser, themeList)); - manager.add(new ThemeMenuAction(MENU_HOLO_LIGHT, "Holo.Light", - configChooser, themeList)); - } - if (hasDeviceDefault) { - manager.add(new ThemeMenuAction(MENU_DEVICE, "DeviceDefault", - configChooser, themeList)); - manager.add(new ThemeMenuAction(MENU_DEVICE_LIGHT, "DeviceDefault.Light", - configChooser, themeList)); - } - manager.add(new ThemeMenuAction(MENU_THEME, "Theme", - configChooser, themeList)); - manager.add(new ThemeMenuAction(MENU_THEME_LIGHT, "Theme.Light", - configChooser, themeList)); - - // TODO: Add generic types like Wallpaper, Dialog, Alert, etc here, with - // submenus for picking it within each theme category? - - manager.add(new Separator()); - manager.add(new ThemeMenuAction(MENU_ALL, "All", - configChooser, themeList)); - - if (currentTheme != null) { - assert currentName != null; - manager.add(new Separator()); - String title = String.format("Open %1$s Declaration...", currentName); - manager.add(new OpenThemeAction(title, configChooser.getEditedFile(), currentTheme)); - } - - Menu menu = manager.createContextMenu(configChooser.getShell()); - - Rectangle bounds = combo.getBounds(); - Point location = new Point(bounds.x, bounds.y + bounds.height); - location = combo.getParent().toDisplay(location); - menu.setLocation(location.x, location.y); - menu.setVisible(true); - } - - @Override - protected void addMenuItems(Menu menu) { - switch (mType) { - case MENU_ALL: - addMenuItems(menu, mThemeList); - break; - - case MENU_MANIFEST: { - IProject project = mConfigChooser.getEditedFile().getProject(); - ManifestInfo manifest = ManifestInfo.get(project); - Configuration configuration = mConfigChooser.getConfiguration(); - String activity = configuration.getActivity(); - if (activity != null) { - ActivityAttributes attributes = manifest.getActivityAttributes(activity); - if (attributes != null) { - String theme = attributes.getTheme(); - if (theme != null) { - addMenuItem(menu, theme, isSelectedTheme(theme)); - } - } - } - - String manifestTheme = manifest.getManifestTheme(); - boolean found = false; - Set<String> allThemes = new HashSet<String>(); - if (manifestTheme != null) { - found = true; - allThemes.add(manifestTheme); - } - for (ActivityAttributes info : manifest.getActivityAttributesMap().values()) { - if (info.getTheme() != null) { - found = true; - allThemes.add(info.getTheme()); - } - } - List<String> sorted = new ArrayList<String>(allThemes); - Collections.sort(sorted); - String current = configuration.getTheme(); - for (String theme : sorted) { - boolean selected = theme.equals(current); - addMenuItem(menu, theme, selected); - } - if (!found) { - addDisabledMessageItem("No themes are registered in the manifest"); - } - break; - } - case MENU_PROJECT: { - int size = mThemeList.size(); - List<String> themes = new ArrayList<String>(size); - for (int i = 0; i < size; i++) { - String theme = mThemeList.get(i); - if (ResourceHelper.isProjectStyle(theme)) { - themes.add(theme); - } - } - if (themes.isEmpty()) { - addDisabledMessageItem("There are no local theme styles in the project"); - } else { - addMenuItems(menu, themes); - } - break; - } - case MENU_THEME: { - // Can't just use the usual filterThemes() call here because we need - // to exclude on multiple prefixes: Holo, DeviceDefault, Light, ... - List<String> themes = new ArrayList<String>(mThemeList.size()); - for (String theme : mThemeList) { - if (theme.startsWith(THEME_PREFIX) - && !theme.startsWith(LIGHT_PREFIX) - && !theme.startsWith(HOLO_PREFIX) - && !theme.startsWith(DEVICE_PREFIX)) { - themes.add(theme); - } - } - - addMenuItems(menu, themes); - break; - } - case MENU_THEME_LIGHT: - addMenuItems(menu, filterThemes(LIGHT_PREFIX, null)); - break; - case MENU_HOLO: - addMenuItems(menu, filterThemes(HOLO_PREFIX, HOLO_LIGHT_PREFIX)); - break; - case MENU_HOLO_LIGHT: - addMenuItems(menu, filterThemes(HOLO_LIGHT_PREFIX, null)); - break; - case MENU_DEVICE: - addMenuItems(menu, filterThemes(DEVICE_PREFIX, DEVICE_LIGHT_PREFIX)); - break; - case MENU_DEVICE_LIGHT: - addMenuItems(menu, filterThemes(DEVICE_LIGHT_PREFIX, null)); - break; - } - } - - private List<String> filterThemes(String include, String exclude) { - List<String> themes = new ArrayList<String>(mThemeList.size()); - for (String theme : mThemeList) { - if (theme.startsWith(include) && (exclude == null || !theme.startsWith(exclude))) { - themes.add(theme); - } - } - - return themes; - } - - private void addMenuItems(Menu menu, List<String> themes) { - String current = mConfigChooser.getConfiguration().getTheme(); - for (String theme : themes) { - addMenuItem(menu, theme, theme.equals(current)); - } - } - - private boolean isSelectedTheme(String theme) { - return theme.equals(mConfigChooser.getConfiguration().getTheme()); - } - - private void addMenuItem(Menu menu, String theme, boolean selected) { - String title = ResourceHelper.styleToTheme(theme); - SelectThemeAction action = new SelectThemeAction(mConfigChooser, title, theme, selected); - new ActionContributionItem(action).fill(menu, -1); - } - - private static class OpenThemeAction extends Action { - private final String mTheme; - private final IFile mFile; - - private OpenThemeAction(String title, IFile file, String theme) { - super(title, IAction.AS_PUSH_BUTTON); - mFile = file; - mTheme = theme; - } - - @Override - public void run() { - IProject project = mFile.getProject(); - IHyperlink[] links = Hyperlinks.getResourceLinks(null, mTheme, project, null); - if (links != null && links.length > 0) { - IHyperlink link = links[0]; - link.open(); - } - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/VaryingConfiguration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/VaryingConfiguration.java deleted file mode 100644 index f472cd6b3..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/VaryingConfiguration.java +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (C) 2012 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.editors.layout.configuration; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.rendering.api.Capability; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; -import com.android.resources.Density; -import com.android.resources.NightMode; -import com.android.resources.UiMode; -import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.devices.Device; -import com.android.sdklib.devices.Hardware; -import com.android.sdklib.devices.Screen; -import com.android.sdklib.devices.State; - -import java.util.Collection; -import java.util.List; - -/** - * An {@linkplain VaryingConfiguration} is a {@link Configuration} which - * inherits all of its values from a different configuration, except for one or - * more attributes where it overrides a custom value, and the overridden value - * will always <b>differ</b> from the inherited value! - * <p> - * For example, a {@linkplain VaryingConfiguration} may state that it - * overrides the locale, and if the inherited locale is "en", then the returned - * locale from the {@linkplain VaryingConfiguration} may be for example "nb", - * but never "en". - * <p> - * The configuration will attempt to make its changed inherited value to be as - * different as possible from the inherited value. Thus, a configuration which - * overrides the device will probably return a phone-sized screen if the - * inherited device is a tablet, or vice versa. - */ -public class VaryingConfiguration extends NestedConfiguration { - /** Variation version; see {@link #setVariation(int)} */ - private int mVariation; - - /** Variation version count; see {@link #setVariationCount(int)} */ - private int mVariationCount; - - /** Bitmask of attributes to be varied/alternated from the parent */ - private int mAlternate; - - /** - * Constructs a new {@linkplain VaryingConfiguration}. - * Construct via - * - * @param chooser the associated chooser - * @param configuration the configuration to inherit from - */ - private VaryingConfiguration( - @NonNull ConfigurationChooser chooser, - @NonNull Configuration configuration) { - super(chooser, configuration); - } - - /** - * Creates a new {@linkplain Configuration} which inherits values from the - * given parent {@linkplain Configuration}, possibly overriding some as - * well. - * - * @param chooser the associated chooser - * @param parent the configuration to inherit values from - * @return a new configuration - */ - @NonNull - public static VaryingConfiguration create(@NonNull ConfigurationChooser chooser, - @NonNull Configuration parent) { - return new VaryingConfiguration(chooser, parent); - } - - /** - * Creates a new {@linkplain VaryingConfiguration} that has the same overriding - * attributes as the given other {@linkplain VaryingConfiguration}. - * - * @param other the configuration to copy overrides from - * @param parent the parent to tie the configuration to for inheriting values - * @return a new configuration - */ - @NonNull - public static VaryingConfiguration create( - @NonNull VaryingConfiguration other, - @NonNull Configuration parent) { - VaryingConfiguration configuration = - new VaryingConfiguration(other.mConfigChooser, parent); - initFrom(configuration, other, other, false); - configuration.mAlternate = other.mAlternate; - configuration.mVariation = other.mVariation; - configuration.mVariationCount = other.mVariationCount; - configuration.syncFolderConfig(); - - return configuration; - } - - /** - * Returns the alternate flags for this configuration. Corresponds to - * the {@code CFG_} flags in {@link ConfigurationClient}. - * - * @return the bitmask - */ - public int getAlternateFlags() { - return mAlternate; - } - - @Override - public void syncFolderConfig() { - super.syncFolderConfig(); - updateDisplayName(); - } - - /** - * Sets the variation version for this - * {@linkplain VaryingConfiguration}. There might be multiple - * {@linkplain VaryingConfiguration} instances inheriting from a - * {@link Configuration}. The variation version allows them to choose - * different complementing values, so they don't all flip to the same other - * (out of multiple choices) value. The {@link #setVariationCount(int)} - * value can be used to determine how to partition the buckets of values. - * Also updates the variation count if necessary. - * - * @param variation variation version - */ - public void setVariation(int variation) { - mVariation = variation; - mVariationCount = Math.max(mVariationCount, variation + 1); - } - - /** - * Sets the number of {@link VaryingConfiguration} variations mapped - * to the same parent configuration as this one. See - * {@link #setVariation(int)} for details. - * - * @param count the total number of variation versions - */ - public void setVariationCount(int count) { - mVariationCount = count; - } - - /** - * Updates the display name in this configuration based on the values and override settings - */ - public void updateDisplayName() { - setDisplayName(computeDisplayName()); - } - - @Override - @NonNull - public Locale getLocale() { - if (isOverridingLocale()) { - return super.getLocale(); - } - Locale locale = mParent.getLocale(); - if (isAlternatingLocale() && locale != null) { - List<Locale> locales = mConfigChooser.getLocaleList(); - for (Locale l : locales) { - // TODO: Try to be smarter about which one we pick; for example, try - // to pick a language that is substantially different from the inherited - // language, such as either with the strings of the largest or shortest - // length, or perhaps based on some geography or population metrics - if (!l.equals(locale)) { - locale = l; - break; - } - } - } - - return locale; - } - - @Override - @Nullable - public IAndroidTarget getTarget() { - if (isOverridingTarget()) { - return super.getTarget(); - } - IAndroidTarget target = mParent.getTarget(); - if (isAlternatingTarget() && target != null) { - List<IAndroidTarget> targets = mConfigChooser.getTargetList(); - if (!targets.isEmpty()) { - // Pick a different target: if you're showing the most recent render target, - // then pick the lowest supported target, and vice versa - IAndroidTarget mostRecent = targets.get(targets.size() - 1); - if (target.equals(mostRecent)) { - // Find oldest supported - ManifestInfo info = ManifestInfo.get(mConfigChooser.getProject()); - int minSdkVersion = info.getMinSdkVersion(); - for (IAndroidTarget t : targets) { - if (t.getVersion().getApiLevel() >= minSdkVersion) { - target = t; - break; - } - } - } else { - target = mostRecent; - } - } - } - - return target; - } - - // Cached values, key=parent's device, cached value=device - private Device mPrevParentDevice; - private Device mPrevDevice; - - @Override - @Nullable - public Device getDevice() { - if (isOverridingDevice()) { - return super.getDevice(); - } - Device device = mParent.getDevice(); - if (isAlternatingDevice() && device != null) { - if (device == mPrevParentDevice) { - return mPrevDevice; - } - - mPrevParentDevice = device; - - // Pick a different device - Collection<Device> devices = mConfigChooser.getDevices(); - - // Divide up the available devices into {@link #mVariationCount} + 1 buckets - // (the + 1 is for the bucket now taken up by the inherited value). - // Then assign buckets to each {@link #mVariation} version, and pick one - // from the bucket assigned to this current configuration's variation version. - - // I could just divide up the device list count, but that would treat a lot of - // very similar phones as having the same kind of variety as the 7" and 10" - // tablets which are sitting right next to each other in the device list. - // Instead, do this by screen size. - - - double smallest = 100; - double biggest = 1; - for (Device d : devices) { - double size = getScreenSize(d); - if (size < 0) { - continue; // no data - } - if (size >= biggest) { - biggest = size; - } - if (size <= smallest) { - smallest = size; - } - } - - int bucketCount = mVariationCount + 1; - double inchesPerBucket = (biggest - smallest) / bucketCount; - - double overriddenSize = getScreenSize(device); - int overriddenBucket = (int) ((overriddenSize - smallest) / inchesPerBucket); - int bucket = (mVariation < overriddenBucket) ? mVariation : mVariation + 1; - double from = inchesPerBucket * bucket + smallest; - double to = from + inchesPerBucket; - if (biggest - to < 0.1) { - to = biggest + 0.1; - } - - boolean canScaleNinePatch = supports(Capability.FIXED_SCALABLE_NINE_PATCH); - for (Device d : devices) { - double size = getScreenSize(d); - if (size >= from && size < to) { - if (!canScaleNinePatch) { - Density density = getDensity(d); - if (density == Density.TV || density == Density.LOW) { - continue; - } - } - - device = d; - break; - } - } - - mPrevDevice = device; - } - - return device; - } - - /** - * Returns the density of the given device - * - * @param device the device to check - * @return the density or null - */ - @Nullable - private static Density getDensity(@NonNull Device device) { - Hardware hardware = device.getDefaultHardware(); - if (hardware != null) { - Screen screen = hardware.getScreen(); - if (screen != null) { - return screen.getPixelDensity(); - } - } - - return null; - } - - /** - * Returns the diagonal length of the given device - * - * @param device the device to check - * @return the diagonal length or -1 - */ - private static double getScreenSize(@NonNull Device device) { - Hardware hardware = device.getDefaultHardware(); - if (hardware != null) { - Screen screen = hardware.getScreen(); - if (screen != null) { - return screen.getDiagonalLength(); - } - } - - return -1; - } - - @Override - @Nullable - public State getDeviceState() { - if (isOverridingDeviceState()) { - return super.getDeviceState(); - } - State state = mParent.getDeviceState(); - if (isAlternatingDeviceState() && state != null) { - State alternate = getNextDeviceState(state); - - return alternate; - } else { - if ((isAlternatingDevice() || isOverridingDevice()) && state != null) { - // If the device differs, I need to look up a suitable equivalent state - // on our device - Device device = getDevice(); - if (device != null) { - return device.getState(state.getName()); - } - } - - return state; - } - } - - @Override - @NonNull - public NightMode getNightMode() { - if (isOverridingNightMode()) { - return super.getNightMode(); - } - NightMode nightMode = mParent.getNightMode(); - if (isAlternatingNightMode() && nightMode != null) { - nightMode = nightMode == NightMode.NIGHT ? NightMode.NOTNIGHT : NightMode.NIGHT; - return nightMode; - } else { - return nightMode; - } - } - - @Override - @NonNull - public UiMode getUiMode() { - if (isOverridingUiMode()) { - return super.getUiMode(); - } - UiMode uiMode = mParent.getUiMode(); - if (isAlternatingUiMode() && uiMode != null) { - // TODO: Use manifest's supports screen to decide which are most relevant - // (as well as which available configuration qualifiers are present in the - // layout) - UiMode[] values = UiMode.values(); - uiMode = values[(uiMode.ordinal() + 1) % values.length]; - return uiMode; - } else { - return uiMode; - } - } - - @Override - @Nullable - public String computeDisplayName() { - return computeDisplayName(getOverrideFlags() | mAlternate, this); - } - - /** - * Sets whether the locale should be alternated by this configuration - * - * @param alternate if true, alternate the inherited value - */ - public void setAlternateLocale(boolean alternate) { - mAlternate |= CFG_LOCALE; - } - - /** - * Returns true if the locale is alternated - * - * @return true if the locale is alternated - */ - public final boolean isAlternatingLocale() { - return (mAlternate & CFG_LOCALE) != 0; - } - - /** - * Sets whether the rendering target should be alternated by this configuration - * - * @param alternate if true, alternate the inherited value - */ - public void setAlternateTarget(boolean alternate) { - mAlternate |= CFG_TARGET; - } - - /** - * Returns true if the target is alternated - * - * @return true if the target is alternated - */ - public final boolean isAlternatingTarget() { - return (mAlternate & CFG_TARGET) != 0; - } - - /** - * Sets whether the device should be alternated by this configuration - * - * @param alternate if true, alternate the inherited value - */ - public void setAlternateDevice(boolean alternate) { - mAlternate |= CFG_DEVICE; - } - - /** - * Returns true if the device is alternated - * - * @return true if the device is alternated - */ - public final boolean isAlternatingDevice() { - return (mAlternate & CFG_DEVICE) != 0; - } - - /** - * Sets whether the device state should be alternated by this configuration - * - * @param alternate if true, alternate the inherited value - */ - public void setAlternateDeviceState(boolean alternate) { - mAlternate |= CFG_DEVICE_STATE; - } - - /** - * Returns true if the device state is alternated - * - * @return true if the device state is alternated - */ - public final boolean isAlternatingDeviceState() { - return (mAlternate & CFG_DEVICE_STATE) != 0; - } - - /** - * Sets whether the night mode should be alternated by this configuration - * - * @param alternate if true, alternate the inherited value - */ - public void setAlternateNightMode(boolean alternate) { - mAlternate |= CFG_NIGHT_MODE; - } - - /** - * Returns true if the night mode is alternated - * - * @return true if the night mode is alternated - */ - public final boolean isAlternatingNightMode() { - return (mAlternate & CFG_NIGHT_MODE) != 0; - } - - /** - * Sets whether the UI mode should be alternated by this configuration - * - * @param alternate if true, alternate the inherited value - */ - public void setAlternateUiMode(boolean alternate) { - mAlternate |= CFG_UI_MODE; - } - - /** - * Returns true if the UI mode is alternated - * - * @return true if the UI mode is alternated - */ - public final boolean isAlternatingUiMode() { - return (mAlternate & CFG_UI_MODE) != 0; - } - -}
\ No newline at end of file |