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