Fix some labels and handle toggle states
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / view / derivate / DerivateView.java
1 package eu.etaxonomy.taxeditor.editor.view.derivate;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collection;
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Map.Entry;
11 import java.util.Set;
12 import java.util.UUID;
13
14 import org.eclipse.core.runtime.IProgressMonitor;
15 import org.eclipse.jface.action.MenuManager;
16 import org.eclipse.jface.util.LocalSelectionTransfer;
17 import org.eclipse.jface.viewers.AbstractTreeViewer;
18 import org.eclipse.jface.viewers.ISelection;
19 import org.eclipse.jface.viewers.IStructuredSelection;
20 import org.eclipse.jface.viewers.StructuredSelection;
21 import org.eclipse.jface.viewers.TreeNode;
22 import org.eclipse.jface.viewers.TreeSelection;
23 import org.eclipse.jface.viewers.TreeViewer;
24 import org.eclipse.swt.SWT;
25 import org.eclipse.swt.dnd.DND;
26 import org.eclipse.swt.dnd.Transfer;
27 import org.eclipse.swt.layout.GridData;
28 import org.eclipse.swt.layout.GridLayout;
29 import org.eclipse.swt.widgets.Composite;
30 import org.eclipse.swt.widgets.Control;
31 import org.eclipse.swt.widgets.Menu;
32 import org.eclipse.swt.widgets.Tree;
33 import org.eclipse.ui.IEditorPart;
34 import org.eclipse.ui.IMemento;
35 import org.eclipse.ui.ISaveablePart2;
36 import org.eclipse.ui.IWorkbenchPart;
37
38 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
39 import eu.etaxonomy.cdm.api.service.IOccurrenceService;
40 import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
41 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
42 import eu.etaxonomy.cdm.model.common.CdmBase;
43 import eu.etaxonomy.cdm.model.molecular.Sequence;
44 import eu.etaxonomy.cdm.model.molecular.SingleRead;
45 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
46 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
47 import eu.etaxonomy.cdm.model.taxon.Taxon;
48 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
49 import eu.etaxonomy.taxeditor.editor.EditorUtil;
50 import eu.etaxonomy.taxeditor.editor.Messages;
51 import eu.etaxonomy.taxeditor.editor.view.derivate.searchFilter.DerivateSearchCompositeController;
52 import eu.etaxonomy.taxeditor.model.IContextListener;
53 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
54 import eu.etaxonomy.taxeditor.model.IPartContentHasFactualData;
55 import eu.etaxonomy.taxeditor.model.IPartContentHasMedia;
56 import eu.etaxonomy.taxeditor.model.IPartContentHasSupplementalData;
57 import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
58 import eu.etaxonomy.taxeditor.store.CdmStore;
59 import eu.etaxonomy.taxeditor.view.AbstractCdmViewPart;
60 import eu.etaxonomy.taxeditor.view.derivateSearch.DerivateContentProvider;
61 import eu.etaxonomy.taxeditor.view.derivateSearch.DerivateLabelProvider;
62
63 /**
64 * Displays the derivate hierarchy of the specimen specified in the editor input.
65 *
66 */
67 public class DerivateView extends AbstractCdmViewPart implements IPartContentHasFactualData, ISaveablePart2,
68 IPartContentHasDetails, IPartContentHasSupplementalData, IPartContentHasMedia, IContextListener {
69 public static final String ID = "eu.etaxonomy.taxeditor.editor.view.derivate.DerivateView"; //$NON-NLS-1$
70
71 public static final String YOU_NEED_TO_SAVE_BEFORE_PERFORMING_THIS_ACTION = Messages.DerivateView_YOU_NEED_TO_SAVE;
72 public static final String VIEW_HAS_UNSAVED_CHANGES = Messages.DerivateView_UNSAVED_CHANGES;
73
74 private static final List<String> SPECIMEN_INIT_STRATEGY = Arrays.asList(new String[] {
75 "descriptions",
76 "annotations",
77 "markers",
78 "credits",
79 "extensions",
80 "rights",
81 "sources",
82 "derivationEvents.derivatives.annotations",
83 "derivationEvents.derivatives.markers",
84 "derivationEvents.derivatives.credits",
85 "derivationEvents.derivatives.extensions",
86 "derivationEvents.derivatives.rights",
87 "derivationEvents.derivatives.sources"
88 });
89
90 private ConversationHolder conversation;
91
92 private TreeViewer viewer;
93
94 private boolean isDirty;
95
96 private final int dndOperations = DND.DROP_MOVE;
97
98 private DerivateLabelProvider labelProvider;
99
100 private Set<SingleRead> multiLinkSingleReads;
101
102 private DerivateContentProvider contentProvider;
103
104 private DerivateSearchCompositeController derivateSearchCompositeController;
105
106 /**
107 * A map with keys being the derivative entities belonging to the {@link UUID}s passed to the constructor
108 * and values being the root elements of the hierarchy (may be the same objects as the derivative entities)
109 */
110 private Map<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>> derivateToRootEntityMap;
111
112 /**
113 * The set of root elements
114 */
115 private Set<SpecimenOrObservationBase<?>> rootElements;
116
117 private ICdmEntitySession cdmEntitySession;
118
119 /**
120 * <code>true</code> if this view is listening to selection changes
121 */
122 private boolean listenToSelectionChange;
123
124 private Taxon selectedTaxon;
125
126 /**
127 * Default constructor
128 */
129 public DerivateView() {
130 init();
131 }
132
133 /**
134 *
135 */
136 private void init() {
137 this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
138 this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
139
140 if (CdmStore.isActive() && conversation == null) {
141 conversation = CdmStore.createConversation();
142 }
143 if (CdmStore.isActive()) {
144 cdmEntitySession = CdmStore.getCurrentSessionManager().newSession(this, true);
145 }
146 //listen to context changes
147 CdmStore.getContextManager().addContextListener(this);
148 }
149
150 @Override
151 public void createPartControl(Composite parent) {
152
153 parent.setLayout(new GridLayout());
154
155 //---search and filter---
156 derivateSearchCompositeController = new DerivateSearchCompositeController(parent, this);
157 derivateSearchCompositeController.setEnabled(CdmStore.isActive());
158 GridData gridDataSearchBar = new GridData();
159 gridDataSearchBar.horizontalAlignment = GridData.FILL;
160 gridDataSearchBar.grabExcessHorizontalSpace = true;
161 derivateSearchCompositeController.setLayoutData(gridDataSearchBar);
162
163 //---tree viewer---
164 viewer = new TreeViewer(new Tree(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION));
165 GridData gridDataTree = new GridData();
166 gridDataTree.horizontalAlignment = GridData.FILL;
167 gridDataTree.verticalAlignment = GridData.FILL;
168 gridDataTree.grabExcessVerticalSpace = true;
169 gridDataTree.grabExcessHorizontalSpace = true;
170 viewer.getTree().setLayoutData(gridDataTree);
171 contentProvider = new DerivateContentProvider();
172 viewer.setContentProvider(contentProvider);
173 labelProvider = new DerivateLabelProvider();
174 labelProvider.setConversation(conversation);
175 viewer.setLabelProvider(labelProvider);
176 viewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
177 // Propagate selection from viewer
178 getSite().setSelectionProvider(viewer);
179
180 //listen to selection changes
181 selectionService = getSite().getWorkbenchWindow().getSelectionService();
182 selectionService.addSelectionListener(this);
183
184 //create context menu
185 MenuManager menuManager = new MenuManager();
186 getSite().registerContextMenu(menuManager, viewer);
187 Control control = viewer.getControl();
188 Menu menu = menuManager.createContextMenu(control);
189 control.setMenu(menu);
190
191 //init tree
192 updateRootEntities();
193
194 //add drag'n'drop support
195 Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer(),};
196 viewer.addDragSupport(dndOperations, transfers, new DerivateDragListener(this));
197 viewer.addDropSupport(dndOperations, transfers, new DerivateDropListener(this));
198 }
199
200 public void updateRootEntities() {
201 updateRootEntities(null);
202 }
203
204 public void updateRootEntities(Collection<UUID> derivativeUuids) {
205 if(conversation!=null){
206 if (!conversation.isBound()) {
207 conversation.bind();
208 }
209 /*
210 * If the active session is not the session of the Derivate Editor then we will
211 * save it, bind temporarily to our session and rebind to the original session.
212 * This happens e.g. if a selection change happens in the taxon editor and
213 * "Link with editor" is enabled. The selection change event and thus the
214 * loading in updateRootEntities() happens in the session of the taxon
215 * editor.
216 */
217 ICdmEntitySession previousCdmEntitySession = CdmStore.getCurrentSessionManager().getActiveSession();
218 if(cdmEntitySession != null) {
219 cdmEntitySession.bind();
220 }
221
222 if(derivativeUuids!=null){
223 this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
224 this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
225 for (UUID uuid : derivativeUuids) {
226 SpecimenOrObservationBase<?> derivate = CdmStore.getService(IOccurrenceService.class).load(uuid, SPECIMEN_INIT_STRATEGY);
227 if(derivate instanceof FieldUnit){
228 derivateToRootEntityMap.put(derivate, derivate);
229 }
230 else {
231 SpecimenOrObservationBase<?> topMostDerivate = EditorUtil.getTopMostDerivate(derivate);
232 if(topMostDerivate!=null){
233 derivateToRootEntityMap.put(derivate, topMostDerivate);
234 }
235 else{
236 derivateToRootEntityMap.put(derivate, derivate);
237 }
238 }
239 }
240 for (SpecimenOrObservationBase<?> specimen : derivateToRootEntityMap.values()) {
241 rootElements.add(specimen);
242 }
243 }
244 viewer.setInput(rootElements);
245 refreshTree();
246 previousCdmEntitySession.bind();
247 }
248 }
249
250 @Override
251 public void doSave(IProgressMonitor monitor) {
252 String taskName = Messages.DerivateView_SAVING_HIERARCHY;
253 monitor.beginTask(taskName, 3);
254 if (!conversation.isBound()) {
255 conversation.bind();
256 }
257 monitor.worked(1);
258
259 // commit the conversation and start a new transaction immediately
260 conversation.commit(true);
261
262 if(CdmStore.getCurrentSessionManager().isRemoting()) {
263 CdmStore.getService(IOccurrenceService.class).merge(new ArrayList<SpecimenOrObservationBase>(rootElements), true);
264 }
265 monitor.worked(1);
266
267 this.setDirty(false);
268 monitor.worked(1);
269 monitor.done();
270 firePropertyChange(PROP_DIRTY);
271 refreshTree();
272 }
273
274 @Override
275 public void doSaveAs() {
276 }
277
278 @Override
279 public String getTitleToolTip() {
280 return Messages.DerivateView_DERIVATIVE_EDITOR;
281 }
282
283 @Override
284 public boolean isDirty() {
285 return isDirty;
286 }
287
288 /**
289 * @param isDirty the isDirty to set
290 */
291 public void setDirty(boolean isDirty) {
292 this.isDirty = isDirty;
293 }
294
295 @Override
296 public boolean isSaveAsAllowed() {
297 return false;
298 }
299
300 @Override
301 public void setFocus() {
302 viewer.getControl().setFocus();
303 //make sure to bind again if maybe in another view the conversation was unbound
304 if(conversation!=null && !conversation.isBound()){
305 conversation.bind();
306 }
307 if(cdmEntitySession != null) {
308 cdmEntitySession.bind();
309 }
310 }
311
312 @Override
313 public void update(CdmDataChangeMap changeEvents) {
314 }
315
316 @Override
317 public ConversationHolder getConversationHolder() {
318 return conversation;
319 }
320
321 /**
322 * @return the viewer
323 */
324 @Override
325 public TreeViewer getViewer() {
326 return viewer;
327 }
328
329 @Override
330 public void changed(Object element) {
331 setDirty(true);
332 firePropertyChange(IEditorPart.PROP_DIRTY);
333 viewer.refresh();
334 }
335
336 @Override
337 public void forceDirty() {
338 changed(null);
339 }
340
341 @Override
342 public Map<Object, List<String>> getPropertyPathsMap() {
343 List<String> specimenPropertyPaths = Arrays.asList(new String[] {
344 "descriptions",
345 "derivationEvents.derivates",
346 "annotations",
347 "markers",
348 "credits",
349 "extensions",
350 "rights",
351 "sources"
352 });
353 Map<Object, List<String>> specimenPropertyPathMap =
354 new HashMap<Object, List<String>>();
355 specimenPropertyPathMap.put(SpecimenOrObservationBase.class,specimenPropertyPaths);
356 return specimenPropertyPathMap;
357 }
358
359 /**
360 * Refreshes the derivate hierarchy tree and expands the tree
361 * to show and select the given object.
362 *
363 * @param expandTo the object to which the tree should be expanded
364 */
365 public void refreshTree(Object expandTo){
366 refreshTree();
367 TreeSelection selection = (TreeSelection) viewer.getSelection();
368 viewer.expandToLevel(selection.getFirstElement(), 1);
369 viewer.setSelection(new StructuredSelection(new TreeNode(expandTo)));
370 }
371
372 /**
373 * Refreshes the derivate hierarchy tree
374 */
375 public void refreshTree(){
376 generateMultiLinkSingleReads();
377 labelProvider.setMultiLinkSingleReads(multiLinkSingleReads);
378 viewer.refresh();
379 }
380
381 //FIXME:Remoting hack to make this work for remoting
382 //This should actually be resolved using remoting post operations
383 public void remove(Object obj) {
384 rootElements.remove(obj);
385 viewer.setInput(rootElements);
386 }
387
388 private void generateMultiLinkSingleReads() {
389 Set<SingleRead> multiLinkSingleReads = new HashSet<SingleRead>();
390 for(Entry<SingleRead, Collection<Sequence>> entry:CdmStore.getService(ISequenceService.class).getSingleReadSequencesMap().entrySet()){
391 if(entry.getValue().size()>1){
392 multiLinkSingleReads.add(entry.getKey());
393 }
394 }
395 this.multiLinkSingleReads = multiLinkSingleReads;
396 }
397
398 /**
399 * @return a set of {@link SingleRead}s that have multiple parents
400 */
401 public Set<SingleRead> getMultiLinkSingleReads() {
402 return this.multiLinkSingleReads;
403 }
404
405 public Object getSelectionInput() {
406 return selectedTaxon;
407 }
408
409 public DerivateLabelProvider getLabelProvider() {
410 return labelProvider;
411 }
412
413 @Override
414 public boolean postOperation(CdmBase objectAffectedByOperation) {
415 refreshTree();
416 if(objectAffectedByOperation!=null){
417 changed(objectAffectedByOperation);
418 }
419 return true;
420 }
421
422 @Override
423 public boolean onComplete() {
424 return true;
425 }
426
427
428 @Override
429 public boolean canAttachMedia() {
430 return true;
431 }
432
433 public void addFieldUnit(FieldUnit fieldUnit) {
434 rootElements.add(fieldUnit);
435 derivateToRootEntityMap.put(fieldUnit, fieldUnit);
436 }
437
438 @Override
439 public ICdmEntitySession getCdmEntitySession() {
440 return cdmEntitySession;
441 }
442
443 @Override
444 public void dispose() {
445 super.dispose();
446 if(conversation!=null){
447 conversation.close();
448 }
449 if(cdmEntitySession != null) {
450 cdmEntitySession.dispose();
451 }
452 }
453
454 @Override
455 public void selectionChanged(IWorkbenchPart part, ISelection selection) {
456 if(listenToSelectionChange && selection instanceof IStructuredSelection){
457 Object selectedElement = ((IStructuredSelection) selection).getFirstElement();
458 if(selectedElement instanceof CdmBase && ((CdmBase) selectedElement).isInstanceOf(Taxon.class)){
459 selectedTaxon = HibernateProxyHelper.deproxy(selectedElement, Taxon.class);
460 Collection<SpecimenOrObservationBase> fieldUnits = CdmStore.getService(IOccurrenceService.class).listFieldUnitsByAssociatedTaxon(selectedTaxon, null, null);
461 Collection<UUID> uuids = new HashSet<UUID>();
462 for (SpecimenOrObservationBase specimenOrObservationBase : fieldUnits) {
463 uuids.add(specimenOrObservationBase.getUuid());
464 }
465 updateRootEntities(uuids);
466 derivateSearchCompositeController.setTaxon(selectedTaxon);
467 setPartName("Derivative Editor: " + selectedTaxon.getName());
468 }
469 // if(part instanceof MultiPageTaxonEditor){
470 // Taxon taxon = ((MultiPageTaxonEditor) part).getTaxon();
471 // }
472 }
473 }
474
475 /**
476 * {@inheritDoc}
477 */
478 @Override
479 public List<SpecimenOrObservationBase<?>> getRootEntities() {
480 return new ArrayList<SpecimenOrObservationBase<?>>(rootElements);
481 }
482
483 @Override
484 public void createViewer(Composite parent) {
485 // TODO Auto-generated method stub
486
487 }
488
489 @Override
490 public boolean isSaveOnCloseNeeded() {
491 return isDirty();
492 }
493
494 @Override
495 public int promptToSaveOnClose() {
496 return ISaveablePart2.DEFAULT;
497 }
498
499 public void toggleListenToSelectionChange() {
500 listenToSelectionChange = !listenToSelectionChange;
501 derivateSearchCompositeController.setEnabled(!listenToSelectionChange);
502 derivateSearchCompositeController.setTaxon(null);
503 if(!listenToSelectionChange){
504 selectedTaxon = null;
505 setPartName("Derivative Editor");
506 }
507 else if(selectedTaxon==null){
508 setPartName("Derivative Editor [no taxon selected]");
509 }
510 }
511
512 public boolean isListenToSelectionChange(){
513 return listenToSelectionChange;
514 }
515
516 /**
517 * {@inheritDoc}
518 */
519 @Override
520 public void contextAboutToStop(IMemento memento, IProgressMonitor monitor) {
521 }
522
523 /**
524 * {@inheritDoc}
525 */
526 @Override
527 public void contextStop(IMemento memento, IProgressMonitor monitor) {
528 derivateSearchCompositeController.setEnabled(false);
529 viewer.setInput(null);
530 }
531
532 /**
533 * {@inheritDoc}
534 */
535 @Override
536 public void contextStart(IMemento memento, IProgressMonitor monitor) {
537 init();
538 derivateSearchCompositeController.setEnabled(!listenToSelectionChange);
539 refreshTree();
540 }
541
542 /**
543 * {@inheritDoc}
544 */
545 @Override
546 public void contextRefresh(IProgressMonitor monitor) {
547 }
548
549 /**
550 * {@inheritDoc}
551 */
552 @Override
553 public void workbenchShutdown(IMemento memento, IProgressMonitor monitor) {
554 }
555
556 }