2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
9 package eu
.etaxonomy
.taxeditor
.editor
.descriptiveDataSet
.matrix
;
12 import java
.util
.ArrayList
;
13 import java
.util
.Collection
;
14 import java
.util
.HashMap
;
15 import java
.util
.List
;
17 import java
.util
.Properties
;
19 import java
.util
.UUID
;
20 import java
.util
.stream
.Collectors
;
22 import org
.apache
.commons
.collections4
.map
.LinkedMap
;
23 import org
.eclipse
.core
.runtime
.ICoreRunnable
;
24 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
25 import org
.eclipse
.core
.runtime
.jobs
.Job
;
26 import org
.eclipse
.jface
.layout
.GridDataFactory
;
27 import org
.eclipse
.jface
.viewers
.ComboViewer
;
28 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
29 import org
.eclipse
.nebula
.widgets
.nattable
.NatTable
;
30 import org
.eclipse
.nebula
.widgets
.nattable
.config
.AbstractRegistryConfiguration
;
31 import org
.eclipse
.nebula
.widgets
.nattable
.config
.CellConfigAttributes
;
32 import org
.eclipse
.nebula
.widgets
.nattable
.config
.ConfigRegistry
;
33 import org
.eclipse
.nebula
.widgets
.nattable
.config
.DefaultNatTableStyleConfiguration
;
34 import org
.eclipse
.nebula
.widgets
.nattable
.config
.IConfigRegistry
;
35 import org
.eclipse
.nebula
.widgets
.nattable
.config
.IEditableRule
;
36 import org
.eclipse
.nebula
.widgets
.nattable
.coordinate
.PositionCoordinate
;
37 import org
.eclipse
.nebula
.widgets
.nattable
.data
.IDataProvider
;
38 import org
.eclipse
.nebula
.widgets
.nattable
.data
.ListDataProvider
;
39 import org
.eclipse
.nebula
.widgets
.nattable
.edit
.EditConfigAttributes
;
40 import org
.eclipse
.nebula
.widgets
.nattable
.export
.command
.ExportCommandHandler
;
41 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.GlazedListsEventLayer
;
42 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.GlazedListsSortModel
;
43 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.tree
.GlazedListTreeData
;
44 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.tree
.GlazedListTreeRowModel
;
45 import org
.eclipse
.nebula
.widgets
.nattable
.freeze
.CompositeFreezeLayer
;
46 import org
.eclipse
.nebula
.widgets
.nattable
.freeze
.FreezeHelper
;
47 import org
.eclipse
.nebula
.widgets
.nattable
.freeze
.FreezeLayer
;
48 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.GridRegion
;
49 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.data
.DefaultColumnHeaderDataProvider
;
50 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.data
.DefaultCornerDataProvider
;
51 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.data
.DefaultRowHeaderDataProvider
;
52 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.data
.FixedSummaryRowHeaderLayer
;
53 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.ColumnHeaderLayer
;
54 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.CornerLayer
;
55 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.DefaultRowHeaderDataLayer
;
56 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.GridLayer
;
57 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.AbstractLayer
;
58 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.CompositeLayer
;
59 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.DataLayer
;
60 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.ILayer
;
61 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.ILayerListener
;
62 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.cell
.ColumnOverrideLabelAccumulator
;
63 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.cell
.ILayerCell
;
64 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.event
.ILayerEvent
;
65 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.stack
.DefaultBodyLayerStack
;
66 import org
.eclipse
.nebula
.widgets
.nattable
.selection
.SelectionLayer
;
67 import org
.eclipse
.nebula
.widgets
.nattable
.selection
.event
.CellSelectionEvent
;
68 import org
.eclipse
.nebula
.widgets
.nattable
.sort
.SortHeaderLayer
;
69 import org
.eclipse
.nebula
.widgets
.nattable
.sort
.config
.SingleClickSortConfiguration
;
70 import org
.eclipse
.nebula
.widgets
.nattable
.style
.DisplayMode
;
71 import org
.eclipse
.nebula
.widgets
.nattable
.style
.HorizontalAlignmentEnum
;
72 import org
.eclipse
.nebula
.widgets
.nattable
.style
.theme
.ModernNatTableThemeConfiguration
;
73 import org
.eclipse
.nebula
.widgets
.nattable
.summaryrow
.DefaultSummaryRowConfiguration
;
74 import org
.eclipse
.nebula
.widgets
.nattable
.summaryrow
.FixedSummaryRowLayer
;
75 import org
.eclipse
.nebula
.widgets
.nattable
.summaryrow
.ISummaryProvider
;
76 import org
.eclipse
.nebula
.widgets
.nattable
.summaryrow
.SummaryRowConfigAttributes
;
77 import org
.eclipse
.nebula
.widgets
.nattable
.summaryrow
.SummaryRowLayer
;
78 import org
.eclipse
.nebula
.widgets
.nattable
.tree
.ITreeRowModel
;
79 import org
.eclipse
.nebula
.widgets
.nattable
.tree
.TreeLayer
;
80 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.menu
.AbstractHeaderMenuConfiguration
;
81 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.menu
.PopupMenuBuilder
;
82 import org
.eclipse
.nebula
.widgets
.nattable
.util
.GUIHelper
;
83 import org
.eclipse
.nebula
.widgets
.nattable
.viewport
.ViewportLayer
;
84 import org
.eclipse
.swt
.SWT
;
85 import org
.eclipse
.swt
.layout
.GridData
;
86 import org
.eclipse
.swt
.layout
.GridLayout
;
87 import org
.eclipse
.swt
.widgets
.Button
;
88 import org
.eclipse
.swt
.widgets
.Composite
;
90 import ca
.odell
.glazedlists
.BasicEventList
;
91 import ca
.odell
.glazedlists
.EventList
;
92 import ca
.odell
.glazedlists
.SortedList
;
93 import ca
.odell
.glazedlists
.TreeList
;
94 import eu
.etaxonomy
.cdm
.api
.application
.CdmApplicationState
;
95 import eu
.etaxonomy
.cdm
.api
.service
.IDescriptiveDataSetService
;
96 import eu
.etaxonomy
.cdm
.api
.service
.IProgressMonitorService
;
97 import eu
.etaxonomy
.cdm
.api
.service
.dto
.RowWrapperDTO
;
98 import eu
.etaxonomy
.cdm
.common
.monitor
.IRemotingProgressMonitor
;
99 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveDataSet
;
100 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
101 import eu
.etaxonomy
.cdm
.model
.description
.FeatureNode
;
102 import eu
.etaxonomy
.cdm
.model
.description
.FeatureTree
;
103 import eu
.etaxonomy
.cdm
.model
.description
.MeasurementUnit
;
104 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
105 import eu
.etaxonomy
.cdm
.model
.description
.State
;
106 import eu
.etaxonomy
.cdm
.persistence
.dto
.SpecimenNodeWrapper
;
107 import eu
.etaxonomy
.taxeditor
.editor
.descriptiveDataSet
.matrix
.categorical
.CategoricalDataCellEditor
;
108 import eu
.etaxonomy
.taxeditor
.editor
.descriptiveDataSet
.matrix
.categorical
.CategoricalDataDisplayConverter
;
109 import eu
.etaxonomy
.taxeditor
.editor
.descriptiveDataSet
.matrix
.quantitative
.QuantitativeDataCellEditor
;
110 import eu
.etaxonomy
.taxeditor
.editor
.descriptiveDataSet
.matrix
.quantitative
.QuantitativeDataDisplayConverter
;
111 import eu
.etaxonomy
.taxeditor
.editor
.descriptiveDataSet
.matrix
.supplementalInfo
.SupplementalInfoDisplayConverter
;
112 import eu
.etaxonomy
.taxeditor
.editor
.l10n
.Messages
;
113 import eu
.etaxonomy
.taxeditor
.model
.MessagingUtils
;
114 import eu
.etaxonomy
.taxeditor
.store
.CdmStore
;
115 import eu
.etaxonomy
.taxeditor
.workbench
.WorkbenchUtility
;
118 * Character matrix editor for editing specimen/taxon descriptions in a table
120 * @since Nov 26, 2017
123 public class CharacterMatrix
extends Composite
{
125 private static final String CHARACTER_MATRIX_STATE_PROPERTIES
= "characterMatrixState.properties"; //$NON-NLS-1$
127 static final int LEADING_COLUMN_COUNT
= 4;
128 static final String TAXON_COLUMN
= "taxon_column"; //$NON-NLS-1$
129 static final String COLLECTOR_COLUMN
= "collector_column"; //$NON-NLS-1$
130 static final String IDENTIFIER_COLUMN
= "identifier_column"; //$NON-NLS-1$
131 static final String COUNTRY_COLUMN
= "country_column"; //$NON-NLS-1$
133 private DescriptiveDataSet descriptiveDataSet
;
135 private NatTable natTable
;
137 private Map
<Integer
, Feature
> indexToFeatureMap
= new HashMap
<>();
139 private Map
<Feature
, List
<State
>> categoricalFeatureToStateMap
= new HashMap
<>();
141 private LinkedMap
<String
, String
> propertyToLabelMap
= new LinkedMap
<>();
143 private EventList
<Object
> descriptions
;
145 private Collection
<SpecimenNodeWrapper
> specimenCache
= null;
147 private ListDataProvider
<Object
> bodyDataProvider
;
149 private FreezeLayer freezeLayer
;
151 private ViewportLayer viewportLayer
;
153 private List
<Feature
> features
;
155 private CharacterMatrixPart part
;
157 private AbstractLayer topMostLayer
;
159 private FixedSummaryRowLayer summaryRowLayer
;
161 private ConfigRegistry configRegistry
;
163 private DefaultBodyLayerStack bodyLayer
;
165 private boolean isTreeView
= true;
167 private CharacterMatrixToolbar toolbar
;
170 public CharacterMatrix(Composite parent
, CharacterMatrixPart part
) {
171 super(parent
, SWT
.NONE
);
173 this.setLayout(new GridLayout());
177 natTable
= new NatTable(this, false);
179 createBottomToolbar();
183 private void createToolBar(){
184 toolbar
= new CharacterMatrixToolbar(this, SWT
.NONE
);
187 @SuppressWarnings("unused")
188 private void createBottomToolbar() {
189 new CharacterMatrixBottomToolbar(this, SWT
.NONE
);
193 private void applyStyles(){
194 ModernNatTableThemeConfiguration configuration
= new ModernNatTableThemeConfiguration();
195 configuration
.summaryRowHAlign
= HorizontalAlignmentEnum
.CENTER
;
196 configuration
.summaryRowBgColor
= GUIHelper
.getColor(255, 255, 153);
197 configuration
.cHeaderBgColor
= GUIHelper
.getColor(211, 211, 211);
198 configuration
.rHeaderBgColor
= GUIHelper
.getColor(211, 211, 211);
199 natTable
.addConfiguration(configuration
);
200 // // NOTE: Getting the colors and fonts from the GUIHelper ensures that
201 // // they are disposed properly (required by SWT)
202 // DefaultNatTableStyleConfiguration natTableConfiguration = new DefaultNatTableStyleConfiguration();
203 // natTableConfiguration.bgColor = GUIHelper.getColor(249, 172, 7);
204 // natTableConfiguration.fgColor = GUIHelper.getColor(30, 76, 19);
205 // natTableConfiguration.hAlign = HorizontalAlignmentEnum.LEFT;
206 // natTableConfiguration.vAlign = VerticalAlignmentEnum.TOP;
207 //// natTableConfiguration.borderStyle = new BorderStyle(1, GUIHelper.getColor(249, 172, 7), LineStyleEnum.SOLID);
209 // // Setup even odd row colors - row colors override the NatTable default
211 // DefaultRowStyleConfiguration rowStyleConfiguration = new DefaultRowStyleConfiguration();
212 // rowStyleConfiguration.oddRowBgColor = ColorResources.getColor(Resources.COLOR_LIST_ODD);
213 // rowStyleConfiguration.evenRowBgColor = ColorResources.getColor(Resources.COLOR_LIST_EVEN);
215 // // Setup selection styling
216 // DefaultSelectionStyleConfiguration selectionStyle = new DefaultSelectionStyleConfiguration();
217 //// selectionStyle.selectionFont = GUIHelper.getFont(new FontData("Verdana", 8, SWT.NORMAL));
218 //// selectionStyle.selectionBgColor = GUIHelper.getColor(217, 232, 251);
219 //// selectionStyle.selectionFgColor = GUIHelper.COLOR_BLACK;
220 //// selectionStyle.anchorBorderStyle = new BorderStyle(1, GUIHelper.COLOR_DARK_GRAY, LineStyleEnum.SOLID);
221 //// selectionStyle.anchorBgColor = GUIHelper.getColor(65, 113, 43);
222 // selectionStyle.selectedHeaderBgColor = GUIHelper.getColor(156, 209, 103);
224 // // Add all style configurations to NatTable
225 // natTable.addConfiguration(natTableConfiguration);
226 // natTable.addConfiguration(rowStyleConfiguration);
227 // natTable.addConfiguration(selectionStyle);
229 // // Column/Row header style and custom painters
230 // DefaultRowHeaderStyleConfiguration rowHeaderConfig = new DefaultRowHeaderStyleConfiguration();
231 // Color rowColumnColor = GUIHelper.getColor(230, 255, 255);
232 // rowHeaderConfig.bgColor = rowColumnColor;
233 // natTable.addConfiguration(rowHeaderConfig);
234 // DefaultColumnHeaderStyleConfiguration columnHeaderStyle = new DefaultColumnHeaderStyleConfiguration();
235 // columnHeaderStyle.bgColor = rowColumnColor;
236 // columnHeaderStyle.font = GUIHelper.getFont(new FontData("Verdana", 9, SWT.BOLD)); //$NON-NLS-1$
237 // natTable.addConfiguration(columnHeaderStyle);
241 void toggleTreeFlat(boolean isTree
, Button btnToggleFlat
, Button btnToggleTree
, Button btnCollapseAll
, Button btnExpandAll
, Button btnFreezeSuppInfo
) {
244 btnToggleFlat
.setEnabled(isTree
);
245 btnToggleTree
.setEnabled(!isTree
);
246 btnCollapseAll
.setEnabled(isTree
);
247 btnExpandAll
.setEnabled(isTree
);
250 public boolean isTreeView() {
254 public void createTable(boolean treeView
){
258 createLayers(treeView
);
263 configureNatTable(treeView
, configRegistry
, topMostLayer
, summaryRowLayer
);
266 * handlers and listeners
268 registerHandlersAndListeners(topMostLayer
);
270 GridDataFactory
.fillDefaults().grab(true, true).applyTo(natTable
);
272 toolbar
.getWsLabel().setText(descriptiveDataSet
.getLabel());
273 toolbar
.getWsLabel().setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
, true, false));
274 toolbar
.getWsLabel().getParent().layout();
276 freezeSupplementalColumns(true);
281 private List
<Feature
> initFeatureList(FeatureNode node
){
282 List
<Feature
> features
= new ArrayList
<>();
283 node
.getChildNodes().forEach(childNode
->
285 features
.add(childNode
.getFeature());
286 features
.addAll(initFeatureList(childNode
));
291 public void initDescriptiveDataSet(DescriptiveDataSet descriptiveDataSet
){
292 this.descriptiveDataSet
= descriptiveDataSet
;
293 //get features/columns stored in descriptive data set
294 FeatureTree tree
= descriptiveDataSet
.getDescriptiveSystem();
295 features
= initFeatureList(tree
.getRoot());
297 //init state data for categorical features
298 features
.forEach(feature
->
300 if(feature
.isSupportsCategoricalData()){
301 List
<State
> supportedStates
= new ArrayList
<>();
302 feature
.getSupportedCategoricalEnumerations().forEach(voc
->supportedStates
.addAll(voc
.getTerms()));
303 categoricalFeatureToStateMap
.put(feature
, supportedStates
);
306 descriptions
= new BasicEventList
<>();
310 private void createLayers(boolean treeView
) {
311 // use the SortedList constructor with 'null' for the Comparator
312 // because the Comparator will be set by configuration
313 SortedList
<Object
> sortedList
= new SortedList
<>(descriptions
, new MatrixRowComparator());
314 // wrap the SortedList with the TreeList
315 TreeList
<Object
> treeList
= new TreeList(sortedList
, new DescriptionTreeFormat(descriptiveDataSet
), TreeList
.NODES_START_EXPANDED
);
319 SpecimenColumnPropertyAccessor columnPropertyAccessor
= new SpecimenColumnPropertyAccessor(this);
320 bodyDataProvider
= treeView?
new ListDataProvider
<>(treeList
, columnPropertyAccessor
):new ListDataProvider
<>(sortedList
, columnPropertyAccessor
);
322 configRegistry
= new ConfigRegistry();
331 - (top) SummaryRowLayer
332 - (bottom) ViewportLayer
338 TreeLayer (default visible)
364 DataLayer bodyDataLayer
= new DataLayer(bodyDataProvider
);
367 CharacterMatrixConfigLabelAccumulator labelAccumulator
= new CharacterMatrixConfigLabelAccumulator(this);
368 bodyDataLayer
.setConfigLabelAccumulator(labelAccumulator
);
371 propertyToLabelMap
.put(TAXON_COLUMN
, Messages
.CharacterMatrix_TAXON
);
372 propertyToLabelMap
.put(COLLECTOR_COLUMN
, Messages
.CharacterMatrix_COLLECTOR_NO
);
373 propertyToLabelMap
.put(IDENTIFIER_COLUMN
, Messages
.CharacterMatrix_IDENTIFIER
);
374 propertyToLabelMap
.put(COUNTRY_COLUMN
, Messages
.CharacterMatrix_COUNTRY
);
375 for(int i
=0;i
<features
.size();i
++){
376 Feature feature
= features
.get(i
);
377 initLabels(i
, feature
);
380 // layer for event handling of GlazedLists and PropertyChanges
381 GlazedListsEventLayer eventLayer
= new GlazedListsEventLayer
<>(bodyDataLayer
, treeList
);
382 GlazedListTreeData treeData
= new GlazedListTreeData
<>(treeList
);
383 ITreeRowModel treeRowModel
= new GlazedListTreeRowModel
<>(treeData
);
385 bodyLayer
= new DefaultBodyLayerStack(
387 viewportLayer
= bodyLayer
.getViewportLayer();
388 final SelectionLayer selectionLayer
= bodyLayer
.getSelectionLayer();
389 freezeLayer
= new FreezeLayer(selectionLayer
);
390 final CompositeFreezeLayer compositeFreezeLayer
= new CompositeFreezeLayer(
391 freezeLayer
, bodyLayer
.getViewportLayer(), selectionLayer
);
392 TreeLayer treeLayer
= new TreeLayer(compositeFreezeLayer
, treeRowModel
);
394 topMostLayer
= treeView?treeLayer
:compositeFreezeLayer
;
396 summaryRowLayer
= new FixedSummaryRowLayer(bodyDataLayer
, topMostLayer
, configRegistry
, false);
397 //regoster labels with summary prefix for summary layer
398 ColumnOverrideLabelAccumulator summaryColumnLabelAccumulator
=new ColumnOverrideLabelAccumulator(bodyDataLayer
);
399 summaryRowLayer
.setConfigLabelAccumulator(summaryColumnLabelAccumulator
);
400 for(int i
=0;i
<features
.size();i
++){
401 Feature feature
= features
.get(i
);
402 summaryColumnLabelAccumulator
.registerColumnOverrides(
403 i
+LEADING_COLUMN_COUNT
,
404 SummaryRowLayer
.DEFAULT_SUMMARY_COLUMN_CONFIG_LABEL_PREFIX
+MatrixUtility
.getProperty(feature
));
406 // because the horizontal dependency is the ViewportLayer
407 // we need to set the composite dependency to false
408 summaryRowLayer
.setHorizontalCompositeDependency(false);
410 CompositeLayer composite
= new CompositeLayer(1, 2);
411 composite
.setChildLayer("SUMMARY", summaryRowLayer
, 0, 0); //$NON-NLS-1$
412 composite
.setChildLayer(GridRegion
.BODY
, topMostLayer
, 0, 1);
416 * column header layer
418 IDataProvider columnHeaderDataProvider
= new DefaultColumnHeaderDataProvider(
419 propertyToLabelMap
.values().toArray(new String
[] {}), propertyToLabelMap
);
420 DataLayer columnHeaderDataLayer
= new DataLayer(columnHeaderDataProvider
);
421 ColumnHeaderLayer columnHeaderLayer
= new ColumnHeaderLayer(columnHeaderDataLayer
, topMostLayer
, selectionLayer
);
423 // add the SortHeaderLayer to the column header layer stack
424 // as we use GlazedLists, we use the GlazedListsSortModel which
425 // delegates the sorting to the SortedList
426 final SortHeaderLayer
<SpecimenDescription
> sortHeaderLayer
= new SortHeaderLayer
<>(
428 new GlazedListsSortModel
<>(
430 columnPropertyAccessor
,
432 columnHeaderDataLayer
));
438 IDataProvider rowHeaderDataProvider
= new DefaultRowHeaderDataProvider(bodyDataProvider
);
439 DefaultRowHeaderDataLayer rowHeaderDataLayer
= new DefaultRowHeaderDataLayer(rowHeaderDataProvider
);
440 FixedSummaryRowHeaderLayer fixedSummaryRowHeaderLayer
= new FixedSummaryRowHeaderLayer(rowHeaderDataLayer
,
441 composite
, selectionLayer
);
442 fixedSummaryRowHeaderLayer
.setSummaryRowLabel("\u2211"); //$NON-NLS-1$
448 ILayer cornerLayer
= new CornerLayer(
449 new DataLayer(new DefaultCornerDataProvider(columnHeaderDataProvider
, rowHeaderDataProvider
)),
450 fixedSummaryRowHeaderLayer
, sortHeaderLayer
);
454 * GRID layer (composition of all other layers)
456 GridLayer gridLayer
= new GridLayer(composite
, sortHeaderLayer
, fixedSummaryRowHeaderLayer
, cornerLayer
);
458 natTable
.setLayer(gridLayer
);
462 private void registerHandlersAndListeners(AbstractLayer topMostLayer
) {
463 // add the ExportCommandHandler to the ViewportLayer in order to make
465 topMostLayer
.registerCommandHandler(new ExportCommandHandler(topMostLayer
));
467 //propagate single cell selection
468 natTable
.addLayerListener(new ILayerListener() {
470 public void handleLayerEvent(ILayerEvent event
) {
471 if(event
instanceof CellSelectionEvent
){
472 CellSelectionEvent cellSelectionEvent
= (CellSelectionEvent
)event
;
473 int columnPosition
= cellSelectionEvent
.getColumnPosition();
474 if(columnPosition
>LEADING_COLUMN_COUNT
){
475 Collection
<ILayerCell
> selectedCells
= cellSelectionEvent
.getSelectionLayer().getSelectedCells();
476 StructuredSelection selection
= new StructuredSelection();
477 if(selectedCells
.size()==1){
478 ILayerCell cell
= selectedCells
.iterator().next();
479 Object dataValue
= cell
.getDataValue();
481 selection
= new StructuredSelection(dataValue
);
484 part
.getSelectionService().setSelection(selection
);
490 //register handler for view configuration menu
491 natTable
.registerCommandHandler(toolbar
.getDisplayPersistenceDialogCommandHandler());
494 private void configureNatTable(boolean treeView
, ConfigRegistry configRegistry
, AbstractLayer topMostLayer
,
495 FixedSummaryRowLayer summaryRowLayer
) {
499 natTable
.setConfigRegistry(configRegistry
);
503 //add default configuration because autoconfigure is set to false in constructor
504 natTable
.addConfiguration(new DefaultNatTableStyleConfiguration());
506 //FIXME: this is for DEBUG ONLY
507 // natTable.addConfiguration(new DebugMenuConfiguration(natTable));
509 // override the default sort configuration and change the mouse bindings
510 // to sort on a single click
512 natTable
.addConfiguration(new SingleClickSortConfiguration());
515 // add the header menu configuration for adding the column header menu
516 // with hide/show actions
517 natTable
.addConfiguration(new AbstractHeaderMenuConfiguration(natTable
) {
520 protected PopupMenuBuilder
createColumnHeaderMenu(NatTable natTable
) {
521 return super.createColumnHeaderMenu(natTable
)
522 .withHideColumnMenuItem()
523 .withShowAllColumnsMenuItem();
528 // add custom configuration for data conversion and add column labels to viewport layer
529 topMostLayer
.addConfiguration(new AbstractRegistryConfiguration() {
531 public void configureRegistry(IConfigRegistry configRegistry
) {
532 //add display converter for string representation
533 configRegistry
.registerConfigAttribute(
534 CellConfigAttributes
.DISPLAY_CONVERTER
,
535 new SupplementalInfoDisplayConverter(),
538 configRegistry
.registerConfigAttribute(
539 CellConfigAttributes
.DISPLAY_CONVERTER
,
540 new SupplementalInfoDisplayConverter(),
543 configRegistry
.registerConfigAttribute(
544 CellConfigAttributes
.DISPLAY_CONVERTER
,
545 new SupplementalInfoDisplayConverter(),
548 configRegistry
.registerConfigAttribute(
549 CellConfigAttributes
.DISPLAY_CONVERTER
,
550 new SupplementalInfoDisplayConverter(),
553 features
.forEach(feature
->registerColumnConfiguration(feature
, configRegistry
));
558 //no summary for the supplemental columns
559 for(int i
=0;i
<LEADING_COLUMN_COUNT
;i
++){
561 summaryRowLayer
.addConfiguration(new DefaultSummaryRowConfiguration() {
563 public void addSummaryProviderConfig(IConfigRegistry configRegistry
) {
564 configRegistry
.registerConfigAttribute(
565 SummaryRowConfigAttributes
.SUMMARY_PROVIDER
,
566 new ISummaryProvider() {
569 public Object
summarize(int columnIndex
) {
574 SummaryRowLayer
.DEFAULT_SUMMARY_COLUMN_CONFIG_LABEL_PREFIX
+index
);
578 //register aggregation configuration for each feature
579 features
.forEach(feature
->summaryRowLayer
.addConfiguration(new AggregationConfiguration(bodyDataProvider
, feature
)));
581 natTable
.configure();
584 void freezeSupplementalColumns(boolean freeze
){
586 FreezeHelper
.freeze(freezeLayer
, viewportLayer
,
587 new PositionCoordinate(viewportLayer
, 0, 0),
588 new PositionCoordinate(viewportLayer
, LEADING_COLUMN_COUNT
-1, -1));
591 FreezeHelper
.unfreeze(freezeLayer
, viewportLayer
);
595 void selectStateItem(ComboViewer comboStates
, String stateName
){
596 String
[] items
= comboStates
.getCombo().getItems();
597 for(int i
=0;i
<items
.length
;i
++){
598 if(items
[i
].equals(stateName
)){
599 comboStates
.getCombo().select(i
);
605 private void initLabels(int index
, Feature feature
) {
606 indexToFeatureMap
.put(index
+LEADING_COLUMN_COUNT
, feature
);
608 String featureLabel
= feature
.getLabel();
609 String property
= featureLabel
;
610 //show unit for quantitative data
611 if(feature
.isSupportsQuantitativeData()){
612 Set
<MeasurementUnit
> recommendedMeasurementUnits
= feature
.getRecommendedMeasurementUnits();
613 if(recommendedMeasurementUnits
.size()>1){
614 MessagingUtils
.warningDialog(Messages
.CharacterMatrix_INIT_PROBLEM
, CharacterMatrix
.class,
615 String
.format(Messages
.CharacterMatrix_INIT_PROBLEM_MESSAGE
, feature
.getLabel()));
617 if(recommendedMeasurementUnits
.size()==1){
618 MeasurementUnit unit
= recommendedMeasurementUnits
.iterator().next();
619 featureLabel
+= " ["+unit
.getIdInVocabulary()+"]"; //$NON-NLS-1$ //$NON-NLS-2$
622 propertyToLabelMap
.put(property
, featureLabel
);
625 private void registerColumnConfiguration(Feature feature
, IConfigRegistry configRegistry
) {
627 configRegistry
.registerConfigAttribute(
628 EditConfigAttributes
.CELL_EDITABLE_RULE
,
629 IEditableRule
.ALWAYS_EDITABLE
,
631 MatrixUtility
.getProperty(feature
)
633 if(feature
.isSupportsQuantitativeData()){
634 //add display converter for string representation
635 configRegistry
.registerConfigAttribute(
636 CellConfigAttributes
.DISPLAY_CONVERTER
,
637 new QuantitativeDataDisplayConverter(),
639 MatrixUtility
.getProperty(feature
));
640 //register quantitative editor
641 configRegistry
.registerConfigAttribute(
642 EditConfigAttributes
.CELL_EDITOR
,
643 new QuantitativeDataCellEditor(feature
, this),
645 MatrixUtility
.getProperty(feature
));
647 else if(feature
.isSupportsCategoricalData()){
648 //add display converter for string representation
649 configRegistry
.registerConfigAttribute(
650 CellConfigAttributes
.DISPLAY_CONVERTER
,
651 new CategoricalDataDisplayConverter(),
653 MatrixUtility
.getProperty(feature
));
655 //add combo box cell editor
657 configRegistry
.registerConfigAttribute(EditConfigAttributes
.CELL_EDITOR
,
658 new CategoricalDataCellEditor(getSupportedStatesForCategoricalFeature(feature
), this, feature
),
660 MatrixUtility
.getProperty(feature
));
667 public void loadDescriptions(DescriptiveDataSet descriptiveDataSet
) {
668 UUID monitorUuid
= CdmStore
.getService(IDescriptiveDataSetService
.class).monitGetRowWrapper(descriptiveDataSet
);
669 IProgressMonitorService progressMonitorService
= CdmApplicationState
.getCurrentAppConfig().getProgressMonitorService();
672 String jobLabel
= "Load character data";
673 Job job
= Job
.create(jobLabel
, (ICoreRunnable
) monitor
-> {
674 monitor
.beginTask(jobLabel
, IProgressMonitor
.UNKNOWN
);
675 while(progressMonitorService
.isMonitorThreadRunning(monitorUuid
)){
676 if(monitor
.isCanceled()){
677 progressMonitorService
.interrupt(monitorUuid
);
680 IRemotingProgressMonitor remotingMonitor
= progressMonitorService
.getRemotingMonitor(monitorUuid
);
681 Collection
<RowWrapperDTO
> wrappers
= (Collection
<RowWrapperDTO
>) remotingMonitor
.getResult();
683 wrappers
.forEach(wrapper
->CharacterMatrix
.this.descriptions
.add(wrapper
));
690 public List
<State
> getSupportedStatesForCategoricalFeature(Feature feature
){
691 return categoricalFeatureToStateMap
.get(feature
);
694 public Map
<Integer
, Feature
> getIndexToFeatureMap() {
695 return indexToFeatureMap
;
698 public LinkedMap
<String
, String
> getPropertyToLabelMap() {
699 return propertyToLabelMap
;
702 public void setDirty() {
706 public NatTable
getNatTable() {
710 public EventList
<Object
> getDescriptions() {
714 public DescriptiveDataSet
getDescriptiveDataSet() {
715 return descriptiveDataSet
;
718 public Collection
<SpecimenNodeWrapper
> getSpecimenCache() {
719 return specimenCache
;
722 public void setSpecimenCache(Collection
<SpecimenNodeWrapper
> specimenCache
) {
723 this.specimenCache
= specimenCache
.stream()
725 //map descriptions on a list of uuids of the described specimen
726 !this.descriptions
.stream()
727 .map(o
->((RowWrapperDTO
)o
).getSpecimen().getUuid())
728 .collect(Collectors
.toList())
729 //an check if the specimen to add is already contained
730 .contains(wrapper
.getUuidAndTitleCache().getUuid())
732 .collect(Collectors
.toList());
735 public Properties
getNatTableState() {
736 return toolbar
.getNatTableState();
739 public ListDataProvider
<Object
> getBodyDataProvider() {
740 return bodyDataProvider
;
743 DefaultBodyLayerStack
getBodyLayer() {
747 File
getStatePropertiesFile() {
748 return new File(WorkbenchUtility
.getBaseLocation(), CHARACTER_MATRIX_STATE_PROPERTIES
);
751 public List
<Feature
> getFeatures() {