Revert "mend"
[taxeditor.git] / eu.etaxonomy.taxeditor.bulkeditor / src / main / java / eu / etaxonomy / taxeditor / bulkeditor / e4 / BulkEditorE4Composite.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
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.
8 */
9
10 package eu.etaxonomy.taxeditor.bulkeditor.e4;
11
12 import java.io.File;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Properties;
18 import java.util.Set;
19
20 import javax.inject.Inject;
21
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;
79
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;
87
88 /**
89 *
90 * @author pplitzner
91 * @since Sep 8, 2017
92 *
93 */
94 public class BulkEditorE4Composite extends Composite {
95
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$
98
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$
101
102 private final List<String> columnList = new ArrayList<>();
103
104 private Composite topComposite;
105
106 @Inject
107 private ESelectionService selService;
108
109 private ISelectionChangedListener selectionChangedListener;
110
111 private Composite bottomComposite;
112
113 @Inject
114 private EMenuService menuService;
115
116 private NatTable natTable;
117
118 private DefaultBodyLayerStack bodyLayer;
119
120 private ListDataProvider<CdmBase> bodyDataProvider;
121
122 private BulkEditorE4 bulkEditor;
123 private BulkEditorSearchE4 bulkEditorSearch;
124
125
126 public BulkEditorE4Composite(BulkEditorE4 bulkEditor, Composite parent, int style) {
127 super(parent, style);
128 parent.setLayout(new GridLayout());
129 this.bulkEditor = bulkEditor;
130
131 this.setLayout(new GridLayout());
132
133 topComposite = new Composite(parent, SWT.NONE);
134 topComposite.setLayout(new GridLayout());
135
136 GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
137 topComposite.setLayoutData(gridData);
138
139 bottomComposite = new Composite(parent, SWT.NONE);
140 bottomComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
141 bottomComposite.setLayout(new GridLayout());
142 }
143
144 @SuppressWarnings("unused")
145 public void init(AbstractBulkEditorInput<?> input){
146
147 input.getPropertyKeys().forEach(key->columnList.add(key));
148
149 bulkEditorSearch = new BulkEditorSearchE4(this, topComposite, SWT.NONE);
150 //layout needed because the search bar is added after @PostConstuct method
151 topComposite.getParent().layout();
152
153 if(input.getEntityUuid()!=null){
154 performSearch(new BulkEditorQuery(input.getEntityUuid().toString()));
155 }
156
157 createTable();
158
159 configureTable();
160
161 styleTable();
162
163 GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
164
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);
170
171 bottomComposite.layout();
172
173 }
174
175 private void createTable(){
176 ConfigRegistry configRegistry = new ConfigRegistry();
177 //property map
178 Map<String, String> propertyToLabels = new HashMap<>();
179 columnList.forEach(columnLabel->propertyToLabels.put(columnLabel, columnLabel));
180
181 String[] propertyNames = columnList.toArray(columnList.toArray(new String[columnList.size()]));
182 //sorted list
183 SortedList<CdmBase> sortedList = new SortedList<>(bulkEditor.getEditorInput().getModel(), bulkEditor.getEditorInput().getTitleComparator());
184 //data provider
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(
191 bodyDataProvider);
192 //body
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);
197
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);
203 }
204 else{
205 dataLayer.setColumnWidthByPosition(i, 100);
206 }
207 }
208
209 //column
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<>(
218 columnHeaderLayer,
219 new GlazedListsSortModel<>(
220 sortedList,
221 columnPropertyAccessor,
222 configRegistry,
223 columnHeaderDataLayer));
224 columnHeaderDataLayer.setConfigLabelAccumulator(
225 new BulkEditorConfigLabelAccumulatorHeader(colHeaderDataProvider, bulkEditor.getEditorInput()));
226 //row
227 DataLayer rowHeaderDataLayer = new DataLayer(rowHeaderDataProvider);
228 RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer,
229 bodyLayer, bodyLayer.getSelectionLayer());
230 rowHeaderDataLayer.setColumnWidthByPosition(0, 50);
231
232 //corner
233 DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(
234 colHeaderDataProvider, rowHeaderDataProvider);
235 DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
236 CornerLayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, sortHeaderLayer);
237 //grid
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);
241
242 dataLayer.setConfigLabelAccumulator(new BulkEditorConfigLabelAccumulator(bodyDataProvider, colHeaderDataProvider,
243 bulkEditor.getEditorInput()));
244
245 natTable.setConfigRegistry(configRegistry);
246
247 //add tooltip to table
248 new BulkEditorTooltip(natTable, colHeaderDataProvider);
249 }
250
251 private void configureTable(){
252
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());
257
258 natTable.addConfiguration(new AbstractUiBindingConfiguration() {
259 @Override
260 public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
261 uiBindingRegistry.registerFirstDoubleClickBinding(new CellEditorMouseEventMatcher(GridRegion.BODY),
262 new MouseEditAction());
263 }
264 });
265
266 //make cells editable to allow selecting the text
267 natTable.addConfiguration(new AbstractRegistryConfiguration() {
268 @Override
269 public void configureRegistry(IConfigRegistry configRegistry) {
270 //make cell editable
271 configRegistry.registerConfigAttribute(
272 EditConfigAttributes.CELL_EDITABLE_RULE,
273 IEditableRule.ALWAYS_EDITABLE,
274 DisplayMode.EDIT);
275 //register editor
276 TextCellEditor editor = new TextCellEditor();
277 editor.setEditable(false);
278 configRegistry.registerConfigAttribute(
279 EditConfigAttributes.CELL_EDITOR,
280 editor,
281 DisplayMode.NORMAL);
282 }
283 });
284
285 //+++CONTEXT MENU+++
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() {
293 @Override
294 public void configureUiBindings(
295 UiBindingRegistry uiBindingRegistry) {
296 // add e4 menu to NatTable
297 new PopupMenuBuilder(natTable, e4Menu)
298 .withHideColumnMenuItem()
299 .withShowAllColumnsMenuItem()
300 .build();
301
302 // register the UI binding for header, corner and body region
303 uiBindingRegistry.registerMouseDownBinding(
304 new MouseEventMatcher(
305 SWT.NONE,
306 null,
307 MouseEventMatcher.RIGHT_BUTTON),
308 new PopupMenuAction(e4Menu));
309 }
310 });
311
312
313 //enable sorting
314 natTable.addConfiguration(new SingleClickSortConfiguration());
315
316 // Custom style for deduplication labels
317 natTable.addConfiguration(new BulkEditorLabelStyleConfiguration());
318
319 //add default configuration because autoconfigure is set to false in constructor
320 natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
321
322 //show boolean values as check boxes
323 natTable.addConfiguration(new AbstractRegistryConfiguration() {
324 @Override
325 public void configureRegistry(IConfigRegistry configRegistry) {
326 configRegistry.registerConfigAttribute(
327 CellConfigAttributes.CELL_PAINTER,
328 new CheckBoxPainter(),
329 DisplayMode.NORMAL,
330 BulkEditorConfigLabelAccumulatorHeader.BOOLEAN_COLUMN);
331
332 configRegistry.registerConfigAttribute(
333 CellConfigAttributes.DISPLAY_CONVERTER,
334 new DefaultBooleanDisplayConverter(),
335 DisplayMode.NORMAL,
336 BulkEditorConfigLabelAccumulatorHeader.BOOLEAN_COLUMN);
337 }
338 });
339
340 natTable.addConfiguration(new AbstractRegistryConfiguration() {
341 @Override
342 public void configureRegistry(IConfigRegistry configRegistry) {
343 configRegistry.registerConfigAttribute(
344 CellConfigAttributes.CELL_PAINTER,
345 new ImagePainter(ImageResources.getImage(ImageResources.LOCK_ICON)),
346 DisplayMode.NORMAL,
347 BulkEditorConfigLabelAccumulatorHeader.CACHE_COLUMN);
348 }
349 });
350
351 //register handler for view configuration menu
352 natTable.registerCommandHandler(bulkEditorSearch.getDisplayPersistenceDialogCommandHandler());
353
354 natTable.configure();
355 }
356
357 private void styleTable(){
358 natTable.setTheme(new ModernNatTableThemeConfiguration());
359 }
360
361 public void performSearch(BulkEditorQuery query) {
362 performSearch(query, null);
363 }
364
365 /** {@inheritDoc}
366 * @param selection */
367 public void performSearch(BulkEditorQuery query, IStructuredSelection selection) {
368 if (query != null) {
369 if(StoreUtil.promptCheckIsDirty(bulkEditor)){
370 return;
371 }
372 bulkEditor.getEditorInput().performSearch(query, selection);
373 bulkEditor.setLastQuery(query);
374 }
375 }
376
377 public void refresh(){
378 natTable.doCommand(new VisualRefreshCommand());
379 }
380
381 public List<String> getColumnList() {
382 return columnList;
383 }
384
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++){
390 if (i>=0){
391 selectedObjects.add(bodyDataProvider.getRowObject(i));
392 }
393 }
394 }
395 return new StructuredSelection(selectedObjects);
396 }
397
398 public void setSelection(IStructuredSelection selection){
399 Object[] objects = selection.toArray();
400 for (Object object : objects) {
401
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);
405 }
406 }
407 }
408
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);
416 }
417 }
418 final TextTransfer textTransfer = TextTransfer.getInstance();
419 final Clipboard clipboard = new Clipboard(Display.getDefault());
420 try {
421 clipboard.setContents(new Object[] { textData.toString() },
422 new Transfer[] { textTransfer });
423 } finally {
424 clipboard.dispose();
425 }
426 }
427
428 ListDataProvider<CdmBase> getBodyDataProvider() {
429 return bodyDataProvider;
430 }
431
432 ESelectionService getSelService() {
433 return selService;
434 }
435
436 NatTable getNatTable() {
437 return natTable;
438 }
439
440 public Properties getNatTableState() {
441 return bulkEditorSearch.getNatTableState();
442 }
443
444 File getStatePropertiesFile() {
445 return bulkEditorSearch.getStatePropertiesFile();
446 }
447
448 }