Project

General

Profile

Download (18.1 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.Serializable;
13
import java.util.ArrayList;
14
import java.util.HashMap;
15
import java.util.List;
16
import java.util.Map;
17

    
18
import javax.inject.Inject;
19

    
20
import org.eclipse.core.runtime.NullProgressMonitor;
21
import org.eclipse.e4.ui.services.EMenuService;
22
import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
23
import org.eclipse.jface.dialogs.MessageDialog;
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.data.IRowIdAccessor;
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.glazedlists.GlazedListsEventLayer;
46
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
47
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
48
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
49
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
50
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
51
import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
52
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
53
import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
54
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
55
import org.eclipse.nebula.widgets.nattable.layer.AbstractLayer;
56
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
57
import org.eclipse.nebula.widgets.nattable.layer.stack.DefaultBodyLayerStack;
58
import org.eclipse.nebula.widgets.nattable.painter.cell.CheckBoxPainter;
59
import org.eclipse.nebula.widgets.nattable.selection.RowSelectionModel;
60
import org.eclipse.nebula.widgets.nattable.selection.RowSelectionProvider;
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.l10n.Messages;
86

    
87
/**
88
 *
89
 * @author pplitzner
90
 * @since Sep 8, 2017
91
 *
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

    
123

    
124
    public BulkEditorE4Composite(BulkEditorE4 bulkEditor, Composite parent, int style) {
125
        super(parent, style);
126
        parent.setLayout(new GridLayout());
127
        this.bulkEditor = bulkEditor;
128

    
129
        this.setLayout(new GridLayout());
130

    
131
        topComposite = new Composite(parent, SWT.NONE);
132
        topComposite.setLayout(new GridLayout());
133

    
134
        GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
135
        topComposite.setLayoutData(gridData);
136

    
137
        bottomComposite = new Composite(parent, SWT.NONE);
138
        bottomComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
139
        bottomComposite.setLayout(new GridLayout());
140
	}
141

    
142
	@SuppressWarnings("unused")
143
    public void init(AbstractBulkEditorInput<?> input){
144

    
145
        input.getPropertyKeys().forEach(key->columnList.add(key));
146

    
147
	    new BulkEditorSearchE4(this, topComposite, SWT.NONE);
148
	    //layout needed because the search bar is added after @PostConstuct method
149
	    topComposite.getParent().layout();
150

    
151
        if(input.getEntityUuid()!=null){
152
            performSearch(new BulkEditorQuery(input.getEntityUuid().toString()));
153
        }
154

    
155
        createTable();
156

    
157
        configureTable();
158

    
159
        styleTable();
160

    
161
        GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
162

    
163
        //propagate selection
164
        selectionChangedListener = (event -> selService.setSelection(getSelection()));
165
        RowSelectionProvider<CdmBase> selectionProvider = new RowSelectionProvider<CdmBase>(bodyLayer.getSelectionLayer(), bodyDataProvider, true);
166
        selectionProvider.addSelectionChangedListener(selectionChangedListener);
167

    
168
        bottomComposite.layout();
169
	}
170

    
171
	private void createTable(){
172
	    ConfigRegistry configRegistry = new ConfigRegistry();
173
	    //property map
174
        Map<String, String> propertyToLabels = new HashMap<>();
175
        columnList.forEach(columnLabel->propertyToLabels.put(columnLabel, columnLabel));
176

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

    
194
        dataLayer.setColumnPercentageSizing(0, true);
195
        dataLayer.setColumnWidthPercentageByPosition(0, 45);
196
        for(int i=1;i<columnList.size();i++){
197
//            if(bulkEditor.getEditorInput().isBooleanProperty(columnList.get(i))){
198
//                dataLayer.setColumnWidthByPosition(i, 20);
199
//            }
200
//            else{
201
                dataLayer.setColumnWidthByPosition(i, 100);
202
//            }
203
        }
204

    
205
        //column
206
        DataLayer columnHeaderDataLayer = new DataLayer(colHeaderDataProvider);
207
        ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(
208
                columnHeaderDataLayer,
209
                bodyLayer, bodyLayer.getSelectionLayer());
210
        // add the SortHeaderLayer to the column header layer stack
211
        // as we use GlazedLists, we use the GlazedListsSortModel which
212
        // delegates the sorting to the SortedList
213
        final SortHeaderLayer<SpecimenDescription> sortHeaderLayer = new SortHeaderLayer<>(
214
                columnHeaderLayer,
215
                new GlazedListsSortModel<>(
216
                        sortedList,
217
                        columnPropertyAccessor,
218
                        configRegistry,
219
                        columnHeaderDataLayer));
220
        //row
221
        DataLayer rowHeaderDataLayer = new DataLayer(rowHeaderDataProvider);
222
        RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer,
223
                bodyLayer, bodyLayer.getSelectionLayer());
224
        rowHeaderDataLayer.setColumnWidthByPosition(0, 50);
225

    
226
        //corner
227
        DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(
228
                colHeaderDataProvider, rowHeaderDataProvider);
229
        DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
230
        CornerLayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, sortHeaderLayer);
231
        //grid
232
        GridLayer gridLayer = new GridLayer(bodyLayer, sortHeaderLayer,
233
                rowHeaderLayer, cornerLayer, false);//set autoconfigure to false to get rid of the single click editing
234
        natTable = new NatTable(bottomComposite, gridLayer, false);
235
        natTable.setConfigRegistry(configRegistry);
236

    
237
        //full row selection
238
        bodyLayer.getSelectionLayer().setSelectionModel(new RowSelectionModel<CdmBase>(bodyLayer.getSelectionLayer(), bodyDataProvider, new IRowIdAccessor<CdmBase>() {
239
            @Override
240
            public Serializable getRowId(CdmBase rowObject) {
241
                return bulkEditor.getEditorInput().getModel().indexOf(rowObject);
242
            }
243
        }));
244

    
245
        dataLayer.setConfigLabelAccumulator(new BulkEditorConfigLabelAccumulator(natTable, bodyDataProvider,
246
                colHeaderDataProvider,
247
                bulkEditor.getEditorInput()));
248

    
249
        //add tooltip to table
250
        new BulkEditorTooltip(natTable);
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
        //show boolean values as check boxes
288
        natTable.addConfiguration(new AbstractRegistryConfiguration() {
289
            @Override
290
            public void configureRegistry(IConfigRegistry configRegistry) {
291
                configRegistry.registerConfigAttribute(
292
                        CellConfigAttributes.CELL_PAINTER,
293
                        new CheckBoxPainter(),
294
                        DisplayMode.NORMAL,
295
                        BulkEditorConfigLabelAccumulator.BOOLEAN_COLUMN);
296

    
297
                configRegistry.registerConfigAttribute(
298
                        CellConfigAttributes.DISPLAY_CONVERTER,
299
                        new DefaultBooleanDisplayConverter(),
300
                        DisplayMode.NORMAL,
301
                        BulkEditorConfigLabelAccumulator.BOOLEAN_COLUMN);
302
            }
303
        });
304

    
305
        //+++CONTEXT MENU+++
306
        menuService.registerContextMenu(natTable, "eu.etaxonomy.taxeditor.bulkeditor.popupmenu.bulkeditor"); //$NON-NLS-1$
307
        // get the menu registered by EMenuService
308
        final Menu e4Menu = natTable.getMenu();
309
        // remove the menu reference from NatTable instance
310
        natTable.setMenu(null);
311
        natTable.addConfiguration(
312
                new AbstractUiBindingConfiguration() {
313
            @Override
314
            public void configureUiBindings(
315
                    UiBindingRegistry uiBindingRegistry) {
316
                // add e4 menu to NatTable
317
                        new PopupMenuBuilder(natTable, e4Menu)
318
                        .withHideColumnMenuItem()
319
                        .withShowAllColumnsMenuItem()
320
                        .build();
321

    
322
                // register the UI binding for header, corner and body region
323
                uiBindingRegistry.registerMouseDownBinding(
324
                        new MouseEventMatcher(
325
                                SWT.NONE,
326
                                null,
327
                                MouseEventMatcher.RIGHT_BUTTON),
328
                        new PopupMenuAction(e4Menu));
329
            }
330
        });
331

    
332

    
333
        //enable sorting
334
        natTable.addConfiguration(new SingleClickSortConfiguration());
335

    
336
        // Custom style for deduplication labels
337
        natTable.addConfiguration(new BulkEditorLabelStyleConfiguration());
338

    
339
        //add default configuration because autoconfigure is set to false in constructor
340
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
341
        natTable.configure();
342
	}
343

    
344
	private void styleTable(){
345
	    natTable.setTheme(new ModernNatTableThemeConfiguration());
346
	}
347

    
348
	public void performSearch(BulkEditorQuery query) {
349
	    performSearch(query, null);
350
	}
351

    
352
	/** {@inheritDoc}
353
	 * @param selection */
354
    public void performSearch(BulkEditorQuery query, IStructuredSelection selection) {
355
        if (query != null) {
356
            if (bulkEditor.isDirty()) {
357
                String[] labels = {Messages.BulkEditorE4_SAVE_AND_SEARCH, Messages.BulkEditorE4_DONT_SAVE,Messages.BulkEditorE4_CANCEL};
358
                MessageDialog dialog =new MessageDialog(topComposite.getShell(), Messages.BulkEditorE4_SAVE_CHANGES_TITLE, null, Messages.BulkEditorE4_SAVE_CHANGES_MESSAGE, MessageDialog.QUESTION,labels, 0);
359
                int result = dialog.open();
360
                if (result == 0) {
361
                    bulkEditor.save(new NullProgressMonitor());
362
                } else if (result == 2){
363
                    return;
364
                }
365
            }
366
            bulkEditor.setDirty(false);
367
            bulkEditor.getEditorInput().performSearch(query, selection);
368
            bulkEditor.setLastQuery(query);
369
        }
370
    }
371

    
372
    public void refresh(){
373
        natTable.doCommand(new VisualRefreshCommand());
374
    }
375

    
376
    public List<String> getColumnList() {
377
        return columnList;
378
    }
379

    
380
    public IStructuredSelection getSelection(){
381
        List<CdmBase> selection = new ArrayList<>();
382
        int[] fullySelectedRowPositions = bodyLayer.getSelectionLayer().getFullySelectedRowPositions();
383
        for (int i : fullySelectedRowPositions) {
384
            if(i<0){
385
                continue;
386
            }
387
            Object rowObject = bodyDataProvider.getRowObject(i);
388
            if(rowObject instanceof CdmBase){
389
                selection.add((CdmBase) rowObject);
390
            }
391
        }
392
        return new StructuredSelection(selection);
393
    }
394

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

    
399
            if(object instanceof CdmBase &&  bulkEditor.getEditorInput().getModel().contains(object)){
400
                bodyLayer.getSelectionLayer().selectRow(0, bodyDataProvider.indexOfRowObject((CdmBase) object), false, false);
401
            }
402
        }
403
    }
404

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

    
424
}
(3-3/7)