.
[taxeditor.git] / taxeditor-editor / src / main / java / eu / etaxonomy / taxeditor / editor / AbstractTaxonEditor.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
15 import org.apache.log4j.Logger;
16 import org.eclipse.core.commands.operations.IUndoContext;
17 import org.eclipse.core.runtime.IAdaptable;
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.jface.action.IMenuManager;
20 import org.eclipse.jface.action.IStatusLineManager;
21 import org.eclipse.jface.action.IToolBarManager;
22 import org.eclipse.jface.action.MenuManager;
23 import org.eclipse.jface.viewers.ISelectionProvider;
24 import org.eclipse.jface.viewers.StructuredSelection;
25 import org.eclipse.swt.SWT;
26 import org.eclipse.swt.widgets.Composite;
27 import org.eclipse.swt.widgets.Control;
28 import org.eclipse.swt.widgets.Display;
29 import org.eclipse.swt.widgets.Menu;
30 import org.eclipse.swt.widgets.Tree;
31 import org.eclipse.ui.IEditorInput;
32 import org.eclipse.ui.IEditorSite;
33 import org.eclipse.ui.PartInitException;
34 import org.eclipse.ui.forms.ManagedForm;
35 import org.eclipse.ui.forms.widgets.ScrolledForm;
36 import org.eclipse.ui.forms.widgets.TableWrapLayout;
37 import org.eclipse.ui.part.EditorPart;
38 import org.eclipse.ui.views.properties.IPropertySheetEntry;
39 import org.eclipse.ui.views.properties.IPropertySheetPage;
40 import org.eclipse.ui.views.properties.IPropertySource;
41 import org.eclipse.ui.views.properties.PropertySheetPage;
42 import org.eclipse.ui.views.properties.PropertySheetSorter;
43 import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
44 import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
45
46 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
47 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
48 import eu.etaxonomy.cdm.model.common.CdmBase;
49 import eu.etaxonomy.cdm.model.taxon.Taxon;
50 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
51 import eu.etaxonomy.taxeditor.model.Resources;
52 import eu.etaxonomy.taxeditor.operations.IPostOperationEnabled;
53 import eu.etaxonomy.taxeditor.propertysheet.PropertySheetUtil;
54
55 /**
56 * The abstract editor for displaying a category of <code>Taxon</code> data, corresponding
57 * to the tabs ("Name", "Descriptive", etc.) at the bottom of a <code>Taxon</code> view. Implements
58 * <code>IAdaptable</code> in order to display properties of the objects whose UI elements have focus.
59 * <p>
60 * Implementing classes can choose to show an object in the property sheet when the
61 * <code>AbstractTaxonEditor</code> gets focus, by passing the object to the method
62 * <code>setDefaultPropertySheetObject</code>, for instance, in the method<code>init</code>.
63 * </p>
64 * @author p.ciardelli
65 * @author n.hoffmann
66 * @created 10.09.2008
67 * @version 1.0
68 */
69 public abstract class AbstractTaxonEditor extends EditorPart implements IAdaptable, IConversationEnabled, IPostOperationEnabled, ITabbedPropertySheetPageContributor {
70 private static final Logger logger = Logger
71 .getLogger(AbstractTaxonEditor.class);
72
73 protected Taxon taxon;
74
75 /**
76 * When this <code>EditorPart</code> gets focus, the data structure of
77 * <code>defaultPropertyObject</code> is displayed in the property sheet.
78 */
79
80 protected ManagedForm managedForm;
81 protected ScrolledForm scrolledForm;
82 protected Composite parent;
83 protected ISelectionProvider provider;
84
85 protected MultiPageTaxonEditor editor;
86
87 protected IHasPropertySource selectedObject;
88
89 protected Composite partComposite;
90 protected GroupedComposite firstGroupedComposite;
91
92 protected Object selectedData;
93
94 protected ConversationHolder conversation;
95
96 private MenuManager menuManager;
97
98 private Menu menu;
99
100 /**
101 * The object that was affected by last operation.
102 */
103 private CdmBase objectAffectedByLastOperation;
104
105 private boolean isRedrawing;
106
107 protected AbstractTaxonEditor(MultiPageTaxonEditor editor){
108 this.editor = editor;
109 this.conversation = editor.getConversationHolder();
110 }
111
112 /* (non-Javadoc)
113 * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
114 */
115 public void doSave(IProgressMonitor monitor) {
116 conversation.commit();
117 }
118
119 /* (non-Javadoc)
120 * @see org.eclipse.ui.part.EditorPart#doSaveAs()
121 */
122 public void doSaveAs() {}
123
124 /* (non-Javadoc)
125 * @see org.eclipse.ui.part.EditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
126 */
127 public void init(IEditorSite site, IEditorInput input)
128 throws PartInitException {
129 if (!(input instanceof IEditorInput))
130 throw new PartInitException(
131 "Invalid Input: Must be IEditorInput");
132
133 if (input.getAdapter(Taxon.class) != null) {
134 taxon = (Taxon) input.getAdapter(Taxon.class);
135 } else {
136 throw new PartInitException(
137 "Invalid Input: Taxon cannot be null");
138 }
139
140 setSite(site);
141 setInput(input);
142
143 provider = new SimpleSelectionProvider();
144 getSite().setSelectionProvider(provider);
145
146
147 logger.trace("New Editor instance created.");
148 }
149
150 public IUndoContext getUndoContext() {
151 return editor.getUndoContext();
152 }
153
154 /**
155 * If there is a default property sheet object with a corresponding property source class,
156 * display it in the property sheet. Otherwise, empty the property sheet with an empty
157 * <code>StructuredSelection</code>.
158 *
159 * @param selectedObject
160 */
161 protected void setSelection(IHasPropertySource selectedObject) {
162
163 // Unpaint last selection - last selection will only be unpainted
164 // when something else on this page is selected
165 if (this.selectedObject instanceof GroupedComposite) {
166 GroupedComposite composite = ((GroupedComposite) this.selectedObject);
167 composite.unpaintBorder();
168 composite.setBackground(Resources.getColor(Resources.COLOR_COMPOSITE_BACKGROUND));
169 }
170
171 // Set the selection to this editor's selected object
172 this.selectedObject = selectedObject;
173
174 // set background on selection TODO wanted to move this to setFocus() on the selectedObject but that doesn't work (infinite loop)
175 ((GroupedComposite) selectedObject).setBackground(Resources.getColor(Resources.COLOR_COMPOSITE_SELECTED));
176
177
178 if (selectedObject instanceof Composite) {
179 this.selectedData = ((Composite) selectedObject).getData();
180 }
181
182 // // Get the selection's property source, pass it to the selection provider
183 // IPropertySource propertySource = null;
184 // if (selectedObject != null) {
185 // propertySource = selectedObject.getPropertySource();
186 // }
187 // if (propertySource == null) {
188 // provider.setSelection(new StructuredSelection());
189 // } else {
190 // provider.setSelection(new StructuredSelection(propertySource));
191 // }
192
193
194 provider.setSelection(new StructuredSelection(this.selectedData));
195
196 }
197
198 /* (non-Javadoc)
199 * @see org.eclipse.ui.part.EditorPart#isDirty()
200 */
201 public boolean isDirty() {
202 return false;
203 }
204
205 /* (non-Javadoc)
206 * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
207 */
208 public boolean isSaveAsAllowed() {
209 return false;
210 }
211
212 /* (non-Javadoc)
213 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
214 */
215 public void createPartControl(Composite composite) {
216
217 this.partComposite = composite;
218
219 createManagedForm(composite);
220 }
221
222 protected void createManagedForm(Composite composite) {
223
224 managedForm = new ManagedForm(composite) {
225 public void dirtyStateChanged() {
226 firePropertyChange(PROP_DIRTY);
227 }
228 public boolean setInput(Object input) {
229 if (input instanceof IHasPropertySource) {
230 setSelection((IHasPropertySource)input);
231 }
232 return super.setInput(input);
233 }
234 };
235
236 scrolledForm = managedForm.getForm();
237 parent = scrolledForm.getBody();
238
239 // register the context menu
240 menuManager = new MenuManager();
241 ISelectionProvider selectionProvider = getSite().getSelectionProvider();
242 getSite().registerContextMenu(getID(), menuManager, selectionProvider);
243
244 menu = menuManager.createContextMenu(parent);
245 scrolledForm.setMenu(menu);
246
247 parent.setData(taxon);
248
249 parent.setLayout(new TableWrapLayout());
250 parent.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
251 }
252
253 @SuppressWarnings("unchecked")
254 public Object getAdapter(Class type) {
255 if (type == IPropertySheetPage.class) {
256 //
257 // PropertySheetPage page = new EditorPropertySheetPage();
258 //
259 // PropertySheetUtil.setPropertySheetPage(page);
260 //
261 // EditorPropertySheetEntry entry = new EditorPropertySheetEntry(taxon, this);
262 // page.setRootEntry(entry);
263 // page.refresh();
264 //
265 // return page;
266
267 TabbedPropertySheetPage page = new TabbedPropertySheetPage(this);
268
269
270 return page;
271
272 }
273
274 return super.getAdapter(type);
275 }
276
277 public Taxon getTaxon() {
278 return taxon;
279 }
280
281 public void setDirty() {
282 managedForm.dirtyStateChanged();
283 }
284
285 public void setFocus(){
286
287 Object selection;
288
289 if(selectedData == null){
290 selection = (objectAffectedByLastOperation == null) ? null : objectAffectedByLastOperation;
291 }else{
292 selection = (objectAffectedByLastOperation == null) ? selectedData : objectAffectedByLastOperation;
293 }
294
295 // make selection
296 if (selection == null){
297 GroupedComposite focusComposite = firstGroupedComposite;
298 setSelection(focusComposite);
299 focusComposite.drawBorder();
300 } else {
301 selectedData = selection;
302
303 for (GroupedComposite composite : getGroupedComposites()) {
304 if (selectedData.equals(composite.getData())) {
305 setSelection((GroupedComposite) composite);
306 ((GroupedComposite) composite).drawBorder();
307 ((GroupedComposite) composite).setSelected();
308 break;
309 }
310 }
311 }
312
313 // reset
314 objectAffectedByLastOperation = null;
315 }
316
317 /**
318 * This method returns all <code>GroupedComposite</code> controls currently attached to this editor.
319 *
320 * @return a <code>List</code> of <code>GroupedComposite</code> controls.
321 */
322 public List<GroupedComposite> getGroupedComposites(){
323 return getGroupedCompositesRecursively(scrolledForm.getBody(), new ArrayList<GroupedComposite>());
324 }
325
326
327 /**
328 * Recursively traverse the composite hierarchy and collect all <code>GroupedComposite</code> controls.
329 *
330 * @param composite the composite to start recursing from
331 * @param groupedComposites a <code>List</code> the found composites are stored in
332 * @return a <code>List</code> containing all found <code>GroupedComposite</code> controls
333 */
334 private List<GroupedComposite> getGroupedCompositesRecursively(Composite composite, List<GroupedComposite> groupedComposites){
335 for(Control child : composite.getChildren()){
336 if(child instanceof GroupedComposite){
337 groupedComposites.add((GroupedComposite) child);
338 }else if(child instanceof Composite){
339 getGroupedCompositesRecursively((Composite) child, groupedComposites);
340 }
341 }
342 return groupedComposites;
343 }
344
345 /**
346 * @return
347 */
348 public List<GroupComposite> getGroupComposites() {
349 return getGroupCompositesRecursively(scrolledForm.getBody(), new ArrayList<GroupComposite>());
350 }
351
352 /**
353 * Recursively traverse the composite hierarchy and collect all <code>GroupComposite</code> controls.
354 *
355 * @param composite the composite to start recursing from
356 * @param groupComposites a <code>List</code> the found composites are stored in
357 * @return a <code>List</code> containing all found <code>GroupComposite</code> controls
358 */
359 private List<GroupComposite> getGroupCompositesRecursively(Composite composite, List<GroupComposite> groupComposites){
360 for(Control child : composite.getChildren()){
361 if(child instanceof GroupComposite){
362 groupComposites.add((GroupComposite) child);
363 }else if(child instanceof Composite){
364 getGroupCompositesRecursively((Composite) child, groupComposites);
365 }
366 }
367 return groupComposites;
368 }
369
370 public void setInput(IEditorInput input){
371 this.setInputWithNotify(input);
372 }
373
374 /*
375 * (non-Javadoc)
376 * @see eu.etaxonomy.cdm.api.conversation.IConversationEnabled#getConversationHolder()
377 */
378 public ConversationHolder getConversationHolder(){
379 return conversation;
380 }
381
382 /*
383 * (non-Javadoc)
384 * @see eu.etaxonomy.cdm.persistence.hibernate.ICdmPostCrudObserver#update(eu.etaxonomy.cdm.persistence.hibernate.CdmCrudEvent)
385 */
386 public void update(CdmDataChangeMap events) {
387 //redraw();
388 }
389
390 /**
391 * Redraws this editor
392 */
393 public boolean redraw() {
394
395 isRedrawing = true;
396
397 this.selectedObject = null;
398 managedForm.getForm().dispose();
399 createManagedForm(partComposite);
400
401 setFocus();
402
403 isRedrawing = false;
404
405 return true;
406
407 }
408
409 public boolean getIsRedrawing() {
410 return isRedrawing;
411 }
412
413 public MultiPageTaxonEditor getMultiPageTaxonEditor() {
414 return editor;
415 }
416
417
418 /*
419 * (non-Javadoc)
420 * @see eu.etaxonomy.taxeditor.store.operations.IPostOperationEnabled#postOperation()
421 */
422 public boolean postOperation(CdmBase objectAffectedByOperation) {
423
424 if (isRedrawing) {
425 return false;
426 }
427
428 if (objectAffectedByOperation == null && selectedData instanceof CdmBase) {
429 this.objectAffectedByLastOperation = (CdmBase) selectedData;
430 } else {
431 this.objectAffectedByLastOperation = objectAffectedByOperation;
432 }
433 editor.setDirty();
434 return redraw();
435 }
436
437
438 /**
439 * @return the managedForm
440 */
441 public ManagedForm getManagedForm() {
442 return managedForm;
443 }
444
445 public Composite getTopLevelComposite() {
446 return this.getManagedForm().getForm().getBody();
447 }
448
449 /**
450 *
451 * @return the ID as defined in plugin.xml
452 */
453 public abstract String getID();
454
455 /**
456 * @return the menu
457 */
458 public Menu getMenu() {
459 return menu;
460 }
461
462 /**
463 * @return the firstGroupedComposite
464 */
465 public GroupedComposite getFirstGroupedComposite() {
466 return firstGroupedComposite;
467 }
468
469 class EditorPropertySheetPage extends PropertySheetPage {
470
471 EditorPropertySheetPage() {
472 super();
473
474 // Override sorter to simply display names as first-in-first-out
475 setSorter(new PropertySheetSorter() {
476 public int compare(IPropertySheetEntry entryA, IPropertySheetEntry entryB) {
477 return 0;
478 }
479 public int compareCategories(String categoryA, String categoryB) {
480 return 0;
481 }
482 public void sort(IPropertySheetEntry[] entries) {
483 // do nothing
484 }
485 });
486 }
487
488 public void makeContributions(IMenuManager menuManager,
489 IToolBarManager toolBarManager, IStatusLineManager statusLineManager) {
490 super.makeContributions(menuManager, toolBarManager, statusLineManager);
491
492 // Remove "Show categories", "Show advanced properties", "Restore default value"
493 toolBarManager.removeAll();
494 menuManager.removeAll();
495 }
496
497 public Control getControl() {
498 Control control = super.getControl();
499
500 // Save the property sheet tree for easy access as needed
501 if (!control.isDisposed()) {
502 if (control instanceof Tree) {
503 PropertySheetUtil.setPropertySheetTree((Tree) control);
504 }
505 }
506 return control;
507 }
508 }
509
510 /**
511 *
512 */
513 public void navigateToNextMember() {
514 try{
515 List<GroupedComposite> composites = getGroupedCompositesRecursively(scrolledForm.getBody(), new ArrayList<GroupedComposite>());
516
517 for(int i = 0; i < composites.size(); i++){
518 if(selectedData == composites.get(i).getData()){
519 selectedData = composites.get(i+1).getData();
520 break;
521 }
522 }
523 setFocus();
524 }catch(IndexOutOfBoundsException e){}
525 }
526
527 /**
528 *
529 */
530 public void navigateToPreviousMember() {
531 try{
532 List<GroupedComposite> composites = getGroupedCompositesRecursively(scrolledForm.getBody(), new ArrayList<GroupedComposite>());
533
534 for(int i = 0; i < composites.size(); i++){
535 if(selectedData == composites.get(i).getData()){
536 selectedData = composites.get(i-1).getData();
537 break;
538 }
539 }
540 setFocus();
541 }catch(IndexOutOfBoundsException e){}
542 }
543
544 /**
545 * Used by ITabbedPropertySheet...
546 */
547 public String getContributorId() {
548 String id = getID();
549 return id;
550 }
551
552 }