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