package edu.ualberta.med.biobank.widgets.report;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.TreeItem;
import edu.ualberta.med.biobank.common.wrappers.ReportWrapper;
import edu.ualberta.med.biobank.gui.common.BgcPlugin;
import edu.ualberta.med.biobank.model.EntityColumn;
import edu.ualberta.med.biobank.model.PropertyModifier;
import edu.ualberta.med.biobank.model.ReportColumn;
public class ColumnSelectWidget extends Composite {
private static final ViewerSorter DISPLAYED_COLUMNS_VIEWER_SORTER = new DisplayedColumnsViewerSorter();
private static final LabelProvider DISPLAYED_COLUMNS_LABEL_PROVIDER = new DisplayedColumnsLabelProvider();
private static final LabelProvider AVAILABLE_COLUMNS_LABEL_PROVIDER = new AvailableColumnsLabelProvider();
private static final ITreeContentProvider AVAILABLE_COLUMNS_TREE_CONTENT_PROVIDER = new AvailableColumnsTreeContentProvider();
private static final Object AVAILABLE_COLUMNS_ROOT_OBJECT = "root"; //$NON-NLS-1$
private static final Comparator<ReportColumnWrapper> REPORT_COLUMN_WRAPER_COMPARTOR = new Comparator<ReportColumnWrapper>() {
@Override
public int compare(ReportColumnWrapper rcw1, ReportColumnWrapper rcw2) {
return rcw1.getReportColumn().getPosition()
.compareTo(rcw2.getReportColumn().getPosition());
}
};
private final ReportWrapper report;
private final Collection<ChangeListener<ColumnChangeEvent>> listeners = new ArrayList<ChangeListener<ColumnChangeEvent>>();
private Composite container;
private TreeViewer available;
private TableViewer displayed;
private Button leftButton, rightButton, upButton, downButton;
public ColumnSelectWidget(Composite parent, int style, ReportWrapper report) {
super(parent, style);
this.report = report;
init();
createContainer();
report.addPropertyChangeListener(
ReportWrapper.REPORT_COLUMN_COLLECTION_CACHE_KEY,
new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent arg0) {
if (!isDisposed()) {
createContainer();
}
}
});
}
public Collection<ReportColumn> getReportColumns() {
Collection<ReportColumn> result = new ArrayList<ReportColumn>();
for (ReportColumnWrapper wrapper : getDisplayedColumns()) {
result.add(wrapper.getReportColumn());
}
return result;
}
public void addColumnChangeListener(
ChangeListener<ColumnChangeEvent> listener) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
public void removeColumnChangeListener(
ChangeListener<ColumnChangeEvent> listener) {
listeners.remove(listener);
}
private void notifyListeners(ColumnChangeEvent event) {
for (ChangeListener<ColumnChangeEvent> listener : listeners) {
listener.handleEvent(event);
}
}
private void init() {
GridLayout layout = new GridLayout(1, false);
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
layout.marginWidth = 0;
layout.marginHeight = 0;
layout.marginBottom = 15;
setLayout(layout);
GridData layoutData = new GridData(GridData.FILL_HORIZONTAL);
layoutData.grabExcessHorizontalSpace = true;
layoutData.minimumHeight = 0;
setLayoutData(layoutData);
}
private void disposeContainer() {
if (container != null && !container.isDisposed()) {
container.dispose();
}
}
private void createContainer() {
disposeContainer();
container = new Composite(this, SWT.NONE);
GridLayout layout = new GridLayout(4, false);
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
layout.marginWidth = 0;
layout.marginHeight = 0;
container.setLayout(layout);
GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
layoutData.horizontalIndent = 0;
layoutData.verticalIndent = 0;
container.setLayoutData(layoutData);
createAvailableTable();
createSwitchButtons();
createDisplayedTable();
createRepositionButtons();
}
private void createAvailableTable() {
Composite subContainer = new Composite(container, SWT.NONE);
subContainer.setLayout(new GridLayout(1, false));
subContainer
.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
createLabel(subContainer, Messages.ColumnSelectWidget_available_label);
available = new TreeViewer(subContainer, SWT.MULTI | SWT.READ_ONLY
| SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
layoutData.heightHint = available.getTree().getItemHeight() * 4 + 2;
layoutData.widthHint = 180;
available.getControl().setLayoutData(layoutData);
available.setSorter(new ViewerSorter());
available.setLabelProvider(AVAILABLE_COLUMNS_LABEL_PROVIDER);
available.setContentProvider(AVAILABLE_COLUMNS_TREE_CONTENT_PROVIDER);
available.setInput(AVAILABLE_COLUMNS_ROOT_OBJECT);
for (EntityColumn entityColumn : report.getEntityColumnCollection()) {
addAvailable(entityColumn);
}
}
private void createDisplayedTable() {
Composite subContainer = new Composite(container, SWT.NONE);
subContainer.setLayout(new GridLayout(1, false));
subContainer
.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
createLabel(subContainer, Messages.ColumnSelectWidget_displayed_label);
displayed = new TableViewer(subContainer, SWT.MULTI | SWT.READ_ONLY
| SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
layoutData.heightHint = displayed.getTable().getItemHeight() * 4;
layoutData.widthHint = 180;
displayed.getControl().setLayoutData(layoutData);
displayed.setContentProvider(new ArrayContentProvider());
displayed.setSorter(DISPLAYED_COLUMNS_VIEWER_SORTER);
displayed.setLabelProvider(DISPLAYED_COLUMNS_LABEL_PROVIDER);
for (ReportColumn reportColumn : report.getReportColumnCollection()) {
displayColumn(reportColumn);
}
updateDisplayedColumnPositions();
}
private void createSwitchButtons() {
Composite subContainer = new Composite(container, SWT.NONE);
subContainer.setLayout(new GridLayout(1, false));
subContainer.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER,
false, true));
createLabel(subContainer, ""); //$NON-NLS-1$
rightButton = createButton(subContainer, BgcPlugin.IMG_ARROW_RIGHT);
leftButton = createButton(subContainer, BgcPlugin.IMG_ARROW_LEFT);
rightButton.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
displayColumns(available.getSelection());
}
});
leftButton.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
removeColumns(displayed.getSelection());
}
});
}
private void createRepositionButtons() {
Composite subContainer = new Composite(container, SWT.NONE);
subContainer.setLayout(new GridLayout(1, false));
subContainer.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER,
false, true));
createLabel(subContainer, ""); //$NON-NLS-1$
upButton = createButton(subContainer, BgcPlugin.IMG_UP);
downButton = createButton(subContainer, BgcPlugin.IMG_DOWN);
upButton.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
moveDisplayedColumns(-1);
}
});
downButton.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
moveDisplayedColumns(1);
}
});
}
private List<ReportColumnWrapper> getSelectedDisplayColumns() {
List<ReportColumnWrapper> selected = new ArrayList<ReportColumnWrapper>();
ISelection selection = displayed.getSelection();
if (selection instanceof IStructuredSelection) {
IStructuredSelection structuredSelection = (IStructuredSelection) selection;
Iterator<?> it = structuredSelection.iterator();
while (it.hasNext()) {
Object o = it.next();
if (o instanceof ReportColumnWrapper) {
selected.add((ReportColumnWrapper) o);
}
}
}
Collections.sort(selected, REPORT_COLUMN_WRAPER_COMPARTOR);
return selected;
}
private List<ReportColumnWrapper> getDisplayedColumns() {
List<ReportColumnWrapper> displayedColumns = new ArrayList<ReportColumnWrapper>();
int numItems = displayed.getTable().getItemCount();
for (int i = 0; i < numItems; i++) {
ReportColumnWrapper wrapper = (ReportColumnWrapper) displayed
.getElementAt(i);
displayedColumns.add(wrapper);
}
Collections.sort(displayedColumns, REPORT_COLUMN_WRAPER_COMPARTOR);
return displayedColumns;
}
private void moveDisplayedColumns(int reqDisp) {
List<ReportColumnWrapper> selected = getSelectedDisplayColumns();
List<ReportColumnWrapper> all = getDisplayedColumns();
if (reqDisp == 0 || selected.isEmpty()) {
return;
}
int numItems = all.size();
int numSelected = selected.size();
ReportColumnWrapper[] newPositions = new ReportColumnWrapper[numItems];
// give the selected items first priority
for (int i = 0; i < numSelected; i++) {
int oldPosition = selected.get(i).getReportColumn().getPosition();
int newPosition = 0;
if (reqDisp > 0) {
// moving down
newPosition = Math.min(oldPosition + reqDisp, numItems
- numSelected + i);
} else {
// moving up
newPosition = Math.max(oldPosition + reqDisp, i);
}
newPositions[newPosition] = selected.get(i);
}
// put the remaining items in the remaining slots and set positions
Queue<ReportColumnWrapper> queue = new LinkedList<ReportColumnWrapper>();
queue.addAll(all);
queue.removeAll(selected);
for (int i = 0; i < numItems; i++) {
if (newPositions[i] == null) {
newPositions[i] = queue.remove();
}
// set the new position
newPositions[i].getReportColumn().setPosition(i);
}
// refresh display
displayed.setInput(all.toArray());
displayed.refresh(true, true);
}
private static Button createButton(Composite parent, String imageName) {
Button button = new Button(parent, SWT.PUSH);
button.setImage(BgcPlugin.getDefault().getImageRegistry()
.get(imageName));
return button;
}
private static Label createLabel(Composite parent, String labelText) {
Label label = new Label(parent, SWT.NONE);
label.setText(labelText);
label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER));
label.setFont(new Font(null, "sans-serif", 8, SWT.BOLD)); //$NON-NLS-1$
return label;
}
private void displayColumns(ISelection selection) {
if (selection instanceof IStructuredSelection) {
List<ReportColumnWrapper> toSelect = new ArrayList<ReportColumnWrapper>();
IStructuredSelection structuredSelection = (IStructuredSelection) selection;
Iterator<?> it = structuredSelection.iterator();
while (it.hasNext()) {
Object o = it.next();
ReportColumnWrapper wrapper = null;
if (o instanceof EntityColumnWrapper) {
EntityColumnWrapper entityColumnWrapper = (EntityColumnWrapper) o;
wrapper = displayColumn(entityColumnWrapper);
} else if (o instanceof PropertyModifierWrapper) {
PropertyModifierWrapper propertyModifierWrapper = (PropertyModifierWrapper) o;
wrapper = displayColumn(propertyModifierWrapper);
}
if (wrapper != null) {
toSelect.add(wrapper);
}
}
// select what was just added to the display column list
if (!toSelect.isEmpty()) {
displayed.setSelection(new StructuredSelection(toSelect), true);
}
}
}
private void removeColumns(ISelection selection) {
if (selection instanceof IStructuredSelection) {
List<EntityColumnWrapper> toSelect = new ArrayList<EntityColumnWrapper>();
IStructuredSelection structuredSelection = (IStructuredSelection) selection;
Iterator<?> it = structuredSelection.iterator();
while (it.hasNext()) {
ReportColumnWrapper reportColumnWrapper = (ReportColumnWrapper) it
.next();
EntityColumnWrapper wrapper = removeColumn(reportColumnWrapper);
toSelect.add(wrapper);
}
// select what was just added to the available column list
if (!toSelect.isEmpty()) {
available.setSelection(new StructuredSelection(toSelect), true);
}
}
}
private void updateDisplayedColumnPositions() {
int numItems = displayed.getTable().getItemCount();
for (int i = 0; i < numItems; i++) {
ReportColumnWrapper wrapper = (ReportColumnWrapper) displayed
.getElementAt(i);
ReportColumn reportColumn = wrapper.getReportColumn();
reportColumn.setPosition(i);
}
}
private static Object getElement(TreeViewer treeViewer, Object needle) {
for (TreeItem item : treeViewer.getTree().getItems()) {
if (needle.equals(item.getData())) {
return item.getData();
}
}
return null;
}
private static Object getElement(TableViewer tableViewer, Object needle) {
for (TableItem item : tableViewer.getTable().getItems()) {
if (needle.equals(item.getData())) {
return item.getData();
}
}
return null;
}
private ReportColumnWrapper displayColumn(ReportColumn reportColumn) {
EntityColumn entityColumn = reportColumn.getEntityColumn();
removeAvailable(entityColumn);
return addDisplayed(reportColumn);
}
private ReportColumnWrapper displayColumn(
EntityColumnWrapper entityColumnWrapper) {
EntityColumn entityColumn = entityColumnWrapper.getEntityColumn();
removeAvailable(entityColumn);
return addDisplayed(entityColumn);
}
private ReportColumnWrapper displayColumn(
PropertyModifierWrapper propertyModifierWrapper) {
EntityColumn entityColumn = propertyModifierWrapper
.getEntityColumnWrapper().getEntityColumn();
removeAvailable(entityColumn);
ReportColumn reportColumn = new ReportColumn();
reportColumn.setEntityColumn(entityColumn);
reportColumn.setPropertyModifier(propertyModifierWrapper
.getPropertyModifier());
return addDisplayed(reportColumn);
}
private EntityColumnWrapper removeColumn(
ReportColumnWrapper reportColumnWrapper) {
removeDisplayed(reportColumnWrapper);
return addAvailable(reportColumnWrapper.getReportColumn()
.getEntityColumn());
}
private EntityColumnWrapper addAvailable(EntityColumn entityColumn) {
EntityColumnWrapper entityColumnWrapper = new EntityColumnWrapper(
entityColumn);
// only add an available EntityColumn if it doesn't already exist
Object o = getElement(available, entityColumnWrapper);
if (o == null) {
available.add(AVAILABLE_COLUMNS_ROOT_OBJECT, entityColumnWrapper);
return entityColumnWrapper;
}
return (EntityColumnWrapper) o;
}
private void removeAvailable(EntityColumn entityColumn) {
available.remove(new EntityColumnWrapper(entityColumn));
}
private ReportColumnWrapper addDisplayed(ReportColumn reportColumn) {
int position = displayed.getTable().getItemCount();
reportColumn.setPosition(position);
ReportColumnWrapper wrapper = new ReportColumnWrapper(reportColumn);
Object o = getElement(displayed, wrapper);
if (o == null) {
displayed.add(wrapper);
EntityColumn entityColumn = reportColumn.getEntityColumn();
notifyListeners(new ColumnChangeEvent(entityColumn));
return wrapper;
}
return (ReportColumnWrapper) o;
}
private ReportColumnWrapper addDisplayed(EntityColumn entityColumn) {
ReportColumn reportColumn = new ReportColumn();
reportColumn.setEntityColumn(entityColumn);
return addDisplayed(reportColumn);
}
private void removeDisplayed(ReportColumnWrapper reportColumnWrapper) {
int index = getElementIndex(displayed, reportColumnWrapper);
if (index != -1) {
displayed.remove(reportColumnWrapper);
// update position of following items
TableItem[] items = displayed.getTable().getItems();
int numItems = items.length;
for (int i = index; i < numItems; i++) {
((ReportColumnWrapper) items[i].getData()).getReportColumn()
.setPosition(i);
}
EntityColumn entityColumn = reportColumnWrapper.getEntityColumn();
notifyListeners(new ColumnChangeEvent(entityColumn));
}
}
private static int getElementIndex(TableViewer tableViewer, Object needle) {
TableItem[] items = tableViewer.getTable().getItems();
int numItems = items.length;
for (int i = 0; i < numItems; i++) {
if (needle.equals(items[i].getData())) {
return i;
}
}
return -1;
}
private static class ReportColumnWrapper extends EntityColumnWrapper {
private final ReportColumn reportColumn;
public ReportColumnWrapper(ReportColumn reportColumn) {
super(reportColumn.getEntityColumn());
this.reportColumn = reportColumn;
}
public ReportColumn getReportColumn() {
return reportColumn;
}
}
private static class EntityColumnWrapper {
private final EntityColumn entityColumn;
private final Integer entityColumnId;
private final Collection<PropertyModifierWrapper> modifiers;
public EntityColumnWrapper(EntityColumn entityColumn) {
this.entityColumn = entityColumn;
this.entityColumnId = entityColumn.getId();
this.modifiers = getModifiers(entityColumn);
}
public EntityColumn getEntityColumn() {
return entityColumn;
}
public Collection<PropertyModifierWrapper> getPropertyModifierCollection() {
return modifiers;
}
@Override
public boolean equals(Object o) {
if (o instanceof EntityColumnWrapper) {
if (((EntityColumnWrapper) o).entityColumnId
.equals(entityColumnId)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return entityColumnId != null ? entityColumnId.hashCode() : 0;
}
private Collection<PropertyModifierWrapper> getModifiers(
EntityColumn entityColumn) {
List<PropertyModifierWrapper> result = new ArrayList<PropertyModifierWrapper>();
for (PropertyModifier modifier : entityColumn.getEntityProperty()
.getPropertyType().getPropertyModifiers()) {
PropertyModifierWrapper wrapper = new PropertyModifierWrapper(
this, modifier);
result.add(wrapper);
}
return result;
}
}
private static class PropertyModifierWrapper {
private final PropertyModifier propertyModifier;
private final EntityColumnWrapper entityColumnWrapper;
public PropertyModifierWrapper(EntityColumnWrapper entityColumnWrapper,
PropertyModifier propertyModifier) {
this.propertyModifier = propertyModifier;
this.entityColumnWrapper = entityColumnWrapper;
}
public PropertyModifier getPropertyModifier() {
return propertyModifier;
}
public EntityColumnWrapper getEntityColumnWrapper() {
return entityColumnWrapper;
}
}
private static class DisplayedColumnsViewerSorter extends ViewerSorter {
@Override
public int compare(Viewer viewer, Object o1, Object o2) {
ReportColumn rc1 = ((ReportColumnWrapper) o1).getReportColumn();
ReportColumn rc2 = ((ReportColumnWrapper) o2).getReportColumn();
return rc1.getPosition().compareTo(rc2.getPosition());
}
}
private static class AvailableColumnsLabelProvider extends LabelProvider {
@Override
public String getText(Object element) {
if (element instanceof EntityColumnWrapper) {
return ((EntityColumnWrapper) element).getEntityColumn()
.getName();
} else if (element instanceof PropertyModifierWrapper) {
return ((PropertyModifierWrapper) element)
.getPropertyModifier().getName();
}
return ""; //$NON-NLS-1$
}
}
private static class AvailableColumnsTreeContentProvider implements
ITreeContentProvider {
@Override
public void dispose() {
//
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
//
}
@Override
public Object[] getElements(Object inputElement) {
if (AVAILABLE_COLUMNS_ROOT_OBJECT.equals(inputElement)) {
return new Object[] {};
}
return null;
}
@Override
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof EntityColumnWrapper) {
EntityColumnWrapper wrapper = (EntityColumnWrapper) parentElement;
return wrapper.getPropertyModifierCollection().toArray();
}
return null;
}
@Override
public Object getParent(Object element) {
if (element instanceof PropertyModifierWrapper) {
PropertyModifierWrapper wrapper = (PropertyModifierWrapper) element;
return wrapper.getEntityColumnWrapper();
}
return null;
}
@Override
public boolean hasChildren(Object element) {
if (element instanceof EntityColumnWrapper) {
EntityColumnWrapper wrapper = (EntityColumnWrapper) element;
return !wrapper.getPropertyModifierCollection().isEmpty();
}
return false;
}
}
private static class DisplayedColumnsLabelProvider extends LabelProvider
implements ITableLabelProvider {
@Override
public Image getColumnImage(Object element, int columnIndex) {
return null;
}
@Override
public String getColumnText(Object element, int columnIndex) {
if (element instanceof ReportColumnWrapper) {
ReportColumn reportColumn = ((ReportColumnWrapper) element)
.getReportColumn();
return getColumnName(reportColumn);
}
return null;
}
}
public static String getColumnName(ReportColumn reportColumn) {
String text = reportColumn.getEntityColumn().getName();
if (reportColumn.getPropertyModifier() != null) {
text += " (" + reportColumn.getPropertyModifier().getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
return text;
}
}