import java.util.UUID;
import java.util.stream.Collectors;
+import javax.inject.Inject;
+
import org.apache.commons.collections4.map.LinkedMap;
import org.eclipse.core.runtime.ICoreRunnable;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.e4.ui.di.UISynchronize;
+import org.eclipse.e4.ui.services.EMenuService;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.nebula.widgets.nattable.NatTable;
+import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration;
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
import org.eclipse.nebula.widgets.nattable.coordinate.PositionCoordinate;
+import org.eclipse.nebula.widgets.nattable.coordinate.Range;
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
import org.eclipse.nebula.widgets.nattable.export.command.ExportCommandHandler;
+import org.eclipse.nebula.widgets.nattable.extension.e4.selection.E4SelectionListener;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.tree.GlazedListTreeData;
import org.eclipse.nebula.widgets.nattable.freeze.FreezeHelper;
import org.eclipse.nebula.widgets.nattable.freeze.FreezeLayer;
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
+import org.eclipse.nebula.widgets.nattable.grid.command.ClientAreaResizeCommand;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
import org.eclipse.nebula.widgets.nattable.summaryrow.SummaryRowLayer;
import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModel;
import org.eclipse.nebula.widgets.nattable.tree.TreeLayer;
+import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
+import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;
+import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuAction;
+import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
-import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
import org.eclipse.swt.SWT;
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.Menu;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.TreeList;
import eu.etaxonomy.cdm.api.application.CdmApplicationState;
-import eu.etaxonomy.cdm.api.service.IDescriptiveDataSetService;
-import eu.etaxonomy.cdm.api.service.IProgressMonitorService;
import eu.etaxonomy.cdm.api.service.dto.RowWrapperDTO;
+import eu.etaxonomy.cdm.api.service.dto.SpecimenRowWrapperDTO;
import eu.etaxonomy.cdm.common.monitor.IRemotingProgressMonitor;
+import eu.etaxonomy.cdm.model.description.Character;
+import eu.etaxonomy.cdm.model.description.DescriptionBase;
import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
import eu.etaxonomy.cdm.model.description.Feature;
import eu.etaxonomy.cdm.model.description.FeatureNode;
import eu.etaxonomy.cdm.model.description.FeatureTree;
import eu.etaxonomy.cdm.model.description.MeasurementUnit;
-import eu.etaxonomy.cdm.model.description.SpecimenDescription;
import eu.etaxonomy.cdm.model.description.State;
import eu.etaxonomy.cdm.persistence.dto.SpecimenNodeWrapper;
+import eu.etaxonomy.taxeditor.editor.internal.TaxeditorEditorPlugin;
import eu.etaxonomy.taxeditor.editor.l10n.Messages;
import eu.etaxonomy.taxeditor.model.MessagingUtils;
+import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
import eu.etaxonomy.taxeditor.store.CdmStore;
import eu.etaxonomy.taxeditor.workbench.WorkbenchUtility;
static final String COUNTRY_COLUMN = "country_column"; //$NON-NLS-1$
static final String LABEL_TAXON_ROW = "TAXON_ROW"; //$NON-NLS-1$
+ static final String LABEL_TAXON_AGGREGATED_DESCRIPTION = "TAXON_AGGREGATED_DESCRIPTION"; //$NON-NLS-1$
+ static final String LABEL_TAXON_AGGREGATED_DESCRIPTION_ICON = "TAXON_AGGREGATED_DESCRIPTION_ICON"; //$NON-NLS-1$
+ static final String LABEL_TAXON_DEFAULT_DESCRIPTION = "TAXON_DEFAULT_DESCRIPTION"; //$NON-NLS-1$
+ static final String LABEL_TAXON_DEFAULT_DESCRIPTION_ICON = "TAXON_DEFAULT_DESCRIPTION_ICON"; //$NON-NLS-1$
+ static final String LABEL_TAXON_LITERATURE_DESCRIPTION = "TAXON_LITERATURE_DESCRIPTION"; //$NON-NLS-1$
+ static final String LABEL_TAXON_LITERATURE_DESCRIPTION_ICON = "TAXON_LITERATURE_DESCRIPTION_ICON"; //$NON-NLS-1$
+ static final String LABEL_TAXON_DESCRIPTION = "LABEL_TAXON_DESCRIPTION"; //$NON-NLS-1$
+ static final String LABEL_DESCRIPTION_HAS_SUPPLEMENTAL_DATA = "LABEL_DESCRIPTION_HAS_SUPPLEMENTAL_DATA"; //$NON-NLS-1$
+
+ @Inject
+ private UISynchronize sync;
+
+ @Inject
+ private EMenuService menuService;
private DescriptiveDataSet descriptiveDataSet;
private Collection<SpecimenNodeWrapper> specimenCache = null;
+ private Map<Feature, CategoricalDataHistogram> featureToHistogramMap = new HashMap<>();
+
+ private Map<Feature, QuantitativeDataStatistics> featureToQuantDataStatisticsMap = new HashMap<>();
+
private ListDataProvider<Object> bodyDataProvider;
private FreezeLayer freezeLayer;
- private ViewportLayer viewportLayer;
-
private List<Feature> features;
private CharacterMatrixPart part;
private CharacterMatrixToolbar toolbar;
+ private boolean isShowTooltips = true;
public CharacterMatrix(Composite parent, CharacterMatrixPart part) {
super(parent, SWT.NONE);
configuration.summaryRowHAlign = HorizontalAlignmentEnum.CENTER;
// NOTE: Getting the colors and fonts from the GUIHelper ensures that
// they are disposed properly (required by SWT)
- configuration.summaryRowBgColor = GUIHelper.getColor(255, 255, 153);
configuration.cHeaderBgColor = GUIHelper.getColor(211, 211, 211);
configuration.rHeaderBgColor = GUIHelper.getColor(211, 211, 211);
natTable.addConfiguration(configuration);
void toggleTreeFlat(boolean isTree, Button btnToggleFlat, Button btnToggleTree, Button btnCollapseAll, Button btnExpandAll, Button btnFreezeSuppInfo) {
isTreeView = isTree;
- createTable(isTree);
+ createTable(isTree, freezeLayer.isFrozen());
btnToggleFlat.setEnabled(isTree);
btnToggleTree.setEnabled(!isTree);
btnCollapseAll.setEnabled(isTree);
return isTreeView;
}
- public void createTable(boolean treeView){
+ public void createTable(boolean treeView, boolean freezeSupplementalColumns){
/**
* layers
*/
toolbar.getWsLabel().getParent().layout();
//initial freeze of supplemental columns
- freezeSupplementalColumns(true);
+ freezeSupplementalColumns(freezeSupplementalColumns);
+
+
+ //add tooltip to table
+ new CategoricalChartTooltip(this);
+ new QuantitativeChartTooltip(this);
this.layout();
+ natTable.doCommand(new ClientAreaResizeCommand(natTable));
}
private List<Feature> initFeatureList(FeatureNode node){
List<Feature> features = new ArrayList<>();
node.getChildNodes().forEach(childNode->
{
- features.add(childNode.getFeature());
+ Feature feature = childNode.getFeature();
+ if(feature.isInstanceOf(Character.class)){
+ features.add(childNode.getFeature());
+ }
features.addAll(initFeatureList(childNode));
});
return features;
GlazedListTreeData treeData = new GlazedListTreeData<>(treeList);
ITreeRowModel treeRowModel = new GlazedListTreeRowModel<>(treeData);
- bodyLayer = new DefaultBodyLayerStack(
- eventLayer);
- viewportLayer = bodyLayer.getViewportLayer();
+ bodyLayer = new DefaultBodyLayerStack(eventLayer);
final SelectionLayer selectionLayer = bodyLayer.getSelectionLayer();
freezeLayer = new FreezeLayer(selectionLayer);
final CompositeFreezeLayer compositeFreezeLayer = new CompositeFreezeLayer(
// add the SortHeaderLayer to the column header layer stack
// as we use GlazedLists, we use the GlazedListsSortModel which
// delegates the sorting to the SortedList
- final SortHeaderLayer<SpecimenDescription> sortHeaderLayer = new SortHeaderLayer<>(
+ final SortHeaderLayer<DescriptionBase> sortHeaderLayer = new SortHeaderLayer<>(
columnHeaderLayer,
new GlazedListsSortModel<>(
sortedList,
// exporting work
topMostLayer.registerCommandHandler(new ExportCommandHandler(topMostLayer));
- //propagate single cell selection
- natTable.addLayerListener(new CellSelectionListener(part));
+ //selection listener
+ E4SelectionListener selectionListener = new CellSelectionListener(part.getSelectionService(),
+ bodyLayer.getSelectionLayer(), bodyDataProvider, part);
+ bodyLayer.getSelectionLayer().addLayerListener(selectionListener);
+ selectionListener.setFullySelectedRowsOnly(false);
//register handler for view configuration menu
natTable.registerCommandHandler(toolbar.getDisplayPersistenceDialogCommandHandler());
natTable.addConfiguration(new CharacterMatrixHeaderMenuConfiguration(natTable));
// add custom configuration for data conversion and add column labels to viewport layer
- topMostLayer.addConfiguration(new DataConversionConfiguration(this));
+ topMostLayer.addConfiguration(new CellEditorDataConversionConfiguration(this));
//register aggregation configuration
summaryRowLayer.addConfiguration(new AggregationConfiguration(this));
+ //+++CONTEXT MENU+++
+ menuService.registerContextMenu(natTable, "eu.etaxonomy.taxeditor.editor.popupmenu.charactermatrix"); //$NON-NLS-1$
+ // get the menu registered by EMenuService
+ final Menu e4Menu = natTable.getMenu();
+ // remove the menu reference from NatTable instance
+ natTable.setMenu(null);
+ natTable.addConfiguration(
+ new AbstractUiBindingConfiguration() {
+ @Override
+ public void configureUiBindings(
+ UiBindingRegistry uiBindingRegistry) {
+ // add e4 menu to NatTable
+ new PopupMenuBuilder(natTable, e4Menu)
+ .build();
+
+ // register the UI binding for header, corner and body region
+ uiBindingRegistry.registerMouseDownBinding(
+ new MouseEventMatcher(
+ SWT.NONE,
+ null,
+ MouseEventMatcher.RIGHT_BUTTON),
+ new PopupMenuAction(e4Menu));
+ }
+ });
+
natTable.configure();
}
void freezeSupplementalColumns(boolean freeze){
if(freeze){
- FreezeHelper.freeze(freezeLayer, viewportLayer,
- new PositionCoordinate(viewportLayer, 0, 0),
- new PositionCoordinate(viewportLayer, LEADING_COLUMN_COUNT-1, -1));
+ FreezeHelper.freeze(freezeLayer, bodyLayer.getViewportLayer(),
+ new PositionCoordinate(bodyLayer.getViewportLayer(), 0, 0),
+ new PositionCoordinate(bodyLayer.getViewportLayer(), LEADING_COLUMN_COUNT-1, -1));
}
else{
- FreezeHelper.unfreeze(freezeLayer, viewportLayer);
+ FreezeHelper.unfreeze(freezeLayer, bodyLayer.getViewportLayer());
}
}
}
public void loadDescriptions(DescriptiveDataSet descriptiveDataSet) {
- UUID monitorUuid = CdmStore.getService(IDescriptiveDataSetService.class).monitGetRowWrapper(descriptiveDataSet);
- IProgressMonitorService progressMonitorService = CdmApplicationState.getCurrentAppConfig().getProgressMonitorService();
+ UUID monitorUuid = CdmApplicationState.getLongRunningTasksService().monitGetRowWrapper(descriptiveDataSet);
-
- String jobLabel = "Load character data";
+ final Collection<RowWrapperDTO> wrappers = new ArrayList<>();
+ String jobLabel = Messages.CharacterMatrix_LOAD_CHARACTER_DATA;
Job job = Job.create(jobLabel, (ICoreRunnable) monitor -> {
- monitor.beginTask(jobLabel, IProgressMonitor.UNKNOWN);
- while(progressMonitorService.isMonitorThreadRunning(monitorUuid)){
- if(monitor.isCanceled()){
- progressMonitorService.interrupt(monitorUuid);
- }
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(jobLabel, IProgressMonitor.UNKNOWN);
+ IRemotingProgressMonitor remotingMonitor;
+ try {
+ remotingMonitor = CdmStore.getProgressMonitorClientManager()
+ .pollMonitor(jobLabel,
+ monitorUuid,
+ 50,
+ null,
+ (List)null,
+ subMonitor);
+ } catch (InterruptedException e) {
+ MessagingUtils.informationDialog(Messages.CharacterMatrix_LOADING_FAILED_TITLE,
+ Messages.CharacterMatrix_LOADING_FAILED_MESSAGE);
+ return;
+ }
+ Object result = remotingMonitor.getResult();
+ if(result instanceof Collection){
+ wrappers.addAll((Collection<RowWrapperDTO>) result);
}
- IRemotingProgressMonitor remotingMonitor = progressMonitorService.getRemotingMonitor(monitorUuid);
- Collection<RowWrapperDTO> wrappers = (Collection<RowWrapperDTO>) remotingMonitor.getResult();
- if(wrappers!=null){
- wrappers.forEach(wrapper->CharacterMatrix.this.descriptions.add(wrapper));
+ if(result instanceof Exception){
+ MessagingUtils.errorDialog("Exception during description loading", this.getClass(), "An exception occured during loading", TaxeditorEditorPlugin.PLUGIN_ID, (Throwable) result, true);
+ }
+ else if(wrappers.isEmpty()){
+ MessagingUtils.informationDialog(Messages.CharacterMatrix_NO_DESCRIPTION_TITLE,
+ Messages.CharacterMatrix_NO_DESCRIPTION_MESSAGE);
}
monitor.done();
});
- job.schedule(1000);
+ job.addJobChangeListener(new JobChangeAdapter(){
+ @Override
+ public void done(IJobChangeEvent event) {
+ sync.syncExec(()->{
+ List<RowWrapperDTO> rowsWithoutTaxonNode = wrappers.stream().filter(row->row.getTaxonNode()==null).collect(Collectors.toList());
+ if(!rowsWithoutTaxonNode.isEmpty()){
+ String collect = rowsWithoutTaxonNode.stream().
+ map(row->row.getDescription().toString())
+ .collect(Collectors.joining("\n\n - ")); //$NON-NLS-1$
+ MessagingUtils.warningDialog(
+ Messages.CharacterMatrix_NO_NODE_FOUND_TITLE,
+ this.getClass(),
+ String.format(Messages.CharacterMatrix_NO_NODE_FOUND_MESSAGE, collect)
+ );
+ }
+ wrappers.stream().filter(row->row.getTaxonNode()!=null).forEach(wrapper->CharacterMatrix.this.descriptions.add(wrapper));
+ loadingDone();
+ });
+ }
+ });
+ job.schedule();
+ }
+
+ public IStructuredSelection getSelection(){
+ Set<Range> selectedRowPositions = bodyLayer.getSelectionLayer().getSelectedRowPositions();
+ List<Object> selectedObjects = new ArrayList<>();
+ for (Range range : selectedRowPositions) {
+ for(int i=range.start;i<range.end;i++){
+ selectedObjects.add(bodyDataProvider.getRowObject(i));
+ }
+ }
+ return new StructuredSelection(selectedObjects);
+ }
+
+ private void loadingDone() {
+ this.part.loadingDone();
}
public List<State> getSupportedStatesForCategoricalFeature(Feature feature){
.filter(wrapper ->
//map descriptions on a list of uuids of the described specimen
!this.descriptions.stream()
- .map(o->((RowWrapperDTO)o).getSpecimen().getUuid())
+ .filter(rowWrapper->rowWrapper instanceof SpecimenRowWrapperDTO)
+ .map(specimenRowWrapper->((SpecimenRowWrapperDTO) specimenRowWrapper).getSpecimen().getUuid())
.collect(Collectors.toList())
//an check if the specimen to add is already contained
.contains(wrapper.getUuidAndTitleCache().getUuid())
return features;
}
+ public Map<Feature, CategoricalDataHistogram> getFeatureToHistogramMap() {
+ return featureToHistogramMap;
+ }
+
+ public Map<Feature, QuantitativeDataStatistics> getFeatureToQuantDataStatisticsMap() {
+ return featureToQuantDataStatisticsMap;
+ }
+
+ public void toogleIsShowTooltips() {
+ this.isShowTooltips = !this.isShowTooltips;
+ }
+
+ public boolean isShowTooltips() {
+ return isShowTooltips;
+ }
+
+ public ICdmEntitySession getCdmEntitiySession(){
+ return part.getCdmEntitySession();
+ }
+
}