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