Project

General

Profile

Download (18.7 KB) Statistics
| Branch: | Tag: | Revision:
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
package eu.etaxonomy.taxeditor.bulkeditor.e4;
10

    
11
import java.io.File;
12
import java.util.ArrayList;
13
import java.util.HashMap;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.Properties;
17
import java.util.Set;
18

    
19
import javax.inject.Inject;
20

    
21
import org.eclipse.e4.core.services.events.IEventBroker;
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.event.WorkbenchEventConstants;
86
import eu.etaxonomy.taxeditor.model.ImageResources;
87
import eu.etaxonomy.taxeditor.store.StoreUtil;
88

    
89
/**
90
 * @author pplitzner
91
 * @since Sep 8, 2017
92
 */
93
public class BulkEditorE4Composite extends Composite {
94

    
95
    public static final String LABEL_CANDIDATE = "LABEL_CANDIDATE"; //$NON-NLS-1$
96
    public static final String LABEL_CANDIDATE_ICON = "LABEL_CANDIDATE_ICON"; //$NON-NLS-1$
97

    
98
    public static final String LABEL_TARGET = "LABEL_TARGET"; //$NON-NLS-1$
99
    public static final String LABEL_TARGET_ICON = "LABEL_TARGET_ICON"; //$NON-NLS-1$
100

    
101
    private final List<String> columnList = new ArrayList<>();
102

    
103
    private Composite topComposite;
104

    
105
    @Inject
106
    private ESelectionService selService;
107

    
108
    private ISelectionChangedListener selectionChangedListener;
109

    
110
    private Composite bottomComposite;
111

    
112
    @Inject
113
    private EMenuService menuService;
114

    
115
    private NatTable natTable;
116

    
117
    private DefaultBodyLayerStack bodyLayer;
118

    
119
    private ListDataProvider<CdmBase> bodyDataProvider;
120

    
121
    private BulkEditorE4 bulkEditor;
122
    private BulkEditorSearchE4 bulkEditorSearch;
123

    
124
    @Inject
125
    private IEventBroker eventBroker;
126

    
127
    
128
    public BulkEditorE4Composite(BulkEditorE4 bulkEditor, Composite parent, int style) {
129
        super(parent, style);
130
        parent.setLayout(new GridLayout());
131
        this.bulkEditor = bulkEditor;
132

    
133
        this.setLayout(new GridLayout());
134

    
135
        topComposite = new Composite(parent, SWT.NONE);
136
        topComposite.setLayout(new GridLayout());
137

    
138
        GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
139
        topComposite.setLayoutData(gridData);
140

    
141
        bottomComposite = new Composite(parent, SWT.NONE);
142
        bottomComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
143
        bottomComposite.setLayout(new GridLayout());
144
	}
145

    
146
    public void init(AbstractBulkEditorInput<?> input){
147

    
148
        input.getPropertyKeys().forEach(key->columnList.add(key));
149

    
150
	    bulkEditorSearch = new BulkEditorSearchE4(this, topComposite, SWT.NONE);
151
	    //layout needed because the search bar is added after @PostConstuct method
152
	    topComposite.getParent().layout();
153

    
154
        if(input.getEntityUuid()!=null){
155
            performSearch(new BulkEditorQuery(input.getEntityUuid().toString()));
156
        }
157

    
158
        createTable();
159

    
160
        configureTable();
161

    
162
        styleTable();
163

    
164
        GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
165

    
166
        //propagate selection
167
        // selection listener
168
        E4SelectionListener<CdmBase> selectionListener = new CellSelectionListener(selService, bodyLayer.getSelectionLayer(), bodyDataProvider, this);
169
        bodyLayer.getSelectionLayer().addLayerListener(selectionListener);
170
        selectionListener.setFullySelectedRowsOnly(false);
171

    
172
        bottomComposite.layout();
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
        //enable sorting
313
        natTable.addConfiguration(new SingleClickSortConfiguration());
314

    
315
        // Custom style for deduplication labels
316
        natTable.addConfiguration(new BulkEditorLabelStyleConfiguration());
317

    
318
        //add default configuration because autoconfigure is set to false in constructor
319
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
320

    
321
        //show boolean values as check boxes
322
        natTable.addConfiguration(new AbstractRegistryConfiguration() {
323
            @Override
324
            public void configureRegistry(IConfigRegistry configRegistry) {
325
                configRegistry.registerConfigAttribute(
326
                        CellConfigAttributes.CELL_PAINTER,
327
                        new CheckBoxPainter(),
328
                        DisplayMode.NORMAL,
329
                        BulkEditorConfigLabelAccumulatorHeader.BOOLEAN_COLUMN);
330

    
331
                configRegistry.registerConfigAttribute(
332
                        CellConfigAttributes.DISPLAY_CONVERTER,
333
                        new DefaultBooleanDisplayConverter(),
334
                        DisplayMode.NORMAL,
335
                        BulkEditorConfigLabelAccumulatorHeader.BOOLEAN_COLUMN);
336
            }
337
        });
338

    
339
        natTable.addConfiguration(new AbstractRegistryConfiguration() {
340
            @Override
341
            public void configureRegistry(IConfigRegistry configRegistry) {
342
                configRegistry.registerConfigAttribute(
343
                        CellConfigAttributes.CELL_PAINTER,
344
                        new ImagePainter(ImageResources.getImage(ImageResources.LOCK_ICON)),
345
                        DisplayMode.NORMAL,
346
                        BulkEditorConfigLabelAccumulatorHeader.CACHE_COLUMN);
347
            }
348
        });
349

    
350
        //register handler for view configuration menu
351
        natTable.registerCommandHandler(bulkEditorSearch.getDisplayPersistenceDialogCommandHandler());
352

    
353
        natTable.configure();
354
	}
355

    
356
	private void styleTable(){
357
	    natTable.setTheme(new ModernNatTableThemeConfiguration());
358
	}
359

    
360
	public void performSearch(BulkEditorQuery query) {
361
	    eventBroker.post(WorkbenchEventConstants.CURRENT_ACTIVE_EDITOR, null);
362
	    performSearch(query, null);
363

    
364
	}
365

    
366
    public void performSearch(BulkEditorQuery query, IStructuredSelection selection) {
367
        if (query != null) {
368
            if(StoreUtil.promptCheckIsDirty(bulkEditor)){
369
                return;
370
            }
371
            bulkEditor.getEditorInput().performSearch(query, selection);
372
            bulkEditor.setLastQuery(query);
373
        }
374
    }
375

    
376
    public void refresh(){
377
        natTable.doCommand(new VisualRefreshCommand());
378
    }
379

    
380
    public List<String> getColumnList() {
381
        return columnList;
382
    }
383

    
384
    public IStructuredSelection getSelection(){
385
        Set<Range> selectedRowPositions = bodyLayer.getSelectionLayer().getSelectedRowPositions();
386
        List<Object> selectedObjects = new ArrayList<>();
387
        for (Range range : selectedRowPositions) {
388
            for(int i=range.start;i<range.end;i++){
389
                if (i>=0){
390
                    selectedObjects.add(bodyDataProvider.getRowObject(i));
391
                }
392
            }
393
        }
394
        return new StructuredSelection(selectedObjects);
395
    }
396

    
397
    public void setSelection(IStructuredSelection selection){
398
        Object[] objects = selection.toArray();
399
        for (Object object : objects) {
400

    
401
            if(object instanceof CdmBase &&  bulkEditor.getEditorInput().getModel().contains(object)){
402
                object = bulkEditor.getEditorInput().getModel().get(bulkEditor.getEditorInput().getModel().indexOf(object));
403
                bodyLayer.getSelectionLayer().selectRow(0, bodyDataProvider.indexOfRowObject((CdmBase) object), false, false);
404
            }
405
        }
406
    }
407

    
408
    public void copyDataToClipboard() {
409
        String textData = ""; //$NON-NLS-1$
410
        IStructuredSelection selection = getSelection();
411
        Object[] objects = selection.toArray();
412
        for (Object object : objects) {
413
            if(object instanceof CdmBase){
414
                textData += bulkEditor.getEditorInput().getText((CdmBase)object);
415
            }
416
        }
417
        final TextTransfer textTransfer = TextTransfer.getInstance();
418
        final Clipboard clipboard = new Clipboard(Display.getDefault());
419
        try {
420
            clipboard.setContents(new Object[] { textData.toString() },
421
                    new Transfer[] { textTransfer });
422
        } finally {
423
            clipboard.dispose();
424
        }
425
    }
426

    
427
    ListDataProvider<CdmBase> getBodyDataProvider() {
428
        return bodyDataProvider;
429
    }
430

    
431
    ESelectionService getSelService() {
432
        return selService;
433
    }
434

    
435
    NatTable getNatTable() {
436
        return natTable;
437
    }
438

    
439
    public Properties getNatTableState() {
440
        return bulkEditorSearch.getNatTableState();
441
    }
442

    
443
    File getStatePropertiesFile() {
444
        return bulkEditorSearch.getStatePropertiesFile();
445
    }
446
}
(4-4/9)