Project

General

Profile

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

    
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.AbstractUiBindingConfiguration;
40
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
41
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
42
import org.eclipse.nebula.widgets.nattable.data.IRowIdAccessor;
43
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
44
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
45
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
46
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
47
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
48
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
49
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
50
import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
51
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
52
import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
53
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
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.selection.command.SelectRowsCommand;
59
import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer;
60
import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;
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.MouseEventMatcher;
64
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuAction;
65
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
66
import org.eclipse.swt.SWT;
67
import org.eclipse.swt.dnd.Clipboard;
68
import org.eclipse.swt.dnd.TextTransfer;
69
import org.eclipse.swt.dnd.Transfer;
70
import org.eclipse.swt.layout.GridData;
71
import org.eclipse.swt.layout.GridLayout;
72
import org.eclipse.swt.widgets.Composite;
73
import org.eclipse.swt.widgets.Display;
74
import org.eclipse.swt.widgets.Menu;
75

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

    
101
/**
102
 *
103
 * @author pplitzner
104
 * @since Sep 8, 2017
105
 *
106
 */
107
public class BulkEditorE4 implements IPartContentHasDetails, IConversationEnabled, IPostOperationEnabled,
108
        IDirtyMarkable, IDerivedUnitFacadePart, IPartContentHasFactualData,
109
        IPartContentHasSupplementalData, IPartContentHasMedia, IE4SavablePart, ITaxonEditor {
110

    
111
    public static final String TYPE_PROPERTY = "Type";
112

    
113
    @Inject
114
	private MDirtyable dirty;
115

    
116
    private AbstractBulkEditorInput input;
117

    
118
    private Composite topComposite;
119

    
120
    private ConversationHolder conversation;
121

    
122
    @Inject
123
    private ESelectionService selService;
124

    
125
    @Inject
126
    private IEventBroker eventBroker;
127

    
128
    private ISelectionChangedListener selectionChangedListener;
129

    
130
    @Inject
131
    private MPart thisPart;
132

    
133
    private BulkEditorQuery lastQuery = null;
134

    
135
    private Composite bottomComposite;
136

    
137
    @Inject
138
    private EMenuService menuService;
139

    
140
    private NatTable natTable;
141

    
142
    private DefaultBodyLayerStack bodyLayer;
143

    
144
    private ListDataProvider<CdmBase> bodyDataProvider;
145

    
146
    @Inject
147
    public BulkEditorE4() {
148
	}
149

    
150
	@SuppressWarnings("unused")
151
    public void init(AbstractBulkEditorInput<?> input){
152
	    this.input = input;
153
	    this.conversation = input.getConversation();
154

    
155
	    new BulkEditorSearchE4(this, topComposite, SWT.NONE);
156
	    //layout needed because the search bar is added after @PostConstuct method
157
	    topComposite.getParent().layout();
158

    
159
	    thisPart.setLabel(input.getEditorName());
160

    
161
        if(input.getEntityUuid()!=null){
162
            performSearch(new BulkEditorQuery(input.getEntityUuid().toString()));
163
        }
164

    
165
        createTable();
166

    
167
        configureTable();
168

    
169
        styleTable();
170

    
171
        GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
172

    
173
        //propagate selection
174
        selectionChangedListener = (event -> selService.setSelection(getSelection()));
175
        RowSelectionProvider<CdmBase> selectionProvider = new RowSelectionProvider<CdmBase>(bodyLayer.getSelectionLayer(), bodyDataProvider, true);
176
        selectionProvider.addSelectionChangedListener(selectionChangedListener);
177
	}
178

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

    
228
        dataLayer.setColumnPercentageSizing(true);
229

    
230
        natTable = new NatTable(bottomComposite, gridLayer, false);
231
        natTable.setConfigRegistry(configRegistry);
232

    
233
        //full row selection
234
        bodyLayer.getSelectionLayer().setSelectionModel(new RowSelectionModel<CdmBase>(bodyLayer.getSelectionLayer(), bodyDataProvider, new IRowIdAccessor<CdmBase>() {
235
            @Override
236
            public Serializable getRowId(CdmBase rowObject) {
237
                return input.getModel().indexOf(rowObject);
238
            }
239
        }));
240
	}
241

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

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

    
280
        //enable sorting
281
        natTable.addConfiguration(new SingleClickSortConfiguration());
282
        //add default configuration because autoconfigure is set to false in constructor
283
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
284
        natTable.configure();
285
	}
286

    
287
	private void styleTable(){
288
	    natTable.setTheme(new ModernNatTableThemeConfiguration());
289
	}
290

    
291
	/** {@inheritDoc} */
292
	@PostConstruct
293
	public void createPartControl(Composite parent) {
294
		parent.setLayout(new GridLayout());
295

    
296
		topComposite = new Composite(parent, SWT.NONE);
297
		topComposite.setLayout(new GridLayout());
298

    
299
		GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
300
		topComposite.setLayoutData(gridData);
301

    
302
		bottomComposite = new Composite(parent, SWT.NONE);
303
		bottomComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
304
		bottomComposite.setLayout(new GridLayout());
305
	}
306

    
307
	@Override
308
	@Persist
309
	public void save(IProgressMonitor monitor) {
310
	    save(monitor, true);
311
	}
312

    
313
	@Focus
314
	public void setFocus() {
315
        //make sure to bind again if maybe in another view the conversation was unbound
316
        if(conversation!=null && !conversation.isBound()){
317
            conversation.bind();
318
        }
319
        if(input!=null && input.getCdmEntitySession()!= null) {
320
            input.getCdmEntitySession().bind();
321
        }
322

    
323
	    //make sure to bind again if maybe in another view the conversation was unbound
324
	    eventBroker.post(WorkbenchEventConstants.CURRENT_ACTIVE_EDITOR, this);
325
	}
326

    
327
	@PreDestroy
328
	public void dispose() {
329
	    if(conversation!=null){
330
	        conversation.unregisterForDataStoreChanges(this);
331
	        conversation.close();
332
	    }
333
	    if(input!=null){
334
	        input.dispose();
335
	    }
336
	    dirty.setDirty(false);
337
	}
338

    
339
	public void save(IProgressMonitor monitor, boolean resetMerge) {
340
	    if (!input.getCdmEntitySession().isActive()){
341
            input.getCdmEntitySession().bind();
342
        }
343
	    input.saveModel(resetMerge);
344

    
345
	    IStructuredSelection selection = getSelection();
346

    
347
        dirty.setDirty(false);
348
        input.dispose();
349
        input.bind();
350
        conversation.commit(true);
351

    
352
        if (lastQuery != null){
353
            performSearch(lastQuery);
354
            setSelection(selection);
355
        }
356
	}
357

    
358
	/** {@inheritDoc} */
359
    public void performSearch(BulkEditorQuery query) {
360
        if (query != null) {
361
            // TODO check if dirty, prompt save
362
            if (isDirty()) {
363
                String[] labels = {Messages.BulkEditorE4_SAVE_AND_SEARCH, Messages.BulkEditorE4_DONT_SAVE,Messages.BulkEditorE4_CANCEL};
364
                MessageDialog dialog =new MessageDialog(topComposite.getShell(), Messages.BulkEditorE4_SAVE_CHANGES_TITLE, null, Messages.BulkEditorE4_SAVE_CHANGES_MESSAGE, MessageDialog.QUESTION,labels, 0);
365
                int result = dialog.open();
366
                if (result == 0) {
367
                    save(new NullProgressMonitor());
368
                } else if (result == 2){
369
                    return;
370
                }
371
            }
372
            dirty.setDirty(false);
373
            input.performSearch(query);
374
            lastQuery = query;
375
        }
376
    }
377

    
378

    
379
    public void refresh(){
380
        natTable.doCommand(new VisualRefreshCommand());
381
    }
382

    
383
    public IStructuredSelection getSelection(){
384
        List<CdmBase> selection = new ArrayList<>();
385
        int[] fullySelectedRowPositions = bodyLayer.getSelectionLayer().getFullySelectedRowPositions();
386
        for (int i : fullySelectedRowPositions) {
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
            if(object instanceof CdmBase){
399
                natTable.doCommand(new SelectRowsCommand(bodyLayer, 0, bodyDataProvider.indexOfRowObject((CdmBase) object), false, false));
400
            }
401
        }
402
    }
403

    
404
    public void setDirty(){
405
        dirty.setDirty(true);
406
    }
407

    
408
    public boolean isDirty() {
409
        return dirty.isDirty();
410
    }
411

    
412
    public AbstractBulkEditorInput getEditorInput() {
413
        return input;
414
    }
415

    
416
    public void copyDataToClipboard() {
417
        String textData = "";
418
        IStructuredSelection selection = getSelection();
419
        Object[] objects = selection.toArray();
420
        for (Object object : objects) {
421
            if(object instanceof CdmBase){
422
                textData += getEditorInput().getText((CdmBase)object);
423
            }
424
        }
425
        final TextTransfer textTransfer = TextTransfer.getInstance();
426
        final Clipboard clipboard = new Clipboard(Display.getDefault());
427
        try {
428
            clipboard.setContents(new Object[] { textData.toString() },
429
                    new Transfer[] { textTransfer });
430
        } finally {
431
            clipboard.dispose();
432
        }
433
    }
434

    
435
    @Override
436
    public void update(CdmDataChangeMap arg0) {
437
    }
438

    
439
    @Override
440
    public boolean canAttachMedia() {
441
        return true;
442
    }
443

    
444
    @Override
445
    public void changed(Object element) {
446
        if(element instanceof DerivedUnitFacade){
447
            DerivedUnit derivedUnit = ((DerivedUnitFacade) element).innerDerivedUnit();
448
            if(derivedUnit!=null){
449
                getEditorInput().addSaveCandidate(derivedUnit);
450
            }
451
            FieldUnit fieldUnit = ((DerivedUnitFacade) element).innerFieldUnit();
452
            if(fieldUnit!=null){
453
                getEditorInput().addSaveCandidate(fieldUnit);
454
            }
455
        }
456
        else if (element instanceof CdmBase) {
457
            getEditorInput().addSaveCandidate((CdmBase)element);
458
            input.replaceInModel((CdmBase) element);
459
        }
460
        dirty.setDirty(true);
461
        setSelection(getSelection());
462
    }
463

    
464
    @Override
465
    public void forceDirty() {
466
        dirty.setDirty(true);
467
    }
468

    
469
    @Override
470
    public boolean postOperation(CdmBase objectAffectedByOperation) {
471
        return false;
472
    }
473

    
474
    @Override
475
    public boolean onComplete() {
476
        return false;
477
    }
478

    
479
    @Override
480
    public ConversationHolder getConversationHolder() {
481
        return conversation;
482
    }
483

    
484
    public BulkEditorQuery getLastQuery() {
485
       return lastQuery;
486
    }
487

    
488
    public void setLastQuery(BulkEditorQuery lastQuery) {
489
       this.lastQuery = lastQuery;
490

    
491
    }
492

    
493
    @Override
494
    public Taxon getTaxon() {
495
        IStructuredSelection selection = getSelection();
496
        if(selection.size()==1) {
497
            Object object = selection.iterator().next();
498
            if(object instanceof Taxon){
499
                return (Taxon) object;
500
            }
501
        }
502
        return null;
503
    }
504

    
505
}
(1-1/5)