Merge branch 'release/3.7.0'
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / ui / section / AbstractEntityCollectionSection.java
1 /**
2 *
3 */
4 package eu.etaxonomy.taxeditor.ui.section;
5
6 import java.util.Collection;
7 import java.util.EnumSet;
8 import java.util.Observable;
9 import java.util.Observer;
10
11 import org.eclipse.jface.action.Action;
12 import org.eclipse.jface.action.IAction;
13 import org.eclipse.jface.action.ToolBarManager;
14 import org.eclipse.jface.resource.ImageDescriptor;
15 import org.eclipse.swt.SWT;
16 import org.eclipse.swt.events.DisposeEvent;
17 import org.eclipse.swt.events.DisposeListener;
18 import org.eclipse.swt.events.SelectionAdapter;
19 import org.eclipse.swt.events.SelectionEvent;
20 import org.eclipse.swt.events.SelectionListener;
21 import org.eclipse.swt.graphics.Color;
22 import org.eclipse.swt.graphics.ImageData;
23 import org.eclipse.swt.widgets.Composite;
24 import org.eclipse.swt.widgets.Control;
25 import org.eclipse.swt.widgets.Label;
26 import org.eclipse.ui.forms.events.ExpansionEvent;
27 import org.eclipse.ui.forms.events.IExpansionListener;
28 import org.eclipse.ui.forms.widgets.ExpandableComposite;
29
30 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
31 import eu.etaxonomy.cdm.common.CdmUtils;
32 import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;
33 import eu.etaxonomy.taxeditor.model.AbstractUtility;
34 import eu.etaxonomy.taxeditor.model.ImageResources;
35 import eu.etaxonomy.taxeditor.preference.IPreferenceKeys;
36 import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
37 import eu.etaxonomy.taxeditor.preference.Resources;
38 import eu.etaxonomy.taxeditor.store.CdmStore;
39 import eu.etaxonomy.taxeditor.store.LoginManager;
40 import eu.etaxonomy.taxeditor.store.StoreUtil;
41 import eu.etaxonomy.taxeditor.ui.element.AbstractFormSection;
42 import eu.etaxonomy.taxeditor.ui.element.CdmFormFactory;
43 import eu.etaxonomy.taxeditor.ui.element.ICdmFormElement;
44
45 /**
46 * This class visualizes an CDM entity of type ENTITY and additionally provides the functionality to add
47 * other elements of type ELEMENT to them.
48 *
49 * @param <ENTITY> A CDM entity which should be visualized by this section.
50 * @param <ELEMENT> An element that can be added (multiple times) to this entity.
51 *
52 * @author n.hoffmann
53 * @version $Id: $
54 */
55
56 public abstract class AbstractEntityCollectionSection<ENTITY, ELEMENT> extends AbstractFormSection<ENTITY> implements IExpansionListener, Observer {
57
58 private static final EnumSet<CRUD> UPDATE = EnumSet.of(CRUD.UPDATE);
59
60 protected Composite container;
61
62 private Label label_empty;
63
64 private String title;
65
66 private AbstractEntityCollectionElement entityCollectionElement;
67
68 public AbstractEntityCollectionSection(CdmFormFactory formFactory, ConversationHolder conversation, ICdmFormElement parentElement, String title, int style) {
69 super(formFactory, parentElement, ExpandableComposite.CLIENT_INDENT | style);
70 this.title = title;
71 this.setText(getTitleString());
72 updateToolbar();
73
74 addExpansionListener(this);
75
76 CdmStore.getLoginManager().addObserver(this);
77 addDisposeListener(new DisposeListener() {
78 @Override
79 public void widgetDisposed(DisposeEvent e) {
80 CdmStore.getLoginManager().deleteObserver(AbstractEntityCollectionSection.this);
81 }
82 });
83 }
84
85 protected Control createToolbar() {
86 ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
87
88 Action addAction = new Action("add", IAction.AS_PUSH_BUTTON){
89 @Override
90 public void run() {
91 ELEMENT element = createNewElement();
92 if(element != null){
93 addElement(element);
94 if(! getSection().isExpanded()) {
95 getSection().setExpanded(true);
96 }
97 internalUpdateSection(true);
98 }
99 }
100 };
101 addAction.setImageDescriptor(new ImageDescriptor() {
102
103 @Override
104 public ImageData getImageData() {
105 return ImageResources.getImage(ImageResources.ADD_ICON).getImageData();
106 }
107 });
108 addAction.setToolTipText(getTooltipString());
109
110 toolBarManager.add(addAction);
111
112 return toolBarManager.createControl(this);
113 }
114
115 /**
116 * using this method is discouraged, use updateToolBar() instead
117 */
118 public void showToolbar(){
119 setTextClient(createToolbar());
120 }
121
122 /**
123 * using this method is discouraged, use updateToolBar() instead
124 */
125 public void removeToolbar(){
126 setTextClient(null);
127 }
128
129 @Override
130 public void setEntity(ENTITY entity) {
131 if(entity != null){
132 super.setEntity(entity);
133 internalUpdateSection(false);
134 }
135 setSectionTitle();
136 updateToolbar();
137 layout();
138 }
139
140 /**
141 * Sets the title for the section. Adds a "+" sign if the collection is not empty for this section.
142 * Override in subclasses if you want to have a different behaviour.
143 */
144 protected void setSectionTitle() {
145 if(getCollection(getEntity()) != null && getCollection(getEntity()).size() > 0){
146 this.setText(getTitleString() + " +");
147 }else{
148 this.setText(getTitleString());
149 }
150 }
151
152 /**
153 * Removes all content from the container
154 */
155 private void destroyDynamicContent(){
156 if(label_empty != null){
157 label_empty.dispose();
158 label_empty = null;
159 }
160 removeElements();
161 }
162
163 /**
164 * Call this method after dynamically changing the client area.
165 * If the options changed is set to <code>true</code>, will also fire a state changed
166 * event to inform the user of unsaved changes.
167 *
168 * @param changed a boolean.
169 */
170 protected void internalUpdateSection(boolean changed){
171 setSectionTitle();
172 destroyDynamicContent();
173 if(isExpanded() || expandSectionWhenContentAvailable()) {
174 renderContent(isExpanded());
175 }
176 if(changed) {
177 firePropertyChangeEvent(this);
178 }
179 }
180
181 /**
182 * Create the elements to be shown in this section client area
183 */
184 private void renderContent(boolean forceExpansion)
185 {
186 Collection<ELEMENT> elements = getCollection(getEntity());
187
188 if(elements == null || elements.isEmpty()){
189 createEmptyContent();
190 }else{
191 createDynamicContents(elements);
192 forceExpansion = true;
193 }
194
195 this.setExpanded(forceExpansion);
196
197 reflow();
198 }
199
200 protected void createEmptyContent(){
201 label_empty = formFactory.createLabel(getLayoutComposite(), getEmptyString());
202 }
203
204 /**
205 * Creates the widgets for the collection
206 *
207 * @param elements a {@link java.util.Collection} object.
208 */
209 protected void createDynamicContents(Collection<ELEMENT> elements)
210 {
211 int i = 0;
212 for(final ELEMENT element : elements){
213 SelectionAdapter removeListener = new SelectionAdapter(){
214 @Override
215 public void widgetSelected(SelectionEvent e) {
216 removeElement(element);
217 internalUpdateSection(true);
218 }
219 };
220 boolean modulo = i++%2 == 0;
221 String colorResource = modulo ? Resources.COLOR_LIST_EVEN : Resources.COLOR_LIST_ODD;
222 createElementComposite(element, removeListener, AbstractUtility.getColor(colorResource));
223 }
224 }
225
226 /**
227 * Create the specific widget for the element
228 *
229 * @param element a ELEMENT object.
230 * @param removeListener a {@link org.eclipse.swt.events.SelectionListener} object.
231 * @param backgroundColor a {@link org.eclipse.swt.graphics.Color} object.
232 */
233 protected void createElementComposite(ELEMENT element, SelectionListener removeListener, Color backgroundColor){
234 entityCollectionElement = formFactory.createEntityCollectionElement(this, element, removeListener, backgroundColor, SWT.NULL);
235 }
236
237 /** {@inheritDoc} */
238 @Override
239 public void setBackground(Color color) {
240 if(label_empty != null && !label_empty.isDisposed()){
241 label_empty.setBackground(color);
242 }
243 super.setBackground(color);
244 }
245
246 /**
247 * <p>getTitleString</p>
248 *
249 * @return a {@link java.lang.String} object.
250 */
251 public String getTitleString() {
252 return CdmUtils.Nz(title);
253 }
254
255 /**
256 * <p>setTitleString</p>
257 *
258 * @param title a {@link java.lang.String} object.
259 */
260 public void setTitleString(String title){
261 this.title = title;
262 setSectionTitle();
263 layout();
264 }
265
266 /** {@inheritDoc} */
267 @Override
268 public void expansionStateChanging(ExpansionEvent e) {
269 // logger.warn("Expansion State Changing");
270 }
271
272 /** {@inheritDoc} */
273 @Override
274 public void expansionStateChanged(ExpansionEvent e) {
275 if(isExpanded()){
276 renderContent(isExpanded());
277 }else{
278 destroyDynamicContent();
279 }
280 }
281
282 private boolean expandSectionWhenContentAvailable(){
283 return PreferencesUtil.getPreferenceStore().getBoolean(IPreferenceKeys.SHOULD_EXPAND_SECTION_WHEN_DATA_AVAILABLE);
284 }
285
286 /**
287 * Remove an element from the entities collection and update the section
288 *
289 * @param element a ELEMENT object.
290 */
291 public void removeElementAndUpdate(ELEMENT element) {
292 removeElement(element);
293 internalUpdateSection(true);
294 }
295
296 @Override
297 public void update(Observable o, Object arg){
298 if(o instanceof LoginManager){
299 updateToolbar();
300 }
301 }
302
303 private void updateToolbar() {
304 if(getEntity() != null && CdmStore.currentAuthentiationHasPermission(StoreUtil.getCdmEntity(getEntity()), UPDATE)){
305 showToolbar();
306 } else {
307 removeToolbar();
308 }
309 }
310
311 public AbstractEntityCollectionElement getEntityCollectionElement() {
312 return entityCollectionElement;
313 }
314
315 /**
316 * Get the specific collection of this entity
317 *
318 * @param entity a ENTITY object.
319 * @return a {@link java.util.Collection} object.
320 */
321 public abstract Collection<ELEMENT> getCollection(ENTITY entity);
322
323 /**
324 * Create a new Element for this collection
325 *
326 * @return a ELEMENT object.
327 */
328 public abstract ELEMENT createNewElement();
329
330 /**
331 * Add an element to the entities collection
332 *
333 * @param element a ELEMENT object.
334 */
335 public abstract void addElement(ELEMENT element);
336
337 /**
338 * Remove an element from the entities collection
339 *
340 * @param element a ELEMENT object.
341 */
342 public abstract void removeElement(ELEMENT element);
343
344 /**
345 * String to display when the collection is empty
346 *
347 * @return a {@link java.lang.String} object.
348 */
349 public abstract String getEmptyString();
350
351 /**
352 * <p>getTooltipString</p>
353 *
354 * @return String to display when hovering the add button
355 */
356 protected abstract String getTooltipString();
357 }