Project

General

Profile

Download (19.4 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

    
10
package eu.etaxonomy.taxeditor.bulkeditor.e4;
11

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

    
20
import javax.inject.Inject;
21

    
22
import org.eclipse.core.runtime.NullProgressMonitor;
23
import org.eclipse.e4.ui.services.EMenuService;
24
import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
25
import org.eclipse.jface.dialogs.MessageDialog;
26
import org.eclipse.jface.layout.GridDataFactory;
27
import org.eclipse.jface.viewers.ISelectionChangedListener;
28
import org.eclipse.jface.viewers.IStructuredSelection;
29
import org.eclipse.jface.viewers.StructuredSelection;
30
import org.eclipse.nebula.widgets.nattable.NatTable;
31
import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommand;
32
import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommandHandler;
33
import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
34
import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration;
35
import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
36
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
37
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
38
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
39
import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
40
import org.eclipse.nebula.widgets.nattable.data.IRowIdAccessor;
41
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
42
import org.eclipse.nebula.widgets.nattable.data.convert.DefaultBooleanDisplayConverter;
43
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
44
import org.eclipse.nebula.widgets.nattable.edit.action.MouseEditAction;
45
import org.eclipse.nebula.widgets.nattable.edit.config.DefaultEditConfiguration;
46
import org.eclipse.nebula.widgets.nattable.edit.editor.TextCellEditor;
47
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
48
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
49
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
50
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
51
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
52
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
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.GridLayer;
56
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
57
import org.eclipse.nebula.widgets.nattable.layer.AbstractLayer;
58
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
59
import org.eclipse.nebula.widgets.nattable.layer.stack.DefaultBodyLayerStack;
60
import org.eclipse.nebula.widgets.nattable.painter.cell.CheckBoxPainter;
61
import org.eclipse.nebula.widgets.nattable.painter.cell.ImagePainter;
62
import org.eclipse.nebula.widgets.nattable.selection.RowSelectionModel;
63
import org.eclipse.nebula.widgets.nattable.selection.RowSelectionProvider;
64
import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer;
65
import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;
66
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
67
import org.eclipse.nebula.widgets.nattable.style.theme.ModernNatTableThemeConfiguration;
68
import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
69
import org.eclipse.nebula.widgets.nattable.ui.matcher.CellEditorMouseEventMatcher;
70
import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;
71
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuAction;
72
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
73
import org.eclipse.swt.SWT;
74
import org.eclipse.swt.dnd.Clipboard;
75
import org.eclipse.swt.dnd.TextTransfer;
76
import org.eclipse.swt.dnd.Transfer;
77
import org.eclipse.swt.layout.GridData;
78
import org.eclipse.swt.layout.GridLayout;
79
import org.eclipse.swt.widgets.Composite;
80
import org.eclipse.swt.widgets.Display;
81
import org.eclipse.swt.widgets.Menu;
82

    
83
import ca.odell.glazedlists.SortedList;
84
import eu.etaxonomy.cdm.model.common.CdmBase;
85
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
86
import eu.etaxonomy.taxeditor.bulkeditor.BulkEditorQuery;
87
import eu.etaxonomy.taxeditor.bulkeditor.input.AbstractBulkEditorInput;
88
import eu.etaxonomy.taxeditor.l10n.Messages;
89
import eu.etaxonomy.taxeditor.model.ImageResources;
90

    
91
/**
92
 *
93
 * @author pplitzner
94
 * @since Sep 8, 2017
95
 *
96
 */
97
public class BulkEditorE4Composite extends Composite {
98

    
99
    public static final String LABEL_CANDIDATE = "LABEL_CANDIDATE"; //$NON-NLS-1$
100
    public static final String LABEL_CANDIDATE_ICON = "LABEL_CANDIDATE_ICON"; //$NON-NLS-1$
101

    
102
    public static final String LABEL_TARGET = "LABEL_TARGET"; //$NON-NLS-1$
103
    public static final String LABEL_TARGET_ICON = "LABEL_TARGET_ICON"; //$NON-NLS-1$
104

    
105
    private final List<String> columnList = new ArrayList<>();
106

    
107
    private Composite topComposite;
108

    
109
    @Inject
110
    private ESelectionService selService;
111

    
112
    private ISelectionChangedListener selectionChangedListener;
113

    
114
    private Composite bottomComposite;
115

    
116
    @Inject
117
    private EMenuService menuService;
118

    
119
    private NatTable natTable;
120

    
121
    private DefaultBodyLayerStack bodyLayer;
122

    
123
    private ListDataProvider<CdmBase> bodyDataProvider;
124

    
125
    private BulkEditorE4 bulkEditor;
126
    private BulkEditorSearchE4 bulkEditorSearch;
127

    
128

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

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

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

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

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

    
147
	@SuppressWarnings("unused")
148
    public void init(AbstractBulkEditorInput<?> input){
149

    
150
        input.getPropertyKeys().forEach(key->columnList.add(key));
151

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

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

    
160
        createTable();
161

    
162
        configureTable();
163

    
164
        styleTable();
165

    
166
        GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
167

    
168
        //propagate selection
169
        selectionChangedListener = (event -> selService.setSelection(getSelection()));
170
        RowSelectionProvider<CdmBase> selectionProvider = new RowSelectionProvider<CdmBase>(bodyLayer.getSelectionLayer(), bodyDataProvider, true);
171
        selectionProvider.addSelectionChangedListener(selectionChangedListener);
172

    
173
        bottomComposite.layout();
174

    
175
	}
176

    
177
	private void createTable(){
178
	    ConfigRegistry configRegistry = new ConfigRegistry();
179
	    //property map
180
        Map<String, String> propertyToLabels = new HashMap<>();
181
        columnList.forEach(columnLabel->propertyToLabels.put(columnLabel, columnLabel));
182

    
183
        String[] propertyNames = columnList.toArray(columnList.toArray(new String[columnList.size()]));
184
        //sorted list
185
        SortedList<CdmBase> sortedList = new SortedList<>(bulkEditor.getEditorInput().getModel(), bulkEditor.getEditorInput().getTitleComparator());
186
        //data provider
187
        BulkEditorPropertyAccessor columnPropertyAccessor = new BulkEditorPropertyAccessor(this, bulkEditor.getEditorInput());
188
        bodyDataProvider = new ListDataProvider<CdmBase>(sortedList,
189
                columnPropertyAccessor);
190
        DefaultColumnHeaderDataProvider colHeaderDataProvider = new DefaultColumnHeaderDataProvider(
191
                propertyNames, propertyToLabels);
192
        DefaultRowHeaderDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(
193
                bodyDataProvider);
194
        //body
195
        DataLayer dataLayer = new DataLayer(bodyDataProvider);
196
        dataLayer.registerCommandHandler(new VisualRefreshCommandHandler());
197
        GlazedListsEventLayer<CdmBase> eventLayer = new GlazedListsEventLayer<>(dataLayer, bulkEditor.getEditorInput().getModel());
198
        bodyLayer = new DefaultBodyLayerStack(eventLayer);
199

    
200
        dataLayer.setColumnPercentageSizing(0, true);
201
        dataLayer.setColumnWidthPercentageByPosition(0, 45);
202
        for(int i=1;i<columnList.size();i++){
203
            if(bulkEditor.getEditorInput().isCacheProperty(columnList.get(i))){
204
                dataLayer.setColumnWidthByPosition(i, 16);
205
            }
206
            else{
207
                dataLayer.setColumnWidthByPosition(i, 100);
208
            }
209
        }
210

    
211
        //column
212
        DataLayer columnHeaderDataLayer = new DataLayer(colHeaderDataProvider);
213
        ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(
214
                columnHeaderDataLayer,
215
                bodyLayer, bodyLayer.getSelectionLayer());
216
        // add the SortHeaderLayer to the column header layer stack
217
        // as we use GlazedLists, we use the GlazedListsSortModel which
218
        // delegates the sorting to the SortedList
219
        final SortHeaderLayer<SpecimenDescription> sortHeaderLayer = new SortHeaderLayer<>(
220
                columnHeaderLayer,
221
                new GlazedListsSortModel<>(
222
                        sortedList,
223
                        columnPropertyAccessor,
224
                        configRegistry,
225
                        columnHeaderDataLayer));
226
        columnHeaderDataLayer.setConfigLabelAccumulator(
227
                new BulkEditorConfigLabelAccumulatorHeader(colHeaderDataProvider, bulkEditor.getEditorInput()));
228
        //row
229
        DataLayer rowHeaderDataLayer = new DataLayer(rowHeaderDataProvider);
230
        RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer,
231
                bodyLayer, bodyLayer.getSelectionLayer());
232
        rowHeaderDataLayer.setColumnWidthByPosition(0, 50);
233

    
234
        //corner
235
        DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(
236
                colHeaderDataProvider, rowHeaderDataProvider);
237
        DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
238
        CornerLayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, sortHeaderLayer);
239
        //grid
240
        GridLayer gridLayer = new GridLayer(bodyLayer, sortHeaderLayer,
241
                rowHeaderLayer, cornerLayer, false);//set autoconfigure to false to get rid of the single click editing
242
        natTable = new NatTable(bottomComposite, gridLayer, false);
243

    
244
        //full row selection
245
        bodyLayer.getSelectionLayer().setSelectionModel(new RowSelectionModel<CdmBase>(bodyLayer.getSelectionLayer(), bodyDataProvider, new IRowIdAccessor<CdmBase>() {
246
            @Override
247
            public Serializable getRowId(CdmBase rowObject) {
248
                return bulkEditor.getEditorInput().getModel().indexOf(rowObject);
249
            }
250
        }));
251

    
252
        dataLayer.setConfigLabelAccumulator(new BulkEditorConfigLabelAccumulator(bodyDataProvider, colHeaderDataProvider,
253
                bulkEditor.getEditorInput()));
254

    
255
        natTable.setConfigRegistry(configRegistry);
256

    
257
        //add tooltip to table
258
        new BulkEditorTooltip(natTable, colHeaderDataProvider);
259
	}
260

    
261
	private void configureTable(){
262

    
263
	    //this configuration enables cell to be editable in general
264
	    //necessary because we disabled this beforehand by setting autoconfigure=false
265
	    //in the GridLayer constructor
266
	    ((AbstractLayer) natTable.getLayer()).addConfiguration(new DefaultEditConfiguration());
267

    
268
        natTable.addConfiguration(new AbstractUiBindingConfiguration() {
269
            @Override
270
            public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
271
                uiBindingRegistry.registerFirstDoubleClickBinding(new CellEditorMouseEventMatcher(GridRegion.BODY),
272
                        new MouseEditAction());
273
            }
274
        });
275

    
276
        //make cells editable to allow selecting the text
277
        natTable.addConfiguration(new AbstractRegistryConfiguration() {
278
            @Override
279
            public void configureRegistry(IConfigRegistry configRegistry) {
280
                //make cell editable
281
                configRegistry.registerConfigAttribute(
282
                        EditConfigAttributes.CELL_EDITABLE_RULE,
283
                        IEditableRule.ALWAYS_EDITABLE,
284
                        DisplayMode.EDIT);
285
                //register editor
286
                TextCellEditor editor = new TextCellEditor();
287
                editor.setEditable(false);
288
                configRegistry.registerConfigAttribute(
289
                        EditConfigAttributes.CELL_EDITOR,
290
                        editor,
291
                        DisplayMode.NORMAL);
292
            }
293
        });
294

    
295
        //+++CONTEXT MENU+++
296
        menuService.registerContextMenu(natTable, "eu.etaxonomy.taxeditor.bulkeditor.popupmenu.bulkeditor"); //$NON-NLS-1$
297
        // get the menu registered by EMenuService
298
        final Menu e4Menu = natTable.getMenu();
299
        // remove the menu reference from NatTable instance
300
        natTable.setMenu(null);
301
        natTable.addConfiguration(
302
                new AbstractUiBindingConfiguration() {
303
            @Override
304
            public void configureUiBindings(
305
                    UiBindingRegistry uiBindingRegistry) {
306
                // add e4 menu to NatTable
307
                        new PopupMenuBuilder(natTable, e4Menu)
308
                        .withHideColumnMenuItem()
309
                        .withShowAllColumnsMenuItem()
310
                        .build();
311

    
312
                // register the UI binding for header, corner and body region
313
                uiBindingRegistry.registerMouseDownBinding(
314
                        new MouseEventMatcher(
315
                                SWT.NONE,
316
                                null,
317
                                MouseEventMatcher.RIGHT_BUTTON),
318
                        new PopupMenuAction(e4Menu));
319
            }
320
        });
321

    
322

    
323
        //enable sorting
324
        natTable.addConfiguration(new SingleClickSortConfiguration());
325

    
326
        // Custom style for deduplication labels
327
        natTable.addConfiguration(new BulkEditorLabelStyleConfiguration());
328

    
329
        //add default configuration because autoconfigure is set to false in constructor
330
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
331

    
332
        //show boolean values as check boxes
333
        natTable.addConfiguration(new AbstractRegistryConfiguration() {
334
            @Override
335
            public void configureRegistry(IConfigRegistry configRegistry) {
336
                configRegistry.registerConfigAttribute(
337
                        CellConfigAttributes.CELL_PAINTER,
338
                        new CheckBoxPainter(),
339
                        DisplayMode.NORMAL,
340
                        BulkEditorConfigLabelAccumulatorHeader.BOOLEAN_COLUMN);
341

    
342
                configRegistry.registerConfigAttribute(
343
                        CellConfigAttributes.DISPLAY_CONVERTER,
344
                        new DefaultBooleanDisplayConverter(),
345
                        DisplayMode.NORMAL,
346
                        BulkEditorConfigLabelAccumulatorHeader.BOOLEAN_COLUMN);
347
            }
348
        });
349

    
350
        natTable.addConfiguration(new AbstractRegistryConfiguration() {
351
            @Override
352
            public void configureRegistry(IConfigRegistry configRegistry) {
353
                configRegistry.registerConfigAttribute(
354
                        CellConfigAttributes.CELL_PAINTER,
355
                        new ImagePainter(ImageResources.getImage(ImageResources.LOCK_ICON)),
356
                        DisplayMode.NORMAL,
357
                        BulkEditorConfigLabelAccumulatorHeader.CACHE_COLUMN);
358
            }
359
        });
360

    
361
        //register handler for view configuration menu
362
        natTable.registerCommandHandler(bulkEditorSearch.getDisplayPersistenceDialogCommandHandler());
363

    
364
        natTable.configure();
365
	}
366

    
367
	private void styleTable(){
368
	    natTable.setTheme(new ModernNatTableThemeConfiguration());
369
	}
370

    
371
	public void performSearch(BulkEditorQuery query) {
372
	    performSearch(query, null);
373
	}
374

    
375
	/** {@inheritDoc}
376
	 * @param selection */
377
    public void performSearch(BulkEditorQuery query, IStructuredSelection selection) {
378
        if (query != null) {
379
            if (bulkEditor.isDirty()) {
380
                String[] labels = {Messages.BulkEditorE4_SAVE_AND_SEARCH, Messages.BulkEditorE4_DONT_SAVE,Messages.BulkEditorE4_CANCEL};
381
                MessageDialog dialog =new MessageDialog(topComposite.getShell(), Messages.BulkEditorE4_SAVE_CHANGES_TITLE, null, Messages.BulkEditorE4_SAVE_CHANGES_MESSAGE, MessageDialog.QUESTION,labels, 0);
382
                int result = dialog.open();
383
                if (result == 0) {
384
                    bulkEditor.save(new NullProgressMonitor());
385
                } else if (result == 2){
386
                    return;
387
                }
388
            }
389
            bulkEditor.setDirty(false);
390
            bulkEditor.getEditorInput().performSearch(query, selection);
391
            bulkEditor.setLastQuery(query);
392
        }
393
    }
394

    
395
    public void refresh(){
396
        natTable.doCommand(new VisualRefreshCommand());
397
    }
398

    
399
    public List<String> getColumnList() {
400
        return columnList;
401
    }
402

    
403
    public IStructuredSelection getSelection(){
404
        List<CdmBase> selection = new ArrayList<>();
405
        int[] fullySelectedRowPositions = bodyLayer.getSelectionLayer().getFullySelectedRowPositions();
406
        for (int i : fullySelectedRowPositions) {
407
            if(i<0){
408
                continue;
409
            }
410
            Object rowObject = bodyDataProvider.getRowObject(i);
411
            if(rowObject instanceof CdmBase){
412
                selection.add((CdmBase) rowObject);
413
            }
414
        }
415
        return new StructuredSelection(selection);
416
    }
417

    
418
    public void setSelection(IStructuredSelection selection){
419
        Object[] objects = selection.toArray();
420
        for (Object object : objects) {
421

    
422
            if(object instanceof CdmBase &&  bulkEditor.getEditorInput().getModel().contains(object)){
423
                bodyLayer.getSelectionLayer().selectRow(0, bodyDataProvider.indexOfRowObject((CdmBase) object), false, false);
424
            }
425
        }
426
    }
427

    
428
    public void copyDataToClipboard() {
429
        String textData = ""; //$NON-NLS-1$
430
        IStructuredSelection selection = getSelection();
431
        Object[] objects = selection.toArray();
432
        for (Object object : objects) {
433
            if(object instanceof CdmBase){
434
                textData += bulkEditor.getEditorInput().getText((CdmBase)object);
435
            }
436
        }
437
        final TextTransfer textTransfer = TextTransfer.getInstance();
438
        final Clipboard clipboard = new Clipboard(Display.getDefault());
439
        try {
440
            clipboard.setContents(new Object[] { textData.toString() },
441
                    new Transfer[] { textTransfer });
442
        } finally {
443
            clipboard.dispose();
444
        }
445
    }
446

    
447
    NatTable getNatTable() {
448
        return natTable;
449
    }
450

    
451
    public Properties getNatTableState() {
452
        return bulkEditorSearch.getNatTableState();
453
    }
454

    
455
    File getStatePropertiesFile() {
456
        return bulkEditorSearch.getStatePropertiesFile();
457
    }
458

    
459
}
(4-4/8)