Project

General

Profile

Download (16.9 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.ConfigRegistry;
34
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
35
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
36
import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
37
import org.eclipse.nebula.widgets.nattable.data.IRowIdAccessor;
38
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
39
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
40
import org.eclipse.nebula.widgets.nattable.edit.action.MouseEditAction;
41
import org.eclipse.nebula.widgets.nattable.edit.config.DefaultEditConfiguration;
42
import org.eclipse.nebula.widgets.nattable.edit.editor.TextCellEditor;
43
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
44
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
45
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
46
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
47
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
48
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
49
import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
50
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
51
import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
52
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
53
import org.eclipse.nebula.widgets.nattable.layer.AbstractLayer;
54
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
55
import org.eclipse.nebula.widgets.nattable.layer.stack.DefaultBodyLayerStack;
56
import org.eclipse.nebula.widgets.nattable.selection.RowSelectionModel;
57
import org.eclipse.nebula.widgets.nattable.selection.RowSelectionProvider;
58
import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer;
59
import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;
60
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
61
import org.eclipse.nebula.widgets.nattable.style.theme.ModernNatTableThemeConfiguration;
62
import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
63
import org.eclipse.nebula.widgets.nattable.ui.matcher.CellEditorMouseEventMatcher;
64
import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;
65
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuAction;
66
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
67
import org.eclipse.swt.SWT;
68
import org.eclipse.swt.dnd.Clipboard;
69
import org.eclipse.swt.dnd.TextTransfer;
70
import org.eclipse.swt.dnd.Transfer;
71
import org.eclipse.swt.layout.GridData;
72
import org.eclipse.swt.layout.GridLayout;
73
import org.eclipse.swt.widgets.Composite;
74
import org.eclipse.swt.widgets.Display;
75
import org.eclipse.swt.widgets.Menu;
76

    
77
import ca.odell.glazedlists.SortedList;
78
import eu.etaxonomy.cdm.model.common.CdmBase;
79
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
80
import eu.etaxonomy.taxeditor.bulkeditor.BulkEditorQuery;
81
import eu.etaxonomy.taxeditor.bulkeditor.input.AbstractBulkEditorInput;
82
import eu.etaxonomy.taxeditor.l10n.Messages;
83

    
84
/**
85
 *
86
 * @author pplitzner
87
 * @since Sep 8, 2017
88
 *
89
 */
90
public class BulkEditorE4Composite extends Composite {
91

    
92
    public static final String LABEL_CANDIDATE = "LABEL_CANDIDATE"; //$NON-NLS-1$
93
    public static final String LABEL_CANDIDATE_ICON = "LABEL_CANDIDATE_ICON"; //$NON-NLS-1$
94

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

    
98
    private final List<String> columnList = new ArrayList<>();
99

    
100
    private Composite topComposite;
101

    
102
    @Inject
103
    private ESelectionService selService;
104

    
105
    private ISelectionChangedListener selectionChangedListener;
106

    
107
    private Composite bottomComposite;
108

    
109
    @Inject
110
    private EMenuService menuService;
111

    
112
    private NatTable natTable;
113

    
114
    private DefaultBodyLayerStack bodyLayer;
115

    
116
    private ListDataProvider<CdmBase> bodyDataProvider;
117

    
118
    private BulkEditorE4 bulkEditor;
119

    
120

    
121
    public BulkEditorE4Composite(BulkEditorE4 bulkEditor, Composite parent, int style) {
122
        super(parent, style);
123
        parent.setLayout(new GridLayout());
124
        this.bulkEditor = bulkEditor;
125

    
126
        this.setLayout(new GridLayout());
127

    
128
        topComposite = new Composite(parent, SWT.NONE);
129
        topComposite.setLayout(new GridLayout());
130

    
131
        GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
132
        topComposite.setLayoutData(gridData);
133

    
134
        bottomComposite = new Composite(parent, SWT.NONE);
135
        bottomComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
136
        bottomComposite.setLayout(new GridLayout());
137
	}
138

    
139
	@SuppressWarnings("unused")
140
    public void init(AbstractBulkEditorInput<?> input){
141

    
142
        input.getPropertyKeys().forEach(key->columnList.add(key));
143

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

    
148
        if(input.getEntityUuid()!=null){
149
            performSearch(new BulkEditorQuery(input.getEntityUuid().toString()));
150
        }
151

    
152
        createTable();
153

    
154
        configureTable();
155

    
156
        styleTable();
157

    
158
        GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
159

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

    
165
        bottomComposite.layout();
166
	}
167

    
168
	private void createTable(){
169
	    ConfigRegistry configRegistry = new ConfigRegistry();
170
	    //property map
171
        Map<String, String> propertyToLabels = new HashMap<>();
172
        for(String columnLabel:columnList){
173
            propertyToLabels.put(columnLabel, columnLabel);
174
        }
175
        String[] propertyNames = columnList.toArray(columnList.toArray(new String[columnList.size()]));
176
        //sorted list
177
        SortedList<CdmBase> sortedList = new SortedList<>(bulkEditor.getEditorInput().getModel(), bulkEditor.getEditorInput().getTitleComparator());
178
        //data provider
179
        BulkEditorPropertyAccessor columnPropertyAccessor = new BulkEditorPropertyAccessor(this, bulkEditor.getEditorInput());
180
        bodyDataProvider = new ListDataProvider<CdmBase>(sortedList,
181
                columnPropertyAccessor);
182
        DefaultColumnHeaderDataProvider colHeaderDataProvider = new DefaultColumnHeaderDataProvider(
183
                propertyNames, propertyToLabels);
184
        DefaultRowHeaderDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(
185
                bodyDataProvider);
186
        //body
187
        DataLayer dataLayer = new DataLayer(bodyDataProvider);
188
        dataLayer.registerCommandHandler(new VisualRefreshCommandHandler());
189
        GlazedListsEventLayer<CdmBase> eventLayer = new GlazedListsEventLayer<>(dataLayer, bulkEditor.getEditorInput().getModel());
190
        bodyLayer = new DefaultBodyLayerStack(eventLayer);
191

    
192
        dataLayer.setColumnPercentageSizing(0, true);
193
        dataLayer.setColumnWidthPercentageByPosition(0, 45);
194
        for(int i=1;i<columnList.size();i++){
195
            dataLayer.setColumnWidthByPosition(i, 100);
196
        }
197

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

    
219
        //corner
220
        DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(
221
                colHeaderDataProvider, rowHeaderDataProvider);
222
        DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
223
        CornerLayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, sortHeaderLayer);
224
        //grid
225
        GridLayer gridLayer = new GridLayer(bodyLayer, sortHeaderLayer,
226
                rowHeaderLayer, cornerLayer, false);//set autoconfigure to false to get rid of the single click editing
227
        natTable = new NatTable(bottomComposite, gridLayer, false);
228
        natTable.setConfigRegistry(configRegistry);
229

    
230
        //full row selection
231
        bodyLayer.getSelectionLayer().setSelectionModel(new RowSelectionModel<CdmBase>(bodyLayer.getSelectionLayer(), bodyDataProvider, new IRowIdAccessor<CdmBase>() {
232
            @Override
233
            public Serializable getRowId(CdmBase rowObject) {
234
                return bulkEditor.getEditorInput().getModel().indexOf(rowObject);
235
            }
236
        }));
237

    
238
        //add label to deduplication rows
239
        dataLayer.setConfigLabelAccumulator(new BulkEditorConfigLabelAccumulator(natTable, bodyDataProvider, bulkEditor.getEditorInput()));
240

    
241
        //add tooltip to table
242
        new BulkEditorTooltip(natTable);
243
	}
244

    
245
	private void configureTable(){
246

    
247
	    //this configuration enables cell to be editable in general
248
	    //necessary because we disabled this beforehand by setting autoconfigure=false
249
	    //in the GridLayer constructor
250
	    ((AbstractLayer) natTable.getLayer()).addConfiguration(new DefaultEditConfiguration());
251

    
252
        natTable.addConfiguration(new AbstractUiBindingConfiguration() {
253
            @Override
254
            public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
255
                uiBindingRegistry.registerFirstDoubleClickBinding(new CellEditorMouseEventMatcher(GridRegion.BODY),
256
                        new MouseEditAction());
257
            }
258
        });
259

    
260
        //make cells editable to allow selecting the text
261
        natTable.addConfiguration(new AbstractRegistryConfiguration() {
262
            @Override
263
            public void configureRegistry(IConfigRegistry configRegistry) {
264
                //make cell editable
265
                configRegistry.registerConfigAttribute(
266
                        EditConfigAttributes.CELL_EDITABLE_RULE,
267
                        IEditableRule.ALWAYS_EDITABLE,
268
                        DisplayMode.EDIT);
269
                //register editor
270
                TextCellEditor editor = new TextCellEditor();
271
                editor.setEditable(false);
272
                configRegistry.registerConfigAttribute(
273
                        EditConfigAttributes.CELL_EDITOR,
274
                        editor,
275
                        DisplayMode.NORMAL);
276
            }
277
        });
278

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

    
296
                // register the UI binding for header, corner and body region
297
                uiBindingRegistry.registerMouseDownBinding(
298
                        new MouseEventMatcher(
299
                                SWT.NONE,
300
                                null,
301
                                MouseEventMatcher.RIGHT_BUTTON),
302
                        new PopupMenuAction(e4Menu));
303
            }
304
        });
305

    
306

    
307
        //enable sorting
308
        natTable.addConfiguration(new SingleClickSortConfiguration());
309

    
310
        // Custom style for deduplication labels
311
        natTable.addConfiguration(new BulkEditorLabelStyleConfiguration());
312

    
313
        //add default configuration because autoconfigure is set to false in constructor
314
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
315
        natTable.configure();
316
	}
317

    
318
	private void styleTable(){
319
	    natTable.setTheme(new ModernNatTableThemeConfiguration());
320
	}
321

    
322
	public void performSearch(BulkEditorQuery query) {
323
	    performSearch(query, null);
324
	}
325

    
326
	/** {@inheritDoc}
327
	 * @param selection */
328
    public void performSearch(BulkEditorQuery query, IStructuredSelection selection) {
329
        if (query != null) {
330
            if (bulkEditor.isDirty()) {
331
                String[] labels = {Messages.BulkEditorE4_SAVE_AND_SEARCH, Messages.BulkEditorE4_DONT_SAVE,Messages.BulkEditorE4_CANCEL};
332
                MessageDialog dialog =new MessageDialog(topComposite.getShell(), Messages.BulkEditorE4_SAVE_CHANGES_TITLE, null, Messages.BulkEditorE4_SAVE_CHANGES_MESSAGE, MessageDialog.QUESTION,labels, 0);
333
                int result = dialog.open();
334
                if (result == 0) {
335
                    bulkEditor.save(new NullProgressMonitor());
336
                } else if (result == 2){
337
                    return;
338
                }
339
            }
340
            bulkEditor.setDirty(false);
341
            bulkEditor.getEditorInput().performSearch(query, selection);
342
            bulkEditor.setLastQuery(query);
343
        }
344
    }
345

    
346
    public void refresh(){
347
        natTable.doCommand(new VisualRefreshCommand());
348
    }
349

    
350
    public List<String> getColumnList() {
351
        return columnList;
352
    }
353

    
354
    public IStructuredSelection getSelection(){
355
        List<CdmBase> selection = new ArrayList<>();
356
        int[] fullySelectedRowPositions = bodyLayer.getSelectionLayer().getFullySelectedRowPositions();
357
        for (int i : fullySelectedRowPositions) {
358
            if(i<0){
359
                continue;
360
            }
361
            Object rowObject = bodyDataProvider.getRowObject(i);
362
            if(rowObject instanceof CdmBase){
363
                selection.add((CdmBase) rowObject);
364
            }
365
        }
366
        return new StructuredSelection(selection);
367
    }
368

    
369
    public void setSelection(IStructuredSelection selection){
370
        Object[] objects = selection.toArray();
371
        for (Object object : objects) {
372

    
373
            if(object instanceof CdmBase &&  bulkEditor.getEditorInput().getModel().contains(object)){
374
                bodyLayer.getSelectionLayer().selectRow(0, bodyDataProvider.indexOfRowObject((CdmBase) object), false, false);
375
            }
376
        }
377
    }
378

    
379
    public void copyDataToClipboard() {
380
        String textData = ""; //$NON-NLS-1$
381
        IStructuredSelection selection = getSelection();
382
        Object[] objects = selection.toArray();
383
        for (Object object : objects) {
384
            if(object instanceof CdmBase){
385
                textData += bulkEditor.getEditorInput().getText((CdmBase)object);
386
            }
387
        }
388
        final TextTransfer textTransfer = TextTransfer.getInstance();
389
        final Clipboard clipboard = new Clipboard(Display.getDefault());
390
        try {
391
            clipboard.setContents(new Object[] { textData.toString() },
392
                    new Transfer[] { textTransfer });
393
        } finally {
394
            clipboard.dispose();
395
        }
396
    }
397

    
398
}
(3-3/7)