/* * Copyright 2015 Diamond Light Source Ltd. * * 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 */ package uk.ac.diamond.scisoft.xpdf.views; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.util.LocalSelectionTransfer; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.ComboBoxCellEditor; import org.eclipse.jface.viewers.DialogCellEditor; import org.eclipse.jface.viewers.EditingSupport; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerDropAdapter; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DragSourceAdapter; import org.eclipse.swt.dnd.DragSourceEvent; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.dnd.TransferData; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Point; 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.Control; import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TableColumn; /** * A class to display and edit samples for the XPDF project. * @author Timothy Spain timothy.spain@diamond.ac.uk * */ class SampleGroupedTable { private SortedSet<Integer> usedIDs; // The set of assigned ID numbers. Should come from the database eventually? // The samples held in the table private List<XPDFSampleParameters> samples; private boolean showSamples, showContainers; // the grouped table object that does the displaying private XPDFGroupedTable groupedTable; private PhaseGroupedTable phaseTable; /** * Constructor. * <p> * takes the same parameters as a Composite, which are passed to the delegated display table. * @param parent * parent Composite in which to insert * @param style * style flags to set */ public SampleGroupedTable(Composite parent, int style) { samples = new ArrayList<XPDFSampleParameters>(); showSamples = true; showContainers = true; groupedTable = new XPDFGroupedTable(parent, SWT.NONE); // Names of the groups List<String> groupNames = new ArrayList<String>(); List<List<ColumnInterface>> groupedColumnInterfaces = new ArrayList<List<ColumnInterface>>(); List<ColumnInterface> columnInterfaces = new ArrayList<ColumnInterface>(); // Define the column groups and the columns they contain groupNames.add("Sample Identification"); columnInterfaces.clear(); columnInterfaces.add(new NameColumnInterface()); columnInterfaces.add(new CodeColumnInterface()); groupedColumnInterfaces.add(columnInterfaces); groupNames.add("Type"); columnInterfaces = new ArrayList<ColumnInterface>(); columnInterfaces.add(new TypeColumnInterface(this)); groupedColumnInterfaces.add(columnInterfaces); groupNames.add("Properties"); columnInterfaces = new ArrayList<ColumnInterface>(); columnInterfaces.add(new PhaseColumnInterface()); columnInterfaces.add(new CompositionColumnInterface()); columnInterfaces.add(new DensityColumnInterface()); columnInterfaces.add(new PackingColumnInterface()); groupedColumnInterfaces.add(columnInterfaces); groupNames.add("Geometry"); columnInterfaces = new ArrayList<ColumnInterface>(); columnInterfaces.add(new ShapeColumnInterface()); columnInterfaces.add(new DimensionColumnInterface()); groupedColumnInterfaces.add(columnInterfaces); // Make up the columns for (int iGroup = 0; iGroup < groupNames.size(); iGroup++) { groupedTable.createColumnGroup(groupNames.get(iGroup), iGroup == groupNames.size() - 1); for (int iColumn = 0; iColumn < groupedColumnInterfaces.get(iGroup).size(); iColumn++) { ColumnInterface colI = groupedColumnInterfaces.get(iGroup).get(iColumn); TableViewerColumn col = groupedTable.addColumn(groupNames.get(iGroup), SWT.NONE); col.getColumn().setText(colI.getName()); groupedTable.setColumnWidth(col, colI.getWeight()); col.setLabelProvider(colI.getLabelProvider()); groupedTable.setColumnEditingSupport(col, colI); if (colI.getSelectionAdapter(this, col) != null) col.getColumn().addSelectionListener(colI.getSelectionAdapter(this, col)); } } SampleParametersContentProvider contentProvider = new SampleParametersContentProvider(); groupedTable.setContentProvider(contentProvider); // The label provider for the column headers // List<String> allColumnNames = new ArrayList<String>(); // for (List<String> groupedNames : groupedColumnNames) // allColumnNames.addAll(groupedNames); // groupedTable.setLabelProvider(new SampleTableLP(allColumnNames)); // The Drag Listener and the Drop Adapter need the Viewer, which // we do not (and should not) have access to at this level. The // final argument in each case is an object that returns the class // when the method generate(Viewer) is called. groupedTable.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY, new Transfer[]{LocalSelectionTransfer.getTransfer()}, new LocalDragSupportListener(groupedTable)); groupedTable.addDropSupport(DND.DROP_MOVE | DND.DROP_COPY, new Transfer[]{LocalSelectionTransfer.getTransfer()}, new LocalViewerDropAdapterFactory(samples, groupedTable)); // Set a SelectionChangedListener to filter the phases in the phases table, based on the selected samples // groupedTable.addSelectionChangedListener(new ISelectionChangedListener() { // // @Override // public void selectionChanged(SelectionChangedEvent event) { // IStructuredSelection selection = (IStructuredSelection) event.getSelection(); // if (selection.size() == 0) { // phaseTable.setVisiblePhases(new ArrayList<XPDFPhase>()); // } else { //// List<XPDFPhase> visiblePhases = new ArrayList<XPDFPhase>(); // Set<XPDFPhase> visiblePhases = new HashSet<XPDFPhase>(); // for (Object oSample : selection.toList()) // if (oSample instanceof XPDFSampleParameters) // visiblePhases.addAll(((XPDFSampleParameters) oSample).getPhases()); // phaseTable.setVisiblePhases(visiblePhases); // } // phaseTable.refresh(); // } // }); } /** * Sets the input of the delegated viewer objects to the List of samples. */ public void setInput() { groupedTable.setInput(samples); } /** * Adds a set of sample parameters. * @param sample * parameters of the sample to be added */ public void add(XPDFSampleParameters sample) { sample.setId(generateUniqueID()); samples.add(sample); groupedTable.refresh(); if (phaseTable != null) phaseTable.addPhases(sample.getPhases()); } /** * Clears all the samples in this table. */ public void clear() { samples.clear(); usedIDs.clear(); groupedTable.refresh(); showSamples(); showContainers(); } /** * Removes all the samples in the provided {@link Collection}. * @param sample * the samples to be removed */ public void removeAll(Collection<XPDFSampleParameters> sample) { samples.removeAll(sample); groupedTable.refresh(); } /** * Returns the list of all samples. * <p> * Use this wisely. * @return list of all the samples in the table. */ public List<XPDFSampleParameters> getAll() { return samples; } /** * Gets the sample parameters at the given index. * @param index * index to retrieve the parameters at. * @return the parameters retrieved */ public XPDFSampleParameters get(int index) { return samples.get(index); } /** * Returns the number of samples in the sample table. * @return the size of the sample table */ public int size() { return samples.size(); } /** * Sets the focus of the underlying Viewers. */ public void setFocus() { groupedTable.setFocus(); } /** * Sets the {@link Layout} data of the underlying Composite. * @param layout */ public void setLayoutData(Object layout) { groupedTable.setLayoutData(layout); } /** * Returns the SWT {@link Control} of the grouped table, for the purposes * of laying out &c. * @return the Control of the grouped table. */ public Control getControl() { return groupedTable; } /** * Sets the samples to be shown */ public void showSamples() { showSamples(true); } /** * Sets the flag as to whether the samples are to be shown * @param show * show the samples? */ public void showSamples(boolean show) { showSamples = show; } /** * Sets the containers to be shown */ public void showContainers() { showContainers(true); } /** * Sets the flag as to whether the containers are to be shown * @param show * show the containers? */ public void showContainers(boolean show) { showContainers = show; } /** * Sets the samples to be hidden */ public void hideSamples() { showSamples(false); } /** * Set the containers to be hidden */ public void hideContainers() { showContainers(false); } /** * Returns whether the table is showing its samples * @return is the table showing samples? */ public boolean isShowingSamples() { return showSamples; } /** * Returns whether the table is showing its containers * @return is the table showing containers? */ public boolean isShowingContainers() { return showContainers; } /** * Refreshes the internal table */ public void refresh() { groupedTable.refresh(); } /** * Returns a list of the samples selected in the table. * @return the selected samples. */ public List<XPDFSampleParameters> getSelectedSamples() { List<XPDFSampleParameters> selectedXPDFParameters = new ArrayList<XPDFSampleParameters>(); ISelection selection = groupedTable.getSelection(); // No items? return, having done nothing. if (selection.isEmpty()) return selectedXPDFParameters; // If it is not an IStructureSelection, then I don't know what to do with it. if (!(selection instanceof IStructuredSelection)) return selectedXPDFParameters; // Get the list of all selected data List<?> selectedData = ((IStructuredSelection) selection).toList(); for (Object datum : selectedData) if (datum instanceof XPDFSampleParameters) selectedXPDFParameters.add((XPDFSampleParameters) datum); return selectedXPDFParameters; } /** * Gets all the phases. * <p> * returns a List of all the {@link XPDFPhase} object of all the phases of * all the samples in the table. * @return a List of all phases. */ public List<XPDFPhase> getAllPhases() { Set<XPDFPhase> usedPhases = new HashSet<XPDFPhase>(); for (XPDFSampleParameters sample : samples) usedPhases.addAll(sample.getPhases()); return new ArrayList<XPDFPhase>(usedPhases); } /** * Create a context menu for the grouped table. * @param menuManager * Manager of the menu to be added. */ public void createContextMenu(MenuManager menuManager) { groupedTable.createContextMenu(menuManager); } private SelectionAdapter getColumnSelectionAdapter(final TableColumn tableColumn, final Comparator<XPDFSampleParameters> comparator) { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent event) { if (comparator == null) return; // Find the present sorted column, if any TableColumn presentSorted = null; int oldSortDirection = SWT.NONE; presentSorted = groupedTable.getSortColumn(); oldSortDirection = groupedTable.getSortDirection(); groupedTable.setSortColumn(null); groupedTable.setSortDirection(SWT.NONE); int newSortDirection = SWT.DOWN; // If the same column is sorted as is now selected, then reverse the sorting if (presentSorted == tableColumn) newSortDirection = (oldSortDirection == SWT.UP) ? SWT.DOWN : SWT.UP; // Do the sort Collections.sort(samples, comparator); if (newSortDirection == SWT.UP) Collections.reverse(samples); groupedTable.setSortColumn(tableColumn); groupedTable.setSortDirection(newSortDirection); groupedTable.refresh(); } }; } /** * Set the phase table related to this. * @param phaseTable * the phase table to be associated */ public void setPhaseTable(PhaseGroupedTable phaseTable) { this.phaseTable = phaseTable; } // // The table label provider does nothing except delegate to the column label provider // class SampleTableLP extends LabelProvider implements ITableLabelProvider { // // final List<String> columns; // // public SampleTableLP(List <String> columns) { // this.columns = columns; // } // // @Override // public Image getColumnImage(Object element, int columnIndex) { // return null; // } // // @Override // public String getColumnText(Object element, int columnIndex) { // return (new SampleTableCLP(columns.get(columnIndex))).getText(element); // } // } private class SampleParametersContentProvider implements IStructuredContentProvider { public SampleParametersContentProvider() { showSamples = true; showContainers = true; } public boolean isShowingContainers() { return showContainers; } public boolean isShowingSamples() { return showSamples; } @Override public void dispose() {} // TODO Auto-generated method stub @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { // viewer.refresh(); } @Override public Object[] getElements(Object inputElement) { if (showSamples && showContainers) { return samples.toArray(new XPDFSampleParameters[]{}); } else if (showContainers) { List<XPDFSampleParameters> containers = new ArrayList<XPDFSampleParameters>(); for (XPDFSampleParameters container : samples) if (!container.isSample()) containers.add(container); return containers.toArray(new XPDFSampleParameters[]{}); } else { List<XPDFSampleParameters> filteredSamples = new ArrayList<XPDFSampleParameters>(); for (XPDFSampleParameters sample : samples) if (sample.isSample()) filteredSamples.add(sample); return filteredSamples.toArray(new XPDFSampleParameters[]{}); } } public void showSamples(boolean show) { showSamples = show; } public void showContainers(boolean show) { showContainers = show; } } // // Column label provider. Use a switch to provide the different data labels for the different columns. // class SampleTableCLP extends ColumnLabelProvider { // // final String columnName; // // public SampleTableCLP(String columnName) { // this.columnName = columnName; // } // // @Override // public String getText(Object element) { // XPDFSampleParameters sample = (XPDFSampleParameters) element; // switch (columnName) { // case "Sample name": return sample.getName(); // case "Code": return Integer.toString(sample.getId()); // case "": return "+"; // case "Phases": // StringBuilder sb = new StringBuilder(); // for (String phase : sample.getPhases()) { // sb.append(phase); // sb.append(", "); // } // if (sb.length() > 2) sb.delete(sb.length()-2, sb.length()); // return sb.toString(); // case "Composition": return sample.getComposition(); // case "Density": return Double.toString(sample.getDensity()); // case "Vol. frac.": return Double.toString(sample.getPackingFraction()); // case "Energy": return Double.toString(sample.getSuggestedEnergy()); // case "μ": return String.format("%.4f", sample.getMu()); // case "Max capillary ID": return Double.toString(sample.getSuggestedCapDiameter()); // case "Beam state": return sample.getBeamState(); // case "Container": return sample.getContainer(); // default: return ""; // } // } // } // drag support for local moves. Copy data private class LocalDragSupportListener extends DragSourceAdapter { private XPDFGroupedTable gT; public LocalDragSupportListener(XPDFGroupedTable gT) { this.gT = gT; } @Override public void dragSetData(DragSourceEvent event) { LocalSelectionTransfer.getTransfer().setSelection(gT.getSelection()); } } private class LocalViewerDropAdapterFactory implements ViewerDropAdapterFactory { List<XPDFSampleParameters> samples; XPDFGroupedTable groupedTable; public LocalViewerDropAdapterFactory(List<XPDFSampleParameters> samples, XPDFGroupedTable groupedTable) { this.samples = samples; this.groupedTable = groupedTable; } public ViewerDropAdapter get(Viewer v) { return new LocalViewerDropAdapter(v, samples, groupedTable); } } // Deals with both dragging and copy-dragging private class LocalViewerDropAdapter extends ViewerDropAdapter { List<XPDFSampleParameters> samples; XPDFGroupedTable groupedTable; public LocalViewerDropAdapter(Viewer tV, List<XPDFSampleParameters> samples, XPDFGroupedTable groupedTable) { super(tV); this.samples = samples; this.groupedTable = groupedTable; } @Override public boolean performDrop(Object data) { XPDFSampleParameters targetEntry = (XPDFSampleParameters) getCurrentTarget(); Object selectionObject = ((IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection()).getFirstElement(); if (selectionObject instanceof XPDFSampleParameters) return dropSamples(targetEntry); else if (selectionObject instanceof XPDFPhase) return dropPhases(targetEntry); else return false; } private boolean dropSamples(XPDFSampleParameters targetEntry) { // Create the List of new sample parameters to be dropped in. List<XPDFSampleParameters> samplesToAdd = new ArrayList<XPDFSampleParameters>(((IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection()).size()); for (Object oSample: ((IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection()).toList()) { XPDFSampleParameters sampleToAdd; if (getCurrentOperation() == DND.DROP_COPY) { sampleToAdd = new XPDFSampleParameters((XPDFSampleParameters) oSample); sampleToAdd.setId(generateUniqueID()); } else { sampleToAdd = (XPDFSampleParameters) oSample; } samplesToAdd.add(sampleToAdd); } int targetIndex; // Deal with removing the originals when moving and get the index to insert the dragees before if (getCurrentOperation() == DND.DROP_MOVE) { // Remove the originals, except the target if it is in the dragged set List<XPDFSampleParameters> samplesToRemove = new ArrayList<XPDFSampleParameters>(samplesToAdd); boolean moveInitialTarget = samplesToAdd.contains(targetEntry); if (moveInitialTarget) samplesToRemove.remove(targetEntry); samples.removeAll(samplesToRemove); // Get the index before which to insert the moved data targetIndex = getDropTargetIndex(targetEntry, getCurrentLocation(), moveInitialTarget); if (targetIndex == -1) return false; if (moveInitialTarget) samples.remove(targetEntry); } else { // Get the index before which to insert the moved data targetIndex = getDropTargetIndex(targetEntry, getCurrentLocation()); if (targetIndex == -1) return false; } boolean success = samples.addAll(targetIndex, samplesToAdd); groupedTable.refresh(); return success; } private boolean dropPhases(XPDFSampleParameters targetEntry) { for (Object phase : ((IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection()).toList()) { targetEntry.addPhase((XPDFPhase) phase); // List<XPDFPhase> phaseList = targetEntry.getPhases(); // phaseList.add((XPDFPhase) phase); // targetEntry.setPhases(phaseList); } groupedTable.refresh(); return true; } @Override public boolean validateDrop(Object target, int operation, TransferData transferType) { // Fine, whatever. Just don't try anything funny. // TODO: real validation. return true; } } private int getDropTargetIndex(XPDFSampleParameters targetEntry, int currentLocation) { return getDropTargetIndex(targetEntry, currentLocation, false); } private int getDropTargetIndex(XPDFSampleParameters targetEntry, int currentLocation, boolean isTargetRemoved) { // If they are identical, there is only one dragee, and the location is ON, then do nothing. if (((IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection()).size() == 1 && targetEntry == ((XPDFSampleParameters) ((IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection()).getFirstElement()) && currentLocation == ViewerDropAdapter.LOCATION_ON) return -1; // Otherwise, copy all the data dragged. int targetIndex; if (targetEntry != null) { targetIndex = samples.indexOf(targetEntry); switch (currentLocation) { case ViewerDropAdapter.LOCATION_BEFORE: case ViewerDropAdapter.LOCATION_ON: break; case ViewerDropAdapter.LOCATION_AFTER: if (!isTargetRemoved) targetIndex++; break; case ViewerDropAdapter.LOCATION_NONE: return -1; default: return -1; } } else { targetIndex = samples.size(); } return targetIndex; } // class SampleTableCESFactory implements EditingSupportFactory { // final String column; // public SampleTableCESFactory(String column) { // this.column = column; // } // @Override // public EditingSupport get(final ColumnViewer v) { // return new SampleTableCES(column, (TableViewer) v); // } // } // // // class SampleTableCES extends EditingSupport { // // final String column; // final TableViewer tV; // // public SampleTableCES(String column, TableViewer tV) { // super(tV); // this.column = column; // this.tV = tV; // }; // @Override // protected CellEditor getCellEditor(Object element) { // return new TextCellEditor(tV.getTable()); //// return new TextCellEditor(); // } // @Override // protected boolean canEdit(Object element) { // if (column == "Code" || column == "" || column == "μ") // return false; // else // return true; // } // @Override // protected Object getValue(Object element) { // XPDFSampleParameters sample = (XPDFSampleParameters) element; // switch (column) { // case "Sample name": return sample.getName(); // case "Code": return sample.getId(); // case "": return null; // TODO: This should eventually show something, but nothing for now. // case "Phases": return (new SampleTableCLP("Phases")).getText(element); // case "Composition": return sample.getComposition(); // case "Density": return Double.toString(sample.getDensity()); // case "Vol. frac.": return Double.toString(sample.getPackingFraction()); // case "Energy": return Double.toString(sample.getSuggestedEnergy()); // case "μ": return 1.0; // case "Max capillary ID": return Double.toString(sample.getSuggestedCapDiameter()); // case "Beam state": return sample.getBeamState(); // case "Container": return sample.getContainer(); // default: return null; // } // } // @Override // protected void setValue(Object element, Object value) { // XPDFSampleParameters sample = (XPDFSampleParameters) element; // String sValue = (String) value; // switch (column) { // case "Sample name": sample.setName(sValue); break; // case "Code": break; // case "": break; // TODO: This should eventually do something. Call a big function, probably. // case "Phases": { // Parse a comma separated list of phases to a list of Strings // String[] arrayOfPhases = sValue.split(","); // List<String> listOfPhases = new ArrayList<String>(); // for (int i = 0; i < arrayOfPhases.length; i++) // listOfPhases.add(arrayOfPhases[i].trim()); // sample.setPhases(listOfPhases); // } break; // case "Composition": sample.setComposition(sValue); break; // case "Density": sample.setDensity(Double.parseDouble(sValue)); break; // case "Vol. frac.": sample.setPackingFraction(Double.parseDouble(sValue)); break; // case "Energy": sample.setSuggestedEnergy(Double.parseDouble(sValue)); break; // case "μ": break; // case "Max capillary ID": sample.setSuggestedCapDiameter(Double.parseDouble(sValue)); break; // case "Beam state": sample.setBeamState(sValue); break; // case "Container": sample.setContainer(sValue); break; // default: break; // } // // Here, only this table needs updating // tV.update(element, null); // } // } // Generate a new id private int generateUniqueID() { final int lowestID = 154; if (usedIDs == null) usedIDs = new TreeSet<Integer>(); int theID = (usedIDs.isEmpty()) ? lowestID : usedIDs.last()+1; usedIDs.add(theID); return theID; } private interface ColumnInterface extends EditingSupportFactory { // Selection Adapter for the group header button public SelectionAdapter getSelectionAdapter(final SampleGroupedTable tab, final TableViewerColumn col); public ColumnLabelProvider getLabelProvider(); public String getName(); public int getWeight(); public boolean presentAsUneditable(Object element); } private static class NameColumnInterface implements ColumnInterface { @Override public EditingSupport get(final ColumnViewer v) { return new EditingSupport(v) { @Override protected CellEditor getCellEditor(Object element) { return new TextCellEditor(((TableViewer) v).getTable()); } @Override protected boolean canEdit(Object element) { return true; } @Override protected Object getValue(Object element) { return (element != null) ? ((XPDFSampleParameters) element).getName() : ""; } @Override protected void setValue(Object element, Object value) { ((XPDFSampleParameters) element).setName( (value != null) ? (String) value : ""); v.refresh(); } }; } @Override public SelectionAdapter getSelectionAdapter(final SampleGroupedTable tab, final TableViewerColumn col) { return tab.getColumnSelectionAdapter(col.getColumn(), new Comparator<XPDFSampleParameters>() { @Override public int compare(XPDFSampleParameters o1, XPDFSampleParameters o2) { return o1.getName().compareTo(o2.getName()); } }); } @Override public ColumnLabelProvider getLabelProvider() { return new ColumnLabelProvider() { @Override public String getText(Object element) { return ((XPDFSampleParameters) element).getName(); } }; } @Override public String getName() { return "Name"; } @Override public int getWeight() { return 20; } @Override public boolean presentAsUneditable(Object element) { return false; } } private static class CodeColumnInterface implements ColumnInterface { @Override public EditingSupport get(ColumnViewer v) { return new EditingSupport(v) { @Override protected void setValue(Object element, Object value) { } @Override protected Object getValue(Object element) { return ((XPDFSampleParameters) element).getId(); } @Override protected CellEditor getCellEditor(Object element) { return null; } @Override protected boolean canEdit(Object element) { return false; } }; } @Override public SelectionAdapter getSelectionAdapter(final SampleGroupedTable tab, final TableViewerColumn col) { return tab.getColumnSelectionAdapter(col.getColumn(), new Comparator<XPDFSampleParameters>() { @Override public int compare(XPDFSampleParameters o1, XPDFSampleParameters o2) { return Integer.compare(o1.getId(), o2.getId()); } }); } @Override public ColumnLabelProvider getLabelProvider() { return new ColumnLabelProvider() { @Override public String getText(Object element) { return "E"+String.format("%05d", ((XPDFSampleParameters)element).getId()); } }; } @Override public String getName() { return "Code"; } @Override public int getWeight() { return 5; } @Override public boolean presentAsUneditable(Object element) { return false; } } private class TypeColumnInterface implements ColumnInterface { private final static String sampleString = "Sample"; private final static String containerString = "Container"; private final String[] comboChoices = {sampleString, containerString}; private SampleGroupedTable sampleTable; public TypeColumnInterface(SampleGroupedTable sampleTable) { this.sampleTable = sampleTable; } @Override public EditingSupport get(final ColumnViewer v) { return new EditingSupport(v) { @Override protected void setValue(Object element, Object value) { switch((int) value) { case(0) : ((XPDFSampleParameters) element).setAsSample(); break; case(1) : ((XPDFSampleParameters) element).setAsContainer(); break; default : break; } sampleTable.refresh(); } @Override protected Object getValue(Object element) { return ((XPDFSampleParameters) element).isSample() ? 0 : 1; } @Override protected CellEditor getCellEditor(Object element) { return new ComboBoxCellEditor(((TableViewer) v).getTable(), comboChoices); } @Override protected boolean canEdit(Object element) { return true; } }; } @Override public SelectionAdapter getSelectionAdapter(final SampleGroupedTable tab, final TableViewerColumn col) { return new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent event) { if (tab.isShowingContainers()) { if (tab.isShowingSamples()) { tab.hideContainers(); tab.showSamples(); } else { tab.showSamples(); tab.showContainers(); } } else { tab.showContainers(); tab.hideSamples(); } tab.refresh(); col.getColumn().setText(getName()); } }; } @Override public ColumnLabelProvider getLabelProvider() { return new ColumnLabelProvider() { @Override public String getText(Object element) { return ((XPDFSampleParameters) element).isSample() ? sampleString : containerString; } }; } @Override public String getName() { if (sampleTable.isShowingSamples() && sampleTable.isShowingContainers()) return sampleString+"/"+containerString; if (sampleTable.isShowingSamples()) return sampleString; else return containerString; } @Override public int getWeight() { return 10; } @Override public boolean presentAsUneditable(Object element) { return false; } } private class PhaseColumnInterface implements ColumnInterface { private String phasesString(Collection<WeightedPhase> phases) { StringBuilder sb = new StringBuilder(); double totalWeight = 0.0; for (WeightedPhase phase : phases) { totalWeight += phase.getWeight(); } for (WeightedPhase phase : phases) { sb.append(phase.getPhase().getName()); sb.append("("); sb.append(Double.toString(phase.getWeight()/totalWeight)); sb.append("), "); } if (sb.length() > 2) sb.delete(sb.length()-2, sb.length()); return sb.toString(); } @Override public EditingSupport get(final ColumnViewer v) { return new EditingSupport(v) { // @Override // protected void setValue(Object element, Object value) { // ((XPDFSampleParameters) element).setComposition((value != null) ? (String) value : null); // v.refresh(); // } @Override protected CellEditor getCellEditor(Object element) { // return new TextCellEditor(((TableViewer) v).getTable()); return new DialogCellEditor(((TableViewer) v).getTable()) { private CompositionDialog compoDialog; private List<WeightedPhase> phases; @Override protected Object openDialogBox(Control cellEditorWindow) { compoDialog = new CompositionDialog(cellEditorWindow.getShell()); compoDialog.createDialogArea(((TableViewer) v).getTable()); compoDialog.setPhases(phases); compoDialog.open(); return null; } @Override protected Button createButton(Composite parent) { Button button = super.createButton(parent); button.setText("+"); return button; } @Override protected Object doGetValue() { if (compoDialog != null) { return compoDialog.getPhases(); } else { return null; } } @Override protected void doSetValue(Object value) { if (value instanceof List<?>) phases = ((List<WeightedPhase>) value); } }; } @Override protected Object getValue(Object element) { if (element instanceof XPDFSampleParameters) { // create a map of phases to fractions XPDFSampleParameters sample = (XPDFSampleParameters) element; List<XPDFPhase> phaseList = sample.getPhases(); List<Double> fractionList = sample.getPhaseWeightings(); List<WeightedPhase> weightedPhases = new ArrayList<WeightedPhase>(); for (int i = 0; i < phaseList.size(); i++) weightedPhases.add(new WeightedPhase(phaseList.get(i), fractionList.get(i))); return weightedPhases; } else { return getLabelProvider().getText(element); } } @Override protected void setValue(Object element, Object value) { if (value instanceof List<?>) { List<?> genericList = (List<?>) value; List<WeightedPhase> phaseList; try { phaseList = (List<WeightedPhase>) genericList; } catch (ClassCastException cCE) { return; } XPDFSampleParameters sample = (XPDFSampleParameters) element; // sample.setPhases(new ArrayList<XPDFPhase>()); sample.clearPhases(); for (WeightedPhase phase : phaseList) { sample.addPhase(phase.getPhase(), phase.getWeight()); } } v.refresh(); } @Override protected boolean canEdit(Object element) { return true; } }; } @Override public SelectionAdapter getSelectionAdapter(SampleGroupedTable tab, TableViewerColumn col) { return null; } @Override public ColumnLabelProvider getLabelProvider() { return new ColumnLabelProvider() { @Override public String getText(Object element) { XPDFSampleParameters sample = ((XPDFSampleParameters) element); List<WeightedPhase> wPhases = new ArrayList<WeightedPhase>(); for (XPDFPhase phase : sample.getPhases()) { wPhases.add(new WeightedPhase(phase, sample.getPhaseWeighting(phase))); } return phasesString(wPhases); } }; } @Override public String getName() { return "Phases"; } @Override public int getWeight() { return 20; } @Override public boolean presentAsUneditable(Object element) { return false; } } private static class CompositionColumnInterface implements ColumnInterface { @Override public EditingSupport get(final ColumnViewer v) { return new EditingSupport(v) { @Override protected void setValue(Object element, Object value) { ((XPDFSampleParameters) element).setComposition((String) value); v.refresh(element); } @Override protected Object getValue(Object element) { return ((XPDFSampleParameters) element).getComposition(); } @Override protected CellEditor getCellEditor(Object element) { return new TextCellEditor(((TableViewer) v).getTable()); } @Override protected boolean canEdit(Object element) { return ((XPDFSampleParameters) element).getPhases().isEmpty(); } }; } @Override public SelectionAdapter getSelectionAdapter(SampleGroupedTable tab, TableViewerColumn col) { return tab.getColumnSelectionAdapter(col.getColumn(), new Comparator<XPDFSampleParameters>() { @Override public int compare(XPDFSampleParameters o1, XPDFSampleParameters o2) { return o1.getComposition().compareTo(o2.getComposition()); } }); } @Override public ColumnLabelProvider getLabelProvider() { return new ColumnLabelProvider() { @Override public String getText(Object element) { return ((XPDFSampleParameters) element).getComposition(); } @Override public Font getFont(Object element) { return (presentAsUneditable((XPDFSampleParameters) element)) ? JFaceResources.getFontRegistry().getItalic(JFaceResources.DEFAULT_FONT) : JFaceResources.getFontRegistry().get(JFaceResources.DEFAULT_FONT); } }; } @Override public String getName() { return "Composition"; } @Override public int getWeight() { return 15; } @Override public boolean presentAsUneditable(Object element) { return !((XPDFSampleParameters) element).getPhases().isEmpty(); } } private static class DensityColumnInterface implements ColumnInterface { @Override public EditingSupport get(final ColumnViewer v) { return new EditingSupport(v) { @Override protected void setValue(Object element, Object value) { ((XPDFSampleParameters) element).setDensity(Double.parseDouble((String) value)); v.refresh(); } @Override protected Object getValue(Object element) { return Double.toString(((XPDFSampleParameters) element).getDensity()); } @Override protected CellEditor getCellEditor(Object element) { return new TextCellEditor(((TableViewer) v).getTable()); } @Override protected boolean canEdit(Object element) { return ((XPDFSampleParameters) element).getPhases().isEmpty(); } }; } @Override public SelectionAdapter getSelectionAdapter(final SampleGroupedTable tab, final TableViewerColumn col) { return tab.getColumnSelectionAdapter(col.getColumn(), new Comparator<XPDFSampleParameters>() { @Override public int compare(XPDFSampleParameters o1, XPDFSampleParameters o2) { return Double.compare(o1.getDensity(), o2.getDensity()); } }); } @Override public ColumnLabelProvider getLabelProvider() { return new ColumnLabelProvider() { @Override public String getText(Object element) { if (element instanceof XPDFSampleParameters) { DecimalFormat threeDP = new DecimalFormat("0.000"); return threeDP.format(((XPDFSampleParameters) element).getDensity()); } else { return "-"; } } @Override public Font getFont(Object element) { return (presentAsUneditable((XPDFSampleParameters) element)) ? JFaceResources.getFontRegistry().getItalic(JFaceResources.DEFAULT_FONT) : JFaceResources.getFontRegistry().get(JFaceResources.DEFAULT_FONT); } }; } @Override public String getName() { return "Density"; } @Override public int getWeight() { return 8; } @Override public boolean presentAsUneditable(Object element) { return !((XPDFSampleParameters) element).getPhases().isEmpty(); } } private static class PackingColumnInterface implements ColumnInterface { @Override public EditingSupport get(final ColumnViewer v) { return new EditingSupport(v) { @Override protected CellEditor getCellEditor(Object element) { return new TextCellEditor(((TableViewer) v).getTable()); } @Override protected boolean canEdit(Object element) { return true; } @Override protected Object getValue(Object element) { return Double.toString(((XPDFSampleParameters) element).getPackingFraction()); } @Override protected void setValue(Object element, Object value) { ((XPDFSampleParameters) element).setPackingFraction(Double.parseDouble((String) value)); v.refresh(); } }; } @Override public SelectionAdapter getSelectionAdapter(SampleGroupedTable tab, TableViewerColumn col) { return tab.getColumnSelectionAdapter(col.getColumn(), new Comparator<XPDFSampleParameters>() { @Override public int compare(XPDFSampleParameters o1, XPDFSampleParameters o2) { return Double.compare(o1.getPackingFraction(), o2.getPackingFraction()); } }); } @Override public ColumnLabelProvider getLabelProvider() { return new ColumnLabelProvider() { @Override public String getText(Object element) { return Double.toString(((XPDFSampleParameters) element).getPackingFraction()); } }; } @Override public String getName() { return "Pack. frac."; } @Override public int getWeight() { return 7; } @Override public boolean presentAsUneditable(Object element) { return false; } } private static class ShapeColumnInterface implements ColumnInterface { private final static String cylinderString = "Cylinder"; final static String cylinderLowerString = "cylinder"; private final static String plateString = "Plate"; final static String plateLowerString = "plate"; private final static String noneString = "Defined by container"; private final static String[] shapeChoices = {cylinderString, plateString, noneString}; @Override public EditingSupport get(final ColumnViewer v) { return new EditingSupport(v) { @Override protected void setValue(Object element, Object value) { switch((int) value) { case(0) : case(1) : ((XPDFSampleParameters) element).setShape(shapeChoices[(int) value]); break; default : ((XPDFSampleParameters) element).setShape(null); } v.refresh(); } @Override protected Object getValue(Object element) { String shapeName = ((XPDFSampleParameters) element).getShapeName(); if (shapeName == null) { return 2; } else { switch (shapeName.toLowerCase()) { case (cylinderLowerString): return 0; case (plateLowerString): return 1; default: return 2; } } } @Override protected CellEditor getCellEditor(Object element) { return new ComboBoxCellEditor(((TableViewer) v).getTable(), shapeChoices); } @Override protected boolean canEdit(Object element) { return true; } }; } @Override public SelectionAdapter getSelectionAdapter(SampleGroupedTable tab, TableViewerColumn col) { return tab.getColumnSelectionAdapter(col.getColumn(), new Comparator<XPDFSampleParameters>() { @Override public int compare(XPDFSampleParameters o1, XPDFSampleParameters o2) { return o1.getShapeName().compareTo(o2.getShapeName()); } }); } @Override public ColumnLabelProvider getLabelProvider() { return new ColumnLabelProvider() { @Override public String getText(Object element) { return ((XPDFSampleParameters) element).getShapeName(); } @Override public Font getFont(Object element) { return (presentAsUneditable((XPDFSampleParameters) element)) ? JFaceResources.getFontRegistry().getItalic(JFaceResources.DEFAULT_FONT) : JFaceResources.getFontRegistry().get(JFaceResources.DEFAULT_FONT); } }; } @Override public String getName() { return "Shape"; } @Override public int getWeight() { return 15; } @Override public boolean presentAsUneditable(Object element) { String shape = ((XPDFSampleParameters) element).getShapeName(); boolean isCylinder = shape.equalsIgnoreCase(cylinderString); boolean isPlate = shape.equalsIgnoreCase(plateString); return !(isCylinder || isPlate); } } private static class DimensionColumnInterface implements ColumnInterface { private final static String cylinderString = "Cylinder"; private final static String plateString = "Plate"; @Override public EditingSupport get(final ColumnViewer v) { return new EditingSupport(v) { @Override protected void setValue(Object element, Object value) { String[] dimStrings = ((String) value).split(",", 2); if (dimStrings.length == 1) { if (!dimStrings[0].isEmpty()) ((XPDFSampleParameters) element).setDimensions(0, Double.parseDouble(dimStrings[0])); } else if (dimStrings.length > 1) ((XPDFSampleParameters) element).setDimensions(Double.parseDouble(dimStrings[0]), Double.parseDouble(dimStrings[1])); v.refresh(); } @Override protected Object getValue(Object element) { double[] dims = ((XPDFSampleParameters) element).getDimensions(); return (dims != null) ? Double.toString(dims[0]) + ", " + Double.toString(dims[1]) : ""; } @Override protected CellEditor getCellEditor(Object element) { return new TextCellEditor(((TableViewer) v).getTable()); } @Override protected boolean canEdit(Object element) { // String shape = ((XPDFSampleParameters) element).getShapeName(); // boolean isCylinder = shape.equalsIgnoreCase(ShapeColumnInterface.cylinderLowerString); // boolean isPlate = shape.equalsIgnoreCase(ShapeColumnInterface.plateLowerString); // return (isCylinder || isPlate) ? // true : // false; return !presentAsUneditable((XPDFSampleParameters) element); } }; } @Override public SelectionAdapter getSelectionAdapter(SampleGroupedTable tab, TableViewerColumn col) { return tab.getColumnSelectionAdapter(col.getColumn(), new Comparator<XPDFSampleParameters>() { @Override public int compare(XPDFSampleParameters o1, XPDFSampleParameters o2) { return ((Double) o1.getDimensions()[0]).compareTo((Double) o2.getDimensions()[0]); } }); } @Override public ColumnLabelProvider getLabelProvider() { return new ColumnLabelProvider() { @Override public String getText(Object element) { double[] dims = ((XPDFSampleParameters) element).getDimensions(); return (dims != null) ? Double.toString(dims[0]) + ", " + Double.toString(dims[1]) : "-"; } @Override public Font getFont(Object element) { return (presentAsUneditable((XPDFSampleParameters) element)) ? JFaceResources.getFontRegistry().getItalic(JFaceResources.DEFAULT_FONT) : JFaceResources.getFontRegistry().get(JFaceResources.DEFAULT_FONT); } }; } @Override public String getName() { return "Dimensions"; } @Override public int getWeight() { return 20; } @Override public boolean presentAsUneditable(Object element) { String shape = ((XPDFSampleParameters) element).getShapeName(); boolean isCylinder = shape.equalsIgnoreCase(cylinderString); boolean isPlate = shape.equalsIgnoreCase(plateString); return !(isCylinder || isPlate); } } private static class WeightedPhase { private XPDFPhase phase; private double weight; public WeightedPhase(XPDFPhase phase, double weight) { this.phase = phase; this.weight = weight; } public XPDFPhase getPhase() { return phase; } public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } public static List<WeightedPhase> makeWeightedPhases(List<XPDFPhase> phases, List<Double> weightings) { List<WeightedPhase> wPhases = new ArrayList<WeightedPhase>(); for (XPDFPhase phase : phases) { wPhases.add(new WeightedPhase(phase, weightings.get(phases.indexOf(phase)))); } return wPhases; } } private class CompositionDialog extends Dialog { private List<WeightedPhase> phases; private TableViewer phaseTableViewer; private Action addPhaseAction, deletePhaseAction; protected CompositionDialog(Shell parentShell) { super(parentShell); } @Override protected Control createDialogArea(Composite parent) { Composite container = (Composite) super.createDialogArea(parent); container.setLayout(new GridLayout(1, true)); Composite tableHolder = new Composite(container, SWT.NONE); tableHolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); tableHolder.setLayout(new TableColumnLayout()); phaseTableViewer = new TableViewer(tableHolder, SWT.BORDER); createColumns(); phaseTableViewer.getTable().setHeaderVisible(true); phaseTableViewer.setContentProvider(new IStructuredContentProvider() { @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { viewer.refresh(); } @Override public void dispose() { } @Override public Object[] getElements(Object inputElement) { return phases.toArray(new WeightedPhase[phases.size()]); } }); phaseTableViewer.setInput(phases); // Add/remove phase actions createActions(); return container; } private void createActions() { addPhaseAction = new AddPhaseAction(); ((AddPhaseAction) addPhaseAction).setPhaseTable(phaseTable); deletePhaseAction = new DeletePhaseAction(); deletePhaseAction.setText("Delete"); deletePhaseAction.setToolTipText("Delete selected phases"); // hookIntoContextMenu(); MenuManager menuMan = new MenuManager("#PopupMenu"); menuMan.setRemoveAllWhenShown(true); menuMan.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(IMenuManager manager) { manager.add(deletePhaseAction); } }); phaseTableViewer.getControl().setMenu(menuMan.createContextMenu(phaseTableViewer.getControl())); } @Override protected void configureShell(Shell newShell) { super.configureShell(newShell); newShell.setText("Sample Composition Editor"); } @Override protected Point getInitialSize() { return new Point(640, 480); } @Override protected boolean isResizable() { return true; } public List<WeightedPhase> getPhases() { return phases; } public void setPhases(List<WeightedPhase> phases) { this.phases = new ArrayList<WeightedPhase>(); for (WeightedPhase phase : phases) this.phases.add(phase); } private void createColumns() { TableViewerColumn phaseColumn = new TableViewerColumn(phaseTableViewer, SWT.NONE, 0), fractionColumn = new TableViewerColumn(phaseTableViewer, SWT.NONE, 1); phaseColumn.getColumn().setText("Phase"); fractionColumn.getColumn().setText("Fraction"); TableColumnLayout tCL = (TableColumnLayout) phaseTableViewer.getTable().getParent().getLayout(); tCL.setColumnData(phaseColumn.getColumn(), new ColumnWeightData(20, false)); tCL.setColumnData(fractionColumn.getColumn(), new ColumnWeightData(10, false)); phaseColumn.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { if (element instanceof WeightedPhase) { return ((WeightedPhase) element).getPhase().getName(); } else { return "?"; } } }); fractionColumn.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { if (element instanceof WeightedPhase) { return Double.toString(((WeightedPhase) element).getWeight()); } else { return "(?)"; } } }); fractionColumn.setEditingSupport(new EditingSupport(fractionColumn.getViewer()) { @Override protected CellEditor getCellEditor(Object element) { return new TextCellEditor(((TableViewer) phaseTableViewer).getTable()); } @Override protected boolean canEdit(Object element) { return true; } @Override protected Object getValue(Object element) { return Double.toString(((WeightedPhase) element).getWeight()); } @Override protected void setValue(Object element, Object value) { ((WeightedPhase) element).setWeight(Double.parseDouble((String) value)); phaseTableViewer.refresh(element); } }); } private class AddPhaseAction extends Action { private PhaseGroupedTable phaseTable; public void setPhaseTable(PhaseGroupedTable phaseTable) { this.phaseTable = phaseTable; } @Override public void run() { // Add an action from the list of phases in the phase table, // excluding those already included in the sample List<XPDFPhase> availablePhases = new ArrayList<XPDFPhase>(phaseTable.getAll()); for (WeightedPhase phase : phases) availablePhases.remove(phase.getPhase()); } } private class DeletePhaseAction extends Action { @Override public void run() { // Get the list of selected phases IStructuredSelection selection = phaseTableViewer.getStructuredSelection(); for (Object selectedObject : selection.toList()) if (selectedObject instanceof WeightedPhase) phases.remove((WeightedPhase) selectedObject); phaseTableViewer.refresh(); } } } }