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