Merge branch 'release/5.19.0'
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / ui / element / AbstractCdmFormElement.java
1 /**
2 * Copyright (C) 2015 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 package eu.etaxonomy.taxeditor.ui.element;
10
11 import java.util.ConcurrentModificationException;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Set;
15
16 import org.eclipse.core.runtime.Assert;
17 import org.eclipse.jface.util.IPropertyChangeListener;
18 import org.eclipse.jface.util.PropertyChangeEvent;
19 import org.eclipse.swt.graphics.Color;
20 import org.eclipse.swt.layout.GridData;
21 import org.eclipse.swt.widgets.Composite;
22 import org.eclipse.swt.widgets.Control;
23 import org.eclipse.ui.forms.widgets.Section;
24 import org.eclipse.ui.forms.widgets.TableWrapData;
25
26 import eu.etaxonomy.taxeditor.model.AbstractUtility;
27 import eu.etaxonomy.taxeditor.model.MessagingUtils;
28
29 /**
30 * @author n.hoffmann
31 */
32 public abstract class AbstractCdmFormElement implements ICdmFormElement {
33
34 protected CdmFormFactory formFactory;
35
36 private List<IPropertyChangeListener> propertyChangeListeners;
37
38 private Composite layoutComposite;
39
40 private final Set<Control> controls = new HashSet<>();
41
42 private final Set<ICdmFormElement> elements = new HashSet<>();
43 private ICdmFormElement parentElement;
44
45 private Color persistentBackgroundColor;
46
47 protected AbstractCdmFormElement(CdmFormFactory formFactory, Composite layoutComposite){
48 this.layoutComposite = layoutComposite;
49 this.formFactory = formFactory;
50 }
51
52 public AbstractCdmFormElement(CdmFormFactory formFactory, ICdmFormElement formElement) {
53 this(formFactory, formElement.getLayoutComposite());
54 this.parentElement = formElement;
55 // addControl(layoutComposite);
56 }
57
58 @Override
59 public CdmFormFactory getFormFactory() {
60 return formFactory;
61 }
62
63 /**
64 * Delegates the focus to <code>this</code> elements main input control
65 */
66 public void setFocus(){
67 // Override in subclasses where needed
68 }
69
70 /**
71 * Returns all Controls that are managed by this element
72 */
73 @Override
74 public Set<Control> getControls(){
75 return controls;
76 }
77
78 /**
79 * Adds the control to the set of controls that are managed by this element
80 */
81 protected void addControl(Control child){
82 controls.add(child);
83 }
84
85 protected void removeControl(Control child){
86 controls.remove(child);
87 }
88
89 /**
90 * <p>Getter for the field <code>elements</code>.</p>
91 *
92 * @return a {@link java.util.Set} object.
93 */
94 @Override
95 public Set<ICdmFormElement> getElements(){
96 return elements;
97 }
98
99 @Override
100 public ICdmFormElement getParentElement(){
101 return parentElement;
102 }
103
104 @Override
105 public void addElement(ICdmFormElement element){
106 elements.add(element);
107 }
108
109 /**
110 * Remove all child {@link ICdmFormElement}s and child {@link Control}s
111 * of the given and the element itself.
112 * @param formElement The element to remove
113 */
114 public void removeElementsAndControls(ICdmFormElement formElement){
115 for(ICdmFormElement childElement : formElement.getElements()){
116 // recursion
117 childElement.removeElements();
118
119 // unregister selection arbitrator
120 if(childElement instanceof ISelectableElement){
121 SelectionArbitrator selectionArbitrator = ((ISelectableElement) childElement).getSelectionArbitrator();
122 if(selectionArbitrator != null){
123 formFactory.destroySelectionArbitrator(selectionArbitrator);
124 }
125 }
126
127 // unregister from property changes
128 formFactory.removePropertyChangeListener(childElement);
129
130 // dispose of the controls
131 removeControls(childElement);
132 }
133 removeControls(formElement);
134 this.elements.remove(formElement);
135 }
136
137 /**
138 * Removes all child {@link ICdmFormElement}s and child {@link Control}s
139 * and the element itself.
140 */
141 @Override
142 public void removeElements(){
143 Set<ICdmFormElement> tmpElements = new HashSet<>(getElements());
144 for (ICdmFormElement childElement : tmpElements) {
145 // recursion
146 childElement.removeElements();
147
148 // unregister selection arbitrator
149 if(childElement instanceof ISelectableElement){
150 SelectionArbitrator selectionArbitrator = ((ISelectableElement) childElement).getSelectionArbitrator();
151 if(selectionArbitrator != null){
152 formFactory.destroySelectionArbitrator(selectionArbitrator);
153 }
154 }
155
156 // unregister from property changes
157 formFactory.removePropertyChangeListener(childElement);
158
159 // dispose of the controls
160 removeControls(childElement);
161 }
162 if(this instanceof ISelectableElement){
163 SelectionArbitrator selectionArbitrator = ((ISelectableElement) this).getSelectionArbitrator();
164 if(selectionArbitrator != null){
165 formFactory.destroySelectionArbitrator(selectionArbitrator);
166 }
167 }
168 removeControls(this);
169 elements.clear();
170 }
171
172 private void removeControls(ICdmFormElement element){
173 if(element instanceof Section){
174 ((Section) element).dispose();
175 element = null;
176 }else{
177 for(Control control : element.getControls()){
178 // we added the layoutComposite of the parental element as the layout composite to this formElement
179 // but we do not want to destroy it.
180 if(control.equals(element.getLayoutComposite())){
181 continue;
182 }else{
183 control.dispose();
184 control = null;
185 }
186 }
187 }
188 }
189
190 @Override
191 public Composite getLayoutComposite() {
192 return layoutComposite;
193 }
194 public void setLayoutComposite(Composite layoutComposite){
195 this.layoutComposite = layoutComposite;
196 }
197
198 public void addIndent(int indent)
199 { this.layoutComposite = formFactory.createComposite(layoutComposite);
200
201 if (this.layoutComposite.getParent().getLayoutData() instanceof TableWrapData){
202 TableWrapData tableWrap = (TableWrapData)this.layoutComposite.getParent().getLayoutData();
203 tableWrap.indent = indent;
204
205 this.layoutComposite.setLayoutData(tableWrap);
206 }
207 if (this.layoutComposite.getLayoutData() instanceof GridData){
208 GridData gridData = new GridData();
209 gridData.verticalIndent = indent;
210 this.layoutComposite.setLayoutData(gridData);
211 }
212 }
213
214 @Override
215 public List<IPropertyChangeListener> getPropertyChangeListeners() {
216 return propertyChangeListeners;
217 }
218 @Override
219 public void setPropertyChangeListeners(List<IPropertyChangeListener> propertyChangeListeners){
220 this.propertyChangeListeners = propertyChangeListeners;
221 }
222
223 @Override
224 public void firePropertyChangeEvent(CdmPropertyChangeEvent event) {
225 //TODO: replace propertyChangeListeners with formFactory.getPropertyChangeListeners() and remove member propertyChangeListeners from AbstractCdmFormElement
226 Assert.isNotNull(propertyChangeListeners, "Property change listeners are not present");
227
228 try{
229 for(Object listener : propertyChangeListeners){
230 ((IPropertyChangeListener)listener).propertyChange(event);
231 }
232 }catch(ConcurrentModificationException e){
233 // There are two cases that produce a CME.
234 // Described here: http://dev.e-taxonomy.eu/trac/ticket/2363#comment:2
235 // and here: http://dev.e-taxonomy.eu/trac/ticket/2438
236 // Ignoring the CME because nothing bad is happening
237 MessagingUtils.warn(getClass(), "ConcurrentModificationException. Can be ignored.");
238 }
239 }
240
241 /**
242 * Fires a {@link CdmPropertyChangeEvent} with the given object as source.
243 *
244 * @param object the object on which the property changed
245 */
246 public void firePropertyChangeEvent(Object object){
247 firePropertyChangeEvent(object, null);
248 }
249
250 /**
251 * Fires a {@link CdmPropertyChangeEvent} with the given object as source also containing the
252 * originating event
253 *
254 * @param object the object on which the property changed
255 * @param originatingEvent the originating event
256 */
257 public void firePropertyChangeEvent(Object object, PropertyChangeEvent originatingEvent){
258 firePropertyChangeEvent(new CdmPropertyChangeEvent(object, originatingEvent));
259 }
260
261 /**
262 * {@inheritDoc}
263 *
264 * This method gets called whenever the toolkit this composite was created with gets a property change notification.
265 *
266 * It is good advice to check whether the PropertyChangeEvent is destined for the implementing composite.
267 * Implementations should also check for null PropertyChangeEvents and return immediately in that case.
268 * @see eu.etaxonomy.taxeditor.ui.element.ICdmFormElement#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
269 */
270 @Override
271 public void propertyChange(PropertyChangeEvent event) {
272 // implement in subclasses
273 }
274
275 @Override
276 public boolean containsFormElement(ICdmFormElement formElement){
277 if(formElement == this){
278 return true;
279 }else{
280 for(ICdmFormElement element : getElements()){
281 boolean contains = element.containsFormElement(formElement);
282 if(contains == true){
283 return true;
284 }
285 }
286 return false;
287 }
288 }
289
290 @Override
291 public void refresh() {
292 // empty default implementation
293 }
294
295 @Override
296 public void setBackground(Color color) {
297 for(ICdmFormElement element : getElements()){
298 element.setBackground(color);
299 }
300 }
301
302 @Override
303 public void setPersistentBackground(Color color) {
304 persistentBackgroundColor = color;
305 setBackground(color);
306 }
307
308 @Override
309 public Color getPersistentBackground() {
310 return persistentBackgroundColor;
311 }
312
313 public Color getColor(String colorId){
314 return AbstractUtility.getColor(colorId);
315 }
316 }