| /* |
| * 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(); |
| } |
| } |
| } |