blob: 2d70278f936271e1e375bd29c3082f149c186241 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal.themes;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.Set;
import org.eclipse.core.commands.common.EventManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.resource.DataFormatException;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.resource.StringConverter;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.util.PrefUtil;
import org.eclipse.ui.themes.ITheme;
import org.eclipse.ui.themes.IThemeManager;
/**
* @since 3.0
*/
public class Theme extends EventManager implements ITheme {
/**
* The translation bundle in which to look up internationalized text.
*/
private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(Theme.class.getName());
private CascadingColorRegistry themeColorRegistry;
private CascadingFontRegistry themeFontRegistry;
private IThemeDescriptor descriptor;
private IPropertyChangeListener themeListener;
private CascadingMap dataMap;
private ThemeRegistry themeRegistry;
private IPropertyChangeListener propertyListener;
/**
* @param descriptor
*/
public Theme(IThemeDescriptor descriptor) {
themeRegistry = ((ThemeRegistry) WorkbenchPlugin.getDefault().getThemeRegistry());
this.descriptor = descriptor;
IWorkbench workbench = PlatformUI.getWorkbench();
if (descriptor != null) {
ITheme defaultTheme = workbench.getThemeManager().getTheme(IThemeManager.DEFAULT_THEME);
ColorDefinition[] colorDefinitions = this.descriptor.getColors();
themeColorRegistry = new CascadingColorRegistry(defaultTheme.getColorRegistry());
if (colorDefinitions.length > 0) {
ThemeElementHelper.populateRegistry(this, colorDefinitions, PrefUtil.getInternalPreferenceStore());
}
FontDefinition[] fontDefinitions = this.descriptor.getFonts();
themeFontRegistry = new CascadingFontRegistry(defaultTheme.getFontRegistry());
if (fontDefinitions.length > 0) {
ThemeElementHelper.populateRegistry(this, fontDefinitions, PrefUtil.getInternalPreferenceStore());
}
dataMap = new CascadingMap(((ThemeRegistry) WorkbenchPlugin.getDefault().getThemeRegistry()).getData(),
descriptor.getData());
}
getColorRegistry().addListener(getCascadeListener());
getColorRegistry().addListener(this::registryColorChangeEvent);
getFontRegistry().addListener(getCascadeListener());
PrefUtil.getInternalPreferenceStore().addPropertyChangeListener(getPropertyListener());
}
/**
* When a color in the registry is updated, update the backing preferences
* appropriately.
*
* @param event the color change event
*/
private void registryColorChangeEvent(PropertyChangeEvent event) {
if (event.getNewValue() instanceof RGB) {
String key = ThemeElementHelper.createPreferenceKey(this, event.getProperty());
IPreferenceStore store = PrefUtil.getInternalPreferenceStore();
if (store.contains(key)) {
RGB newColor = (RGB) event.getNewValue();
if (store.isDefault(key)) {
RGB oldColor = PreferenceConverter.getDefaultColor(store, key);
if (oldColor == PreferenceConverter.COLOR_DEFAULT_DEFAULT) {
// If the preference is set to default, but there is no actual default value,
// then the preference state is inconsistent. Do nothing.
} else if (!newColor.equals(oldColor))
PreferenceConverter.setValue(store, key, newColor);
} else {
RGB oldColor = PreferenceConverter.getColor(store, key);
if (!newColor.equals(oldColor)) {
oldColor = PreferenceConverter.getDefaultColor(store, key);
if (oldColor != PreferenceConverter.COLOR_DEFAULT_DEFAULT && newColor.equals(oldColor))
store.setToDefault(key);
else
PreferenceConverter.setValue(store, key, newColor);
}
}
}
}
}
/**
* Listener that is responsible for responding to preference changes.
*
* @return the property change listener
*/
private IPropertyChangeListener getPropertyListener() {
if (propertyListener == null) {
propertyListener = new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
String[] split = ThemeElementHelper.splitPropertyName(Theme.this, event.getProperty());
String key = split[1];
String theme = split[0];
if (key.equals(IWorkbenchPreferenceConstants.CURRENT_THEME_ID)) {
return;
}
try {
String thisTheme = getId();
if (Objects.equals(thisTheme, theme)) {
if (getFontRegistry().hasValueFor(key)) {
FontData[] data = event.getNewValue() instanceof String
? PreferenceConverter.basicGetFontData((String) event.getNewValue())
: (FontData[]) event.getNewValue();
getFontRegistry().put(key, data);
processDefaultsTo(key, data);
} else if (getColorRegistry().hasValueFor(key)) {
RGB rgb = event.getNewValue() instanceof String
? StringConverter.asRGB((String) event.getNewValue())
: (RGB) event.getNewValue();
if (!Objects.equals(getColorRegistry().getRGB(key), rgb)) {
getColorRegistry().put(key, rgb);
processDefaultsTo(key, rgb);
}
}
}
} catch (DataFormatException e) {
// no-op
}
}
/**
* Process all fonts that default to the given ID.
*
* @param key the font ID
* @param fd the new FontData for defaulted fonts
*/
private void processDefaultsTo(String key, FontData[] fd) {
FontDefinition[] defs = WorkbenchPlugin.getDefault().getThemeRegistry().getFontsFor(getId());
for (FontDefinition fontDefinition : defs) {
String defaultsTo = fontDefinition.getDefaultsTo();
if (defaultsTo != null && defaultsTo.equals(key)) {
IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
if (store.isDefault(
ThemeElementHelper.createPreferenceKey(Theme.this, fontDefinition.getId()))) {
getFontRegistry().put(fontDefinition.getId(), fd);
processDefaultsTo(fontDefinition.getId(), fd);
}
}
}
}
/**
* Process all colors that default to the given ID.
*
* @param key the color ID
* @param rgb the new RGB value for defaulted colors
*/
private void processDefaultsTo(String key, RGB rgb) {
ColorDefinition[] defs = WorkbenchPlugin.getDefault().getThemeRegistry().getColorsFor(getId());
for (ColorDefinition colorDefinition : defs) {
String defaultsTo = colorDefinition.getDefaultsTo();
if (defaultsTo != null && defaultsTo.equals(key)) {
IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
String prefkey = ThemeElementHelper.createPreferenceKey(Theme.this,
colorDefinition.getId());
if (store.isDefault(prefkey)) {
PreferenceConverter.setDefault(store, prefkey, rgb);
getColorRegistry().put(colorDefinition.getId(), rgb);
processDefaultsTo(colorDefinition.getId(), rgb);
}
}
}
}
};
}
return propertyListener;
}
/**
* Listener that is responsible for rebroadcasting events fired from the base
* font/color registry
*/
private IPropertyChangeListener getCascadeListener() {
if (themeListener == null) {
themeListener = this::firePropertyChange;
}
return themeListener;
}
@Override
public ColorRegistry getColorRegistry() {
if (themeColorRegistry != null) {
return themeColorRegistry;
}
return WorkbenchThemeManager.getInstance().getDefaultThemeColorRegistry();
}
@Override
public FontRegistry getFontRegistry() {
if (themeFontRegistry != null) {
return themeFontRegistry;
}
return WorkbenchThemeManager.getInstance().getDefaultThemeFontRegistry();
}
@Override
public void dispose() {
if (themeColorRegistry != null) {
themeColorRegistry.removeListener(themeListener);
themeColorRegistry.removeListener(this::registryColorChangeEvent);
themeColorRegistry.dispose();
}
if (themeFontRegistry != null) {
themeFontRegistry.removeListener(themeListener);
themeFontRegistry.dispose();
}
PrefUtil.getInternalPreferenceStore().removePropertyChangeListener(getPropertyListener());
}
@Override
public String getId() {
return descriptor == null ? IThemeManager.DEFAULT_THEME : descriptor.getId();
}
@Override
public void addPropertyChangeListener(IPropertyChangeListener listener) {
addListenerObject(listener);
}
@Override
public void removePropertyChangeListener(IPropertyChangeListener listener) {
removeListenerObject(listener);
}
private void firePropertyChange(PropertyChangeEvent event) {
for (Object listener : getListeners()) {
((IPropertyChangeListener) listener).propertyChange(event);
}
}
@Override
public String getLabel() {
return descriptor == null ? RESOURCE_BUNDLE.getString("DefaultTheme.label") : descriptor.getName(); //$NON-NLS-1$
}
@Override
public String getString(String key) {
if (dataMap != null) {
return (String) dataMap.get(key);
}
return (String) themeRegistry.getData().get(key);
}
@Override
public Set keySet() {
if (dataMap != null) {
return dataMap.keySet();
}
return themeRegistry.getData().keySet();
}
@Override
public int getInt(String key) {
String string = getString(key);
if (string == null) {
return 0;
}
try {
return Integer.parseInt(string);
} catch (NumberFormatException e) {
return 0;
}
}
@Override
public boolean getBoolean(String key) {
String string = getString(key);
if (string == null) {
return false;
}
return Boolean.parseBoolean(getString(key));
}
}