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