7b21fe6fd3dc0f492f682f52e3197fa02b04e736
[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
45 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
46 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
47 import eu.etaxonomy.cdm.model.common.CdmBase;
48 import eu.etaxonomy.cdm.model.taxon.Taxon;
49 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
50 import eu.etaxonomy.taxeditor.model.Resources;
51 import eu.etaxonomy.taxeditor.operations.IPostOperationEnabled;
52 import eu.etaxonomy.taxeditor.propertysheet.EditorPropertySheetEntry;
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 // // FIXME TabbedPropertySheet stuff
194 // provider.setSelection(new StructuredSelection(this.selectedObject));
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 // // FIXME TabbedPropertySheet stuff
268 // EditorTabbedPropertySheetPage page = new EditorTabbedPropertySheetPage(this);
269 // return page;
270 }
271
272 return super.getAdapter(type);
273 }
274
275 public Taxon getTaxon() {
276 return taxon;
277 }
278
279 public void setDirty() {
280 managedForm.dirtyStateChanged();
281 }
282
283
284
285 /*
286 * (non-Javadoc)
287 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
288 */
289 @Override
290 public void setFocus(){
291 Object selection;
292
293 if(selectedData == null){
294 selection = (objectAffectedByLastOperation == null) ? null : objectAffectedByLastOperation;
295 }else{
296 selection = (objectAffectedByLastOperation == null) ? selectedData : objectAffectedByLastOperation;
297 }
298
299 // make selection
300 if (selection == null){
301 GroupedComposite focusComposite = firstGroupedComposite;
302 setSelection(focusComposite);
303 focusComposite.drawBorder();
304 } else {
305 selectedData = selection;
306
307 for (GroupedComposite composite : getGroupedComposites()) {
308 if (selectedData.equals(composite.getData())) {
309 setSelection((GroupedComposite) composite);
310 ((GroupedComposite) composite).drawBorder();
311 ((GroupedComposite) composite).setSelected();
312 break;
313 }
314 }
315 }
316
317 // reset
318 objectAffectedByLastOperation = null;
319 }
320
321 /**
322 * This method returns all <code>GroupedComposite</code> controls currently attached to this editor.
323 *
324 * @return a <code>List</code> of <code>GroupedComposite</code> controls.
325 */
326 public List<GroupedComposite> getGroupedComposites(){
327 return getGroupedCompositesRecursively(scrolledForm.getBody(), new ArrayList<GroupedComposite>());
328 }
329
330
331 /**
332 * Recursively traverse the composite hierarchy and collect all <code>GroupedComposite</code> controls.
333 *
334 * @param composite the composite to start recursing from
335 * @param groupedComposites a <code>List</code> the found composites are stored in
336 * @return a <code>List</code> containing all found <code>GroupedComposite</code> controls
337 */
338 private List<GroupedComposite> getGroupedCompositesRecursively(Composite composite, List<GroupedComposite> groupedComposites){
339 for(Control child : composite.getChildren()){
340 if(child instanceof GroupedComposite){
341 groupedComposites.add((GroupedComposite) child);
342 }else if(child instanceof Composite){
343 getGroupedCompositesRecursively((Composite) child, groupedComposites);
344 }
345 }
346 return groupedComposites;
347 }
348
349 /**
350 * @return
351 */
352 public List<GroupComposite> getGroupComposites() {
353 return getGroupCompositesRecursively(scrolledForm.getBody(), new ArrayList<GroupComposite>());
354 }
355
356 /**
357 * Recursively traverse the composite hierarchy and collect all <code>GroupComposite</code> controls.
358 *
359 * @param composite the composite to start recursing from
360 * @param groupComposites a <code>List</code> the found composites are stored in
361 * @return a <code>List</code> containing all found <code>GroupComposite</code> controls
362 */
363 private List<GroupComposite> getGroupCompositesRecursively(Composite composite, List<GroupComposite> groupComposites){
364 for(Control child : composite.getChildren()){
365 if(child instanceof GroupComposite){
366 groupComposites.add((GroupComposite) child);
367 getGroupCompositesRecursively((Composite) child, groupComposites);
368 }
369 }
370 return groupComposites;
371 }
372
373 public void setInput(IEditorInput input){
374 this.setInputWithNotify(input);
375 }
376
377 /*
378 * (non-Javadoc)
379 * @see eu.etaxonomy.cdm.api.conversation.IConversationEnabled#getConversationHolder()
380 */
381 public ConversationHolder getConversationHolder(){
382 return conversation;
383 }
384
385 /*
386 * (non-Javadoc)
387 * @see eu.etaxonomy.cdm.persistence.hibernate.ICdmPostCrudObserver#update(eu.etaxonomy.cdm.persistence.hibernate.CdmCrudEvent)
388 */
389 public void update(CdmDataChangeMap events) {
390 //redraw();
391 }
392
393 /**
394 * Redraws this editor
395 * return true on success
396 */
397 public boolean redraw(){
398 return redraw(true);
399 }
400
401 /**
402 * Redraws the editor controls
403 *
404 * @param focus if set to true the focus is set on the editor
405 * @return true on success
406 */
407 public boolean redraw(boolean focus) {
408
409 isRedrawing = true;
410
411 this.selectedObject = null;
412 managedForm.getForm().dispose();
413 createManagedForm(partComposite);
414
415 setFocus();
416
417 isRedrawing = false;
418
419 return true;
420
421 }
422
423 public boolean getIsRedrawing() {
424 return isRedrawing;
425 }
426
427 public MultiPageTaxonEditor getMultiPageTaxonEditor() {
428 return editor;
429 }
430
431
432 /*
433 * (non-Javadoc)
434 * @see eu.etaxonomy.taxeditor.store.operations.IPostOperationEnabled#postOperation()
435 */
436 public boolean postOperation(CdmBase objectAffectedByOperation) {
437
438 if (isRedrawing) {
439 return false;
440 }
441
442 if (objectAffectedByOperation == null && selectedData instanceof CdmBase) {
443 this.objectAffectedByLastOperation = (CdmBase) selectedData;
444 } else {
445 this.objectAffectedByLastOperation = objectAffectedByOperation;
446 }
447 editor.setDirty();
448 return redraw();
449 }
450
451
452 /**
453 * @return the managedForm
454 */
455 public ManagedForm getManagedForm() {
456 return managedForm;
457 }
458
459 public Composite getTopLevelComposite() {
460 return this.getManagedForm().getForm().getBody();
461 }
462
463 /**
464 *
465 * @return the ID as defined in plugin.xml
466 */
467 public abstract String getID();
468
469 /**
470 * @return the menu
471 */
472 public Menu getMenu() {
473 return menu;
474 }
475
476 /**
477 * @return the firstGroupedComposite
478 */
479 public GroupedComposite getFirstGroupedComposite() {
480 return firstGroupedComposite;
481 }
482
483 class EditorPropertySheetPage extends PropertySheetPage {
484
485 EditorPropertySheetPage() {
486 super();
487
488 // Override sorter to simply display names as first-in-first-out
489 setSorter(new PropertySheetSorter() {
490 public int compare(IPropertySheetEntry entryA, IPropertySheetEntry entryB) {
491 return 0;
492 }
493 public int compareCategories(String categoryA, String categoryB) {
494 return 0;
495 }
496 public void sort(IPropertySheetEntry[] entries) {
497 // do nothing
498 }
499 });
500 }
501
502 public void makeContributions(IMenuManager menuManager,
503 IToolBarManager toolBarManager, IStatusLineManager statusLineManager) {
504 super.makeContributions(menuManager, toolBarManager, statusLineManager);
505
506 // Remove "Show categories", "Show advanced properties", "Restore default value"
507 toolBarManager.removeAll();
508 menuManager.removeAll();
509 }
510
511 public Control getControl() {
512 Control control = super.getControl();
513
514 // Save the property sheet tree for easy access as needed
515 if (!control.isDisposed()) {
516 if (control instanceof Tree) {
517 PropertySheetUtil.setPropertySheetTree((Tree) control);
518 }
519 }
520 return control;
521 }
522 }
523
524 /**
525 *
526 */
527 public void navigateToNextMember() {
528 try{
529 List<GroupedComposite> composites = getGroupedCompositesRecursively(scrolledForm.getBody(), new ArrayList<GroupedComposite>());
530
531 for(int i = 0; i < composites.size(); i++){
532 if(selectedData == composites.get(i).getData()){
533 selectedData = composites.get(i+1).getData();
534 break;
535 }
536 }
537 setFocus();
538 }catch(IndexOutOfBoundsException e){}
539 }
540
541 /**
542 *
543 */
544 public void navigateToPreviousMember() {
545 try{
546 List<GroupedComposite> composites = getGroupedCompositesRecursively(scrolledForm.getBody(), new ArrayList<GroupedComposite>());
547
548 for(int i = 0; i < composites.size(); i++){
549 if(selectedData == composites.get(i).getData()){
550 selectedData = composites.get(i-1).getData();
551 break;
552 }
553 }
554 setFocus();
555 }catch(IndexOutOfBoundsException e){}
556 }
557
558 /**
559 * Used by ITabbedPropertySheet...
560 */
561 public String getContributorId() {
562 String id = getID();
563 return id;
564 }
565
566 }