Project

General

Profile

Download (21.5 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.contexts.IEclipseContext;
25
import org.eclipse.e4.core.di.annotations.Optional;
26
import org.eclipse.e4.core.services.events.IEventBroker;
27
import org.eclipse.e4.ui.di.Focus;
28
import org.eclipse.e4.ui.di.Persist;
29
import org.eclipse.e4.ui.di.UIEventTopic;
30
import org.eclipse.e4.ui.model.application.ui.MDirtyable;
31
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
32
import org.eclipse.e4.ui.services.EMenuService;
33
import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
34
import org.eclipse.jface.dialogs.MessageDialog;
35
import org.eclipse.jface.layout.GridDataFactory;
36
import org.eclipse.jface.viewers.ISelectionChangedListener;
37
import org.eclipse.jface.viewers.IStructuredSelection;
38
import org.eclipse.jface.viewers.StructuredSelection;
39
import org.eclipse.nebula.widgets.nattable.NatTable;
40
import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommand;
41
import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommandHandler;
42
import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
43
import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration;
44
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
45
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
46
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
47
import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
48
import org.eclipse.nebula.widgets.nattable.data.IRowIdAccessor;
49
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
50
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
51
import org.eclipse.nebula.widgets.nattable.edit.action.MouseEditAction;
52
import org.eclipse.nebula.widgets.nattable.edit.config.DefaultEditConfiguration;
53
import org.eclipse.nebula.widgets.nattable.edit.editor.TextCellEditor;
54
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
55
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
56
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
57
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
58
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
59
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
60
import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
61
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
62
import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
63
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
64
import org.eclipse.nebula.widgets.nattable.layer.AbstractLayer;
65
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
66
import org.eclipse.nebula.widgets.nattable.layer.stack.DefaultBodyLayerStack;
67
import org.eclipse.nebula.widgets.nattable.selection.RowSelectionModel;
68
import org.eclipse.nebula.widgets.nattable.selection.RowSelectionProvider;
69
import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer;
70
import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;
71
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
72
import org.eclipse.nebula.widgets.nattable.style.theme.ModernNatTableThemeConfiguration;
73
import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
74
import org.eclipse.nebula.widgets.nattable.ui.matcher.CellEditorMouseEventMatcher;
75
import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;
76
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuAction;
77
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
78
import org.eclipse.swt.SWT;
79
import org.eclipse.swt.dnd.Clipboard;
80
import org.eclipse.swt.dnd.TextTransfer;
81
import org.eclipse.swt.dnd.Transfer;
82
import org.eclipse.swt.layout.GridData;
83
import org.eclipse.swt.layout.GridLayout;
84
import org.eclipse.swt.widgets.Composite;
85
import org.eclipse.swt.widgets.Display;
86
import org.eclipse.swt.widgets.Menu;
87

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

    
112
/**
113
 *
114
 * @author pplitzner
115
 * @since Sep 8, 2017
116
 *
117
 */
118
public class BulkEditorE4 implements IPartContentHasDetails, IConversationEnabled, IPostOperationEnabled,
119
        IDirtyMarkable, IDerivedUnitFacadePart, IPartContentHasFactualData,
120
        IPartContentHasSupplementalData, IPartContentHasMedia, IE4SavablePart, ITaxonEditor {
121

    
122
    public static final String TARGET_LABEL = "target";
123
    public static final String CANDIDATE_LABEL = "candidate";
124

    
125
    public static final String TYPE_PROPERTY = Messages.BulkEditorE4_TYPE;
126

    
127
    @Inject
128
	private MDirtyable dirty;
129

    
130
    private AbstractBulkEditorInput input;
131

    
132
    private Composite topComposite;
133

    
134
    private ConversationHolder conversation;
135

    
136
    @Inject
137
    private ESelectionService selService;
138

    
139
    @Inject
140
    private IEventBroker eventBroker;
141

    
142
    private ISelectionChangedListener selectionChangedListener;
143

    
144
    @Inject
145
    IEclipseContext context;
146

    
147
    @Inject
148
    private MPart thisPart;
149

    
150
    private BulkEditorQuery lastQuery = null;
151

    
152
    private Composite bottomComposite;
153

    
154
    @Inject
155
    private EMenuService menuService;
156

    
157
    private NatTable natTable;
158

    
159
    private DefaultBodyLayerStack bodyLayer;
160

    
161
    private ListDataProvider<CdmBase> bodyDataProvider;
162

    
163
    @Inject
164
    public BulkEditorE4() {
165
	}
166

    
167
	@SuppressWarnings("unused")
168
    public void init(AbstractBulkEditorInput<?> input){
169
	    this.input = input;
170
	    this.conversation = input.getConversation();
171

    
172
	    new BulkEditorSearchE4(this, topComposite, SWT.NONE);
173
	    //layout needed because the search bar is added after @PostConstuct method
174
	    topComposite.getParent().layout();
175

    
176
	    thisPart.setLabel(input.getEditorName());
177

    
178
        if(input.getEntityUuid()!=null){
179
            performSearch(new BulkEditorQuery(input.getEntityUuid().toString()));
180
        }
181

    
182
        createTable();
183

    
184
        configureTable();
185

    
186
        styleTable();
187

    
188
        GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
189

    
190
        //propagate selection
191
        selectionChangedListener = (event -> selService.setSelection(getSelection()));
192
        RowSelectionProvider<CdmBase> selectionProvider = new RowSelectionProvider<CdmBase>(bodyLayer.getSelectionLayer(), bodyDataProvider, true);
193
        selectionProvider.addSelectionChangedListener(selectionChangedListener);
194

    
195
        bottomComposite.layout();
196
	}
197

    
198
	private void createTable(){
199
	    ConfigRegistry configRegistry = new ConfigRegistry();
200
	    //property map
201
        Map<String, String> propertyToLabels = new HashMap<>();
202
        propertyToLabels.put(getEditorInput().getName(), getEditorInput().getName());
203
        propertyToLabels.put(TYPE_PROPERTY, TYPE_PROPERTY);
204
        String[] propertyNames = new String[] { input.getName(), TYPE_PROPERTY };
205
        //sorted list
206
        SortedList<CdmBase> sortedList = new SortedList<>(input.getModel(), input.getTitleComparator());
207
        //data provider
208
        BulkEditorPropertyAccessor columnPropertyAccessor = new BulkEditorPropertyAccessor(input);
209
        bodyDataProvider = new ListDataProvider<CdmBase>(sortedList,
210
                columnPropertyAccessor);
211
        DefaultColumnHeaderDataProvider colHeaderDataProvider = new DefaultColumnHeaderDataProvider(
212
                propertyNames, propertyToLabels);
213
        DefaultRowHeaderDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(
214
                bodyDataProvider);
215
        //body
216
        DataLayer dataLayer = new DataLayer(bodyDataProvider);
217
        dataLayer.registerCommandHandler(new VisualRefreshCommandHandler());
218
        GlazedListsEventLayer<CdmBase> eventLayer = new GlazedListsEventLayer<>(dataLayer, input.getModel());
219
        bodyLayer = new DefaultBodyLayerStack(eventLayer);
220

    
221
        dataLayer.setColumnPercentageSizing(true);
222
        dataLayer.setColumnWidthPercentageByPosition(0, 80);
223
        dataLayer.setColumnWidthPercentageByPosition(1, 20);
224

    
225
        //column
226
        DataLayer columnHeaderDataLayer = new DataLayer(colHeaderDataProvider);
227
        ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(
228
                columnHeaderDataLayer,
229
                bodyLayer, bodyLayer.getSelectionLayer());
230
        // add the SortHeaderLayer to the column header layer stack
231
        // as we use GlazedLists, we use the GlazedListsSortModel which
232
        // delegates the sorting to the SortedList
233
        final SortHeaderLayer<SpecimenDescription> sortHeaderLayer = new SortHeaderLayer<>(
234
                columnHeaderLayer,
235
                new GlazedListsSortModel<>(
236
                        sortedList,
237
                        columnPropertyAccessor,
238
                        configRegistry,
239
                        columnHeaderDataLayer));
240
        //row
241
        DataLayer rowHeaderDataLayer = new DataLayer(rowHeaderDataProvider);
242
        RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer,
243
                bodyLayer, bodyLayer.getSelectionLayer());
244

    
245
        //corner
246
        DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(
247
                colHeaderDataProvider, rowHeaderDataProvider);
248
        DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
249
        CornerLayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, sortHeaderLayer);
250
        //grid
251
        GridLayer gridLayer = new GridLayer(bodyLayer, sortHeaderLayer,
252
                rowHeaderLayer, cornerLayer, false);//set autoconfigure to false to get rid of the single click editing
253
        natTable = new NatTable(bottomComposite, gridLayer, false);
254
        natTable.setConfigRegistry(configRegistry);
255

    
256
        //full row selection
257
        bodyLayer.getSelectionLayer().setSelectionModel(new RowSelectionModel<CdmBase>(bodyLayer.getSelectionLayer(), bodyDataProvider, new IRowIdAccessor<CdmBase>() {
258
            @Override
259
            public Serializable getRowId(CdmBase rowObject) {
260
                return input.getModel().indexOf(rowObject);
261
            }
262
        }));
263

    
264
        //add label to deduplication rows
265
        dataLayer.setConfigLabelAccumulator(new BulkEditorConfigLabelAccumulator(bodyDataProvider, input));
266

    
267
        //add tooltip to table
268
        new BulkEditorTooltip(natTable);
269
	}
270

    
271
	private void configureTable(){
272

    
273
	    //this configuration enables cell to be editable in general
274
	    //necessary because we disabled this beforehand by setting autoconfigure=false
275
	    //in the GridLayer constructor
276
	    ((AbstractLayer) natTable.getLayer()).addConfiguration(new DefaultEditConfiguration());
277

    
278
        natTable.addConfiguration(new AbstractUiBindingConfiguration() {
279
            @Override
280
            public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
281
                uiBindingRegistry.registerFirstDoubleClickBinding(new CellEditorMouseEventMatcher(GridRegion.BODY),
282
                        new MouseEditAction());
283
            }
284
        });
285

    
286
        //make cells editable to allow selecting the text
287
        natTable.addConfiguration(new AbstractRegistryConfiguration() {
288
            @Override
289
            public void configureRegistry(IConfigRegistry configRegistry) {
290
                //make cell editable
291
                configRegistry.registerConfigAttribute(
292
                        EditConfigAttributes.CELL_EDITABLE_RULE,
293
                        IEditableRule.ALWAYS_EDITABLE,
294
                        DisplayMode.EDIT);
295
                //register editor
296
                TextCellEditor editor = new TextCellEditor();
297
                editor.setEditable(false);
298
                configRegistry.registerConfigAttribute(
299
                        EditConfigAttributes.CELL_EDITOR,
300
                        editor,
301
                        DisplayMode.NORMAL);
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
                    .build();
319

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

    
330

    
331
        //enable sorting
332
        natTable.addConfiguration(new SingleClickSortConfiguration());
333

    
334
        // Custom style for deduplication labels
335
        natTable.addConfiguration(new LabelStyleConfiguration());
336

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

    
342
	private void styleTable(){
343
	    natTable.setTheme(new ModernNatTableThemeConfiguration());
344
	}
345

    
346
	/** {@inheritDoc} */
347
	@PostConstruct
348
	public void createPartControl(Composite parent) {
349
		parent.setLayout(new GridLayout());
350

    
351
		topComposite = new Composite(parent, SWT.NONE);
352
		topComposite.setLayout(new GridLayout());
353

    
354
		GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
355
		topComposite.setLayoutData(gridData);
356

    
357
		bottomComposite = new Composite(parent, SWT.NONE);
358
		bottomComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
359
		bottomComposite.setLayout(new GridLayout());
360
	}
361

    
362
	@Override
363
	@Persist
364
	public void save(IProgressMonitor monitor) {
365
	    save(monitor, true);
366
	}
367

    
368
	@Focus
369
	public void setFocus() {
370
        //make sure to bind again if maybe in another view the conversation was unbound
371
        if(conversation!=null && !conversation.isBound()){
372
            conversation.bind();
373
        }
374
        if(input!=null && input.getCdmEntitySession()!= null) {
375
            input.getCdmEntitySession().bind();
376
        }
377

    
378
	    //make sure to bind again if maybe in another view the conversation was unbound
379
	    eventBroker.post(WorkbenchEventConstants.CURRENT_ACTIVE_EDITOR, this);
380
	}
381

    
382
	@PreDestroy
383
	public void dispose() {
384
	    if(conversation!=null){
385
	        conversation.unregisterForDataStoreChanges(this);
386
	        conversation.close();
387
	    }
388
	    if(input!=null){
389
	        input.dispose();
390
	    }
391
	    dirty.setDirty(false);
392
	}
393

    
394
	public void save(IProgressMonitor monitor, boolean resetMerge) {
395
	    if (!input.getCdmEntitySession().isActive()){
396
            input.getCdmEntitySession().bind();
397
        }
398
	    input.saveModel(resetMerge);
399

    
400
	    IStructuredSelection selection = getSelection();
401

    
402
        dirty.setDirty(false);
403
        input.dispose();
404
        input.bind();
405
        conversation.commit(true);
406

    
407
        if (lastQuery != null){
408
            performSearch(lastQuery, selection);
409
        }
410
	}
411

    
412
	public void performSearch(BulkEditorQuery query) {
413
	    performSearch(query, null);
414
	}
415

    
416
	/** {@inheritDoc}
417
	 * @param selection */
418
    public void performSearch(BulkEditorQuery query, IStructuredSelection selection) {
419
        if (query != null) {
420
            // TODO check if dirty, prompt save
421
            if (isDirty()) {
422
                String[] labels = {Messages.BulkEditorE4_SAVE_AND_SEARCH, Messages.BulkEditorE4_DONT_SAVE,Messages.BulkEditorE4_CANCEL};
423
                MessageDialog dialog =new MessageDialog(topComposite.getShell(), Messages.BulkEditorE4_SAVE_CHANGES_TITLE, null, Messages.BulkEditorE4_SAVE_CHANGES_MESSAGE, MessageDialog.QUESTION,labels, 0);
424
                int result = dialog.open();
425
                if (result == 0) {
426
                    save(new NullProgressMonitor());
427
                } else if (result == 2){
428
                    return;
429
                }
430
            }
431
            dirty.setDirty(false);
432
            input.performSearch(query, selection);
433
            lastQuery = query;
434
        }
435
    }
436

    
437
    @Optional
438
    @Inject
439
    private void updateAfterSearch(@UIEventTopic(WorkbenchEventConstants.BULK_EDITOR_SEARCH_FINISHED)IStructuredSelection selection){
440
        if(selection!=null){
441
            setSelection(selection);
442
        }
443
    }
444

    
445
    public void refresh(){
446
        natTable.doCommand(new VisualRefreshCommand());
447
    }
448

    
449
    public IStructuredSelection getSelection(){
450
        List<CdmBase> selection = new ArrayList<>();
451
        int[] fullySelectedRowPositions = bodyLayer.getSelectionLayer().getFullySelectedRowPositions();
452
        for (int i : fullySelectedRowPositions) {
453
            Object rowObject = bodyDataProvider.getRowObject(i);
454
            if(rowObject instanceof CdmBase){
455
                selection.add((CdmBase) rowObject);
456
            }
457
        }
458
        return new StructuredSelection(selection);
459
    }
460

    
461
    public void setSelection(IStructuredSelection selection){
462
        Object[] objects = selection.toArray();
463
        for (Object object : objects) {
464

    
465
            if(object instanceof CdmBase &&  input.getModel().contains(object)){
466
                bodyLayer.getSelectionLayer().selectRow(0, bodyDataProvider.indexOfRowObject((CdmBase) object), false, false);
467
            }
468
        }
469
    }
470

    
471
    public void setDirty(){
472
        dirty.setDirty(true);
473
    }
474

    
475
    public boolean isDirty() {
476
        return dirty.isDirty();
477
    }
478

    
479
    public AbstractBulkEditorInput getEditorInput() {
480
        return input;
481
    }
482

    
483
    public void copyDataToClipboard() {
484
        String textData = "";
485
        IStructuredSelection selection = getSelection();
486
        Object[] objects = selection.toArray();
487
        for (Object object : objects) {
488
            if(object instanceof CdmBase){
489
                textData += getEditorInput().getText((CdmBase)object);
490
            }
491
        }
492
        final TextTransfer textTransfer = TextTransfer.getInstance();
493
        final Clipboard clipboard = new Clipboard(Display.getDefault());
494
        try {
495
            clipboard.setContents(new Object[] { textData.toString() },
496
                    new Transfer[] { textTransfer });
497
        } finally {
498
            clipboard.dispose();
499
        }
500
    }
501

    
502
    @Override
503
    public void update(CdmDataChangeMap arg0) {
504
    }
505

    
506
    @Override
507
    public boolean canAttachMedia() {
508
        return true;
509
    }
510

    
511
    @Override
512
    public void changed(Object element) {
513
        if(element instanceof DerivedUnitFacade){
514
            DerivedUnit derivedUnit = ((DerivedUnitFacade) element).innerDerivedUnit();
515
            if(derivedUnit!=null){
516
                getEditorInput().addSaveCandidate(derivedUnit);
517
            }
518
            FieldUnit fieldUnit = ((DerivedUnitFacade) element).innerFieldUnit();
519
            if(fieldUnit!=null){
520
                getEditorInput().addSaveCandidate(fieldUnit);
521
            }
522
        }
523
        else if (element instanceof CdmBase) {
524
            getEditorInput().addSaveCandidate((CdmBase)element);
525
            input.replaceInModel((CdmBase) element);
526
        }
527
        dirty.setDirty(true);
528
        setSelection(new StructuredSelection(element));
529
    }
530

    
531
    @Override
532
    public void forceDirty() {
533
        dirty.setDirty(true);
534
    }
535

    
536
    @Override
537
    public boolean postOperation(CdmBase objectAffectedByOperation) {
538
        return false;
539
    }
540

    
541
    @Override
542
    public boolean onComplete() {
543
        return false;
544
    }
545

    
546
    @Override
547
    public ConversationHolder getConversationHolder() {
548
        return conversation;
549
    }
550

    
551
    public BulkEditorQuery getLastQuery() {
552
       return lastQuery;
553
    }
554

    
555
    public void setLastQuery(BulkEditorQuery lastQuery) {
556
       this.lastQuery = lastQuery;
557

    
558
    }
559

    
560
    @Override
561
    public Taxon getTaxon() {
562
        IStructuredSelection selection = getSelection();
563
        if(selection.size()==1) {
564
            Object object = selection.iterator().next();
565
            if(object instanceof Taxon){
566
                return (Taxon) object;
567
            }
568
        }
569
        return null;
570
    }
571

    
572
}
(2-2/8)