/*
* Copyright (c) 2004-2011 Marco Maccaferri and others.
* 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:
* Marco Maccaferri - initial API and implementation
*/
package org.eclipsetrader.ui.internal.views;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.map.IMapChangeListener;
import org.eclipse.core.databinding.observable.map.IObservableMap;
import org.eclipse.core.databinding.observable.map.MapChangeEvent;
import org.eclipse.core.databinding.observable.map.WritableMap;
import org.eclipse.core.databinding.observable.set.IObservableSet;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipsetrader.ui.QueuedRealm;
import org.eclipsetrader.ui.internal.ats.ViewerObservableMap;
public class WatchListViewTickDecorator {
private final Realm realm = QueuedRealm.getInstance();
private final IObservableSet knownElements;
private Color positiveBackgroundColor;
private Color negativeBackgroundColor;
private Color evenRowsColor;
private Color oddRowsColor;
private boolean drawOutline;
private boolean fadeEffect;
private WatchListViewCellAttribute positiveTickAttribute;
private WatchListViewCellAttribute[] positiveFadeAttributes = new WatchListViewCellAttribute[3];
private WatchListViewCellAttribute negativeTickAttribute;
private WatchListViewCellAttribute[] negativeFadeAttributes = new WatchListViewCellAttribute[3];
private final Map<String, Map<Object, TickData>> decoratorMap = new HashMap<String, Map<Object, TickData>>();
private final List<WatchListViewCellLabelProvider> labelProviders = new ArrayList<WatchListViewCellLabelProvider>();
private Timer timer;
private class TickData {
public Object element;
public Object value;
public double diff;
public int counter;
public WritableMap attributeMap;
public TickData(Object element, Object value) {
this.element = element;
this.value = value;
this.counter = -1;
}
public void setValue(Object value) {
Object oldValue = this.value;
if (timer != null) {
if (oldValue instanceof IAdaptable && value instanceof IAdaptable) {
Number oldNumber = (Number) ((IAdaptable) oldValue).getAdapter(Number.class);
Number newNumber = (Number) ((IAdaptable) value).getAdapter(Number.class);
if (oldNumber != null && newNumber != null) {
diff = newNumber.doubleValue() - oldNumber.doubleValue();
if (diff > 0) {
attributeMap.put(element, positiveTickAttribute);
counter = positiveFadeAttributes.length + 2;
}
else if (diff < 0) {
attributeMap.put(element, negativeTickAttribute);
counter = negativeFadeAttributes.length + 2;
}
}
}
}
this.value = value;
}
public void reset() {
if (counter < 0) {
return;
}
counter--;
if (counter < 0) {
attributeMap.put(element, null);
}
else if (diff > 0) {
if (counter < positiveFadeAttributes.length) {
attributeMap.put(element, positiveFadeAttributes[counter]);
}
}
else if (diff < 0) {
if (counter < negativeFadeAttributes.length) {
attributeMap.put(element, negativeFadeAttributes[counter]);
}
}
}
}
public WatchListViewTickDecorator(IObservableSet knownElements) {
this.knownElements = knownElements;
}
public void setEnabled(boolean enable) {
if (enable) {
if (timer == null) {
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
for (Map<Object, TickData> tickMap : decoratorMap.values()) {
for (TickData data : tickMap.values()) {
data.reset();
}
}
}
});
}
}, 100, 500);
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
for (WatchListViewCellLabelProvider labelProvider : labelProviders) {
labelProvider.setOwnerDrawEnabled(drawOutline);
}
}
});
}
}
else {
if (timer != null) {
timer.cancel();
realm.exec(new Runnable() {
@Override
public void run() {
for (WatchListViewCellLabelProvider labelProvider : labelProviders) {
labelProvider.setOwnerDrawEnabled(false);
}
for (Map<Object, TickData> tickMap : decoratorMap.values()) {
for (TickData data : tickMap.values()) {
for (Object element : data.attributeMap.keySet()) {
data.attributeMap.put(element, null);
}
}
}
}
});
timer = null;
}
}
}
public void setRowColors(Color evenRowsColor, Color oddRowsColor) {
this.evenRowsColor = evenRowsColor;
this.oddRowsColor = oddRowsColor;
buildColors();
}
public void setTickColors(Color positiveColor, Color negativeColor) {
positiveBackgroundColor = positiveColor;
negativeBackgroundColor = negativeColor;
positiveTickAttribute = new WatchListViewCellAttribute();
positiveTickAttribute.evenBackground = positiveColor;
positiveTickAttribute.oddBackground = positiveColor;
negativeTickAttribute = new WatchListViewCellAttribute();
negativeTickAttribute.evenBackground = negativeColor;
negativeTickAttribute.oddBackground = negativeColor;
buildColors();
}
public void setFadeEffect(boolean fadeEffect) {
this.fadeEffect = fadeEffect;
buildColors();
}
public void setDrawOutline(boolean enabled) {
this.drawOutline = enabled;
for (WatchListViewCellLabelProvider labelProvider : labelProviders) {
labelProvider.setOwnerDrawEnabled(enabled);
}
}
public CellLabelProvider createCellLabelProvider(final String key) {
ViewerObservableMap valueMap = new ViewerObservableMap(realm, knownElements, key);
final WritableMap attributeMap = new WritableMap(realm);
Map<Object, TickData> tickMap = new HashMap<Object, WatchListViewTickDecorator.TickData>();
decoratorMap.put(key, tickMap);
valueMap.addMapChangeListener(new IMapChangeListener() {
@Override
public void handleMapChange(MapChangeEvent event) {
Map<Object, TickData> tickMap = decoratorMap.get(key);
for (Object elementKey : event.diff.getChangedKeys()) {
if (tickMap.get(elementKey) == null) {
TickData data = new TickData(elementKey, event.getObservableMap().get(elementKey));
data.attributeMap = attributeMap;
tickMap.put(elementKey, data);
}
else {
TickData data = tickMap.get(elementKey);
data.setValue(event.getObservableMap().get(elementKey));
}
}
}
});
WatchListViewCellLabelProvider labelProvider = new WatchListViewCellLabelProvider(new IObservableMap[] {
valueMap, attributeMap
});
labelProvider.setOwnerDrawEnabled(drawOutline);
labelProviders.add(labelProvider);
return labelProvider;
}
private void buildColors() {
if (positiveBackgroundColor == null || negativeBackgroundColor == null || evenRowsColor == null || oddRowsColor == null) {
return;
}
for (int i = 0; i < positiveFadeAttributes.length; i++) {
if (positiveFadeAttributes[i] != null) {
positiveFadeAttributes[i].evenBackground.dispose();
positiveFadeAttributes[i].oddBackground.dispose();
}
positiveFadeAttributes[i] = new WatchListViewCellAttribute();
}
RGB backgroundColor = positiveBackgroundColor.getRGB();
int steps = 100 / (positiveFadeAttributes.length + 1);
for (int i = 0, ratio = steps; i < positiveFadeAttributes.length; i++, ratio += steps) {
if (fadeEffect) {
RGB rgb = blend(backgroundColor, evenRowsColor.getRGB(), ratio);
positiveFadeAttributes[i].evenBackground = new Color(Display.getDefault(), rgb);
}
else {
positiveFadeAttributes[i].evenBackground = new Color(Display.getDefault(), evenRowsColor.getRGB());
}
}
steps = 100 / (positiveFadeAttributes.length + 1);
for (int i = 0, ratio = steps; i < positiveFadeAttributes.length; i++, ratio += steps) {
if (fadeEffect) {
RGB rgb = blend(backgroundColor, oddRowsColor.getRGB(), ratio);
positiveFadeAttributes[i].oddBackground = new Color(Display.getDefault(), rgb);
}
else {
positiveFadeAttributes[i].oddBackground = new Color(Display.getDefault(), oddRowsColor.getRGB());
}
}
for (int i = 0; i < negativeFadeAttributes.length; i++) {
if (negativeFadeAttributes[i] != null) {
negativeFadeAttributes[i].evenBackground.dispose();
negativeFadeAttributes[i].oddBackground.dispose();
}
negativeFadeAttributes[i] = new WatchListViewCellAttribute();
}
backgroundColor = negativeBackgroundColor.getRGB();
steps = 100 / (negativeFadeAttributes.length + 1);
for (int i = 0, ratio = steps; i < negativeFadeAttributes.length; i++, ratio += steps) {
if (fadeEffect) {
RGB rgb = blend(backgroundColor, evenRowsColor.getRGB(), ratio);
negativeFadeAttributes[i].evenBackground = new Color(Display.getDefault(), rgb);
}
else {
negativeFadeAttributes[i].evenBackground = new Color(Display.getDefault(), evenRowsColor.getRGB());
}
}
steps = 100 / (negativeFadeAttributes.length + 1);
for (int i = 0, ratio = steps; i < negativeFadeAttributes.length; i++, ratio += steps) {
if (fadeEffect) {
RGB rgb = blend(backgroundColor, oddRowsColor.getRGB(), ratio);
negativeFadeAttributes[i].oddBackground = new Color(Display.getDefault(), rgb);
}
else {
negativeFadeAttributes[i].oddBackground = new Color(Display.getDefault(), oddRowsColor.getRGB());
}
}
}
private RGB blend(RGB c1, RGB c2, int ratio) {
int r = blend(c1.red, c2.red, ratio);
int g = blend(c1.green, c2.green, ratio);
int b = blend(c1.blue, c2.blue, ratio);
return new RGB(r, g, b);
}
private int blend(int v1, int v2, int ratio) {
return (ratio * v1 + (100 - ratio) * v2) / 100;
}
public void dispose() {
if (timer != null) {
timer.cancel();
}
disposeColors();
}
private void disposeColors() {
for (int i = 0; i < positiveFadeAttributes.length; i++) {
if (positiveFadeAttributes[i] != null) {
positiveFadeAttributes[i].evenBackground.dispose();
positiveFadeAttributes[i].oddBackground.dispose();
positiveFadeAttributes[i] = null;
}
}
for (int i = 0; i < negativeFadeAttributes.length; i++) {
if (negativeFadeAttributes[i] != null) {
negativeFadeAttributes[i].evenBackground.dispose();
negativeFadeAttributes[i].oddBackground.dispose();
negativeFadeAttributes[i] = null;
}
}
}
}