Project

General

Profile

Download (19.8 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.Collection;
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.core.services.events.IEventBroker;
23
import org.eclipse.e4.ui.services.EMenuService;
24
import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
25
import org.eclipse.jface.layout.GridDataFactory;
26
import org.eclipse.jface.viewers.ISelectionChangedListener;
27
import org.eclipse.jface.viewers.IStructuredSelection;
28
import org.eclipse.jface.viewers.StructuredSelection;
29
import org.eclipse.nebula.widgets.nattable.NatTable;
30
import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommand;
31
import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommandHandler;
32
import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
33
import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration;
34
import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
35
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
36
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
37
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
38
import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
39
import org.eclipse.nebula.widgets.nattable.coordinate.Range;
40
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
41
import org.eclipse.nebula.widgets.nattable.data.convert.DefaultBooleanDisplayConverter;
42
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
43
import org.eclipse.nebula.widgets.nattable.edit.action.MouseEditAction;
44
import org.eclipse.nebula.widgets.nattable.edit.config.DefaultEditConfiguration;
45
import org.eclipse.nebula.widgets.nattable.edit.editor.TextCellEditor;
46
import org.eclipse.nebula.widgets.nattable.extension.e4.selection.E4SelectionListener;
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.cell.ILayerCell;
60
import org.eclipse.nebula.widgets.nattable.layer.stack.DefaultBodyLayerStack;
61
import org.eclipse.nebula.widgets.nattable.painter.cell.CheckBoxPainter;
62
import org.eclipse.nebula.widgets.nattable.painter.cell.ImagePainter;
63
import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer;
64
import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;
65
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
66
import org.eclipse.nebula.widgets.nattable.style.theme.ModernNatTableThemeConfiguration;
67
import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
68
import org.eclipse.nebula.widgets.nattable.ui.matcher.CellEditorMouseEventMatcher;
69
import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;
70
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuAction;
71
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
72
import org.eclipse.swt.SWT;
73
import org.eclipse.swt.dnd.Clipboard;
74
import org.eclipse.swt.dnd.TextTransfer;
75
import org.eclipse.swt.dnd.Transfer;
76
import org.eclipse.swt.layout.GridData;
77
import org.eclipse.swt.layout.GridLayout;
78
import org.eclipse.swt.widgets.Composite;
79
import org.eclipse.swt.widgets.Display;
80
import org.eclipse.swt.widgets.Menu;
81

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

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

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

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

    
103
    private final List<String> columnList = new ArrayList<>();
104

    
105
    private Composite topComposite;
106

    
107
    @Inject
108
    private ESelectionService selService;
109

    
110
    private ISelectionChangedListener selectionChangedListener;
111

    
112
    private Composite bottomComposite;
113

    
114
    @Inject
115
    private EMenuService menuService;
116

    
117
    private NatTable natTable;
118

    
119
    private DefaultBodyLayerStack bodyLayer;
120

    
121
    private ListDataProvider<CdmBase> bodyDataProvider;
122

    
123
    private BulkEditor bulkEditor;
124
    private BulkEditorSearch bulkEditorSearch;
125

    
126
    @Inject
127
    private IEventBroker eventBroker;
128

    
129

    
130
    public BulkEditorComposite(BulkEditor bulkEditor, Composite parent, int style) {
131
        super(parent, style);
132
        parent.setLayout(new GridLayout());
133
        this.bulkEditor = bulkEditor;
134

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

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

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

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

    
148
    public void init(AbstractBulkEditorInput<?> input){
149

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

    
152
	    bulkEditorSearch = new BulkEditorSearch(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
        // selection listener
170
        E4SelectionListener<CdmBase> selectionListener = new CellSelectionListener(selService, bodyLayer.getSelectionLayer(), bodyDataProvider, this);
171
        bodyLayer.getSelectionLayer().addLayerListener(selectionListener);
172
        selectionListener.setFullySelectedRowsOnly(false);
173

    
174
        bottomComposite.layout();
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
        dataLayer.setConfigLabelAccumulator(new BulkEditorConfigLabelAccumulator(bodyDataProvider, colHeaderDataProvider,
245
                bulkEditor.getEditorInput()));
246

    
247
        natTable.setConfigRegistry(configRegistry);
248

    
249
        //add tooltip to table
250
        new BulkEditorTooltip(natTable, colHeaderDataProvider);
251
	}
252

    
253
	private void configureTable(){
254

    
255
	    //this configuration enables cell to be editable in general
256
	    //necessary because we disabled this beforehand by setting autoconfigure=false
257
	    //in the GridLayer constructor
258
	    ((AbstractLayer) natTable.getLayer()).addConfiguration(new DefaultEditConfiguration());
259

    
260
        natTable.addConfiguration(new AbstractUiBindingConfiguration() {
261
            @Override
262
            public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
263
                uiBindingRegistry.registerFirstDoubleClickBinding(new CellEditorMouseEventMatcher(GridRegion.BODY),
264
                        new MouseEditAction());
265
            }
266
        });
267

    
268
        //make cells editable to allow selecting the text
269
        natTable.addConfiguration(new AbstractRegistryConfiguration() {
270
            @Override
271
            public void configureRegistry(IConfigRegistry configRegistry) {
272
                //make cell editable
273
                configRegistry.registerConfigAttribute(
274
                        EditConfigAttributes.CELL_EDITABLE_RULE,
275
                        IEditableRule.ALWAYS_EDITABLE,
276
                        DisplayMode.EDIT);
277
                //register editor
278
                TextCellEditor editor = new TextCellEditor();
279
                editor.setEditable(false);
280
                configRegistry.registerConfigAttribute(
281
                        EditConfigAttributes.CELL_EDITOR,
282
                        editor,
283
                        DisplayMode.NORMAL);
284
            }
285
        });
286

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

    
304
                // register the UI binding for header, corner and body region
305
                uiBindingRegistry.registerMouseDownBinding(
306
                        new MouseEventMatcher(
307
                                SWT.NONE,
308
                                null,
309
                                MouseEventMatcher.RIGHT_BUTTON),
310
                        new PopupMenuAction(e4Menu));
311
            }
312
        });
313

    
314
        //enable sorting
315
        natTable.addConfiguration(new SingleClickSortConfiguration());
316

    
317
        // Custom style for deduplication labels
318
        natTable.addConfiguration(new BulkEditorLabelStyleConfiguration());
319

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

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

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

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

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

    
355
        natTable.configure();
356
	}
357

    
358
	private void styleTable(){
359
	    natTable.setTheme(new ModernNatTableThemeConfiguration());
360
	}
361

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

    
366
	}
367

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

    
378
    public void refresh(){
379
    	IStructuredSelection sel = getSelection();
380
        natTable.doCommand(new VisualRefreshCommand());
381
        this.setSelection(sel);
382
    }
383

    
384
    public List<String> getColumnList() {
385
        return columnList;
386
    }
387

    
388
    public IStructuredSelection getCellSelection(){
389

    
390
        Set<Range> selectedRowPositions = bodyLayer.getSelectionLayer().getSelectedRowPositions();
391
        Collection<ILayerCell> cells = bodyLayer.getSelectionLayer().getSelectedCells();
392
        if (cells.size() == 1){
393
            return new StructuredSelection(cells.iterator().next().getDataValue());
394
        }
395
        List<Object> selectedObjects = new ArrayList<>();
396
        for (Range range : selectedRowPositions) {
397
            for(int i=range.start;i<range.end;i++){
398
                if (i>=0){
399
                    selectedObjects.add(bodyDataProvider.getRowObject(i));
400
                }
401
            }
402
        }
403
        return new StructuredSelection(selectedObjects);
404
    }
405

    
406
    public IStructuredSelection getSelection(){
407

    
408
        Set<Range> selectedRowPositions = bodyLayer.getSelectionLayer().getSelectedRowPositions();
409
        List<Object> selectedObjects = new ArrayList<>();
410
        for (Range range : selectedRowPositions) {
411
            for(int i=range.start;i<range.end;i++){
412
                if (i>=0){
413
                    selectedObjects.add(bodyDataProvider.getRowObject(i));
414
                }
415
            }
416
        }
417
        return new StructuredSelection(selectedObjects);
418
    }
419

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

    
424
            if(object instanceof CdmBase &&  bulkEditor.getEditorInput().getModel().contains(object)){
425
                object = bulkEditor.getEditorInput().getModel().get(bulkEditor.getEditorInput().getModel().indexOf(object));
426
                bodyLayer.getSelectionLayer().selectRow(0, bodyDataProvider.indexOfRowObject((CdmBase) object), false, true);
427
            }
428
        }
429
    }
430

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

    
454
    protected ListDataProvider<CdmBase> getBodyDataProvider() {
455
        return bodyDataProvider;
456
    }
457

    
458
    protected ESelectionService getSelService() {
459
        return selService;
460
    }
461

    
462
    protected NatTable getNatTable() {
463
        return natTable;
464
    }
465

    
466
    protected Properties getNatTableState() {
467
        return bulkEditorSearch.getNatTableState();
468
    }
469

    
470
    protected File getStatePropertiesFile() {
471
        return bulkEditorSearch.getStatePropertiesFile();
472
    }
473
}
(2-2/9)