ref #6971 Dispose input session when disposing editor
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / name / e4 / TaxonNameEditorE4.java
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.editor.name.e4;
11
12 import java.util.ArrayList;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Set;
16
17 import javax.annotation.PostConstruct;
18 import javax.annotation.PreDestroy;
19 import javax.inject.Inject;
20
21 import org.apache.commons.lang.StringUtils;
22 import org.eclipse.core.commands.operations.IUndoContext;
23 import org.eclipse.core.commands.operations.UndoContext;
24 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.core.runtime.OperationCanceledException;
26 import org.eclipse.e4.core.contexts.IEclipseContext;
27 import org.eclipse.e4.core.services.events.IEventBroker;
28 import org.eclipse.e4.ui.di.Focus;
29 import org.eclipse.e4.ui.di.Persist;
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.viewers.ISelection;
36 import org.eclipse.jface.viewers.ISelectionProvider;
37 import org.eclipse.jface.viewers.StructuredSelection;
38 import org.eclipse.swt.dnd.DND;
39 import org.eclipse.swt.dnd.DropTarget;
40 import org.eclipse.swt.dnd.Transfer;
41 import org.eclipse.swt.graphics.Color;
42 import org.eclipse.swt.widgets.Composite;
43 import org.eclipse.ui.ISelectionListener;
44 import org.eclipse.ui.IWorkbenchPart;
45 import org.eclipse.ui.IWorkbenchPartReference;
46 import org.eclipse.ui.forms.ManagedForm;
47 import org.eclipse.ui.forms.widgets.FormToolkit;
48 import org.eclipse.ui.forms.widgets.ScrolledForm;
49 import org.eclipse.ui.forms.widgets.TableWrapLayout;
50
51 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
52 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
53 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
54 import eu.etaxonomy.cdm.model.common.CdmBase;
55 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
56 import eu.etaxonomy.cdm.model.taxon.Taxon;
57 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
58 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
59 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
60 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
61 import eu.etaxonomy.taxeditor.editor.CdmDataTransfer;
62 import eu.etaxonomy.taxeditor.editor.ISecuredEditor;
63 import eu.etaxonomy.taxeditor.editor.ITaxonEditor;
64 import eu.etaxonomy.taxeditor.editor.e4.TaxonEditorInputE4;
65 import eu.etaxonomy.taxeditor.editor.l10n.Messages;
66 import eu.etaxonomy.taxeditor.editor.name.e4.container.AbstractGroupE4;
67 import eu.etaxonomy.taxeditor.editor.name.e4.container.AbstractGroupedContainerE4;
68 import eu.etaxonomy.taxeditor.editor.name.e4.container.AcceptedGroupE4;
69 import eu.etaxonomy.taxeditor.editor.name.e4.container.AcceptedNameContainerE4;
70 import eu.etaxonomy.taxeditor.editor.name.e4.container.ContainerFactoryE4;
71 import eu.etaxonomy.taxeditor.editor.name.e4.container.HomotypicalSynonymGroupE4;
72 import eu.etaxonomy.taxeditor.editor.name.e4.container.MisappliedGroupE4;
73 import eu.etaxonomy.taxeditor.editor.name.e4.dnd.NameEditorDropTargetListenerE4;
74 import eu.etaxonomy.taxeditor.event.WorkbenchEventConstants;
75 import eu.etaxonomy.taxeditor.model.AbstractUtility;
76 import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
77 import eu.etaxonomy.taxeditor.model.IPartChangeListener;
78 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
79 import eu.etaxonomy.taxeditor.model.IPartContentHasFactualData;
80 import eu.etaxonomy.taxeditor.model.IPartContentHasMedia;
81 import eu.etaxonomy.taxeditor.model.IPartContentHasSupplementalData;
82 import eu.etaxonomy.taxeditor.model.MessagingUtils;
83 import eu.etaxonomy.taxeditor.model.TaxeditorPartService;
84 import eu.etaxonomy.taxeditor.operation.IPostOperationEnabled;
85 import eu.etaxonomy.taxeditor.preference.Resources;
86 import eu.etaxonomy.taxeditor.security.RequiredPermissions;
87 import eu.etaxonomy.taxeditor.store.CdmStore;
88 import eu.etaxonomy.taxeditor.workbench.part.IE4SavablePart;
89
90 /**
91 *
92 * @author pplitzner
93 * @date Aug 24, 2017
94 *
95 */
96 public class TaxonNameEditorE4 implements IConversationEnabled, IDirtyMarkable, IPartContentHasDetails,
97 IPartContentHasSupplementalData, IPartContentHasMedia, IPartContentHasFactualData, IPartChangeListener,
98 ISelectionListener, ISecuredEditor, IPostOperationEnabled, IE4SavablePart, ITaxonEditor, IDropTargetableE4 {
99
100 private Taxon taxon;
101
102 private ManagedForm managedForm;
103 private ScrolledForm scrolledForm;
104 private Composite parent;
105 private ISelectionProvider simpleSelectionProvider;
106
107 private TaxonBase selection;
108
109 private ConversationHolder conversation;
110
111 private AcceptedGroupE4 acceptedGroup;
112 private List<HomotypicalSynonymGroupE4> heterotypicSynonymGroups = new ArrayList<>();
113 private MisappliedGroupE4 misappliedGroup;
114
115 private DropTarget target;
116
117 private TaxonBase objectAffectedByLastOperation;
118
119 @Inject
120 private EMenuService menuService;
121
122 @Inject
123 private ESelectionService selService;
124
125 @Inject
126 private IEclipseContext context;
127
128 @Inject
129 private MDirtyable dirty;
130
131 private MPart thisPart;
132
133 private TaxonEditorInputE4 input;
134
135 private UndoContext undoContext;
136
137 @Inject
138 private IEventBroker eventBroker;
139
140 @Inject
141 public TaxonNameEditorE4() {
142 undoContext = new UndoContext();
143 }
144
145
146 @PostConstruct
147 public void createPartControl(Composite parent, MPart thisPart) {
148 this.thisPart = thisPart;
149
150 createManagedForm(parent);
151
152 TaxeditorPartService.getInstance().addListener(
153 TaxeditorPartService.PART_ACTIVATED, this);
154
155 }
156
157 protected void createManagedForm(Composite composite) {
158
159 managedForm = new ManagedForm(composite) {
160
161 @Override
162 public void dirtyStateChanged() {
163 dirty.setDirty(true);
164 }
165
166 @Override
167 public boolean setInput(Object input) {
168 if (input instanceof AbstractGroupedContainerE4) {
169 TaxonBase newSelection = ((AbstractGroupedContainerE4) input).getData();
170 if(selection!=newSelection || TaxonNameEditorE4.this.isDirty()){
171 selection = newSelection;
172 selService.setSelection(new StructuredSelection(selection));
173 }
174 }else if(input == null){
175 selection = null;
176 selService.setSelection(new StructuredSelection());
177 }
178
179
180 return super.setInput(input);
181 }
182 };
183
184 scrolledForm = managedForm.getForm();
185 parent = scrolledForm.getBody();
186
187 parent.setData(taxon);
188
189 TableWrapLayout layout = new TableWrapLayout();
190 layout.leftMargin = 0;
191 layout.rightMargin = 0;
192 layout.topMargin = 0;
193 layout.bottomMargin = 0;
194
195 layout.verticalSpacing = 0;
196 layout.horizontalSpacing = 0;
197
198 parent.setLayout(layout);
199 parent.setBackground(AbstractUtility
200 .getColor(Resources.COLOR_COMPOSITE_BACKGROUND));
201 }
202
203 public void createOrUpdateNameComposites() {
204 ContainerFactoryE4.createOrUpdateAcceptedTaxonsHomotypicGroup(this);
205 ContainerFactoryE4.createOrUpdateHeterotypicSynonymyGroups(this);
206 ContainerFactoryE4.createOrUpdateMisapplicationsGroup(this);
207
208
209 // Redraw composite
210 managedForm.reflow(true);
211 managedForm.refresh();
212 }
213
214 @Override
215 public Taxon getTaxon() {
216 return HibernateProxyHelper.deproxy(taxon);
217 }
218
219 public void setDirty() {
220 managedForm.dirtyStateChanged();
221 }
222
223 @Focus
224 public void setFocus() {
225 //make sure to bind again if maybe in another view the conversation was unbound
226 if(conversation!=null && !conversation.isBound()){
227 conversation.bind();
228 }
229 if(input!=null){
230 if (getSelectedContainer() == null) {
231 throw new IllegalStateException(
232 Messages.TaxonNameEditor_THERE_SHOULD_ALWAYS_BE);
233 }
234 getSelectedContainer().setSelected();
235
236 // check permissions
237 boolean doEnable = permissionsSatisfied();
238 managedForm.getForm().setEnabled(doEnable);
239 }
240 eventBroker.post(WorkbenchEventConstants.CURRENT_ACTIVE_EDITOR, this);
241 }
242
243 @Override
244 public boolean permissionsSatisfied() {
245 TaxonNode taxonNode = input.getTaxonNode();
246 boolean doEnable = CdmStore.currentAuthentiationHasPermission(taxonNode, RequiredPermissions.TAXONNODE_EDIT);
247 return doEnable;
248 }
249
250 @Override
251 public ConversationHolder getConversationHolder() {
252 return conversation;
253 }
254
255 /** {@inheritDoc} */
256 @Override
257 public void update(CdmDataChangeMap events) {
258 // redraw();
259 }
260
261 /**
262 * Redraws this editor return true on success
263 *
264 * @return a boolean.
265 */
266 public boolean redraw() {
267 return redraw(true);
268 }
269
270 /**
271 * {@inheritDoc}
272 *
273 * Redraws the editor controls
274 */
275 public boolean redraw(boolean focus) {
276
277 createOrUpdateNameComposites();
278
279 if (focus) {
280 setFocus();
281 }
282
283 return true;
284 }
285
286 @Override
287 public boolean postOperation(CdmBase objectAffectedByOperation) {
288
289 changed(objectAffectedByOperation);
290
291 redraw(false);
292
293 if (objectAffectedByOperation instanceof TaxonBase) {
294 objectAffectedByLastOperation = (TaxonBase) objectAffectedByOperation;
295 }
296
297 return true;
298 }
299
300 public ManagedForm getManagedForm() {
301 return managedForm;
302 }
303
304
305 /**
306 * <p>
307 * checkForEmptyNames
308 * </p>
309 *
310 * @return true if there are empty names
311 */
312 public boolean checkForEmptyNames() {
313 for (AbstractGroupedContainerE4 container : getGroupedContainers()) {
314 if (container.getName() == null
315 || StringUtils.isEmpty(container.getName().getTitleCache())) {
316 return true;
317 }
318 }
319 return false;
320 }
321
322 public Set<AbstractGroupedContainerE4> getEmptyContainers() {
323 Set<AbstractGroupedContainerE4> containersWithEmptyNames = new HashSet<>();
324
325 for (AbstractGroupedContainerE4 container : getGroupedContainers()) {
326 if (container.getName() == null
327 || StringUtils.isEmpty(container.getName().getTitleCache())) {
328 containersWithEmptyNames.add(container);
329 }
330 }
331
332 return containersWithEmptyNames;
333 }
334
335 /** {@inheritDoc} */
336 @Override
337 @Persist
338 public void save(IProgressMonitor monitor) {
339
340 monitor.beginTask(Messages.TaxonNameEditor_SAVING_NAMES, getGroupedContainers().size());
341 if (!conversation.isBound()) {
342 conversation.bind();
343 }
344 monitor.worked(1);
345
346 // check for empty names
347 if (checkForEmptyNames()) {
348 MessageDialog.openWarning(AbstractUtility.getShell(), Messages.MultiPageTaxonEditor_NO_NAME_SPECIFIED,
349 Messages.MultiPageTaxonEditor_NO_NAME_SPECIFIED_MESSAGE);
350 return;
351 }
352 for (AbstractGroupedContainerE4 container : getGroupedContainers()) {
353
354 monitor.subTask(Messages.TaxonNameEditor_SAVING_COMPOSITES
355 + container.getTaxonBase().getTitleCache());
356 container.persistName();
357
358 // In case the progress monitor was canceled throw an exception.
359 if (monitor.isCanceled()) {
360 throw new OperationCanceledException();
361 }
362
363 // Otherwise declare this step as done.
364 monitor.worked(1);
365
366 }
367 input.merge();
368 // commit the conversation and start a new transaction immediately
369 conversation.commit(true);
370
371
372 dirty.setDirty(false);
373
374 // Stop the progress monitor.
375 monitor.done();
376 }
377
378 public void init(TaxonEditorInputE4 input) {
379
380 if (!(input != null)) {
381 MessagingUtils.error(this.getClass(), new Exception(Messages.TaxonNameEditor_INVALID_INPUT));
382 return;
383 }
384
385 if (input.getAdapter(Taxon.class) != null) {
386 taxon = CdmBase.deproxy(input.getAdapter(Taxon.class), Taxon.class);
387 } else {
388 MessagingUtils.error(this.getClass(), new Exception(Messages.TaxonNameEditor_INVALID_INPUT_TAXON_NULL));
389 return;
390 }
391
392 this.input = input;
393 conversation = input.getConversationHolder();
394
395 createOrUpdateNameComposites();
396
397 createDragSupport();
398
399 setPartName();
400
401 //set initial selection
402 TaxonBase initiallySelectedTaxonBase = input.getInitiallySelectedTaxonBase();
403 if(initiallySelectedTaxonBase!=null){
404 selService.setSelection(new StructuredSelection(initiallySelectedTaxonBase));
405 getContainer(initiallySelectedTaxonBase).setSelected();
406 }
407 }
408
409 private void createDragSupport() {
410 // Listen for names being dragged outside of existing homotypic groups -
411 // user wants to create a new group
412 Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
413 int operations = DND.DROP_MOVE;
414 if (target == null) {
415 target = new DropTarget(parent, operations);
416 target.setTransfer(types);
417 target.addDropListener(new NameEditorDropTargetListenerE4(this));
418 }
419 }
420
421 public AcceptedNameContainerE4 getAcceptedNameContainer() {
422 return getAcceptedGroup().getAcceptedNameContainer();
423 }
424
425 public HomotypicalSynonymGroupE4 getHomotypicalGroupContainer(
426 HomotypicalGroup homotypicalGroup) {
427 for (HomotypicalSynonymGroupE4 group : getHeterotypicSynonymGroups()) {
428 if (group.getGroup().equals(homotypicalGroup)) {
429 return group;
430 }
431 }
432
433 return null;
434 }
435
436 /**
437 * <p>
438 * getDirtyNames
439 * </p>
440 *
441 * @return a Set containing all composites that have been edited
442 */
443 public Set<AbstractGroupedContainerE4> getDirtyNames() {
444 Set<AbstractGroupedContainerE4> dirtyNames = new HashSet<>();
445
446 for (AbstractGroupedContainerE4 composite : getGroupedContainers()) {
447 if (composite.isDirty()) {
448 dirtyNames.add(composite);
449 }
450 }
451
452 return dirtyNames;
453 }
454
455 public List<AbstractGroupedContainerE4> getGroupedContainers() {
456 List<AbstractGroupedContainerE4> groupedComposites = new ArrayList<>();
457
458 for (AbstractGroupE4 group : getAllGroups()) {
459 if (group!= null){
460 groupedComposites.addAll(group.getGroupedContainers());
461 }
462 }
463
464 return groupedComposites;
465 }
466
467 public List<AbstractGroupE4> getAllGroups() {
468 List<AbstractGroupE4> allGroups = new ArrayList<>();
469
470 allGroups.add(getAcceptedGroup());
471
472 heterotypicSynonymGroups = getHeterotypicSynonymGroups();
473
474 if (heterotypicSynonymGroups != null) {
475 allGroups.addAll(heterotypicSynonymGroups);
476 }
477
478 if (misappliedGroup != null) {
479 allGroups.add(misappliedGroup);
480 }
481
482 return allGroups;
483 }
484
485 @Override
486 public IEclipseContext getContext() {
487 return context;
488 }
489
490 public boolean isDirty() {
491 return dirty.isDirty();
492 }
493
494 @PreDestroy
495 public void dispose() {
496 if(conversation!=null){
497 conversation.unregisterForDataStoreChanges(this);
498 conversation.close();
499 }
500 if(input!=null){
501 input.dispose();
502 }
503 eventBroker.post(WorkbenchEventConstants.CURRENT_ACTIVE_EDITOR, null);
504 }
505
506 /** {@inheritDoc} */
507 @Override
508 public void selectionChanged(IWorkbenchPart part, ISelection selection) {
509
510 }
511
512 public AbstractGroupedContainerE4 getSelectedContainer() {
513 return (selection != null) ? getContainer(selection)
514 : getAcceptedNameContainer();
515 }
516
517 @Override
518 public void dragEntered() {
519 // TODO change this
520 getControl().setBackground(
521 AbstractUtility.getColor(Resources.COLOR_DRAG_ENTER));
522 }
523
524 @Override
525 public void dragLeft() {
526 getControl().setBackground(
527 AbstractUtility.getColor(Resources.COLOR_COMPOSITE_BACKGROUND));
528 }
529
530
531 public void setMisapplicationsGroup(MisappliedGroupE4 misappliedGroup) {
532 this.misappliedGroup = misappliedGroup;
533 }
534
535 public FormToolkit getToolkit() {
536 return managedForm.getToolkit();
537 }
538
539 public List<HomotypicalSynonymGroupE4> getHeterotypicSynonymGroups() {
540 return heterotypicSynonymGroups;
541 }
542
543 public void addHeterotypicSynonymGroup(HomotypicalSynonymGroupE4 group) {
544 heterotypicSynonymGroups.add(group);
545 }
546
547 public AcceptedGroupE4 getAcceptedGroup() {
548 return acceptedGroup;
549 }
550
551 public void setAcceptedGroup(AcceptedGroupE4 acceptedGroup) {
552 this.acceptedGroup = acceptedGroup;
553 }
554
555 public MisappliedGroupE4 getMisappliedGroup() {
556 return misappliedGroup;
557 }
558
559 public boolean isActive() {
560 return this.equals(AbstractUtility.getActivePart());
561 }
562
563 @Override
564 public boolean onComplete() {
565 getContainer(objectAffectedByLastOperation).setSelected();
566 return true;
567 }
568
569 /** {@inheritDoc} */
570 @Override
571 public void partChanged(Integer eventType, IWorkbenchPartReference partRef) {
572 if (!partRef.getPart(false).equals(this)) {
573 // getSelectedObject().colorSelected(AbstractGroupedContainer.SELECTED_NO_FOCUS);
574 }
575 }
576
577 public void removeGroup(AbstractGroupE4 group) {
578 if (group != null) {
579 group.dispose();
580
581 //if (heterotypicSynonymGroups != null) {
582 heterotypicSynonymGroups.remove(group);
583 //}
584 }
585 }
586
587 public AbstractGroupedContainerE4 getContainer(TaxonBase taxonBase) {
588 List<AbstractGroupedContainerE4> groupedContainers = getGroupedContainers();
589 for (AbstractGroupedContainerE4 container : groupedContainers) {
590 if (container.getData().equals(taxonBase)
591 && container.getNameViewer().getTextWidget() != null) {
592 return container;
593 }
594 }
595 return getAcceptedNameContainer();
596 }
597
598 public void setOnError() {
599 Color disabledColor = AbstractUtility.getColor(Resources.COLOR_EDITOR_ERROR);
600 setEnabled(false, disabledColor);
601 }
602
603 public void setDisabled(){
604 Color disabledColor = AbstractUtility.getColor(Resources.COLOR_TEXT_DISABLED_BACKGROUND);
605 setEnabled(false, disabledColor);
606 }
607
608 protected void setEnabled(boolean enabled, Color background) {
609
610 for(AbstractGroupedContainerE4 groupedContainer : getGroupedContainers()){
611 groupedContainer.setEnabled(enabled);
612 }
613
614 // send an empty selection to the current provider - TODO only on error ???
615 if (!enabled) {
616 getManagedForm().setInput(null);
617
618 for (AbstractGroupedContainerE4 groupedContainer : getGroupedContainers()) {
619 groupedContainer.setBackground(background);
620 }
621 }
622 getControl().setBackground(background);
623 }
624
625 @Override
626 public void changed(Object element) {
627 // setDirty(true);
628 // if the attribute is null then do not set the dirty flag -> hotfix for the problem that for tasks done in service methods the changes are saved automatically
629 if (element != null){
630 dirty.setDirty(true);
631 //refresh part title
632 //TODO: refresh taxon node in taxon navigator
633 setPartName();
634 }
635
636 if (element instanceof TaxonBase) {
637 AbstractGroupedContainerE4 container = getContainer((TaxonBase) element);
638 if (container != null) {
639 container.refresh();
640 }
641 }
642 if (element instanceof TaxonRelationship) {
643 AbstractGroupedContainerE4 container = getContainer(((TaxonRelationship) element).getFromTaxon());
644 if (container != null) {
645 container.refresh();
646 }
647 }
648 }
649
650 public void setPartName(){
651 thisPart.setLabel(this.taxon.getName().getFullTitleCache());
652 }
653
654 @Override
655 public void forceDirty() {
656 setDirty();
657 }
658
659
660 public IUndoContext getUndoContext() {
661 return undoContext;
662 }
663
664 @Override
665 public Composite getControl(){
666 return managedForm.getForm().getBody();
667 }
668
669 public EMenuService getMenuService() {
670 return menuService;
671 }
672
673 public ESelectionService getSelectionService() {
674 return selService;
675 }
676
677
678 /**
679 * {@inheritDoc}
680 */
681 @Override
682 public boolean canAttachMedia() {
683 return true;
684 }
685
686 public TaxonEditorInputE4 getEditorInput() {
687 return input;
688 }
689
690 /**
691 * {@inheritDoc}
692 */
693 @Override
694 public TaxonNameEditorE4 getEditor() {
695 return this;
696 }
697
698 }