2 * Copyright (C) 2007 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.
10 package eu
.etaxonomy
.taxeditor
.bulkeditor
.e4
;
13 import java
.util
.ArrayList
;
14 import java
.util
.HashMap
;
15 import java
.util
.List
;
17 import java
.util
.Properties
;
20 import javax
.inject
.Inject
;
22 import org
.eclipse
.e4
.ui
.services
.EMenuService
;
23 import org
.eclipse
.e4
.ui
.workbench
.modeling
.ESelectionService
;
24 import org
.eclipse
.jface
.layout
.GridDataFactory
;
25 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
26 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
27 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
28 import org
.eclipse
.nebula
.widgets
.nattable
.NatTable
;
29 import org
.eclipse
.nebula
.widgets
.nattable
.command
.VisualRefreshCommand
;
30 import org
.eclipse
.nebula
.widgets
.nattable
.command
.VisualRefreshCommandHandler
;
31 import org
.eclipse
.nebula
.widgets
.nattable
.config
.AbstractRegistryConfiguration
;
32 import org
.eclipse
.nebula
.widgets
.nattable
.config
.AbstractUiBindingConfiguration
;
33 import org
.eclipse
.nebula
.widgets
.nattable
.config
.CellConfigAttributes
;
34 import org
.eclipse
.nebula
.widgets
.nattable
.config
.ConfigRegistry
;
35 import org
.eclipse
.nebula
.widgets
.nattable
.config
.DefaultNatTableStyleConfiguration
;
36 import org
.eclipse
.nebula
.widgets
.nattable
.config
.IConfigRegistry
;
37 import org
.eclipse
.nebula
.widgets
.nattable
.config
.IEditableRule
;
38 import org
.eclipse
.nebula
.widgets
.nattable
.coordinate
.Range
;
39 import org
.eclipse
.nebula
.widgets
.nattable
.data
.ListDataProvider
;
40 import org
.eclipse
.nebula
.widgets
.nattable
.data
.convert
.DefaultBooleanDisplayConverter
;
41 import org
.eclipse
.nebula
.widgets
.nattable
.edit
.EditConfigAttributes
;
42 import org
.eclipse
.nebula
.widgets
.nattable
.edit
.action
.MouseEditAction
;
43 import org
.eclipse
.nebula
.widgets
.nattable
.edit
.config
.DefaultEditConfiguration
;
44 import org
.eclipse
.nebula
.widgets
.nattable
.edit
.editor
.TextCellEditor
;
45 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.e4
.selection
.E4SelectionListener
;
46 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.GlazedListsEventLayer
;
47 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.GlazedListsSortModel
;
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
.layer
.ColumnHeaderLayer
;
53 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.CornerLayer
;
54 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.GridLayer
;
55 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.RowHeaderLayer
;
56 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.AbstractLayer
;
57 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.DataLayer
;
58 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.stack
.DefaultBodyLayerStack
;
59 import org
.eclipse
.nebula
.widgets
.nattable
.painter
.cell
.CheckBoxPainter
;
60 import org
.eclipse
.nebula
.widgets
.nattable
.painter
.cell
.ImagePainter
;
61 import org
.eclipse
.nebula
.widgets
.nattable
.sort
.SortHeaderLayer
;
62 import org
.eclipse
.nebula
.widgets
.nattable
.sort
.config
.SingleClickSortConfiguration
;
63 import org
.eclipse
.nebula
.widgets
.nattable
.style
.DisplayMode
;
64 import org
.eclipse
.nebula
.widgets
.nattable
.style
.theme
.ModernNatTableThemeConfiguration
;
65 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.binding
.UiBindingRegistry
;
66 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.matcher
.CellEditorMouseEventMatcher
;
67 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.matcher
.MouseEventMatcher
;
68 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.menu
.PopupMenuAction
;
69 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.menu
.PopupMenuBuilder
;
70 import org
.eclipse
.swt
.SWT
;
71 import org
.eclipse
.swt
.dnd
.Clipboard
;
72 import org
.eclipse
.swt
.dnd
.TextTransfer
;
73 import org
.eclipse
.swt
.dnd
.Transfer
;
74 import org
.eclipse
.swt
.layout
.GridData
;
75 import org
.eclipse
.swt
.layout
.GridLayout
;
76 import org
.eclipse
.swt
.widgets
.Composite
;
77 import org
.eclipse
.swt
.widgets
.Display
;
78 import org
.eclipse
.swt
.widgets
.Menu
;
80 import ca
.odell
.glazedlists
.SortedList
;
81 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
82 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
83 import eu
.etaxonomy
.taxeditor
.bulkeditor
.BulkEditorQuery
;
84 import eu
.etaxonomy
.taxeditor
.bulkeditor
.input
.AbstractBulkEditorInput
;
85 import eu
.etaxonomy
.taxeditor
.model
.ImageResources
;
86 import eu
.etaxonomy
.taxeditor
.store
.StoreUtil
;
94 public class BulkEditorE4Composite
extends Composite
{
96 public static final String LABEL_CANDIDATE
= "LABEL_CANDIDATE"; //$NON-NLS-1$
97 public static final String LABEL_CANDIDATE_ICON
= "LABEL_CANDIDATE_ICON"; //$NON-NLS-1$
99 public static final String LABEL_TARGET
= "LABEL_TARGET"; //$NON-NLS-1$
100 public static final String LABEL_TARGET_ICON
= "LABEL_TARGET_ICON"; //$NON-NLS-1$
102 private final List
<String
> columnList
= new ArrayList
<>();
104 private Composite topComposite
;
107 private ESelectionService selService
;
109 private ISelectionChangedListener selectionChangedListener
;
111 private Composite bottomComposite
;
114 private EMenuService menuService
;
116 private NatTable natTable
;
118 private DefaultBodyLayerStack bodyLayer
;
120 private ListDataProvider
<CdmBase
> bodyDataProvider
;
122 private BulkEditorE4 bulkEditor
;
123 private BulkEditorSearchE4 bulkEditorSearch
;
126 public BulkEditorE4Composite(BulkEditorE4 bulkEditor
, Composite parent
, int style
) {
127 super(parent
, style
);
128 parent
.setLayout(new GridLayout());
129 this.bulkEditor
= bulkEditor
;
131 this.setLayout(new GridLayout());
133 topComposite
= new Composite(parent
, SWT
.NONE
);
134 topComposite
.setLayout(new GridLayout());
136 GridData gridData
= new GridData(SWT
.FILL
, SWT
.TOP
, true, false);
137 topComposite
.setLayoutData(gridData
);
139 bottomComposite
= new Composite(parent
, SWT
.NONE
);
140 bottomComposite
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
141 bottomComposite
.setLayout(new GridLayout());
144 @SuppressWarnings("unused")
145 public void init(AbstractBulkEditorInput
<?
> input
){
147 input
.getPropertyKeys().forEach(key
->columnList
.add(key
));
149 bulkEditorSearch
= new BulkEditorSearchE4(this, topComposite
, SWT
.NONE
);
150 //layout needed because the search bar is added after @PostConstuct method
151 topComposite
.getParent().layout();
153 if(input
.getEntityUuid()!=null){
154 performSearch(new BulkEditorQuery(input
.getEntityUuid().toString()));
163 GridDataFactory
.fillDefaults().grab(true, true).applyTo(natTable
);
165 //propagate selection
166 // selection listener
167 E4SelectionListener
<CdmBase
> selectionListener
= new CellSelectionListener(selService
, bodyLayer
.getSelectionLayer(), bodyDataProvider
, this);
168 bodyLayer
.getSelectionLayer().addLayerListener(selectionListener
);
169 selectionListener
.setFullySelectedRowsOnly(false);
171 bottomComposite
.layout();
175 private void createTable(){
176 ConfigRegistry configRegistry
= new ConfigRegistry();
178 Map
<String
, String
> propertyToLabels
= new HashMap
<>();
179 columnList
.forEach(columnLabel
->propertyToLabels
.put(columnLabel
, columnLabel
));
181 String
[] propertyNames
= columnList
.toArray(columnList
.toArray(new String
[columnList
.size()]));
183 SortedList
<CdmBase
> sortedList
= new SortedList
<>(bulkEditor
.getEditorInput().getModel(), bulkEditor
.getEditorInput().getTitleComparator());
185 BulkEditorPropertyAccessor columnPropertyAccessor
= new BulkEditorPropertyAccessor(this, bulkEditor
.getEditorInput());
186 bodyDataProvider
= new ListDataProvider
<CdmBase
>(sortedList
,
187 columnPropertyAccessor
);
188 DefaultColumnHeaderDataProvider colHeaderDataProvider
= new DefaultColumnHeaderDataProvider(
189 propertyNames
, propertyToLabels
);
190 DefaultRowHeaderDataProvider rowHeaderDataProvider
= new DefaultRowHeaderDataProvider(
193 DataLayer dataLayer
= new DataLayer(bodyDataProvider
);
194 dataLayer
.registerCommandHandler(new VisualRefreshCommandHandler());
195 GlazedListsEventLayer
<CdmBase
> eventLayer
= new GlazedListsEventLayer
<>(dataLayer
, bulkEditor
.getEditorInput().getModel());
196 bodyLayer
= new DefaultBodyLayerStack(eventLayer
);
198 dataLayer
.setColumnPercentageSizing(0, true);
199 dataLayer
.setColumnWidthPercentageByPosition(0, 45);
200 for(int i
=1;i
<columnList
.size();i
++){
201 if(bulkEditor
.getEditorInput().isCacheProperty(columnList
.get(i
))){
202 dataLayer
.setColumnWidthByPosition(i
, 16);
205 dataLayer
.setColumnWidthByPosition(i
, 100);
210 DataLayer columnHeaderDataLayer
= new DataLayer(colHeaderDataProvider
);
211 ColumnHeaderLayer columnHeaderLayer
= new ColumnHeaderLayer(
212 columnHeaderDataLayer
,
213 bodyLayer
, bodyLayer
.getSelectionLayer());
214 // add the SortHeaderLayer to the column header layer stack
215 // as we use GlazedLists, we use the GlazedListsSortModel which
216 // delegates the sorting to the SortedList
217 final SortHeaderLayer
<SpecimenDescription
> sortHeaderLayer
= new SortHeaderLayer
<>(
219 new GlazedListsSortModel
<>(
221 columnPropertyAccessor
,
223 columnHeaderDataLayer
));
224 columnHeaderDataLayer
.setConfigLabelAccumulator(
225 new BulkEditorConfigLabelAccumulatorHeader(colHeaderDataProvider
, bulkEditor
.getEditorInput()));
227 DataLayer rowHeaderDataLayer
= new DataLayer(rowHeaderDataProvider
);
228 RowHeaderLayer rowHeaderLayer
= new RowHeaderLayer(rowHeaderDataLayer
,
229 bodyLayer
, bodyLayer
.getSelectionLayer());
230 rowHeaderDataLayer
.setColumnWidthByPosition(0, 50);
233 DefaultCornerDataProvider cornerDataProvider
= new DefaultCornerDataProvider(
234 colHeaderDataProvider
, rowHeaderDataProvider
);
235 DataLayer cornerDataLayer
= new DataLayer(cornerDataProvider
);
236 CornerLayer cornerLayer
= new CornerLayer(cornerDataLayer
, rowHeaderLayer
, sortHeaderLayer
);
238 GridLayer gridLayer
= new GridLayer(bodyLayer
, sortHeaderLayer
,
239 rowHeaderLayer
, cornerLayer
, false);//set autoconfigure to false to get rid of the single click editing
240 natTable
= new NatTable(bottomComposite
, gridLayer
, false);
242 dataLayer
.setConfigLabelAccumulator(new BulkEditorConfigLabelAccumulator(bodyDataProvider
, colHeaderDataProvider
,
243 bulkEditor
.getEditorInput()));
245 natTable
.setConfigRegistry(configRegistry
);
247 //add tooltip to table
248 new BulkEditorTooltip(natTable
, colHeaderDataProvider
);
251 private void configureTable(){
253 //this configuration enables cell to be editable in general
254 //necessary because we disabled this beforehand by setting autoconfigure=false
255 //in the GridLayer constructor
256 ((AbstractLayer
) natTable
.getLayer()).addConfiguration(new DefaultEditConfiguration());
258 natTable
.addConfiguration(new AbstractUiBindingConfiguration() {
260 public void configureUiBindings(UiBindingRegistry uiBindingRegistry
) {
261 uiBindingRegistry
.registerFirstDoubleClickBinding(new CellEditorMouseEventMatcher(GridRegion
.BODY
),
262 new MouseEditAction());
266 //make cells editable to allow selecting the text
267 natTable
.addConfiguration(new AbstractRegistryConfiguration() {
269 public void configureRegistry(IConfigRegistry configRegistry
) {
271 configRegistry
.registerConfigAttribute(
272 EditConfigAttributes
.CELL_EDITABLE_RULE
,
273 IEditableRule
.ALWAYS_EDITABLE
,
276 TextCellEditor editor
= new TextCellEditor();
277 editor
.setEditable(false);
278 configRegistry
.registerConfigAttribute(
279 EditConfigAttributes
.CELL_EDITOR
,
286 menuService
.registerContextMenu(natTable
, "eu.etaxonomy.taxeditor.bulkeditor.popupmenu.bulkeditor"); //$NON-NLS-1$
287 // get the menu registered by EMenuService
288 final Menu e4Menu
= natTable
.getMenu();
289 // remove the menu reference from NatTable instance
290 natTable
.setMenu(null);
291 natTable
.addConfiguration(
292 new AbstractUiBindingConfiguration() {
294 public void configureUiBindings(
295 UiBindingRegistry uiBindingRegistry
) {
296 // add e4 menu to NatTable
297 new PopupMenuBuilder(natTable
, e4Menu
)
298 .withHideColumnMenuItem()
299 .withShowAllColumnsMenuItem()
302 // register the UI binding for header, corner and body region
303 uiBindingRegistry
.registerMouseDownBinding(
304 new MouseEventMatcher(
307 MouseEventMatcher
.RIGHT_BUTTON
),
308 new PopupMenuAction(e4Menu
));
314 natTable
.addConfiguration(new SingleClickSortConfiguration());
316 // Custom style for deduplication labels
317 natTable
.addConfiguration(new BulkEditorLabelStyleConfiguration());
319 //add default configuration because autoconfigure is set to false in constructor
320 natTable
.addConfiguration(new DefaultNatTableStyleConfiguration());
322 //show boolean values as check boxes
323 natTable
.addConfiguration(new AbstractRegistryConfiguration() {
325 public void configureRegistry(IConfigRegistry configRegistry
) {
326 configRegistry
.registerConfigAttribute(
327 CellConfigAttributes
.CELL_PAINTER
,
328 new CheckBoxPainter(),
330 BulkEditorConfigLabelAccumulatorHeader
.BOOLEAN_COLUMN
);
332 configRegistry
.registerConfigAttribute(
333 CellConfigAttributes
.DISPLAY_CONVERTER
,
334 new DefaultBooleanDisplayConverter(),
336 BulkEditorConfigLabelAccumulatorHeader
.BOOLEAN_COLUMN
);
340 natTable
.addConfiguration(new AbstractRegistryConfiguration() {
342 public void configureRegistry(IConfigRegistry configRegistry
) {
343 configRegistry
.registerConfigAttribute(
344 CellConfigAttributes
.CELL_PAINTER
,
345 new ImagePainter(ImageResources
.getImage(ImageResources
.LOCK_ICON
)),
347 BulkEditorConfigLabelAccumulatorHeader
.CACHE_COLUMN
);
351 //register handler for view configuration menu
352 natTable
.registerCommandHandler(bulkEditorSearch
.getDisplayPersistenceDialogCommandHandler());
354 natTable
.configure();
357 private void styleTable(){
358 natTable
.setTheme(new ModernNatTableThemeConfiguration());
361 public void performSearch(BulkEditorQuery query
) {
362 performSearch(query
, null);
366 * @param selection */
367 public void performSearch(BulkEditorQuery query
, IStructuredSelection selection
) {
369 if(StoreUtil
.promptCheckIsDirty(bulkEditor
)){
372 bulkEditor
.getEditorInput().performSearch(query
, selection
);
373 bulkEditor
.setLastQuery(query
);
377 public void refresh(){
378 natTable
.doCommand(new VisualRefreshCommand());
381 public List
<String
> getColumnList() {
385 public IStructuredSelection
getSelection(){
386 Set
<Range
> selectedRowPositions
= bodyLayer
.getSelectionLayer().getSelectedRowPositions();
387 List
<Object
> selectedObjects
= new ArrayList
<>();
388 for (Range range
: selectedRowPositions
) {
389 for(int i
=range
.start
;i
<range
.end
;i
++){
391 selectedObjects
.add(bodyDataProvider
.getRowObject(i
));
395 return new StructuredSelection(selectedObjects
);
398 public void setSelection(IStructuredSelection selection
){
399 Object
[] objects
= selection
.toArray();
400 for (Object object
: objects
) {
402 if(object
instanceof CdmBase
&& bulkEditor
.getEditorInput().getModel().contains(object
)){
403 object
= bulkEditor
.getEditorInput().getModel().get(bulkEditor
.getEditorInput().getModel().indexOf(object
));
404 bodyLayer
.getSelectionLayer().selectRow(0, bodyDataProvider
.indexOfRowObject((CdmBase
) object
), false, false);
409 public void copyDataToClipboard() {
410 String textData
= ""; //$NON-NLS-1$
411 IStructuredSelection selection
= getSelection();
412 Object
[] objects
= selection
.toArray();
413 for (Object object
: objects
) {
414 if(object
instanceof CdmBase
){
415 textData
+= bulkEditor
.getEditorInput().getText((CdmBase
)object
);
418 final TextTransfer textTransfer
= TextTransfer
.getInstance();
419 final Clipboard clipboard
= new Clipboard(Display
.getDefault());
421 clipboard
.setContents(new Object
[] { textData
.toString() },
422 new Transfer
[] { textTransfer
});
428 ListDataProvider
<CdmBase
> getBodyDataProvider() {
429 return bodyDataProvider
;
432 ESelectionService
getSelService() {
436 NatTable
getNatTable() {
440 public Properties
getNatTableState() {
441 return bulkEditorSearch
.getNatTableState();
444 File
getStatePropertiesFile() {
445 return bulkEditorSearch
.getStatePropertiesFile();