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