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