/*******************************************************************************
* Copyright (c) 2006-2013 The RCP Company 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:
* The RCP Company - initial API and implementation
*******************************************************************************/
package com.rcpcompany.uibindings.internal.utils;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.databinding.observable.set.ISetChangeListener;
import org.eclipse.core.databinding.observable.set.SetChangeEvent;
import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import com.rcpcompany.uibindings.BindingState;
import com.rcpcompany.uibindings.IBindingContext;
import com.rcpcompany.uibindings.IColumnBinding;
import com.rcpcompany.uibindings.IUIBindingsPackage;
import com.rcpcompany.uibindings.IViewerBinding;
import com.rcpcompany.uibindings.internal.Activator;
import com.rcpcompany.uibindings.utils.IManagerRunnable;
import com.rcpcompany.uibindings.utils.ISortableTableAdapter;
import com.rcpcompany.utils.logging.LogUtils;
/**
* The implementation of {@link ISortableTableAdapter}.
*
* TODO: SWTB
*
* @author Tonny Madsen, The RCP Company
*/
public class SortableTableAdapter implements ISortableTableAdapter, DisposeListener, SelectionListener {
/**
* Constructs and initializes a new sorter...
*
*
* @param viewer the binding
*/
public SortableTableAdapter(IViewerBinding viewer) {
myViewerBinding = viewer;
Assert.isLegal(myViewerBinding.getViewer() instanceof TableViewer, "The column sorter only accepts tables");
myTableViewer = (TableViewer) myViewerBinding.getViewer();
myTable = myTableViewer.getTable();
init();
}
/**
* initializes this adapter.
* <p>
* If the binding is not in state OK, then wait...
*/
protected void init() {
if (myViewerBinding.getState() != BindingState.OK) {
/*
* If not in state OK, then wait until we get there...
*/
final AdapterImpl l = new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
if (msg.isTouch()) return;
if (msg.getFeature() != IUIBindingsPackage.Literals.BINDING__STATE) return;
switch (myViewerBinding.getState()) {
case OK:
init();
//$FALL-THROUGH$ fallthrough
case DISPOSED:
myViewerBinding.eAdapters().remove(this);
break;
default:
break;
}
}
};
myViewerBinding.eAdapters().add(l);
return;
}
myViewerBinding.registerService(this);
myTable.addDisposeListener(this);
for (final IColumnBinding cb : myViewerBinding.getColumns()) {
addColumn(cb);
}
myViewerBinding.eAdapters().add(new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
if (msg.isTouch()) return;
if (msg.getFeature() != IUIBindingsPackage.Literals.VIEWER_BINDING__COLUMNS) return;
switch (msg.getEventType()) {
case Notification.REMOVE:
case Notification.SET:
addColumn((IColumnBinding) msg.getOldValue());
break;
}
switch (msg.getEventType()) {
case Notification.ADD:
case Notification.SET:
removeColumn((IColumnBinding) msg.getNewValue());
break;
}
}
});
myViewerBinding.getElements().addSetChangeListener(myViewerSetListener);
}
/**
* Removes the sorting functionality again.
*/
@Override
public void dispose() {
myViewerBinding.unregisterService(this);
if (myTable.isDisposed()) return;
/*
* If the viewer binding is not being disposed, then remove the comparator
*/
if (!myViewerBinding.isDisposed()) {
myTableViewer.setComparator(null);
}
myTable.removeDisposeListener(this);
for (final IColumnBinding cb : myViewerBinding.getColumns()) {
removeColumn(cb);
}
}
protected void addColumn(final IColumnBinding cb) {
cb.getColumnAdapter().addSelectionListener(this);
}
protected void removeColumn(final IColumnBinding cb) {
cb.getColumnAdapter().removeSelectionListener(this);
}
/**
* The binding for the viewer
*
* TODO make this an interface
*/
protected final IViewerBinding myViewerBinding;
/**
* The table viewer
*/
protected final TableViewer myTableViewer;
/**
* The raw SWT table
*/
protected final Table myTable;
/**
* Monitor of the viewer list.
*/
private final ISetChangeListener myViewerSetListener = new ISetChangeListener() {
@Override
public void handleSetChange(SetChangeEvent event) {
refreshViewer();
}
};
/**
* The sorter for the table
*/
protected ViewerComparator myViewerComparator = new ViewerComparator() {
@Override
public void sort(Viewer viewer, Object[] elements) {
final TableColumn sortColumn = myTable.getSortColumn();
if (Activator.getDefault().TRACE_SORTING) {
LogUtils.debug(this, ">> " + Arrays.toString(elements) + "\ncolumn=" + sortColumn);
}
if (sortColumn == null) return;
final int dir = myTable.getSortDirection() == SWT.UP ? 1 : -1;
final IColumnBinding column = (IColumnBinding) IBindingContext.Factory.getBindingForWidget(sortColumn);
if (column == null) {
LogUtils.error(SortableTableAdapter.this, "Cannot find Binding from TableColumn");
return;
}
// Map all the strings to strings, so it is easier to sort them
final Map<Object, Object> values = new HashMap<Object, Object>();
Comparator<Object> comparator = null;
final Class<?> columnType = column.getDataType().getDataType();
if (columnType == Byte.TYPE) {
for (final Object o : elements) {
values.put(o, column.getValue(o).getValue());
}
comparator = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
final Byte s1 = (Byte) values.get(o1);
final Byte s2 = (Byte) values.get(o2);
if (s1 == null || s2 == null) return 0;
return dir * (s2 - s1);
}
};
} else if (columnType == Short.TYPE) {
for (final Object o : elements) {
values.put(o, column.getValue(o).getValue());
}
comparator = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
final Short s1 = (Short) values.get(o1);
final Short s2 = (Short) values.get(o2);
if (s1 == null || s2 == null) return 0;
return dir * (s2 - s1);
}
};
} else if (columnType == Integer.TYPE) {
for (final Object o : elements) {
values.put(o, column.getValue(o).getValue());
}
comparator = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
final Integer s1 = (Integer) values.get(o1);
final Integer s2 = (Integer) values.get(o2);
if (s1 == null || s2 == null) return 0;
return dir * (s2 - s1);
}
};
} else if (columnType == Long.TYPE) {
for (final Object o : elements) {
values.put(o, column.getValue(o).getValue());
}
comparator = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
final Long s1 = (Long) values.get(o1);
final Long s2 = (Long) values.get(o2);
if (s1 == null || s2 == null) return 0;
final long d = s2 - s1;
if (d == 0) return 0;
return dir * (d < 0 ? 1 : -1);
}
};
} else if (columnType == Float.TYPE) {
for (final Object o : elements) {
values.put(o, column.getValue(o).getValue());
}
comparator = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
final Float s1 = (Float) values.get(o1);
final Float s2 = (Float) values.get(o2);
if (s1 == null || s2 == null) return 0;
final float d = s2 - s1;
if (d == 0) return 0;
return dir * (d < 0 ? 1 : -1);
}
};
} else if (columnType == Double.TYPE) {
for (final Object o : elements) {
values.put(o, column.getValue(o).getValue());
}
comparator = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
final Double s1 = (Double) values.get(o1);
final Double s2 = (Double) values.get(o2);
if (s1 == null || s2 == null) return 0;
final Double d = s2 - s1;
if (d == 0) return 0;
return dir * (d < 0 ? 1 : -1);
}
};
} else {
for (final Object o : elements) {
values.put(o, column.getDisplayText(o));
}
comparator = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
final String s1 = (String) values.get(o1);
final String s2 = (String) values.get(o2);
if (s1 == null || s2 == null) return 0;
return dir * s1.compareToIgnoreCase(s2);
}
};
}
// Sort them
if (comparator != null) {
Arrays.sort(elements, comparator);
}
if (Activator.getDefault().TRACE_SORTING) {
LogUtils.debug(this, "<< " + Arrays.toString(elements));
}
}
};
@Override
public void widgetDisposed(DisposeEvent e) {
dispose();
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
@Override
public void widgetSelected(SelectionEvent e) {
// TODO: change to mouse event or access state
// determine new sort column and direction
final TableColumn sortColumn = myTable.getSortColumn();
TableColumn clickColumn = (TableColumn) e.widget;
int dir = myTable.getSortDirection();
if (Activator.getDefault().TRACE_SORTING) {
LogUtils.debug(this, ">> clickColumn: " + clickColumn + ", sortColumn: " + sortColumn + ", dir: " + dir);
}
if (sortColumn == clickColumn) {
switch (dir) {
case SWT.UP:
dir = SWT.DOWN;
break;
case SWT.DOWN:
dir = SWT.NONE;
clickColumn = null;
break;
}
} else {
dir = SWT.UP;
}
if (Activator.getDefault().TRACE_SORTING) {
LogUtils.debug(this, "<< new sortColumn: " + clickColumn + ", dir: " + dir);
}
myTable.setSortColumn(clickColumn);
myTable.setSortDirection(dir);
if (clickColumn != null) {
if (sortColumn == null) {
/*
* Just started to sort, so we set the comparator - this will implicitly refresh
*/
myTableViewer.setComparator(myViewerComparator);
} else {
/*
* Otherwise refresh
*/
myTableViewer.refresh();
}
} else {
myTableViewer.setComparator(null);
}
}
/**
* Refreshes the viewer if needed.
*/
public void refreshViewer() {
if (myTableViewer.getComparator() != null) {
/*
* Have to wait with the update as we otherwise end up adding the same element to the
* viewer twice..
*/
IManagerRunnable.Factory.asyncExec("refresh", myTableViewer, new Runnable() {
@Override
public void run() {
if (!myTableViewer.getTable().isDisposed()) {
myTableViewer.refresh();
}
}
});
}
}
}