blob: 503399434b6b40c6227a46821f08e91b496b995e [file] [log] [blame]
/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.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/legal/epl-v10.html
*
* 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.google.dart.tools.ui.theme;
import com.google.dart.tools.deploy.Activator;
import com.google.dart.tools.ui.internal.text.editor.DartEditor;
import com.google.dart.tools.ui.theme.mapper.GenericMapper;
import com.google.dart.tools.ui.theme.mapper.ThemePreferenceMapper;
import com.google.dart.tools.ui.theme.preferences.ThemePreferencePage;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.BACKGROUND;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.CURRENT_LINE;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.DARTDOC;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.DARTDOC_KEYWORD;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.DARTDOC_LINK;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.DARTDOC_TAG;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.DEBUG_CURRENT_INSTRUCTION_POINTER;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.DEBUG_SECONDARY_INSTRUCTION_POINTER;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.FIELD;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.FOREGROUND;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.LOCAL_VARIABLE;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.METHOD;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.MULTI_LINE_COMMENT;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.OCCURRENCE_INDICATION;
import static com.google.dart.tools.ui.theme.ColorThemeKeys.WRITE_OCCURRENCE_INDICATION;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.preferences.WorkingCopyManager;
import org.eclipse.ui.texteditor.ChainedPreferenceStore;
import org.osgi.framework.Bundle;
import org.osgi.service.prefs.BackingStoreException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
/**
* Load and apply color themes.
*
* @see com.github.eclipsecolortheme.ColorThemeManager
*/
public class ColorThemeManager {
private static class IEclipsePreferencesAdapter extends DartEditor.EclipsePreferencesAdapter {
private IEclipsePreferences store;
IEclipsePreferencesAdapter(IEclipsePreferences prefs) {
super(null, null);
store = prefs;
}
@Override
protected IEclipsePreferences getNode() {
return store;
}
}
public static final String DEFAULT_THEME_NAME = "Default";
public static ColorTheme parseTheme(InputStream input) throws ParserConfigurationException,
SAXException, IOException {
ColorTheme theme = new ColorTheme();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(input);
Element root = document.getDocumentElement();
theme.setId(root.getAttribute("id")); // $NON-NLS-1$
theme.setName(root.getAttribute("name")); // $NON-NLS-1$
theme.setAuthor(root.getAttribute("author")); // $NON-NLS-1$
theme.setWebsite(root.getAttribute("website")); // $NON-NLS-1$
Map<String, ColorThemeSetting> entries = new HashMap<String, ColorThemeSetting>();
NodeList entryNodes = root.getChildNodes();
for (int i = 0; i < entryNodes.getLength(); i++) {
Node entryNode = entryNodes.item(i);
if (entryNode.hasAttributes()) {
String color = entryNode.getAttributes().getNamedItem("color").getNodeValue(); // $NON-NLS-1$
Node nodeBold = entryNode.getAttributes().getNamedItem("bold"); // $NON-NLS-1$
Node nodeItalic = entryNode.getAttributes().getNamedItem("italic"); // $NON-NLS-1$
Node nodeUnderline = entryNode.getAttributes().getNamedItem("underline"); // $NON-NLS-1$
Node nodeStrikethrough = entryNode.getAttributes().getNamedItem("strikethrough"); // $NON-NLS-1$
ColorThemeSetting setting = new ColorThemeSetting(color);
if (nodeBold != null) {
setting.setBoldEnabled(Boolean.parseBoolean(nodeBold.getNodeValue()));
}
if (nodeItalic != null) {
setting.setItalicEnabled(Boolean.parseBoolean(nodeItalic.getNodeValue()));
}
if (nodeStrikethrough != null) {
setting.setStrikethroughEnabled(Boolean.parseBoolean(nodeStrikethrough.getNodeValue()));
}
if (nodeUnderline != null) {
setting.setUnderlineEnabled(Boolean.parseBoolean(nodeUnderline.getNodeValue()));
}
entries.put(entryNode.getNodeName(), setting);
}
}
theme.setEntries(entries);
return theme;
}
private static void addUndefinedDefaults(ColorTheme theme, Map<String, ColorTheme> themes) {
ColorTheme defaults = themes.get(DEFAULT_THEME_NAME);
if (theme == defaults) {
return;
}
Map<String, ColorThemeSetting> themeEntries = theme.getEntries();
Map<String, ColorThemeSetting> defaultEntries = defaults.getEntries();
for (String name : defaultEntries.keySet()) {
if (!themeEntries.containsKey(name)) {
themeEntries.put(name, defaultEntries.get(name).copy());
}
}
}
private static void amendThemeEntries(Map<String, ColorThemeSetting> theme) {
applyDefault(theme, METHOD, FOREGROUND);
applyDefault(theme, FIELD, FOREGROUND);
applyDefault(theme, LOCAL_VARIABLE, FOREGROUND);
applyDefault(theme, DARTDOC, MULTI_LINE_COMMENT);
applyDefault(theme, DARTDOC_LINK, DARTDOC);
applyDefault(theme, DARTDOC_TAG, DARTDOC);
applyDefault(theme, DARTDOC_KEYWORD, DARTDOC);
applyDefault(theme, OCCURRENCE_INDICATION, BACKGROUND);
applyDefault(theme, WRITE_OCCURRENCE_INDICATION, OCCURRENCE_INDICATION);
applyDefault(theme, DEBUG_CURRENT_INSTRUCTION_POINTER, CURRENT_LINE);
applyDefault(theme, DEBUG_SECONDARY_INSTRUCTION_POINTER, CURRENT_LINE);
}
private static void applyDefault(Map<String, ColorThemeSetting> theme, ColorThemeKeys key,
ColorThemeKeys defaultKey) {
if (!theme.containsKey(key.name)) {
theme.put(key.name, theme.get(defaultKey.name));
}
}
private static IPreferenceStore getPreferenceStore() {
return ThemePreferencePage.globalPreferences();
}
private static void readImportedThemes(Map<String, ColorTheme> themes) {
IPreferenceStore store = getPreferenceStore();
for (int i = 1;; i++) {
String xml = store.getString("importedColorTheme" + i); // $NON-NLS-1$
if (xml == null || xml.length() == 0) {
break;
}
try {
ColorTheme theme = parseTheme(new ByteArrayInputStream(xml.getBytes()));
amendThemeEntries(theme.getEntries());
addUndefinedDefaults(theme, themes);
themes.put(theme.getName(), theme);
} catch (Exception e) {
// TODO(messick): Add proper error reporting
System.err.println("Error while parsing imported theme"); // $NON-NLS-1$
e.printStackTrace();
}
}
}
private static void readStockThemes(Map<String, ColorTheme> themes) {
IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(
Activator.EXTENSION_POINT_ID_THEME);
try {
for (IConfigurationElement e : config) {
String xml = e.getAttribute("file"); // $NON-NLS-1$
String contributorPluginId = e.getContributor().getName();
Bundle bundle = Platform.getBundle(contributorPluginId);
InputStream input = (InputStream) bundle.getResource(xml).getContent();
ColorTheme theme = parseTheme(input);
amendThemeEntries(theme.getEntries());
themes.put(theme.getName(), theme);
}
} catch (Exception e) {
e.printStackTrace();
}
for (ColorTheme theme : themes.values()) {
addUndefinedDefaults(theme, themes);
}
}
private Map<String, ColorTheme> themes;
private Set<ThemePreferenceMapper> editors;
private WorkingCopyManager preferenceManager;
/** Creates a new color theme manager. */
public ColorThemeManager() {
preferenceManager = new WorkingCopyManager();
themes = new HashMap<String, ColorTheme>();
readStockThemes(themes);
readImportedThemes(themes);
editors = new HashSet<ThemePreferenceMapper>();
IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(
Activator.EXTENSION_POINT_ID_MAPPER);
try {
for (IConfigurationElement e : config) {
final Object o = e.createExecutableExtension("class"); // $NON-NLS-1$
if (o instanceof ThemePreferenceMapper) {
String pluginId = e.getAttribute("pluginId"); // $NON-NLS-1$
ThemePreferenceMapper mapper = (ThemePreferenceMapper) o;
mapper.setPluginId(pluginId, preferenceManager);
if (o instanceof GenericMapper) {
String xml = e.getAttribute("xml"); // $NON-NLS-1$
String contributorPluginId = e.getContributor().getName();
Bundle bundle = Platform.getBundle(contributorPluginId);
InputStream input = (InputStream) bundle.getResource(xml).getContent();
((GenericMapper) mapper).parseMapping(input);
}
editors.add(mapper);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Changes the preferences of other plugins to apply the color theme.
*
* @param theme The name of the color theme to apply.
*/
public void applyTheme(String theme) {
for (ThemePreferenceMapper editor : editors) {
applyThemeIn(theme, editor);
}
}
public void clearImportedThemes() {
IPreferenceStore store = getPreferenceStore();
for (int i = 1; store.contains("importedColorTheme" + i); i++) { // $NON-NLS-1$
store.setToDefault("importedColorTheme" + i); // $NON-NLS-1$
}
themes.clear();
readStockThemes(themes);
}
public IPreferenceStore createCombinedPreferenceStore() {
IPreferenceStore[] prefs = new IPreferenceStore[editors.size()];
int i = 0;
for (ThemePreferenceMapper editor : editors) {
IEclipsePreferences pref = editor.getPreviewPreferences();
prefs[i++] = new IEclipsePreferencesAdapter(pref);
}
final ChainedPreferenceStore chain = new ChainedPreferenceStore(prefs);
for (ThemePreferenceMapper editor : editors) {
IEclipsePreferences pref = editor.getPreviewPreferences();
pref.addPreferenceChangeListener(new IPreferenceChangeListener() {
@Override
public void preferenceChange(PreferenceChangeEvent event) {
chain.firePropertyChangeEvent(event.getKey(), event.getOldValue(), event.getNewValue());
}
});
}
return chain;
}
/**
* Returns the theme stored under the supplied name.
*
* @param name The name of the theme.
* @return The requested theme or <code>null</code> if none was stored under the supplied name.
*/
public ColorTheme getTheme(String name) {
return themes.get(name);
}
/**
* Returns all available color themes.
*
* @return all available color themes.
*/
public Set<ColorTheme> getThemes() {
return new HashSet<ColorTheme>(themes.values());
}
/**
* Changes the preferences of the preview to apply the color theme.
*
* @param theme The name of the color theme to apply.
*/
public void previewTheme(final String theme) {
for (final ThemePreferenceMapper editor : editors) {
editor.previewRun(new Runnable() {
@Override
public void run() {
applyThemeIn(theme, editor);
}
});
}
}
/**
* Adds the color theme to the list and saves it to the preferences. Existing themes will be
* overwritten with the new content.
*
* @param content The content of the color theme file.
* @return The saved color theme, or <code>null</code> if the theme was not valid.
*/
public ColorTheme saveTheme(String content) {
ColorTheme theme;
try {
theme = ColorThemeManager.parseTheme(new ByteArrayInputStream(content.getBytes()));
String name = theme.getName();
themes.put(name, theme);
IPreferenceStore store = getPreferenceStore();
for (int i = 1;; i++) {
if (!store.contains("importedColorTheme" + i)) { // $NON-NLS-1$
store.putValue("importedColorTheme" + i, content); // $NON-NLS-1$
break;
}
}
return theme;
} catch (Exception e) {
return null;
}
}
public void undoPreview() {
String name = getPreferenceStore().getString("colorTheme");
if (name.isEmpty()) {
name = DEFAULT_THEME_NAME;
}
previewTheme(name);
}
private void applyThemeIn(String theme, ThemePreferenceMapper editor) {
editor.clear();
if (themes.get(theme) != null) {
editor.map(themes.get(theme).getEntries());
}
try {
editor.flush();
} catch (BackingStoreException e) {
// TODO(messick): Show a proper error message.
e.printStackTrace();
}
}
}