fix #6059 Save expand state for details view, supplemental view
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / ui / element / AbstractFormSection.java
1 /**
2 *
3 */
4 package eu.etaxonomy.taxeditor.ui.element;
5
6 import java.util.ConcurrentModificationException;
7 import java.util.HashSet;
8 import java.util.List;
9 import java.util.Set;
10
11 import org.eclipse.core.runtime.Assert;
12 import org.eclipse.jface.util.IPropertyChangeListener;
13 import org.eclipse.jface.util.PropertyChangeEvent;
14 import org.eclipse.jface.viewers.ISelectionChangedListener;
15 import org.eclipse.jface.viewers.ISelectionProvider;
16 import org.eclipse.jface.viewers.IStructuredSelection;
17 import org.eclipse.jface.viewers.SelectionChangedEvent;
18 import org.eclipse.jface.viewers.StructuredSelection;
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.events.SelectionEvent;
21 import org.eclipse.swt.events.SelectionListener;
22 import org.eclipse.swt.graphics.Color;
23 import org.eclipse.swt.widgets.Composite;
24 import org.eclipse.swt.widgets.Control;
25 import org.eclipse.swt.widgets.Display;
26 import org.eclipse.swt.widgets.TypedListener;
27 import org.eclipse.swt.widgets.Widget;
28 import org.eclipse.ui.IEditorPart;
29 import org.eclipse.ui.forms.events.ExpansionEvent;
30 import org.eclipse.ui.forms.events.IExpansionListener;
31 import org.eclipse.ui.forms.widgets.Section;
32 import org.eclipse.ui.forms.widgets.TableWrapLayout;
33 import org.eclipse.ui.forms.widgets.ToggleHyperlink;
34
35 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
36 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
37 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
38 import eu.etaxonomy.taxeditor.model.AbstractUtility;
39 import eu.etaxonomy.taxeditor.model.MessagingUtils;
40 import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
41
42 /**
43 * <p>
44 * Abstract super class for a {@link Section} GUI element that visualizes a CDM
45 * entity, manages a conversation and listens to selections
46 * </p>
47 *
48 * @param <ENTITY> A CDM entity which should be visualized by this section.
49 *
50 * @author n.hoffmann
51 * @created Feb 22, 2010
52 * @version 1.0
53 * @param <T>
54 */
55 //TODO shouldn't ENTITY be bound with super class ICdmBase for example (AbstractFormSection<ENTITY extends ICdmBase>)?
56 public abstract class AbstractFormSection<ENTITY> extends Section implements ISelectionChangedListener, IEntityElement<ENTITY>, IConversationEnabled {
57
58 /**
59 * The default number of columns in detail sections
60 */
61 public static final int DEFAULT_NUM_COLUMNS = 2;
62
63 private ISelectionProvider selectionProvider;
64
65 private ENTITY entity;
66
67 private final Set<ICdmFormElement> elements = new HashSet<ICdmFormElement>();
68
69 protected CdmFormFactory formFactory;
70
71 private List<IPropertyChangeListener> propertyChangeListeners;
72
73 private ICdmFormElement parentElement;
74
75 private Color persistentBackgroundColor;
76
77 /**
78 * <p>
79 * Constructor for AbstractFormSection.
80 * </p>
81 *
82 * @param conversation
83 * TODO
84 * @param style
85 * a int.
86 * @param formFactory
87 * a {@link eu.etaxonomy.taxeditor.ui.element.CdmFormFactory}
88 * object.
89 * @param parentElement
90 * a {@link eu.etaxonomy.taxeditor.ui.element.ICdmFormElement}
91 * object.
92 * @param <ENTITY>
93 * a ENTITY object.
94 */
95 protected AbstractFormSection(CdmFormFactory formFactory, ICdmFormElement parentElement, int style) {
96 super(parentElement.getLayoutComposite(), style);
97
98 this.parentElement = parentElement;
99
100 this.formFactory = formFactory;
101
102 this.setLayoutData(LayoutConstants.FILL());
103
104 Composite client = formFactory.createComposite(this, SWT.WRAP);
105 client.setBackgroundMode(SWT.INHERIT_DEFAULT);
106
107 TableWrapLayout layout = LayoutConstants.LAYOUT();
108 layout.bottomMargin = 10;
109 layout.rightMargin = 5;
110 layout.horizontalSpacing = 5;
111
112 client.setLayout(layout);
113
114 this.setClient(client);
115
116 }
117
118 /**
119 * <p>
120 * Constructor for AbstractFormSection.
121 * </p>
122 *
123 * @param formFactory
124 * a {@link eu.etaxonomy.taxeditor.ui.element.CdmFormFactory}
125 * object.
126 * @param conversation
127 * a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
128 * object.
129 * @param parentElement
130 * a {@link eu.etaxonomy.taxeditor.ui.element.ICdmFormElement}
131 * object.
132 * @param selectionProvider
133 * a {@link org.eclipse.jface.viewers.ISelectionProvider} object.
134 * @param style
135 * a int.
136 */
137 protected AbstractFormSection(CdmFormFactory formFactory, ICdmFormElement parentElement, ISelectionProvider selectionProvider, int style) {
138 this(formFactory, parentElement, style);
139 this.selectionProvider = selectionProvider;
140 }
141
142 /**
143 * <p>
144 * Getter for the field <code>propertyChangeListeners</code>.
145 * </p>
146 *
147 * @return a {@link java.util.Set} object.
148 */
149 @Override
150 public List<IPropertyChangeListener> getPropertyChangeListeners() {
151 return propertyChangeListeners;
152 }
153
154 /** {@inheritDoc} */
155 @Override
156 public void setPropertyChangeListeners(
157 List<IPropertyChangeListener> propertyChangeListeners) {
158 this.propertyChangeListeners = propertyChangeListeners;
159 }
160
161 /**
162 * <p>
163 * Setter for the field <code>entity</code>.
164 * </p>
165 *
166 * @param entity
167 * a ENTITY object.
168 */
169 public void setEntity(ENTITY entity) {
170 this.entity = entity;
171 addExpandListener();
172 }
173
174 /*
175 * (non-Javadoc)
176 *
177 * @see eu.etaxonomy.taxeditor.forms.IEntityElement#getEntity()
178 */
179 /**
180 * <p>
181 * Getter for the field <code>entity</code>.
182 * </p>
183 *
184 * @return a ENTITY object.
185 */
186 @Override
187 public ENTITY getEntity() {
188 return entity;
189 }
190
191 /**
192 * <p>
193 * getToggle
194 * </p>
195 *
196 * @return a {@link org.eclipse.ui.forms.widgets.ToggleHyperlink} object.
197 */
198 public ToggleHyperlink getToggle() {
199 return this.toggle;
200 }
201
202 /**
203 * <p>
204 * getSection
205 * </p>
206 *
207 * @return a {@link eu.etaxonomy.taxeditor.ui.element.AbstractFormSection}
208 * object.
209 */
210 public AbstractFormSection<ENTITY> getSection() {
211 return this;
212 }
213
214 /*
215 * (non-Javadoc)
216 *
217 * @see
218 * eu.etaxonomy.taxeditor.forms.IPropertyChangeEmitter#firePropertyChangeEvent
219 * ()
220 */
221 /** {@inheritDoc} */
222 @Override
223 public void firePropertyChangeEvent(CdmPropertyChangeEvent event) {
224 Assert.isNotNull(propertyChangeListeners, "No property change listeners.");
225 try {
226 for (Object listener : propertyChangeListeners) {
227 ((IPropertyChangeListener) listener).propertyChange(event);
228 }
229 } catch (ConcurrentModificationException e) {
230 MessagingUtils.warn(getClass(), "ConcurrentModificationException while handling PropertyChangeEvents."
231 + " It seems like this is not critical");
232 }
233 }
234
235 /**
236 * Fires a {@link CdmPropertyChangeEvent} with the given object as source.
237 *
238 * @param object
239 * the object on which the property changed
240 */
241 public void firePropertyChangeEvent(Object object) {
242 firePropertyChangeEvent(object, null);
243 }
244
245 /**
246 * Fires a {@link CdmPropertyChangeEvent} with the given object as source
247 * also containing the originating event
248 *
249 * @param object
250 * the object on which the property changed
251 * @param originatingEvent
252 * the originating event
253 */
254 public void firePropertyChangeEvent(Object object,
255 PropertyChangeEvent originatingEvent) {
256 firePropertyChangeEvent(new CdmPropertyChangeEvent(object,
257 originatingEvent));
258 }
259
260 /*
261 * (non-Javadoc)
262 *
263 * @see org.eclipse.swt.widgets.Composite#setFocus()
264 */
265 /** {@inheritDoc} */
266 @Override
267 public boolean setFocus() {
268 return getClient().setFocus();
269 }
270
271 /*
272 * (non-Javadoc)
273 *
274 * @see
275 * org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse
276 * .jface.util.PropertyChangeEvent)
277 */
278 /** {@inheritDoc} */
279 @Override
280 public void propertyChange(PropertyChangeEvent event) {
281 firePropertyChangeEvent(new CdmPropertyChangeEvent(this, event));
282 }
283
284 /*
285 * (non-Javadoc)
286 *
287 * @see
288 * org.eclipse.swt.widgets.Control#setBackground(org.eclipse.swt.graphics
289 * .Color)
290 */
291 /** {@inheritDoc} */
292 @Override
293 public void setBackground(Color color) {
294 for (ICdmFormElement element : getElements()) {
295 element.setBackground(color);
296 }
297 getLayoutComposite().setBackground(color);
298 super.setBackground(color);
299 }
300
301 @Override
302 public void setPersistentBackground(Color color) {
303 persistentBackgroundColor = color;
304 setBackground(color);
305 }
306
307 @Override
308 public Color getPersistentBackground() {
309 return persistentBackgroundColor;
310 }
311
312
313 /**
314 * <p>
315 * widgetSelected
316 * </p>
317 *
318 * @param e
319 * a {@link org.eclipse.swt.events.SelectionEvent} object.
320 */
321 public void widgetSelected(SelectionEvent e) {
322 Widget widget = e.widget;
323
324 if (widget instanceof Control) {
325 Control control = (Control) widget;
326 if (checkControlAncestryForWidget(control)) {
327 if (getEntity() != null) {
328 IStructuredSelection selection = new StructuredSelection(getEntity());
329 if (selectionProvider != null) {
330 selectionProvider.setSelection(selection);
331 }
332 }
333 }
334 }
335 }
336
337 private boolean checkControlAncestryForWidget(Control control) {
338 if (control.equals(this)) {
339 return true;
340 } else {
341 Control parent = control.getParent();
342 if (parent == null) {
343 return false;
344 } else {
345 return checkControlAncestryForWidget(parent);
346 }
347 }
348 }
349
350 /** {@inheritDoc} */
351 @Override
352 public void setSelected(boolean selected) {
353 if (selected) {
354 setBackground(Display.getCurrent().getSystemColor(
355 SWT.COLOR_LIST_SELECTION));
356 } else {
357 setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
358 }
359 }
360
361 /** {@inheritDoc} */
362 @Override
363 public void selectionChanged(SelectionChangedEvent event) {
364 if (event.getSelection() == CdmFormFactory.EMPTY_SELECTION) {
365 return;
366 }
367
368 IStructuredSelection selection = (IStructuredSelection) event
369 .getSelection();
370 setSelected(false);
371
372 Object selectedObject = selection.getFirstElement();
373
374 if (selectedObject != null && selectedObject.equals(getEntity())) {
375 setSelected(true);
376 }
377 }
378
379 /**
380 * <p>
381 * addSelectionListener
382 * </p>
383 *
384 * @param listener
385 * a {@link org.eclipse.swt.events.SelectionListener} object.
386 */
387 public void addSelectionListener(SelectionListener listener) {
388 addListener(SWT.Selection, new TypedListener(listener));
389 }
390
391 /**
392 * <p>
393 * removeSelectionListener
394 * </p>
395 *
396 * @param listener
397 * a {@link org.eclipse.swt.events.SelectionListener} object.
398 */
399 public void removeSelectionListener(SelectionListener listener) {
400 removeListener(SWT.Selection, listener);
401 }
402
403 /** {@inheritDoc} */
404 @Override
405 public void addElement(ICdmFormElement element) {
406 elements.add(element);
407 }
408
409 /**
410 * <p>
411 * removeElement
412 * </p>
413 *
414 * @param element
415 * a {@link eu.etaxonomy.taxeditor.ui.element.ICdmFormElement}
416 * object.
417 */
418 protected void removeElement(ICdmFormElement element) {
419 elements.remove(element);
420 }
421
422 /**
423 * <p>
424 * removeElements
425 * </p>
426 */
427 @Override
428 public void removeElements() {
429 for (ICdmFormElement childElement : getElements()) {
430 // recursion
431 childElement.removeElements();
432
433 // unregister selection arbitrator
434 if (childElement instanceof ISelectableElement) {
435 ISelectableElement selectableElement = (ISelectableElement) childElement;
436 if (selectableElement.getSelectionArbitrator() != null) {
437 formFactory.destroySelectionArbitrator(selectableElement
438 .getSelectionArbitrator());
439 }
440 }
441
442 // unregister propertyChangeListener
443 formFactory.removePropertyChangeListener(childElement);
444
445 // dispose of the controls
446 for (Control control : childElement.getControls()) {
447 // we added the layoutComposite of the parental element as the
448 // layout composite to this formElement
449 // but we do not want to destroy it.
450 if (control.equals(childElement.getLayoutComposite())) {
451 continue;
452 } else {
453 control.dispose();
454 control = null;
455 }
456 }
457 }
458
459 elements.clear();
460 }
461
462 /**
463 * <p>
464 * Getter for the field <code>parentElement</code>.
465 * </p>
466 *
467 * @return a {@link eu.etaxonomy.taxeditor.ui.element.ICdmFormElement} object.
468 */
469 @Override
470 public ICdmFormElement getParentElement() {
471 return parentElement;
472 }
473
474 /**
475 * <p>
476 * Getter for the field <code>elements</code>.
477 * </p>
478 *
479 * @return a {@link java.util.Set} object.
480 */
481 @Override
482 public Set<ICdmFormElement> getElements() {
483 return elements;
484 }
485
486 /**
487 * <p>
488 * getControls
489 * </p>
490 *
491 * @return a {@link java.util.Set} object.
492 */
493 @Override
494 public Set<Control> getControls() {
495 Set<Control> controls = new HashSet<Control>();
496
497 for (Control control : getChildren()) {
498 controls.add(control);
499 }
500
501 return controls;
502 }
503
504 /** {@inheritDoc} */
505 @Override
506 public void dispose() {
507 removeElements();
508 super.dispose();
509 }
510
511 /**
512 * <p>
513 * getLayoutComposite
514 * </p>
515 *
516 * @return a {@link org.eclipse.swt.widgets.Composite} object.
517 */
518 @Override
519 public Composite getLayoutComposite() {
520 return (Composite) getClient();
521 }
522
523 /** {@inheritDoc} */
524 @Override
525 public boolean containsFormElement(ICdmFormElement formElement) {
526 if (formElement == this) {
527 return true;
528 } else {
529 for (ICdmFormElement element : getElements()) {
530 boolean contains = element.containsFormElement(formElement);
531 if (contains == true) {
532 return true;
533 }
534 }
535 return false;
536 }
537 }
538
539 /**
540 * <p>
541 * Getter for the field <code>formFactory</code>.
542 * </p>
543 *
544 * @return a {@link eu.etaxonomy.taxeditor.ui.element.CdmFormFactory} object.
545 */
546 @Override
547 public CdmFormFactory getFormFactory() {
548 return formFactory;
549 }
550
551 /*
552 * (non-Javadoc)
553 *
554 * @see eu.etaxonomy.taxeditor.forms.ICdmFormElement#refresh()
555 */
556 /**
557 * <p>
558 * refresh
559 * </p>
560 */
561 @Override
562 public void refresh() {
563 // empty default implementation
564
565 }
566
567 /**
568 * <p>
569 * getConversationHolder
570 * </p>
571 *
572 * @return a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
573 * object.
574 */
575 @Override
576 public ConversationHolder getConversationHolder() {
577 if(AbstractUtility.getActivePart() instanceof IConversationEnabled){
578 return ((IConversationEnabled) AbstractUtility.getActivePart()).getConversationHolder();
579 }
580 if(getParentElement() instanceof RootElement || getParentElement() == null){
581
582 IEditorPart activeEditor = AbstractUtility.getActiveEditor();
583 if(activeEditor instanceof IConversationEnabled){
584 ConversationHolder conversation = ((IConversationEnabled) AbstractUtility.getActiveEditor()).getConversationHolder();
585 return conversation;
586 }
587 }else if(getParentElement() instanceof IConversationEnabled){
588 return ((IConversationEnabled) getParentElement()).getConversationHolder();
589 }
590 MessagingUtils.messageDialog("Could not get conversation for AbstractFormSection",
591 getClass(), "There is an error in the implementation. There should have been an active editor but it wasn't",
592 new IllegalArgumentException());
593 return null;
594
595 }
596
597 /** {@inheritDoc} */
598 @Override
599 public void update(CdmDataChangeMap changeEvents) {
600
601 }
602
603 private class ExpandListener implements IExpansionListener{
604 @Override
605 public void expansionStateChanging(ExpansionEvent e) {
606 }
607 @Override
608 public void expansionStateChanged(ExpansionEvent e) {
609 PreferencesUtil.getPreferenceStore().setValue(getPrefKey(), e.getState());
610 }
611 }
612
613 /**
614 * Adds a custom implementation of IExpansionListener to this section
615 * which stores the expansion state in the preferences
616 */
617 private void addExpandListener() {
618 PreferencesUtil.getPreferenceStore().setDefault(getPrefKey(), isExpanded());
619 setExpanded(PreferencesUtil.getPreferenceStore().getBoolean(getPrefKey()));
620 addExpansionListener(new ExpandListener());
621 }
622
623 private String getPrefKey() {
624 return this.getClass().getCanonicalName()+";"+entity.getClass().getCanonicalName();
625 }
626
627
628 }