2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.taxeditor
.editor
.name
.e4
;
12 import java
.util
.ArrayList
;
13 import java
.util
.HashSet
;
14 import java
.util
.List
;
17 import javax
.annotation
.PostConstruct
;
18 import javax
.annotation
.PreDestroy
;
19 import javax
.inject
.Inject
;
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
.di
.annotations
.Optional
;
28 import org
.eclipse
.e4
.core
.services
.events
.IEventBroker
;
29 import org
.eclipse
.e4
.ui
.di
.Focus
;
30 import org
.eclipse
.e4
.ui
.di
.Persist
;
31 import org
.eclipse
.e4
.ui
.di
.UIEventTopic
;
32 import org
.eclipse
.e4
.ui
.model
.application
.ui
.MDirtyable
;
33 import org
.eclipse
.e4
.ui
.model
.application
.ui
.basic
.MPart
;
34 import org
.eclipse
.e4
.ui
.services
.EMenuService
;
35 import org
.eclipse
.e4
.ui
.workbench
.modeling
.ESelectionService
;
36 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
37 import org
.eclipse
.jface
.viewers
.ISelection
;
38 import org
.eclipse
.jface
.viewers
.ISelectionProvider
;
39 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
40 import org
.eclipse
.swt
.dnd
.DND
;
41 import org
.eclipse
.swt
.dnd
.DropTarget
;
42 import org
.eclipse
.swt
.dnd
.Transfer
;
43 import org
.eclipse
.swt
.graphics
.Color
;
44 import org
.eclipse
.swt
.widgets
.Composite
;
45 import org
.eclipse
.ui
.ISelectionListener
;
46 import org
.eclipse
.ui
.IWorkbenchPart
;
47 import org
.eclipse
.ui
.IWorkbenchPartReference
;
48 import org
.eclipse
.ui
.forms
.ManagedForm
;
49 import org
.eclipse
.ui
.forms
.widgets
.FormToolkit
;
50 import org
.eclipse
.ui
.forms
.widgets
.ScrolledForm
;
51 import org
.eclipse
.ui
.forms
.widgets
.TableWrapLayout
;
53 import eu
.etaxonomy
.cdm
.api
.conversation
.ConversationHolder
;
54 import eu
.etaxonomy
.cdm
.api
.conversation
.IConversationEnabled
;
55 import eu
.etaxonomy
.cdm
.api
.service
.DeleteResult
;
56 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
57 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
58 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
59 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
60 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
61 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
62 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
63 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
64 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.CdmDataChangeMap
;
65 import eu
.etaxonomy
.taxeditor
.editor
.CdmDataTransfer
;
66 import eu
.etaxonomy
.taxeditor
.editor
.EditorUtil
;
67 import eu
.etaxonomy
.taxeditor
.editor
.ISecuredEditor
;
68 import eu
.etaxonomy
.taxeditor
.editor
.ITaxonEditor
;
69 import eu
.etaxonomy
.taxeditor
.editor
.e4
.TaxonEditorInputE4
;
70 import eu
.etaxonomy
.taxeditor
.editor
.l10n
.Messages
;
71 import eu
.etaxonomy
.taxeditor
.editor
.name
.e4
.container
.AbstractGroupE4
;
72 import eu
.etaxonomy
.taxeditor
.editor
.name
.e4
.container
.AbstractGroupedContainerE4
;
73 import eu
.etaxonomy
.taxeditor
.editor
.name
.e4
.container
.AcceptedGroupE4
;
74 import eu
.etaxonomy
.taxeditor
.editor
.name
.e4
.container
.AcceptedNameContainerE4
;
75 import eu
.etaxonomy
.taxeditor
.editor
.name
.e4
.container
.ContainerFactoryE4
;
76 import eu
.etaxonomy
.taxeditor
.editor
.name
.e4
.container
.HomotypicalSynonymGroupE4
;
77 import eu
.etaxonomy
.taxeditor
.editor
.name
.e4
.container
.MisappliedGroupE4
;
78 import eu
.etaxonomy
.taxeditor
.editor
.name
.e4
.dnd
.NameEditorDropTargetListenerE4
;
79 import eu
.etaxonomy
.taxeditor
.event
.EventUtility
;
80 import eu
.etaxonomy
.taxeditor
.event
.WorkbenchEventConstants
;
81 import eu
.etaxonomy
.taxeditor
.model
.AbstractUtility
;
82 import eu
.etaxonomy
.taxeditor
.model
.IDirtyMarkable
;
83 import eu
.etaxonomy
.taxeditor
.model
.IPartChangeListener
;
84 import eu
.etaxonomy
.taxeditor
.model
.IPartContentHasDetails
;
85 import eu
.etaxonomy
.taxeditor
.model
.IPartContentHasFactualData
;
86 import eu
.etaxonomy
.taxeditor
.model
.IPartContentHasMedia
;
87 import eu
.etaxonomy
.taxeditor
.model
.IPartContentHasSupplementalData
;
88 import eu
.etaxonomy
.taxeditor
.model
.MessagingUtils
;
89 import eu
.etaxonomy
.taxeditor
.model
.TaxeditorPartService
;
90 import eu
.etaxonomy
.taxeditor
.operation
.IPostOperationEnabled
;
91 import eu
.etaxonomy
.taxeditor
.preference
.Resources
;
92 import eu
.etaxonomy
.taxeditor
.security
.RequiredPermissions
;
93 import eu
.etaxonomy
.taxeditor
.store
.CdmStore
;
94 import eu
.etaxonomy
.taxeditor
.workbench
.part
.IE4SavablePart
;
102 public class TaxonNameEditorE4
implements IConversationEnabled
, IDirtyMarkable
, IPartContentHasDetails
,
103 IPartContentHasSupplementalData
, IPartContentHasMedia
, IPartContentHasFactualData
, IPartChangeListener
,
104 ISelectionListener
, ISecuredEditor
, IPostOperationEnabled
, IE4SavablePart
, ITaxonEditor
, IDropTargetableE4
{
108 private ManagedForm managedForm
;
109 private ScrolledForm scrolledForm
;
110 private Composite parent
;
111 private ISelectionProvider simpleSelectionProvider
;
113 private TaxonBase selection
;
115 private ConversationHolder conversation
;
117 private AcceptedGroupE4 acceptedGroup
;
118 private List
<HomotypicalSynonymGroupE4
> heterotypicSynonymGroups
= new ArrayList
<>();
119 private MisappliedGroupE4 misappliedGroup
;
121 private DropTarget target
;
123 private TaxonBase objectAffectedByLastOperation
;
126 private EMenuService menuService
;
129 private ESelectionService selService
;
132 private IEclipseContext context
;
135 private MDirtyable dirty
;
137 private MPart thisPart
;
139 private TaxonEditorInputE4 input
;
141 private UndoContext undoContext
;
144 private IEventBroker eventBroker
;
147 public TaxonNameEditorE4() {
148 undoContext
= new UndoContext();
153 public void createPartControl(Composite parent
, MPart thisPart
) {
154 this.thisPart
= thisPart
;
156 createManagedForm(parent
);
158 TaxeditorPartService
.getInstance().addListener(
159 TaxeditorPartService
.PART_ACTIVATED
, this);
163 protected void createManagedForm(Composite composite
) {
165 managedForm
= new ManagedForm(composite
) {
168 public void dirtyStateChanged() {
169 dirty
.setDirty(true);
173 public boolean setInput(Object input
) {
174 if (input
instanceof AbstractGroupedContainerE4
) {
175 TaxonBase newSelection
= ((AbstractGroupedContainerE4
) input
).getData();
176 if(selection
!=newSelection
|| TaxonNameEditorE4
.this.isDirty()){
177 selection
= newSelection
;
178 selService
.setSelection(new StructuredSelection(selection
));
180 }else if(input
== null){
182 selService
.setSelection(new StructuredSelection());
186 return super.setInput(input
);
190 scrolledForm
= managedForm
.getForm();
191 parent
= scrolledForm
.getBody();
193 parent
.setData(taxon
);
195 TableWrapLayout layout
= new TableWrapLayout();
196 layout
.leftMargin
= 0;
197 layout
.rightMargin
= 0;
198 layout
.topMargin
= 0;
199 layout
.bottomMargin
= 0;
201 layout
.verticalSpacing
= 0;
202 layout
.horizontalSpacing
= 0;
204 parent
.setLayout(layout
);
205 parent
.setBackground(AbstractUtility
206 .getColor(Resources
.COLOR_COMPOSITE_BACKGROUND
));
209 public void createOrUpdateNameComposites(boolean accepted
, boolean heterotypicGroups
, boolean misappliedNames
) {
211 ContainerFactoryE4
.createOrUpdateAcceptedTaxonsHomotypicGroup(this);
213 if (heterotypicGroups
){
214 ContainerFactoryE4
.createOrUpdateHeterotypicSynonymyGroups(this);
216 if (misappliedNames
){
217 ContainerFactoryE4
.createOrUpdateMisapplicationsGroup(this);
223 managedForm
.refresh();
224 managedForm
.reflow(true);
230 public Taxon
getTaxon() {
231 return HibernateProxyHelper
.deproxy(taxon
);
234 public void setDirty() {
235 managedForm
.dirtyStateChanged();
239 public void setFocus() {
240 //make sure to bind again if maybe in another view the conversation was unbound
241 if(conversation
!=null && !conversation
.isBound()){
246 if (getSelectedContainer() == null) {
247 throw new IllegalStateException(
248 Messages
.TaxonNameEditor_THERE_SHOULD_ALWAYS_BE
);
250 getSelectedContainer().setSelected();
251 if (!input
.getCdmEntitySession().isActive()){
256 boolean doEnable
= permissionsSatisfied();
257 managedForm
.getForm().setEnabled(doEnable
);
259 eventBroker
.post(WorkbenchEventConstants
.CURRENT_ACTIVE_EDITOR
, this);
263 public boolean permissionsSatisfied() {
264 TaxonNode taxonNode
= input
.getTaxonNode();
265 boolean doEnable
= CdmStore
.currentAuthentiationHasPermission(taxonNode
, RequiredPermissions
.TAXONNODE_EDIT
);
270 public ConversationHolder
getConversationHolder() {
276 public void update(CdmDataChangeMap events
) {
281 * Redraws this editor return true on success
285 public boolean redraw() {
286 return redraw(true, true, true, true);
292 * Redraws the editor controls
294 public boolean redraw(boolean focus
, boolean accepted
, boolean heterotypic
, boolean misappliedNames
) {
296 createOrUpdateNameComposites(accepted
, heterotypic
, misappliedNames
);
306 public boolean postOperation(CdmBase objectAffectedByOperation
) {
308 changed(objectAffectedByOperation
);
310 redraw(true, true,true, true);
312 if (objectAffectedByOperation
instanceof TaxonBase
) {
313 objectAffectedByLastOperation
= (TaxonBase
) objectAffectedByOperation
;
319 public ManagedForm
getManagedForm() {
329 * @return true if there are empty names
331 public boolean checkForEmptyNames() {
332 for (AbstractGroupedContainerE4 container
: getGroupedContainers()) {
333 if (container
.getName() == null
334 || StringUtils
.isEmpty(container
.getName().getTitleCache())) {
341 public Set
<AbstractGroupedContainerE4
> getEmptyContainers() {
342 Set
<AbstractGroupedContainerE4
> containersWithEmptyNames
= new HashSet
<>();
344 for (AbstractGroupedContainerE4 container
: getGroupedContainers()) {
345 if (container
.getName() == null
346 || StringUtils
.isEmpty(container
.getName().getTitleCache())) {
347 containersWithEmptyNames
.add(container
);
351 return containersWithEmptyNames
;
357 public void save(IProgressMonitor monitor
) {
359 monitor
.beginTask(Messages
.TaxonNameEditor_SAVING_NAMES
, getGroupedContainers().size());
360 if (!conversation
.isBound()) {
364 conversation
.commit(true);
367 // check for empty names
368 if (checkForEmptyNames()) {
369 MessageDialog
.openWarning(AbstractUtility
.getShell(), Messages
.MultiPageTaxonEditor_NO_NAME_SPECIFIED
,
370 Messages
.MultiPageTaxonEditor_NO_NAME_SPECIFIED_MESSAGE
);
373 for (AbstractGroupedContainerE4 container
: getGroupedContainers()) {
375 monitor
.subTask(Messages
.TaxonNameEditor_SAVING_COMPOSITES
376 + container
.getTaxonBase().getTitleCache());
377 container
.persistName();
379 // In case the progress monitor was canceled throw an exception.
380 if (monitor
.isCanceled()) {
381 throw new OperationCanceledException();
384 // Otherwise declare this step as done.
389 // commit the conversation and start a new transaction immediately
390 conversation
.commit(true);
393 dirty
.setDirty(false);
394 EventUtility
.postEvent(WorkbenchEventConstants
.REFRESH_NAVIGATOR
, true);
395 // Stop the progress monitor.
399 public void init(TaxonEditorInputE4 input
) {
400 if (this.input
!= null){
401 this.input
.dispose();
402 // this.acceptedGroup = null;
403 // this.heterotypicSynonymGroups.clear();
404 // this.misappliedGroup = null;
406 if (!(input
!= null)) {
407 MessagingUtils
.error(this.getClass(), new Exception(Messages
.TaxonNameEditor_INVALID_INPUT
));
411 if (input
.getAdapter(Taxon
.class) != null) {
412 taxon
= CdmBase
.deproxy(input
.getAdapter(Taxon
.class), Taxon
.class);
414 MessagingUtils
.error(this.getClass(), new Exception(Messages
.TaxonNameEditor_INVALID_INPUT_TAXON_NULL
));
419 conversation
= input
.getConversationHolder();
421 createOrUpdateNameComposites(true, true, true);
427 //set initial selection
428 TaxonBase initiallySelectedTaxonBase
= input
.getInitiallySelectedTaxonBase();
429 if(initiallySelectedTaxonBase
!=null){
430 selService
.setSelection(new StructuredSelection(initiallySelectedTaxonBase
));
431 getContainer(initiallySelectedTaxonBase
).setSelected();
435 private void createDragSupport() {
436 // Listen for names being dragged outside of existing homotypic groups -
437 // user wants to create a new group
438 Transfer
[] types
= new Transfer
[] { CdmDataTransfer
.getInstance() };
439 int operations
= DND
.DROP_MOVE
;
440 if (target
== null) {
441 target
= new DropTarget(parent
, operations
);
442 target
.setTransfer(types
);
443 target
.addDropListener(new NameEditorDropTargetListenerE4(this));
447 public AcceptedNameContainerE4
getAcceptedNameContainer() {
448 return getAcceptedGroup().getAcceptedNameContainer();
451 public HomotypicalSynonymGroupE4
getHomotypicalGroupContainer(
452 HomotypicalGroup homotypicalGroup
) {
453 for (HomotypicalSynonymGroupE4 group
: getHeterotypicSynonymGroups()) {
454 if (group
.getGroup().equals(homotypicalGroup
)) {
467 * @return a Set containing all composites that have been edited
469 public Set
<AbstractGroupedContainerE4
> getDirtyNames() {
470 Set
<AbstractGroupedContainerE4
> dirtyNames
= new HashSet
<>();
472 for (AbstractGroupedContainerE4 composite
: getGroupedContainers()) {
473 if (composite
.isDirty()) {
474 dirtyNames
.add(composite
);
481 public List
<AbstractGroupedContainerE4
> getGroupedContainers() {
482 List
<AbstractGroupedContainerE4
> groupedComposites
= new ArrayList
<>();
484 for (AbstractGroupE4 group
: getAllGroups()) {
486 groupedComposites
.addAll(group
.getGroupedContainers());
490 return groupedComposites
;
493 public List
<AbstractGroupE4
> getAllGroups() {
494 List
<AbstractGroupE4
> allGroups
= new ArrayList
<>();
496 allGroups
.add(getAcceptedGroup());
498 heterotypicSynonymGroups
= getHeterotypicSynonymGroups();
500 if (heterotypicSynonymGroups
!= null) {
501 allGroups
.addAll(heterotypicSynonymGroups
);
504 if (misappliedGroup
!= null) {
505 allGroups
.add(misappliedGroup
);
512 public IEclipseContext
getContext() {
516 public boolean isDirty() {
517 return dirty
.isDirty();
521 public void dispose() {
522 if(conversation
!=null){
523 conversation
.unregisterForDataStoreChanges(this);
524 conversation
.close();
529 dirty
.setDirty(false);
530 eventBroker
.post(WorkbenchEventConstants
.CURRENT_ACTIVE_EDITOR
, null);
535 public void selectionChanged(IWorkbenchPart part
, ISelection selection
) {
536 System
.err
.println("Bla");
539 public AbstractGroupedContainerE4
getSelectedContainer() {
540 return (selection
!= null) ?
getContainer(selection
)
541 : getAcceptedNameContainer();
545 public void dragEntered() {
547 getControl().setBackground(
548 AbstractUtility
.getColor(Resources
.COLOR_DRAG_ENTER
));
552 public void dragLeft() {
553 getControl().setBackground(
554 AbstractUtility
.getColor(Resources
.COLOR_COMPOSITE_BACKGROUND
));
558 public void setMisapplicationsGroup(MisappliedGroupE4 misappliedGroup
) {
559 this.misappliedGroup
= misappliedGroup
;
562 public FormToolkit
getToolkit() {
563 return managedForm
.getToolkit();
566 public List
<HomotypicalSynonymGroupE4
> getHeterotypicSynonymGroups() {
567 return heterotypicSynonymGroups
;
570 public void addHeterotypicSynonymGroup(HomotypicalSynonymGroupE4 group
) {
571 heterotypicSynonymGroups
.add(group
);
574 public AcceptedGroupE4
getAcceptedGroup() {
575 return acceptedGroup
;
578 public void setAcceptedGroup(AcceptedGroupE4 acceptedGroup
) {
579 this.acceptedGroup
= acceptedGroup
;
582 public MisappliedGroupE4
getMisappliedGroup() {
583 return misappliedGroup
;
586 public boolean isActive() {
587 return this.equals(AbstractUtility
.getActivePart());
591 public boolean onComplete() {
592 getContainer(objectAffectedByLastOperation
).setSelected();
598 public void partChanged(Integer eventType
, IWorkbenchPartReference partRef
) {
599 if (!partRef
.getPart(false).equals(this)) {
600 // getSelectedObject().colorSelected(AbstractGroupedContainer.SELECTED_NO_FOCUS);
604 public void removeGroup(AbstractGroupE4 group
) {
608 //if (heterotypicSynonymGroups != null) {
609 heterotypicSynonymGroups
.remove(group
);
614 public AbstractGroupedContainerE4
getContainer(TaxonBase taxonBase
) {
615 @SuppressWarnings("rawtypes")
616 List
<AbstractGroupedContainerE4
> groupedContainers
= getGroupedContainers();
617 for (AbstractGroupedContainerE4 container
: groupedContainers
) {
618 if (container
.getData().equals(taxonBase
)
619 && container
.getNameViewer().getTextWidget() != null) {
623 return getAcceptedNameContainer();
626 public void setOnError() {
627 Color disabledColor
= AbstractUtility
.getColor(Resources
.COLOR_EDITOR_ERROR
);
628 setEnabled(false, disabledColor
);
631 public void setDisabled(){
632 Color disabledColor
= AbstractUtility
.getColor(Resources
.COLOR_TEXT_DISABLED_BACKGROUND
);
633 setEnabled(false, disabledColor
);
636 protected void setEnabled(boolean enabled
, Color background
) {
638 for(AbstractGroupedContainerE4 groupedContainer
: getGroupedContainers()){
639 groupedContainer
.setEnabled(enabled
);
642 // send an empty selection to the current provider - TODO only on error ???
644 getManagedForm().setInput(null);
646 for (AbstractGroupedContainerE4 groupedContainer
: getGroupedContainers()) {
647 groupedContainer
.setBackground(background
);
650 getControl().setBackground(background
);
654 public void changed(Object element
) {
656 // 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
657 if (element
!= null){
658 dirty
.setDirty(true);
660 //TODO: refresh taxon node in taxon navigator
664 if (element
instanceof TaxonBase
) {
665 AbstractGroupedContainerE4 container
= getContainer((TaxonBase
) element
);
666 if (container
!= null) {
670 if (element
instanceof TaxonRelationship
) {
671 AbstractGroupedContainerE4 container
= getContainer(((TaxonRelationship
) element
).getFromTaxon());
672 if (container
!= null) {
678 public void setPartName(){
679 //FIXME: temporary fix for #6437 to avoid outdated title caches
680 thisPart
.setLabel(this.taxon
.getName().generateFullTitle());
681 // thisPart.setLabel(this.taxon.getName().getFullTitleCache());
685 public void forceDirty() {
690 public IUndoContext
getUndoContext() {
695 public Composite
getControl(){
696 return managedForm
.getForm().getBody();
699 public EMenuService
getMenuService() {
703 public ESelectionService
getSelectionService() {
712 public boolean canAttachMedia() {
716 public TaxonEditorInputE4
getEditorInput() {
724 public TaxonNameEditorE4
getEditor() {
730 private void updateView(@UIEventTopic(WorkbenchEventConstants
.REFRESH_NAME_EDITOR
)CdmBase cdmbase
){
732 if (EventUtility
.getTaxonEditor().equals(this) || (this.taxon
.equals(cdmbase
) || this.taxon
.getName().equals(cdmbase
))){
733 this.redraw(false, true, true, true);
735 if (cdmbase
instanceof TaxonBase
){
736 this.selection
= (TaxonBase
) cdmbase
;
745 private void updatefromDelete(@UIEventTopic(WorkbenchEventConstants
.DELETE_DERIVATIVE
)DeleteResult result
){
746 if(taxon
.getName()==null){
749 Set
<DerivedUnit
> typeDesignationSpecimens
= new HashSet
<>();
750 this.taxon
.getName().getSpecimenTypeDesignations().forEach(designation
->typeDesignationSpecimens
.add(designation
.getTypeSpecimen()));
751 //check if any deleted object was a type specimen
752 if(result
.getUpdatedObjects().stream()
753 //filter only DerivedUnits
754 .filter(cdmBase
->cdmBase
.isInstanceOf(DerivedUnit
.class))
755 //deproxy from CdmBase to DerivedUnit
756 .map(unit
->HibernateProxyHelper
.deproxy(unit
, DerivedUnit
.class))
757 //check for match in type designations
758 .anyMatch(unit
->typeDesignationSpecimens
.contains(unit
))){
759 EditorUtil
.updateEditor(this.input
.getTaxonNode(), this);