Merge branch 'develop' of wp5.e-taxonomy.eu:/var/git/taxeditor into remoting-4.0
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / MultiPageTaxonEditor.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;
11
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.UUID;
15
16 import org.eclipse.core.commands.operations.IUndoContext;
17 import org.eclipse.core.commands.operations.UndoContext;
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.jface.dialogs.MessageDialog;
20 import org.eclipse.ui.IEditorInput;
21 import org.eclipse.ui.IEditorPart;
22 import org.eclipse.ui.IEditorSite;
23 import org.eclipse.ui.PartInitException;
24 import org.eclipse.ui.forms.editor.FormEditor;
25
26 import eu.etaxonomy.cdm.api.application.CdmApplicationState;
27 import eu.etaxonomy.cdm.api.application.CdmChangeEvent;
28 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
29 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
30 import eu.etaxonomy.cdm.model.common.CdmBase;
31 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
32 import eu.etaxonomy.cdm.model.taxon.Taxon;
33 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
34 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
35 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
36 import eu.etaxonomy.taxeditor.editor.internal.TaxeditorEditorPlugin;
37 import eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor;
38 import eu.etaxonomy.taxeditor.editor.name.container.AbstractGroupedContainer;
39 import eu.etaxonomy.taxeditor.model.AbstractUtility;
40 import eu.etaxonomy.taxeditor.model.DataChangeBridge;
41 import eu.etaxonomy.taxeditor.model.IDataChangeBehavior;
42 import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
43 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
44 import eu.etaxonomy.taxeditor.model.IPartContentHasFactualData;
45 import eu.etaxonomy.taxeditor.model.IPartContentHasMedia;
46 import eu.etaxonomy.taxeditor.model.MessagingUtils;
47 import eu.etaxonomy.taxeditor.operation.IPostOperationEnabled;
48 import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
49 import eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled;
50
51 /**
52 *
53 * Generates the tabbed editor with <code>TaxonNameEditor</code> on top and tabs
54 * for "Descriptions", "Concepts", "Geography", etc.
55 *
56 * @author p.ciardelli
57 * @author n.hoffmann
58 * @created 15.05.2008
59 * @version 1.0
60 */
61 public class MultiPageTaxonEditor extends FormEditor implements
62 IPartContentHasFactualData, IConversationEnabled, ICdmEntitySessionEnabled, IPostOperationEnabled,
63 IDirtyMarkable, IPartContentHasDetails, ISecuredEditor, IPartContentHasMedia {
64
65 /** Constant <code>ID="eu.etaxonomy.taxeditor.editor.taxon"{trunked}</code> */
66 public static final String ID = "eu.etaxonomy.taxeditor.editor.taxon";
67
68 private boolean dirty;
69
70 private ICdmEntitySession cdmEntitySession;
71 private ConversationHolder conversation;
72 private IDataChangeBehavior dataChangeBehavior;
73 private IUndoContext undoContext;
74
75 private TaxonEditorInput input;
76
77 /**
78 * <p>
79 * Constructor for MultiPageTaxonEditor.
80 * </p>
81 */
82 public MultiPageTaxonEditor() {
83 super();
84 undoContext = new UndoContext();
85
86 }
87
88 /** {@inheritDoc} */
89 @Override
90 public void dispose() {
91 if(cdmEntitySession != null) {
92 cdmEntitySession.dispose();
93 }
94 conversation.unregisterForDataStoreChanges(this);
95 conversation.close();
96 super.dispose();
97 }
98
99 /*
100 * (non-Javadoc)
101 *
102 * @see org.eclipse.ui.forms.editor.FormEditor#addPages()
103 */
104 /** {@inheritDoc} */
105 @Override
106 protected void addPages() {
107 input = (TaxonEditorInput) getEditorInput();
108 conversation = input.getConversationHolder();
109 conversation.registerForDataStoreChanges(this);
110 CdmApplicationState.getCurrentDataChangeService().register(this);
111 try {
112 addPage(Page.NAME.getIndex(), new TaxonNameEditor(this),
113 getEditorInput());
114 // setPageText(Page.NAME.getIndex(), Page.NAME.getTitle());
115
116 // TODO lazy create
117 // addPage(Page.DESCRIPTIVE.getIndex(), new
118 // TaxonDescriptionTreeEditor(this), getEditorInput());
119 // setPageText(Page.DESCRIPTIVE.getIndex(),
120 // Page.DESCRIPTIVE.getTitle());
121
122 // EditorUtil.showPropertySheet();
123
124 } catch (PartInitException e) {
125 MessagingUtils.error(getClass(), e);
126 }
127 }
128
129 /** {@inheritDoc} */
130 @Override
131 public void doSave(IProgressMonitor monitor) {
132 monitor.beginTask("Saving Editor", 4);
133 try {
134 if (!conversation.isBound()) {
135 conversation.bind();
136 }
137 monitor.worked(1);
138
139 for (IEditorPart editorPage : getPages()) {
140 if (editorPage instanceof TaxonNameEditor) {
141 if (((TaxonNameEditor) editorPage).checkForEmptyNames()) {
142 MessageDialog.openWarning(AbstractUtility.getShell(), "No Name Specified",
143 "An attempt was made to save a taxon or synonym with "
144 + "an empty name. Operation was cancelled.");
145 return;
146 }
147 }
148
149 editorPage.doSave(monitor);
150 monitor.worked(1);
151 }
152
153 // commit the conversation and start a new transaction immediately
154
155 input.update();
156 conversation.commit(true);
157 monitor.worked(1);
158
159 this.setDirty(false);
160 monitor.worked(1);
161 } catch (Exception e) {
162 setFocus();
163 MessagingUtils.operationDialog(this, e, TaxeditorEditorPlugin.PLUGIN_ID,"saving a taxon", " Please close and reopen the taxon again.");
164 disableEditor(true);
165 } finally {
166 monitor.done();
167 }
168 }
169
170 private void disableEditor(boolean isOnError) {
171 for (IMultiPageTaxonEditorPage editorPage : getPages()) {
172 if(isOnError){
173 editorPage.setOnError();
174 }else {
175 editorPage.setDisabled();
176 }
177 }
178 CdmApplicationState.getCurrentDataChangeService().unregister(this);
179 conversation.unregisterForDataStoreChanges(this);
180 conversation.close();
181 setDirty(false);
182 }
183
184 private void setDirty(boolean dirty) {
185 this.dirty = dirty;
186 firePropertyChange(PROP_DIRTY);
187 }
188
189 /*
190 * (non-Javadoc)
191 *
192 * @see org.eclipse.ui.part.MultiPageEditorPart#isDirty()
193 */
194 /**
195 * <p>
196 * isDirty
197 * </p>
198 *
199 * @return a boolean.
200 */
201 @Override
202 public boolean isDirty() {
203 return dirty;
204 }
205
206 /*
207 * (non-Javadoc)
208 *
209 * @see org.eclipse.ui.forms.editor.FormEditor#editorDirtyStateChanged()
210 */
211 /** {@inheritDoc} */
212 @Override
213 public void editorDirtyStateChanged() {
214 dirty = true;
215 super.editorDirtyStateChanged();
216 }
217
218 /**
219 * {@inheritDoc}
220 *
221 * Checks whether nested editors are calling
222 * <code>firePropertyChange(PROP_DIRTY)</code> to signal an edit has taken
223 * place before passing property change along to
224 * <code>super.handlePropertyChange(int propertyId)</code>.
225 */
226 /*
227 * (non-Javadoc)
228 *
229 * @see org.eclipse.ui.part.MultiPageEditorPart#handlePropertyChange(int)
230 */
231 @Override
232 protected void handlePropertyChange(int propertyId) {
233 if (propertyId == PROP_DIRTY) {
234 setDirty(true);
235 }
236 super.handlePropertyChange(propertyId);
237 }
238
239 /** {@inheritDoc} */
240 @Override
241 public void doSaveAs() {
242 }
243
244 /** {@inheritDoc} */
245 @Override
246 public boolean isSaveAsAllowed() {
247 return false;
248 }
249
250 /** {@inheritDoc} */
251 @Override
252 public void init(IEditorSite site, IEditorInput input)
253 throws PartInitException {
254
255 if (!(input instanceof TaxonEditorInput)) {
256 throw new PartInitException(
257 "Invalid Input: Must be TaxonEditorInput");
258 }
259
260 this.input = (TaxonEditorInput) input;
261 cdmEntitySession = this.input.getCdmEntitySession();
262
263 // try {
264 // // Listen for name changes,
265 // // change tab for this taxon editor accordingly
266 // getTaxon().addPropertyChangeListener("name",
267 // new PropertyChangeListener() {
268 // public void propertyChange(PropertyChangeEvent e) {
269 // setPartName();
270 // }
271 // });
272 // } catch (NullPointerException e) {
273 // EditorUtil.warn(getClass(),
274 // "Caught an NPE while initing an editor. This is most " +
275 // "likely due to the unsuccesful attempt to restore the former " +
276 // "state of the application. We ignore this because the workbench " +
277 // "will simply be reset.");
278 // }
279 setPartName();
280
281 super.init(site, input);
282 }
283
284 /**
285 * Calls <code>MultiPageEditorPart.setPartName(String partName)</code> with
286 * text appropriate to the state of the taxon: any taxon that has been saved
287 * will by necessity have a name to display; a new taxon should display
288 * "New taxon" in the editor tab.
289 */
290 protected void setPartName() {
291
292 String partName = null;
293 TaxonNameBase<?, ?> name = getTaxon().getName();
294
295 if (name != null) {
296 partName = name.getTitleCache();
297 }
298
299 if (partName == null || partName.equals("")) {
300 partName = ("New taxon");
301 }
302
303 setPartName(partName);
304 }
305
306 /**
307 * {@inheritDoc}
308 *
309 * Editor pages call this in their postOperation to notify the
310 * MultiPageTaxonEditor of unsaved changes
311 */
312 @Override
313 public void changed(Object element) {
314 // setDirty(true);
315 dirty = true;
316 super.editorDirtyStateChanged();
317 if (element instanceof TaxonBase) {
318 TaxonNameEditor page = (TaxonNameEditor) getPage(Page.NAME);
319 AbstractGroupedContainer container = page.getContainer((TaxonBase) element);
320 if (container != null) {
321 container.refresh();
322 }
323 }
324 //refresh part title
325 //TODO: refresh taxon node in taxon navigator
326 setPartName();
327 }
328
329 /* (non-Javadoc)
330 * @see eu.etaxonomy.taxeditor.model.IDirtyMarkableSelectionProvider#forceDirty()
331 */
332 @Override
333 public void forceDirty() {
334 changed(null);
335 }
336
337 /**
338 * The accepted taxon that is the input for this editor
339 *
340 * @return the accepted taxon
341 */
342 public Taxon getTaxon() {
343 return input.getTaxon();
344 }
345
346 /*
347 * (non-Javadoc)
348 *
349 * @see
350 * eu.etaxonomy.cdm.api.conversation.IConversationEnabled#getConversationHolder
351 * ()
352 */
353 /**
354 * <p>
355 * getConversationHolder
356 * </p>
357 *
358 * @return a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
359 * object.
360 */
361 @Override
362 public ConversationHolder getConversationHolder() {
363 return conversation;
364 }
365
366 /**
367 * <p>
368 * setConversationHolder
369 * </p>
370 *
371 * @param conversation
372 * a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
373 * object.
374 */
375 public void setConversationHolder(ConversationHolder conversation) {
376 this.conversation = conversation;
377 }
378
379 /**
380 * <p>
381 * Getter for the field <code>undoContext</code>.
382 * </p>
383 *
384 * @return a {@link org.eclipse.core.commands.operations.IUndoContext}
385 * object.
386 */
387 public IUndoContext getUndoContext() {
388 return undoContext;
389 }
390
391 /**
392 * <p>
393 * Setter for the field <code>undoContext</code>.
394 * </p>
395 *
396 * @param undoContext
397 * a {@link org.eclipse.core.commands.operations.IUndoContext}
398 * object.
399 */
400 public void setUndoContext(IUndoContext undoContext) {
401 this.undoContext = undoContext;
402 }
403
404 /** {@inheritDoc} */
405 @Override
406 public void setFocus() {
407 // logger.warn("Setting focus to editor");
408 // bind the conversation
409 getConversationHolder().bind();
410 if(cdmEntitySession != null) {
411 cdmEntitySession.bind();
412 }
413 // pass focus to the active editor page
414 getActiveEditor().setFocus();
415 }
416
417 /*
418 * (non-Javadoc)
419 *
420 * @see
421 * eu.etaxonomy.cdm.persistence.hibernate.ICdmPostCrudObserver#update(eu
422 * .etaxonomy.cdm.persistence.hibernate.CdmCrudEvent)
423 */
424 /** {@inheritDoc} */
425 @Override
426 public void update(CdmDataChangeMap events) {
427 if (dataChangeBehavior == null) {
428 dataChangeBehavior = new MultiPageTaxonEditorDataChangeBehaviour(this);
429 }
430
431 DataChangeBridge.handleDataChange(events, dataChangeBehavior);
432 }
433
434 /*
435 * (non-Javadoc)
436 *
437 * @see
438 * eu.etaxonomy.taxeditor.store.operations.IPostOperationEnabled#postOperation
439 * ()
440 */
441 /** {@inheritDoc} */
442 @Override
443 public boolean postOperation(CdmBase objectAffectedByOperation) {
444 setDirty(true);
445
446 for (IEditorPart editor : this.getPages()) {
447 if (editor instanceof IPostOperationEnabled) {
448 ((IPostOperationEnabled) editor).postOperation(objectAffectedByOperation);
449 } else {
450 MessagingUtils.warn(getClass(), "postOperation not enabled for editor " + editor);
451 }
452 }
453 MessagingUtils.warn(getClass(), "postOperation called on MultiPageTaxonEditor. Can you make it more specific?");
454
455 return false;
456 }
457
458 /**
459 * Returns an <code>IEditorPart</code> implementation by type
460 *
461 * @param page
462 * the page type
463 * @return a {@link eu.etaxonomy.taxeditor.editor.IMultiPageTaxonEditorPage}
464 * object.
465 */
466 public IMultiPageTaxonEditorPage getPage(Page page) {
467 for (IEditorPart editor : this.getPages()) {
468 if (editor.getClass().equals(page.getClazz())) {
469 return (IMultiPageTaxonEditorPage) editor;
470 }
471 }
472 return null;
473 }
474
475 /**
476 * Return a list of <code>AbstractTaxonEditor</code>s registered with this
477 * <code>MultiPageTaxonEditor</code>.
478 *
479 * @return a {@link java.util.List} object.
480 */
481 public List<IMultiPageTaxonEditorPage> getPages() {
482 ArrayList<IMultiPageTaxonEditorPage> editors = new ArrayList<IMultiPageTaxonEditorPage>();
483 for (int i = 0; i < this.getPageCount(); i++) {
484
485 editors.add((IMultiPageTaxonEditorPage) this.getEditor(i));
486 }
487 return editors;
488 }
489
490 /**
491 * Refreshes a certain page of the MultipageTaxonEditor
492 *
493 * @param page
494 * a {@link eu.etaxonomy.taxeditor.editor.Page} object.
495 * @return a boolean.
496 */
497 public boolean redraw(Page page) {
498 return redraw(page, true);
499 }
500
501 /**
502 * Refreshes a certain page of the MultipageTaxonEditor and sets focus to
503 * that page
504 *
505 * @param page
506 * a {@link eu.etaxonomy.taxeditor.editor.Page} object.
507 * @param focus
508 * a boolean.
509 * @return a boolean.
510 */
511 public boolean redraw(Page page, boolean focus) {
512 IMultiPageTaxonEditorPage editorPage = getPage(page);
513 return editorPage != null && editorPage.redraw(focus);
514 }
515
516 /**
517 * <p>
518 * onComplete
519 * </p>
520 *
521 * @return a boolean.
522 */
523 @Override
524 public boolean onComplete() {
525 return false;
526 }
527
528 /**
529 * Reloads the data for this
530 */
531 public void reload() {
532 if (isDirty()) {
533 MessagingUtils.warningDialog("Editor has unsaved data", getClass(), "This editor can not be "
534 + "refreshed because it contains unsaved data. Refreshing "
535 + "this editor would discard the changes. Please save this editor, "
536 + "close and reopen it manually in order to get the latest content");
537 } else {
538 TaxonEditorInput input = (TaxonEditorInput) getEditorInput();
539
540 UUID uuid = input.getTaxonNode().getUuid();
541
542 conversation.clear();
543
544 try {
545 TaxonEditorInput newInput = TaxonEditorInput.NewInstance(uuid);
546 setInput(newInput);
547 for (IMultiPageTaxonEditorPage editorPart : getPages()) {
548 editorPart.redraw();
549 }
550 } catch (Exception e) {
551 MessagingUtils.messageDialog("Error refreshing editor", getClass(), "Could not refresh this editor", e);
552 }
553 }
554 }
555
556 @Override
557 public String toString() {
558 return String.format("%s[%s]", this.getClass().getSimpleName(), getEditorInput());
559 }
560
561 @Override
562 public boolean permissionsSatisfied() {
563 IEditorPart activeEditor = getActiveEditor();
564 if(activeEditor != null && ISecuredEditor.class.isAssignableFrom(activeEditor.getClass())){
565 return ((ISecuredEditor)activeEditor).permissionsSatisfied();
566 }
567 return true;
568 }
569
570 /* (non-Javadoc)
571 * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled#getCdmEntitySession()
572 */
573 @Override
574 public ICdmEntitySession getCdmEntitySession() {
575 return cdmEntitySession;
576 }
577
578 /* (non-Javadoc)
579 * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled#getRootEntities()
580 */
581 @Override
582 public List<TaxonNode> getRootEntities() {
583 return input.getRootEntities();
584 }
585
586 /* (non-Javadoc)
587 * @see eu.etaxonomy.cdm.api.application.ICdmChangeListener#onChange(eu.etaxonomy.cdm.api.application.CdmChangeEvent)
588 */
589 @Override
590 public void onChange(CdmChangeEvent event) {
591 // TODO Auto-generated method stub
592
593 }
594 }