/******************************************************************************
* Copyright (C) 2013 Fabio Zadrozny
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Fabio Zadrozny <fabiofz@gmail.com> - initial API and implementation
******************************************************************************/
package org.python.pydev.shared_ui;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
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.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.resource.StringConverter;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.console.IOConsole;
import org.eclipse.ui.console.IOConsoleOutputStream;
import org.python.pydev.shared_core.log.Log;
import org.python.pydev.shared_ui.utils.RunInUiThread;
/**
* The main use for this class is:
*
* ConsoleColorCache.getDefault().keepConsoleColorsSynched(console);
*
* It should be called when the console is created and whenever a new stream is created (to
* set the color for that stream -- which must be registered in a map in the themeConsoleStreamToColor
* attribute).
*
* @author Fabio
*/
@SuppressWarnings("deprecation")
public class ConsoleColorCache implements IPreferenceChangeListener {
private static ConsoleColorCache instance;
private static Object instanceLock = new Object();
private List<WeakReference<IOConsole>> weakrefs = new ArrayList<WeakReference<IOConsole>>();
private final Object referencesLock = new Object();
private ConsoleColorCache() {
IEclipsePreferences node = new InstanceScope().getNode("org.eclipse.debug.ui");
node.addPreferenceChangeListener(this);
}
public static ConsoleColorCache getDefault() {
if (instance == null) {
synchronized (instanceLock) {
if (instance == null) {
instance = new ConsoleColorCache();
}
}
}
return instance;
}
protected Map<RGB, Color> cache = new HashMap<RGB, Color>(4);
public Color getColor(RGB rgb) {
Color color = cache.get(rgb);
if (color == null) {
color = new Color(Display.getCurrent(), rgb);
cache.put(rgb, color);
}
return color;
}
private Color getDebugColor(String key) {
IEclipsePreferences node = new InstanceScope().getNode("org.eclipse.debug.ui");
String color = node.get(key, null);
if (color != null) {
try {
return getDefault().getColor(StringConverter.asRGB(color));
} catch (Exception e) {
Log.log(e);
}
}
return null;
}
/**
* In this method we'll make sure a console will have its colors properly updated.
*
* @param console This is the console to keep updated.
*
* The console should have a getAttribute("themeConsoleStreamToColor") which returns a Map<IOConsoleOutputStream, String>
* where the values may be:
*
* "console.output" or "console.error"
*/
public void keepConsoleColorsSynched(IOConsole console) {
updateConsole(console);
addRef(console);
}
@SuppressWarnings({ "unchecked" })
private void updateConsole(final IOConsole console) {
Runnable r = new Runnable() {
@Override
public void run() {
//Should be: DebugUIPlugin.getPreferenceColor(IDebugPreferenceConstants.CONSOLE_BAKGROUND_COLOR)
//but we don't want to add that dependency.
try {
Color color = getDebugColor("org.eclipse.debug.ui.consoleBackground");
if (color != null) {
console.setBackground(color);
}
} catch (Exception e) {
Log.log(e);
}
try {
Map<IOConsoleOutputStream, String> streamToColor = (Map<IOConsoleOutputStream, String>) console
.getAttribute("themeConsoleStreamToColor");
if (streamToColor != null) {
Set<Entry<IOConsoleOutputStream, String>> entrySet = streamToColor.entrySet();
for (Entry<IOConsoleOutputStream, String> entry : entrySet) {
String value = entry.getValue();
if ("console.output".equals(value)) {
Color color = getDebugColor("org.eclipse.debug.ui.outColor");
if (color != null) {
entry.getKey().setColor(color);
}
} else if ("console.error".equals(value)) {
Color color = getDebugColor("org.eclipse.debug.ui.errorColor");
if (color != null) {
entry.getKey().setColor(color);
}
} else {
Log.log("Unrecognized value (expected console.output or console.error):" + value);
}
}
}
} catch (Exception e) {
Log.log(e);
}
}
};
RunInUiThread.async(r, true);
}
private void addRef(IOConsole console) {
synchronized (referencesLock) {
//We'll clear the current references and add the new one if it's not there already.
int size = weakrefs.size();
for (int i = 0; i < size; i++) {
WeakReference<IOConsole> ref = weakrefs.get(i);
Object object = ref.get();
if (object == console) {
return; //already there (nothing to add).
}
if (object == null) {
weakrefs.remove(i);
i--;
size--;
}
}
//Add the new reference.
weakrefs.add(new WeakReference<IOConsole>(console));
}
}
@Override
public void preferenceChange(PreferenceChangeEvent event) {
String key = event.getKey();
if ("org.eclipse.debug.ui.consoleBackground".equals(key) || "org.eclipse.debug.ui.outColor".equals(key)
|| "org.eclipse.debug.ui.errorColor".equals(key)) {
synchronized (referencesLock) {
ArrayList<IOConsole> currentRefs = getCurrentRefs();
for (IOConsole console : currentRefs) {
updateConsole(console);
}
}
}
}
private ArrayList<IOConsole> getCurrentRefs() {
int size = weakrefs.size();
ArrayList<IOConsole> currentRefs = new ArrayList<IOConsole>(size);
for (int i = 0; i < size; i++) {
WeakReference<IOConsole> ref = weakrefs.get(i);
IOConsole object = ref.get();
if (object == null) {
weakrefs.remove(i);
i--;
size--;
} else {
currentRefs.add(object);
}
}
return currentRefs;
}
}