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