Project

General

Profile

Download (16.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

    
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 TYPE_PROPERTY = Messages.BulkEditorE4_TYPE;
93

    
94
    public static final String CANDIDATE_LABEL = "candidate";
95

    
96
    public static final String TARGET_LABEL = "target";
97

    
98
    private Composite topComposite;
99

    
100
    @Inject
101
    private ESelectionService selService;
102

    
103
    private ISelectionChangedListener selectionChangedListener;
104

    
105
    private Composite bottomComposite;
106

    
107
    @Inject
108
    private EMenuService menuService;
109

    
110
    private NatTable natTable;
111

    
112
    private DefaultBodyLayerStack bodyLayer;
113

    
114
    private ListDataProvider<CdmBase> bodyDataProvider;
115

    
116
    private BulkEditorE4 bulkEditor;
117

    
118

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

    
124
        this.setLayout(new GridLayout());
125

    
126
        topComposite = new Composite(parent, SWT.NONE);
127
        topComposite.setLayout(new GridLayout());
128

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

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

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

    
140
	    new BulkEditorSearchE4(this, topComposite, SWT.NONE);
141
	    //layout needed because the search bar is added after @PostConstuct method
142
	    topComposite.getParent().layout();
143

    
144
        if(input.getEntityUuid()!=null){
145
            performSearch(new BulkEditorQuery(input.getEntityUuid().toString()));
146
        }
147

    
148
        createTable();
149

    
150
        configureTable();
151

    
152
        styleTable();
153

    
154
        GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
155

    
156
        //propagate selection
157
        selectionChangedListener = (event -> selService.setSelection(getSelection()));
158
        RowSelectionProvider<CdmBase> selectionProvider = new RowSelectionProvider<CdmBase>(bodyLayer.getSelectionLayer(), bodyDataProvider, true);
159
        selectionProvider.addSelectionChangedListener(selectionChangedListener);
160

    
161
        bottomComposite.layout();
162
	}
163

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

    
187
        dataLayer.setColumnPercentageSizing(true);
188
        dataLayer.setColumnWidthPercentageByPosition(0, 80);
189
        dataLayer.setColumnWidthPercentageByPosition(1, 20);
190

    
191
        //column
192
        DataLayer columnHeaderDataLayer = new DataLayer(colHeaderDataProvider);
193
        ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(
194
                columnHeaderDataLayer,
195
                bodyLayer, bodyLayer.getSelectionLayer());
196
        // add the SortHeaderLayer to the column header layer stack
197
        // as we use GlazedLists, we use the GlazedListsSortModel which
198
        // delegates the sorting to the SortedList
199
        final SortHeaderLayer<SpecimenDescription> sortHeaderLayer = new SortHeaderLayer<>(
200
                columnHeaderLayer,
201
                new GlazedListsSortModel<>(
202
                        sortedList,
203
                        columnPropertyAccessor,
204
                        configRegistry,
205
                        columnHeaderDataLayer));
206
        //row
207
        DataLayer rowHeaderDataLayer = new DataLayer(rowHeaderDataProvider);
208
        RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer,
209
                bodyLayer, bodyLayer.getSelectionLayer());
210

    
211
        //corner
212
        DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(
213
                colHeaderDataProvider, rowHeaderDataProvider);
214
        DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
215
        CornerLayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, sortHeaderLayer);
216
        //grid
217
        GridLayer gridLayer = new GridLayer(bodyLayer, sortHeaderLayer,
218
                rowHeaderLayer, cornerLayer, false);//set autoconfigure to false to get rid of the single click editing
219
        natTable = new NatTable(bottomComposite, gridLayer, false);
220
        natTable.setConfigRegistry(configRegistry);
221

    
222
        //full row selection
223
        bodyLayer.getSelectionLayer().setSelectionModel(new RowSelectionModel<CdmBase>(bodyLayer.getSelectionLayer(), bodyDataProvider, new IRowIdAccessor<CdmBase>() {
224
            @Override
225
            public Serializable getRowId(CdmBase rowObject) {
226
                return bulkEditor.getEditorInput().getModel().indexOf(rowObject);
227
            }
228
        }));
229

    
230
        //add label to deduplication rows
231
        dataLayer.setConfigLabelAccumulator(new BulkEditorConfigLabelAccumulator(natTable, bodyDataProvider, bulkEditor.getEditorInput()));
232

    
233
        //add tooltip to table
234
        new BulkEditorTooltip(natTable);
235
	}
236

    
237
	private void configureTable(){
238

    
239
	    //this configuration enables cell to be editable in general
240
	    //necessary because we disabled this beforehand by setting autoconfigure=false
241
	    //in the GridLayer constructor
242
	    ((AbstractLayer) natTable.getLayer()).addConfiguration(new DefaultEditConfiguration());
243

    
244
        natTable.addConfiguration(new AbstractUiBindingConfiguration() {
245
            @Override
246
            public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
247
                uiBindingRegistry.registerFirstDoubleClickBinding(new CellEditorMouseEventMatcher(GridRegion.BODY),
248
                        new MouseEditAction());
249
            }
250
        });
251

    
252
        //make cells editable to allow selecting the text
253
        natTable.addConfiguration(new AbstractRegistryConfiguration() {
254
            @Override
255
            public void configureRegistry(IConfigRegistry configRegistry) {
256
                //make cell editable
257
                configRegistry.registerConfigAttribute(
258
                        EditConfigAttributes.CELL_EDITABLE_RULE,
259
                        IEditableRule.ALWAYS_EDITABLE,
260
                        DisplayMode.EDIT);
261
                //register editor
262
                TextCellEditor editor = new TextCellEditor();
263
                editor.setEditable(false);
264
                configRegistry.registerConfigAttribute(
265
                        EditConfigAttributes.CELL_EDITOR,
266
                        editor,
267
                        DisplayMode.NORMAL);
268
            }
269
        });
270

    
271
        //+++CONTEXT MENU+++
272
        menuService.registerContextMenu(natTable, "eu.etaxonomy.taxeditor.bulkeditor.popupmenu.bulkeditor"); //$NON-NLS-1$
273
        // get the menu registered by EMenuService
274
        final Menu e4Menu = natTable.getMenu();
275
        // remove the menu reference from NatTable instance
276
        natTable.setMenu(null);
277
        natTable.addConfiguration(
278
                new AbstractUiBindingConfiguration() {
279
            @Override
280
            public void configureUiBindings(
281
                    UiBindingRegistry uiBindingRegistry) {
282
                // add e4 menu to NatTable
283
                new PopupMenuBuilder(natTable, e4Menu)
284
                    .build();
285

    
286
                // register the UI binding for header, corner and body region
287
                uiBindingRegistry.registerMouseDownBinding(
288
                        new MouseEventMatcher(
289
                                SWT.NONE,
290
                                null,
291
                                MouseEventMatcher.RIGHT_BUTTON),
292
                        new PopupMenuAction(e4Menu));
293
            }
294
        });
295

    
296

    
297
        //enable sorting
298
        natTable.addConfiguration(new SingleClickSortConfiguration());
299

    
300
        // Custom style for deduplication labels
301
        natTable.addConfiguration(new BulkEditorLabelStyleConfiguration());
302

    
303
        //add default configuration because autoconfigure is set to false in constructor
304
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
305
        natTable.configure();
306
	}
307

    
308
	private void styleTable(){
309
	    natTable.setTheme(new ModernNatTableThemeConfiguration());
310
	}
311

    
312
	public void performSearch(BulkEditorQuery query) {
313
	    performSearch(query, null);
314
	}
315

    
316
	/** {@inheritDoc}
317
	 * @param selection */
318
    public void performSearch(BulkEditorQuery query, IStructuredSelection selection) {
319
        if (query != null) {
320
            if (bulkEditor.isDirty()) {
321
                String[] labels = {Messages.BulkEditorE4_SAVE_AND_SEARCH, Messages.BulkEditorE4_DONT_SAVE,Messages.BulkEditorE4_CANCEL};
322
                MessageDialog dialog =new MessageDialog(topComposite.getShell(), Messages.BulkEditorE4_SAVE_CHANGES_TITLE, null, Messages.BulkEditorE4_SAVE_CHANGES_MESSAGE, MessageDialog.QUESTION,labels, 0);
323
                int result = dialog.open();
324
                if (result == 0) {
325
                    bulkEditor.save(new NullProgressMonitor());
326
                } else if (result == 2){
327
                    return;
328
                }
329
            }
330
            bulkEditor.setDirty(false);
331
            bulkEditor.getEditorInput().performSearch(query, selection);
332
            bulkEditor.setLastQuery(query);
333
        }
334
    }
335

    
336
    public void refresh(){
337
        natTable.doCommand(new VisualRefreshCommand());
338
    }
339

    
340
    public IStructuredSelection getSelection(){
341
        List<CdmBase> selection = new ArrayList<>();
342
        int[] fullySelectedRowPositions = bodyLayer.getSelectionLayer().getFullySelectedRowPositions();
343
        for (int i : fullySelectedRowPositions) {
344
            /*
345
             * Differentiation between "index" and "position" is important here
346
             * "position" is the current visible index "index" refers to the
347
             * underlying data model deleting an entity could lead to an
348
             * IndexOutOfBoundExceptions if the position is used
349
             */
350
            int rowIndexByPosition = natTable.getRowIndexByPosition(i);
351
            Object rowObject = bodyDataProvider.getRowObject(rowIndexByPosition);
352
            if(rowObject instanceof CdmBase){
353
                selection.add((CdmBase) rowObject);
354
            }
355
        }
356
        return new StructuredSelection(selection);
357
    }
358

    
359
    public void setSelection(IStructuredSelection selection){
360
        Object[] objects = selection.toArray();
361
        for (Object object : objects) {
362

    
363
            if(object instanceof CdmBase &&  bulkEditor.getEditorInput().getModel().contains(object)){
364
                bodyLayer.getSelectionLayer().selectRow(0, bodyDataProvider.indexOfRowObject((CdmBase) object), false, false);
365
            }
366
        }
367
    }
368

    
369
    public void copyDataToClipboard() {
370
        String textData = "";
371
        IStructuredSelection selection = getSelection();
372
        Object[] objects = selection.toArray();
373
        for (Object object : objects) {
374
            if(object instanceof CdmBase){
375
                textData += bulkEditor.getEditorInput().getText((CdmBase)object);
376
            }
377
        }
378
        final TextTransfer textTransfer = TextTransfer.getInstance();
379
        final Clipboard clipboard = new Clipboard(Display.getDefault());
380
        try {
381
            clipboard.setContents(new Object[] { textData.toString() },
382
                    new Transfer[] { textTransfer });
383
        } finally {
384
            clipboard.dispose();
385
        }
386
    }
387

    
388
}
(3-3/7)