ref #7912 Centralize layout
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / descriptiveDataSet / DescriptiveDataSetEditor.java
1 /**
2 * Copyright (C) 2017 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 package eu.etaxonomy.taxeditor.editor.descriptiveDataSet;
10
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashSet;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.UUID;
20 import java.util.stream.Collectors;
21
22 import javax.annotation.PostConstruct;
23 import javax.annotation.PreDestroy;
24 import javax.inject.Inject;
25 import javax.inject.Named;
26
27 import org.eclipse.core.runtime.IProgressMonitor;
28 import org.eclipse.e4.core.contexts.ContextInjectionFactory;
29 import org.eclipse.e4.core.contexts.IEclipseContext;
30 import org.eclipse.e4.ui.di.Focus;
31 import org.eclipse.e4.ui.di.Persist;
32 import org.eclipse.e4.ui.model.application.ui.MDirtyable;
33 import org.eclipse.e4.ui.services.EMenuService;
34 import org.eclipse.e4.ui.services.IServiceConstants;
35 import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
36 import org.eclipse.jface.util.LocalSelectionTransfer;
37 import org.eclipse.jface.viewers.IStructuredSelection;
38 import org.eclipse.jface.viewers.StructuredSelection;
39 import org.eclipse.jface.viewers.Viewer;
40 import org.eclipse.jface.window.Window;
41 import org.eclipse.swt.SWT;
42 import org.eclipse.swt.dnd.DND;
43 import org.eclipse.swt.dnd.Transfer;
44 import org.eclipse.swt.events.FocusAdapter;
45 import org.eclipse.swt.events.FocusEvent;
46 import org.eclipse.swt.events.KeyAdapter;
47 import org.eclipse.swt.events.KeyEvent;
48 import org.eclipse.swt.events.ModifyEvent;
49 import org.eclipse.swt.events.ModifyListener;
50 import org.eclipse.swt.events.SelectionAdapter;
51 import org.eclipse.swt.widgets.Composite;
52 import org.eclipse.swt.widgets.Display;
53 import org.eclipse.swt.widgets.Shell;
54
55 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
56 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
57 import eu.etaxonomy.cdm.api.service.IDescriptiveDataSetService;
58 import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
59 import eu.etaxonomy.cdm.api.service.ITermService;
60 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
61 import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
62 import eu.etaxonomy.cdm.model.description.FeatureTree;
63 import eu.etaxonomy.cdm.model.location.NamedArea;
64 import eu.etaxonomy.cdm.model.name.Rank;
65 import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
66 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
67 import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
68 import eu.etaxonomy.cdm.persistence.dto.TermDto;
69 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
70 import eu.etaxonomy.taxeditor.editor.l10n.Messages;
71 import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
72 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
73 import eu.etaxonomy.taxeditor.model.IPartContentHasSupplementalData;
74 import eu.etaxonomy.taxeditor.model.MessagingUtils;
75 import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
76 import eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled;
77 import eu.etaxonomy.taxeditor.store.CdmStore;
78 import eu.etaxonomy.taxeditor.ui.element.CdmFormFactory;
79 import eu.etaxonomy.taxeditor.workbench.part.IE4SavablePart;
80
81 /**
82 * Editor for configuring DescriptiveDataSets
83 * @author pplitzner
84 * @since Nov 21, 2017
85 *
86 */
87 public class DescriptiveDataSetEditor implements IE4SavablePart, IConversationEnabled, ICdmEntitySessionEnabled,
88 IPartContentHasDetails, IPartContentHasSupplementalData, IDirtyMarkable {
89
90 private DescriptiveDataSetComposite composite;
91
92 private ConversationHolder conversation;
93
94 private ICdmEntitySession cdmEntitySession;
95
96 private DescriptiveDataSet descriptiveDataSet;
97
98 private final int dndOperations = DND.DROP_MOVE;
99
100 @Inject
101 private MDirtyable dirty;
102
103 @Inject
104 private ESelectionService selectionService;
105
106 private ModifyListener labelModifyListener;
107
108 @PostConstruct
109 public void create(Composite parent, IEclipseContext context, @Named(IServiceConstants.ACTIVE_SHELL)Shell shell,
110 EMenuService menuService){
111 if (CdmStore.isActive()){
112 if(conversation == null){
113 conversation = CdmStore.createConversation();
114 }
115 if(cdmEntitySession == null){
116 cdmEntitySession = CdmStore.getCurrentSessionManager().newSession(this, true);
117 }
118 }
119 else{
120 return;
121 }
122 CdmFormFactory cdmFormFactory = new CdmFormFactory(Display.getCurrent());
123 ContextInjectionFactory.inject(cdmFormFactory, context);
124
125 composite = new DescriptiveDataSetComposite(parent, SWT.NONE);
126
127 labelModifyListener = new ModifyListener() {
128 @Override
129 public void modifyText(ModifyEvent e) {
130 descriptiveDataSet.setLabel(composite.getTxt_label().getText());
131 dirty.setDirty(true);
132 }
133 };
134 composite.getRankMin().addSelectionChangedListener(event->dirty.setDirty(true));
135 composite.getRankMin().setText("Select min rank...");
136 composite.getRankMax().addSelectionChangedListener(event->dirty.setDirty(true));
137 composite.getRankMin().setText("Select max rank...");
138 composite.getBtnRemoveRankMin().addSelectionListener(new SelectionAdapter() {
139 @Override
140 public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
141 composite.getRankMin().setElement(null);
142 dirty.setDirty(true);
143 }
144 });
145 composite.getBtnRemoveRankMax().addSelectionListener(new SelectionAdapter() {
146 @Override
147 public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
148 composite.getRankMax().setElement(null);
149 dirty.setDirty(true);
150 }
151 });
152
153 composite.getBtnChooseArea().addSelectionListener(new SelectionAdapter() {
154
155 @Override
156 public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
157 AreasSelectionDialog areasSelectionDialog = new AreasSelectionDialog(composite.getShell(), composite.getAreas());
158 if(areasSelectionDialog.open()==Window.OK){
159 List<TermDto> selectedAreas = areasSelectionDialog.getSelectedAreas();
160 if(selectedAreas!=null){
161 composite.setAreas(selectedAreas);
162 dirty.setDirty(true);
163 }
164 }
165 }
166 });
167
168 composite.getFeatureTreeEditorComposite().init("Character Tree", null, null, e->selectionService.setSelection(e.getSelection()), this, null);
169
170 //add drag'n'drop support
171 Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer()};
172 composite.getTaxonNodeTree().addDropSupport(dndOperations, transfers, new TaxonNodeDropAdapter(this));
173
174 composite.getTaxonNodeTree().getTree().addKeyListener(new KeyAdapter() {
175 @Override
176 public void keyPressed(KeyEvent e) {
177 if(e.character==SWT.DEL){
178 removeSelectedTaxonNodes();
179 }
180 }
181 });
182
183
184 //create context menu
185 menuService.registerContextMenu(composite.getTaxonNodeTree().getControl(), "eu.etaxonomy.taxeditor.editor.descriptiveDataSet.DescriptiveDataSetEditor.popupmenu.specimeneditor");
186
187 }
188
189 public void init(UUID descriptiveDataSetUuid) {
190 this.descriptiveDataSet = cdmEntitySession.remoteLoad(CdmStore.getService(IDescriptiveDataSetService.class), descriptiveDataSetUuid);
191 if(descriptiveDataSet.getLabel()!=null){
192 composite.getTxt_label().setText(descriptiveDataSet.getLabel());
193 }
194 if(descriptiveDataSet.getDescriptiveSystem()!=null){
195 composite.setCharacters(descriptiveDataSet.getDescriptiveSystem());
196 }
197 Rank maxRank = descriptiveDataSet.getMaxRank();
198 if(maxRank!=null){
199 composite.setRankMax(maxRank);
200 }
201 Rank minRank = descriptiveDataSet.getMinRank();
202 if(minRank!=null){
203 composite.setRankMin(minRank);
204 }
205 Set<NamedArea> geoFilter = descriptiveDataSet.getGeoFilter();
206 if(geoFilter!=null && !geoFilter.isEmpty()){
207 Set<TermDto> terms = geoFilter.stream().map(filter->TermDto.fromTerm(filter, true)).collect(Collectors.toSet());
208 composite.setAreas(new ArrayList<>(terms));
209 }
210 Set<TaxonNode> taxonSubtreeFilter = descriptiveDataSet.getTaxonSubtreeFilter();
211 if(taxonSubtreeFilter!=null){
212 composite.getTaxonNodeTree().setInput(taxonSubtreeFilter);
213 }
214 composite.getTxt_label().addModifyListener(labelModifyListener);
215 composite.getTxt_label().addFocusListener(new FocusAdapter() {
216 @Override
217 public void focusGained(FocusEvent e) {
218 super.focusGained(e);
219 selectionService.setSelection(new StructuredSelection(descriptiveDataSet));
220 }
221 });
222 selectionService.setSelection(new StructuredSelection(descriptiveDataSet));
223 }
224
225 public void addTaxonNode(TaxonNodeDto taxonNodeDto){
226 TaxonNode taxonNode = cdmEntitySession.remoteLoad(CdmStore.getService(ITaxonNodeService.class), taxonNodeDto.getUuid());
227 //check if node belongs to same classification
228 Set<TaxonNode> taxonSubtreeFilter = descriptiveDataSet.getTaxonSubtreeFilter();
229 if(taxonSubtreeFilter!=null && !taxonSubtreeFilter.isEmpty()){
230 if(!taxonSubtreeFilter.iterator().next().getClassification().equals(taxonNode.getClassification())){
231 MessagingUtils.warningDialog(Messages.TaxonNodeDropAdapter_CLASSIFICATIONS_NO_MATCH, this.getClass(),
232 Messages.TaxonNodeDropAdapter_CLASSIFICATIONS_NO_MATCH_MESSAGE);
233 return;
234 }
235 }
236 Viewer taxonTreeViewer = getTaxonTreeViewer();
237 Object input = taxonTreeViewer.getInput();
238 Collection<TaxonNode> treeNodes;
239 if(input==null){
240 treeNodes = new ArrayList<>();
241 }
242 else{
243 treeNodes = (Collection<TaxonNode>) input;
244 }
245 treeNodes.add(taxonNode);
246 taxonTreeViewer.setInput(treeNodes);
247 dirty.setDirty(true);
248 }
249
250 public void removeSelectedTaxonNodes(){
251 IStructuredSelection selection = (IStructuredSelection) composite.getTaxonNodeTree().getSelection();
252 if(selection.toList().stream().anyMatch(object->!(object instanceof TaxonNode))){
253 MessagingUtils.warningDialog(Messages.DescriptiveDataSetEditor_DELETE_FAIL_TITLE, this.getClass(), Messages.DescriptiveDataSetEditor_DELETE_FAIL_MESSAGE);
254 return;
255 }
256 Iterator<Object> iterator = selection.iterator();
257 Viewer taxonTreeViewer = getTaxonTreeViewer();
258 Collection<TaxonNode> input = (Collection<TaxonNode>) taxonTreeViewer.getInput();
259 while(iterator.hasNext()){
260 Object next = iterator.next();
261 TaxonNode taxonNode = (TaxonNode) next;
262 input.remove(taxonNode);
263 descriptiveDataSet.removeTaxonSubtree(taxonNode);
264 }
265 taxonTreeViewer.setInput(input);
266 dirty.setDirty(true);
267 }
268
269 @Persist
270 @Override
271 public void save(IProgressMonitor monitor) {
272 Collection<TermDto> areas = composite.getAreas();
273 Object input = composite.getTaxonNodeTree().getInput();
274 if(input!=null){
275 descriptiveDataSet.setTaxonSubtreeFilter(new HashSet<>());//clear existing filter
276 Collection<ITaxonTreeNode> taxonNodes = (Collection<ITaxonTreeNode>) input;
277 for (ITaxonTreeNode taxonTreeNode : taxonNodes) {
278 if(taxonTreeNode instanceof TaxonNode){
279 TaxonNode taxonNode = (TaxonNode)taxonTreeNode;
280 descriptiveDataSet.addTaxonSubtree(taxonNode);
281 }
282 }
283 }
284 FeatureTree characters = composite.getCharacters();
285 if(characters!=null){
286 //save characters because they can be modified in this editor
287 characters.getDistinctFeatures().forEach(character->CdmStore.getService(ITermService.class).merge(character,true));
288 }
289
290
291 DefinedTermBase rankMaxSelection = composite.getRankMax().getSelection();
292 Rank rankMax = null;
293 if(rankMaxSelection instanceof Rank){
294 rankMax = (Rank) rankMaxSelection;
295 }
296 DefinedTermBase rankMinSelection = composite.getRankMin().getSelection();
297 Rank rankMin = null;
298 if(rankMinSelection instanceof Rank){
299 rankMin = (Rank) rankMinSelection;
300 }
301
302 descriptiveDataSet.setMaxRank(rankMax);
303 descriptiveDataSet.setMinRank(rankMin);
304 descriptiveDataSet.setDescriptiveSystem(characters);
305 List<DefinedTermBase> terms = CdmStore.getService(ITermService.class)
306 .load(areas.stream().map(area -> area.getUuid()).collect(Collectors.toList()), null);
307 Set<NamedArea> areaTerms = new HashSet<>();
308 terms.forEach(term->areaTerms.add((NamedArea) term));
309 descriptiveDataSet.setGeoFilter(areaTerms);
310
311 conversation.commit();
312 CdmStore.getService(IDescriptiveDataSetService.class).merge(descriptiveDataSet, true);
313
314 dirty.setDirty(false);
315 }
316
317 @Override
318 public boolean isDirty() {
319 return dirty.isDirty();
320 }
321
322 @PreDestroy
323 public void dispose() {
324 if (conversation != null) {
325 conversation.close();
326 conversation = null;
327 }
328 if(cdmEntitySession != null) {
329 cdmEntitySession.dispose();
330 cdmEntitySession = null;
331 }
332 dirty.setDirty(false);
333 }
334
335 @Focus
336 public void setFocus() {
337 if(composite!=null){
338 composite.setFocus();
339 }
340 if (getConversationHolder() != null) {
341 getConversationHolder().bind();
342 }
343 if(cdmEntitySession != null) {
344 cdmEntitySession.bind();
345 }
346 }
347
348 public Viewer getTaxonTreeViewer() {
349 return composite.getTaxonNodeTree();
350 }
351
352 public DescriptiveDataSet getDescriptiveDataSet() {
353 return descriptiveDataSet;
354 }
355
356 @Override
357 public void update(CdmDataChangeMap arg0) {
358 }
359
360 @Override
361 public ICdmEntitySession getCdmEntitySession() {
362 return cdmEntitySession;
363 }
364
365 @Override
366 public Collection<DescriptiveDataSet> getRootEntities() {
367 return Collections.singleton(descriptiveDataSet);
368 }
369
370 @Override
371 public Map<Object, List<String>> getPropertyPathsMap() {
372 return null;
373 }
374
375 @Override
376 public ConversationHolder getConversationHolder() {
377 return conversation;
378 }
379
380 @Override
381 public void changed(Object element) {
382 dirty.setDirty(true);
383 }
384
385 @Override
386 public void forceDirty() {
387 dirty.setDirty(true);
388 }
389
390 }