/*
* 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.charts.views;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Date;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.beans.BeansObservables;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.Observables;
import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
import org.eclipse.jface.databinding.viewers.ObservableValueEditingSupport;
import org.eclipse.jface.databinding.viewers.TreeStructureAdvisor;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.viewers.CellNavigationStrategy;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.TreeViewerEditor;
import org.eclipse.jface.viewers.TreeViewerFocusCellManager;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Tree;
import org.eclipsetrader.core.feed.IHistory;
import org.eclipsetrader.core.feed.TimeSpan;
import org.eclipsetrader.ui.CellEditorValueProperty;
import org.eclipsetrader.ui.Util;
public class HistoryDataDetailEditor {
public static final String PROP_DIRTY = "dirty";
private final HistoryDataDetailEditorModel model;
private final DataBindingContext dbc;
private TreeViewer viewer;
private boolean dirty;
private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
private final PropertyChangeListener dirtyChangeListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
changeSupport.firePropertyChange(PROP_DIRTY, dirty, dirty = true);
}
};
public HistoryDataDetailEditor(Composite parent) {
dbc = new DataBindingContext();
model = new HistoryDataDetailEditorModel();
model.addPropertyChangeListener(dirtyChangeListener);
createViewer(parent);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
void createViewer(Composite parent) {
Tree tree = new Tree(parent, SWT.FULL_SELECTION | SWT.MULTI);
tree.setHeaderVisible(true);
tree.setLinesVisible(true);
viewer = new TreeViewer(tree);
IObservableFactory observableFactory = new IObservableFactory() {
@Override
public IObservable createObservable(Object target) {
if (target instanceof HistoryDataDetailEditorModel) {
return Observables.unmodifiableObservableList(((HistoryDataDetailEditorModel) target).getList());
}
if (target instanceof HistoryDataEditorModel) {
return Observables.unmodifiableObservableList(((HistoryDataEditorModel) target).getList());
}
return null;
}
};
TreeStructureAdvisor treeStructureAdvisor = new TreeStructureAdvisor() {
@Override
public Boolean hasChildren(Object element) {
if (element instanceof HistoryDataEditorModel) {
return true;
}
return false;
}
};
ObservableListTreeContentProvider contentProvider = new ObservableListTreeContentProvider(observableFactory, treeStructureAdvisor);
viewer.setContentProvider(contentProvider);
GC gc = new GC(parent);
FontMetrics fontMetrics = gc.getFontMetrics();
gc.dispose();
TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
viewerColumn.getColumn().setText("Time");
viewerColumn.getColumn().setWidth(Dialog.convertHorizontalDLUsToPixels(fontMetrics, 70));
viewerColumn.setEditingSupport(ObservableValueEditingSupport.create(viewer, dbc,
new TextCellEditor(tree), CellEditorValueProperty.timeValue(), BeanProperties.value(HistoryDataElement.PROP_DATE)));
viewerColumn = new TreeViewerColumn(viewer, SWT.RIGHT);
viewerColumn.getColumn().setText("Open");
viewerColumn.getColumn().setWidth(Dialog.convertHorizontalDLUsToPixels(fontMetrics, 55));
viewerColumn.setEditingSupport(ObservableValueEditingSupport.create(viewer, dbc,
new TextCellEditor(tree), CellEditorValueProperty.doubleValue(), BeanProperties.value(HistoryDataElement.PROP_OPEN)));
viewerColumn = new TreeViewerColumn(viewer, SWT.RIGHT);
viewerColumn.getColumn().setText("High");
viewerColumn.getColumn().setWidth(Dialog.convertHorizontalDLUsToPixels(fontMetrics, 55));
viewerColumn.setEditingSupport(ObservableValueEditingSupport.create(viewer, dbc,
new TextCellEditor(tree), CellEditorValueProperty.doubleValue(), BeanProperties.value(HistoryDataElement.PROP_HIGH)));
viewerColumn = new TreeViewerColumn(viewer, SWT.RIGHT);
viewerColumn.getColumn().setText("Low");
viewerColumn.getColumn().setWidth(Dialog.convertHorizontalDLUsToPixels(fontMetrics, 55));
viewerColumn.setEditingSupport(ObservableValueEditingSupport.create(viewer, dbc,
new TextCellEditor(tree), CellEditorValueProperty.doubleValue(), BeanProperties.value(HistoryDataElement.PROP_LOW)));
viewerColumn = new TreeViewerColumn(viewer, SWT.RIGHT);
viewerColumn.getColumn().setText("Close");
viewerColumn.getColumn().setWidth(Dialog.convertHorizontalDLUsToPixels(fontMetrics, 55));
viewerColumn.setEditingSupport(ObservableValueEditingSupport.create(viewer, dbc,
new TextCellEditor(tree), CellEditorValueProperty.doubleValue(), BeanProperties.value(HistoryDataElement.PROP_CLOSE)));
viewerColumn = new TreeViewerColumn(viewer, SWT.RIGHT);
viewerColumn.getColumn().setText("Volume");
viewerColumn.getColumn().setWidth(Dialog.convertHorizontalDLUsToPixels(fontMetrics, 70));
viewerColumn.setEditingSupport(ObservableValueEditingSupport.create(viewer, dbc,
new TextCellEditor(tree), CellEditorValueProperty.longValue(), BeanProperties.value(HistoryDataElement.PROP_VOLUME)));
if (Platform.WS_GTK.equals(Platform.getWS())) {
viewerColumn = new TreeViewerColumn(viewer, SWT.RIGHT);
viewerColumn.getColumn().setWidth(1);
}
final String[] properties = new String[] {
HistoryDataElement.PROP_DATE,
HistoryDataElement.PROP_OPEN,
HistoryDataElement.PROP_HIGH,
HistoryDataElement.PROP_LOW,
HistoryDataElement.PROP_CLOSE,
HistoryDataElement.PROP_VOLUME,
""
};
DataViewerLabelProvider labelProvider = new DataViewerLabelProvider(BeansObservables.observeMaps(contentProvider.getKnownElements(), properties));
labelProvider.setDateFormat(Util.getTimeFormat());
viewer.setLabelProvider(labelProvider);
viewer.setSorter(new ViewerSorter() {
@Override
public int compare(Viewer viewer, Object o1, Object o2) {
if ((o1 instanceof HistoryDataEditorModel) && (o2 instanceof HistoryDataEditorModel)) {
TimeSpan ts1 = ((HistoryDataEditorModel) o1).getBarSize();
TimeSpan ts2 = ((HistoryDataEditorModel) o2).getBarSize();
if (ts1.higherThan(ts2)) {
return 1;
}
if (ts2.higherThan(ts1)) {
return -1;
}
return 0;
}
else if ((o1 instanceof HistoryDataElement) && (o2 instanceof HistoryDataElement)) {
HistoryDataElement e1 = (HistoryDataElement) o1;
HistoryDataElement e2 = (HistoryDataElement) o2;
if (e1.getDate() != null && e2.getDate() != null) {
return e1.getDate().compareTo(e2.getDate());
}
if (e1.getDate() != null && e2.getDate() == null) {
return -1;
}
if (e1.getDate() == null && e2.getDate() != null) {
return 1;
}
}
return 0;
}
});
CellNavigationStrategy naviStrat = new CellNavigationStrategy() {
@Override
public ViewerCell findSelectedCell(ColumnViewer viewer, ViewerCell currentSelectedCell, Event event) {
ViewerCell cell = super.findSelectedCell(viewer, currentSelectedCell, event);
if (cell != null) {
if (cell.getColumnIndex() == properties.length - 1) {
return null;
}
Tree tree = ((TreeViewer) viewer).getTree();
tree.showColumn(tree.getColumn(cell.getColumnIndex()));
}
return cell;
}
};
TreeViewerFocusCellManager focusCellManager = new TreeViewerFocusCellManager(viewer, new FocusCellOwnerDrawHighlighter(viewer), naviStrat);
ColumnViewerEditorActivationStrategy activationStrategy = new ColumnViewerEditorActivationStrategy(viewer) {
@Override
protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.CR) || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
}
};
TreeViewerEditor.create(viewer, focusCellManager, activationStrategy, ColumnViewerEditor.TABBING_HORIZONTAL | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR | ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION);
tree.addTraverseListener(new TraverseListener() {
@Override
public void keyTraversed(TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_RETURN || e.detail == SWT.TRAVERSE_ESCAPE) {
e.doit = false;
}
}
});
tree.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (viewer.isCellEditorActive()) {
return;
}
if (e.keyCode == SWT.INSERT) {
IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
HistoryDataEditorModel parent = null;
int index = 0;
if (!selection.isEmpty()) {
parent = model.getParent(selection.getFirstElement());
if (parent == null) {
return;
}
index = parent.getList().indexOf(selection.getFirstElement());
if (index == -1) {
index = parent.getList().size();
}
}
final HistoryDataElement element = new HistoryDataElement();
parent.getList().add(index, element);
e.doit = false;
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (!viewer.getControl().isDisposed()) {
viewer.editElement(element, 0);
}
}
});
}
else if (e.character == SWT.DEL) {
IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
for (Object element : selection.toList()) {
HistoryDataEditorModel parent = model.getParent(selection.getFirstElement());
if (parent != null) {
if (parent.getList().remove(element)) {
changeSupport.firePropertyChange(PROP_DIRTY, dirty, dirty = true);
}
}
}
}
}
});
viewer.setInput(model);
}
public void load(IHistory history, Date date) {
IHistory[] childHistory = history.getDay(date);
model.set(childHistory);
}
public Control getControl() {
return viewer.getControl();
}
public boolean isDirty() {
return dirty;
}
public void setDirty(boolean dirty) {
this.dirty = dirty;
}
public HistoryDataDetailEditorModel getModel() {
return model;
}
public TreeViewer getViewer() {
return viewer;
}
public void dispose() {
PropertyChangeListener[] listeners = changeSupport.getPropertyChangeListeners();
for (int i = 0; i < listeners.length; i++) {
changeSupport.removePropertyChangeListener(listeners[i]);
}
viewer.getControl().dispose();
model.dispose();
dbc.dispose();
}
}