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