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