Project

General

Profile

Download (19.6 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.annotation.PostConstruct;
19
import javax.annotation.PreDestroy;
20
import javax.inject.Inject;
21

    
22
import org.eclipse.core.runtime.IProgressMonitor;
23
import org.eclipse.core.runtime.NullProgressMonitor;
24
import org.eclipse.e4.core.services.events.IEventBroker;
25
import org.eclipse.e4.ui.di.Focus;
26
import org.eclipse.e4.ui.di.Persist;
27
import org.eclipse.e4.ui.model.application.ui.MDirtyable;
28
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
29
import org.eclipse.e4.ui.services.EMenuService;
30
import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
31
import org.eclipse.jface.dialogs.MessageDialog;
32
import org.eclipse.jface.layout.GridDataFactory;
33
import org.eclipse.jface.viewers.ISelectionChangedListener;
34
import org.eclipse.jface.viewers.IStructuredSelection;
35
import org.eclipse.jface.viewers.StructuredSelection;
36
import org.eclipse.nebula.widgets.nattable.NatTable;
37
import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommand;
38
import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommandHandler;
39
import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
40
import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration;
41
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
42
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
43
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
44
import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
45
import org.eclipse.nebula.widgets.nattable.data.IRowIdAccessor;
46
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
47
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
48
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
49
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
50
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
51
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
52
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
53
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
54
import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
55
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
56
import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
57
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
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.selection.RowSelectionModel;
61
import org.eclipse.nebula.widgets.nattable.selection.RowSelectionProvider;
62
import org.eclipse.nebula.widgets.nattable.selection.command.SelectRowsCommand;
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.MouseEventMatcher;
69
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuAction;
70
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
71
import org.eclipse.swt.SWT;
72
import org.eclipse.swt.dnd.Clipboard;
73
import org.eclipse.swt.dnd.TextTransfer;
74
import org.eclipse.swt.dnd.Transfer;
75
import org.eclipse.swt.layout.GridData;
76
import org.eclipse.swt.layout.GridLayout;
77
import org.eclipse.swt.widgets.Composite;
78
import org.eclipse.swt.widgets.Display;
79
import org.eclipse.swt.widgets.Menu;
80

    
81
import ca.odell.glazedlists.SortedList;
82
import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
83
import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
84
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
85
import eu.etaxonomy.cdm.model.common.CdmBase;
86
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
87
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
88
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
89
import eu.etaxonomy.cdm.model.taxon.Taxon;
90
import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
91
import eu.etaxonomy.taxeditor.bulkeditor.BulkEditorQuery;
92
import eu.etaxonomy.taxeditor.bulkeditor.input.AbstractBulkEditorInput;
93
import eu.etaxonomy.taxeditor.bulkeditor.input.sortprovider.TitleCacheComparator;
94
import eu.etaxonomy.taxeditor.editor.ITaxonEditor;
95
import eu.etaxonomy.taxeditor.event.WorkbenchEventConstants;
96
import eu.etaxonomy.taxeditor.l10n.Messages;
97
import eu.etaxonomy.taxeditor.model.IDerivedUnitFacadePart;
98
import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
99
import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
100
import eu.etaxonomy.taxeditor.model.IPartContentHasFactualData;
101
import eu.etaxonomy.taxeditor.model.IPartContentHasMedia;
102
import eu.etaxonomy.taxeditor.model.IPartContentHasSupplementalData;
103
import eu.etaxonomy.taxeditor.operation.IPostOperationEnabled;
104
import eu.etaxonomy.taxeditor.workbench.part.IE4SavablePart;
105

    
106
/**
107
 *
108
 * @author pplitzner
109
 * @since Sep 8, 2017
110
 *
111
 */
112
public class BulkEditorE4 implements IPartContentHasDetails, IConversationEnabled, IPostOperationEnabled,
113
        IDirtyMarkable, IDerivedUnitFacadePart, IPartContentHasFactualData,
114
        IPartContentHasSupplementalData, IPartContentHasMedia, IE4SavablePart, ITaxonEditor {
115

    
116
    public static final String TYPE_PROPERTY = "Type";
117

    
118
    @Inject
119
	private MDirtyable dirty;
120

    
121
    private AbstractBulkEditorInput input;
122

    
123
    private Composite topComposite;
124

    
125
    private ConversationHolder conversation;
126

    
127
    @Inject
128
    private ESelectionService selService;
129

    
130
    @Inject
131
    private IEventBroker eventBroker;
132

    
133
    private ISelectionChangedListener selectionChangedListener;
134

    
135
    @Inject
136
    private MPart thisPart;
137

    
138
    private BulkEditorQuery lastQuery = null;
139

    
140
    private Composite bottomComposite;
141

    
142
    @Inject
143
    private EMenuService menuService;
144

    
145
    private NatTable natTable;
146

    
147
    private DefaultBodyLayerStack bodyLayer;
148

    
149
    private ListDataProvider<CdmBase> bodyDataProvider;
150

    
151
    @Inject
152
    public BulkEditorE4() {
153
	}
154

    
155
	@SuppressWarnings("unused")
156
    public void init(AbstractBulkEditorInput<?> input){
157
	    this.input = input;
158
	    this.conversation = input.getConversation();
159

    
160
	    new BulkEditorSearchE4(this, topComposite, SWT.NONE);
161
	    //layout needed because the search bar is added after @PostConstuct method
162
	    topComposite.getParent().layout();
163

    
164
	    thisPart.setLabel(input.getEditorName());
165

    
166
        if(input.getEntityUuid()!=null){
167
            performSearch(new BulkEditorQuery(input.getEntityUuid().toString()));
168
        }
169

    
170
        createTable();
171

    
172
        configureTable();
173

    
174
        styleTable();
175

    
176
        GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
177

    
178
        //propagate selection
179
        selectionChangedListener = (event -> selService.setSelection(getSelection()));
180
        RowSelectionProvider<CdmBase> selectionProvider = new RowSelectionProvider<CdmBase>(bodyLayer.getSelectionLayer(), bodyDataProvider, true);
181
        selectionProvider.addSelectionChangedListener(selectionChangedListener);
182
	}
183

    
184
	private void createTable(){
185
	    ConfigRegistry configRegistry = new ConfigRegistry();
186
	    //property map
187
        Map<String, String> propertyToLabels = new HashMap<>();
188
        propertyToLabels.put(getEditorInput().getName(), getEditorInput().getName());
189
        propertyToLabels.put(TYPE_PROPERTY, TYPE_PROPERTY);
190
        String[] propertyNames = new String[] { input.getName(), TYPE_PROPERTY };
191
        //sorted list
192
        SortedList<CdmBase> sortedList = new SortedList<>(input.getModel(), new TitleCacheComparator());
193
        //data provider
194
        BulkEditorPropertyAccessor columnPropertyAccessor = new BulkEditorPropertyAccessor(input);
195
        bodyDataProvider = new ListDataProvider<CdmBase>(sortedList,
196
                columnPropertyAccessor);
197
        DefaultColumnHeaderDataProvider colHeaderDataProvider = new DefaultColumnHeaderDataProvider(
198
                propertyNames, propertyToLabels);
199
        DefaultRowHeaderDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(
200
                bodyDataProvider);
201
        //body
202
        DataLayer dataLayer = new DataLayer(bodyDataProvider);
203
        dataLayer.registerCommandHandler(new VisualRefreshCommandHandler());
204
        GlazedListsEventLayer<CdmBase> eventLayer = new GlazedListsEventLayer<>(dataLayer, input.getModel());
205
        bodyLayer = new DefaultBodyLayerStack(eventLayer);
206
        //column
207
        DataLayer columnHeaderDataLayer = new DataLayer(colHeaderDataProvider);
208
        ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(
209
                columnHeaderDataLayer,
210
                bodyLayer, bodyLayer.getSelectionLayer());
211
        // add the SortHeaderLayer to the column header layer stack
212
        // as we use GlazedLists, we use the GlazedListsSortModel which
213
        // delegates the sorting to the SortedList
214
        final SortHeaderLayer<SpecimenDescription> sortHeaderLayer = new SortHeaderLayer<>(
215
                columnHeaderLayer,
216
                new GlazedListsSortModel<>(
217
                        sortedList,
218
                        columnPropertyAccessor,
219
                        configRegistry,
220
                        columnHeaderDataLayer));
221
        //row
222
        RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(new DataLayer(rowHeaderDataProvider, 50, 20),
223
                bodyLayer, bodyLayer.getSelectionLayer());
224
        //corner
225
        DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(
226
                colHeaderDataProvider, rowHeaderDataProvider);
227
        CornerLayer cornerLayer = new CornerLayer(new DataLayer(
228
                cornerDataProvider), rowHeaderLayer, sortHeaderLayer);
229
        //grid
230
        GridLayer gridLayer = new GridLayer(bodyLayer, sortHeaderLayer,
231
                rowHeaderLayer, cornerLayer);
232

    
233
        dataLayer.setColumnPercentageSizing(true);
234

    
235
        natTable = new NatTable(bottomComposite, gridLayer, false);
236
        natTable.setConfigRegistry(configRegistry);
237

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

    
247
	private void configureTable(){
248
        //+++CONTEXT MENU+++
249
        menuService.registerContextMenu(natTable, "eu.etaxonomy.taxeditor.bulkeditor.popupmenu.bulkeditor"); //$NON-NLS-1$
250
        // get the menu registered by EMenuService
251
        final Menu e4Menu = natTable.getMenu();
252
        // remove the menu reference from NatTable instance
253
        natTable.setMenu(null);
254
        natTable.addConfiguration(
255
                new AbstractUiBindingConfiguration() {
256
            @Override
257
            public void configureUiBindings(
258
                    UiBindingRegistry uiBindingRegistry) {
259
                // add e4 menu to NatTable
260
                new PopupMenuBuilder(natTable, e4Menu)
261
                    .build();
262

    
263
                // register the UI binding for header, corner and body region
264
                uiBindingRegistry.registerMouseDownBinding(
265
                        new MouseEventMatcher(
266
                                SWT.NONE,
267
                                GridRegion.BODY,
268
                                MouseEventMatcher.RIGHT_BUTTON),
269
                        new PopupMenuAction(e4Menu));
270
                uiBindingRegistry.registerMouseDownBinding(
271
                        new MouseEventMatcher(
272
                                SWT.NONE,
273
                                GridRegion.COLUMN_HEADER,
274
                                MouseEventMatcher.RIGHT_BUTTON),
275
                        new PopupMenuAction(e4Menu));
276
                uiBindingRegistry.registerMouseDownBinding(
277
                        new MouseEventMatcher(
278
                                SWT.NONE,
279
                                GridRegion.CORNER,
280
                                MouseEventMatcher.RIGHT_BUTTON),
281
                        new PopupMenuAction(e4Menu));
282
            }
283
        });
284

    
285
        //make cells editable to allow selecting the text
286
        natTable.addConfiguration(new AbstractRegistryConfiguration() {
287
            @Override
288
            public void configureRegistry(IConfigRegistry configRegistry) {
289
              //make cell editable
290
                configRegistry.registerConfigAttribute(
291
                        EditConfigAttributes.CELL_EDITABLE_RULE,
292
                        IEditableRule.ALWAYS_EDITABLE,
293
                        DisplayMode.EDIT);
294
            }
295
        });
296

    
297
        //enable sorting
298
        natTable.addConfiguration(new SingleClickSortConfiguration());
299
        //add default configuration because autoconfigure is set to false in constructor
300
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
301
        natTable.configure();
302
	}
303

    
304
	private void styleTable(){
305
	    natTable.setTheme(new ModernNatTableThemeConfiguration());
306
	}
307

    
308
	/** {@inheritDoc} */
309
	@PostConstruct
310
	public void createPartControl(Composite parent) {
311
		parent.setLayout(new GridLayout());
312

    
313
		topComposite = new Composite(parent, SWT.NONE);
314
		topComposite.setLayout(new GridLayout());
315

    
316
		GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
317
		topComposite.setLayoutData(gridData);
318

    
319
		bottomComposite = new Composite(parent, SWT.NONE);
320
		bottomComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
321
		bottomComposite.setLayout(new GridLayout());
322
	}
323

    
324
	@Override
325
	@Persist
326
	public void save(IProgressMonitor monitor) {
327
	    save(monitor, true);
328
	}
329

    
330
	@Focus
331
	public void setFocus() {
332
        //make sure to bind again if maybe in another view the conversation was unbound
333
        if(conversation!=null && !conversation.isBound()){
334
            conversation.bind();
335
        }
336
        if(input!=null && input.getCdmEntitySession()!= null) {
337
            input.getCdmEntitySession().bind();
338
        }
339

    
340
	    //make sure to bind again if maybe in another view the conversation was unbound
341
	    eventBroker.post(WorkbenchEventConstants.CURRENT_ACTIVE_EDITOR, this);
342
	}
343

    
344
	@PreDestroy
345
	public void dispose() {
346
	    if(conversation!=null){
347
	        conversation.unregisterForDataStoreChanges(this);
348
	        conversation.close();
349
	    }
350
	    if(input!=null){
351
	        input.dispose();
352
	    }
353
	    dirty.setDirty(false);
354
	}
355

    
356
	public void save(IProgressMonitor monitor, boolean resetMerge) {
357
	    if (!input.getCdmEntitySession().isActive()){
358
            input.getCdmEntitySession().bind();
359
        }
360
	    input.saveModel(resetMerge);
361

    
362
	    IStructuredSelection selection = getSelection();
363

    
364
        dirty.setDirty(false);
365
        input.dispose();
366
        input.bind();
367
        conversation.commit(true);
368

    
369
        if (lastQuery != null){
370
            performSearch(lastQuery);
371
            setSelection(selection);
372
        }
373
	}
374

    
375
	/** {@inheritDoc} */
376
    public void performSearch(BulkEditorQuery query) {
377
        if (query != null) {
378
            // TODO check if dirty, prompt save
379
            if (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
                    save(new NullProgressMonitor());
385
                } else if (result == 2){
386
                    return;
387
                }
388
            }
389
            dirty.setDirty(false);
390
            input.performSearch(query);
391
            lastQuery = query;
392
        }
393
    }
394

    
395

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

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

    
412
    public void setSelection(IStructuredSelection selection){
413
        Object[] objects = selection.toArray();
414
        for (Object object : objects) {
415
            if(object instanceof CdmBase){
416
                natTable.doCommand(new SelectRowsCommand(bodyLayer, 0, bodyDataProvider.indexOfRowObject((CdmBase) object), false, false));
417
            }
418
        }
419
    }
420

    
421
    public void setDirty(){
422
        dirty.setDirty(true);
423
    }
424

    
425
    public boolean isDirty() {
426
        return dirty.isDirty();
427
    }
428

    
429
    public AbstractBulkEditorInput getEditorInput() {
430
        return input;
431
    }
432

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

    
452
    @Override
453
    public void update(CdmDataChangeMap arg0) {
454
    }
455

    
456
    @Override
457
    public boolean canAttachMedia() {
458
        return true;
459
    }
460

    
461
    @Override
462
    public void changed(Object element) {
463
        if(element instanceof DerivedUnitFacade){
464
            DerivedUnit derivedUnit = ((DerivedUnitFacade) element).innerDerivedUnit();
465
            if(derivedUnit!=null){
466
                getEditorInput().addSaveCandidate(derivedUnit);
467
            }
468
            FieldUnit fieldUnit = ((DerivedUnitFacade) element).innerFieldUnit();
469
            if(fieldUnit!=null){
470
                getEditorInput().addSaveCandidate(fieldUnit);
471
            }
472
        }
473
        else if (element instanceof CdmBase) {
474
            getEditorInput().addSaveCandidate((CdmBase)element);
475
            input.replaceInModel((CdmBase) element);
476
        }
477
        dirty.setDirty(true);
478
        setSelection(getSelection());
479
    }
480

    
481
    @Override
482
    public void forceDirty() {
483
        dirty.setDirty(true);
484
    }
485

    
486
    @Override
487
    public boolean postOperation(CdmBase objectAffectedByOperation) {
488
        return false;
489
    }
490

    
491
    @Override
492
    public boolean onComplete() {
493
        return false;
494
    }
495

    
496
    @Override
497
    public ConversationHolder getConversationHolder() {
498
        return conversation;
499
    }
500

    
501
    public BulkEditorQuery getLastQuery() {
502
       return lastQuery;
503
    }
504

    
505
    public void setLastQuery(BulkEditorQuery lastQuery) {
506
       this.lastQuery = lastQuery;
507

    
508
    }
509

    
510
    @Override
511
    public Taxon getTaxon() {
512
        IStructuredSelection selection = getSelection();
513
        if(selection.size()==1) {
514
            Object object = selection.iterator().next();
515
            if(object instanceof Taxon){
516
                return (Taxon) object;
517
            }
518
        }
519
        return null;
520
    }
521

    
522
}
(1-1/5)