minor
[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.model.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 addAction(toolBarManager);
145
146 return toolBarManager.createControl(this);
147 }
148
149 protected void addAction(ToolBarManager toolBarManager) {
150 // default implementation empty
151 }
152
153 /**
154 * using this method is discouraged, use updateToolBar() instead
155 */
156 public void showToolbar(){
157 setTextClient(createToolbar());
158 }
159
160 /**
161 * using this method is discouraged, use updateToolBar() instead
162 */
163 public void removeToolbar(){
164 setTextClient(null);
165 }
166
167 @Override
168 public void setEntity(ENTITY entity) {
169 if(entity != null){
170 super.setEntity(entity);
171 internalUpdateSection(false);
172 }
173 setSectionTitle();
174 updateToolbar();
175 layout();
176 }
177
178 /**
179 * Sets the title for the section. Adds a "+" sign if the collection is not empty for this section.
180 * Override in subclasses if you want to have a different behaviour.
181 */
182 protected void setSectionTitle() {
183 Collection<ELEMENT> collection = getCollection(getEntity());
184 if(collection != null && collection.size() > 0){
185 this.setText(getTitleString() + " +");
186 }else{
187 this.setText(getTitleString());
188 }
189 }
190
191 /**
192 * Removes all content from the container
193 */
194 private void destroyDynamicContent(){
195 if(label_empty != null){
196 label_empty.dispose();
197 label_empty = null;
198 }
199 removeElements();
200 }
201
202 /**
203 * Call this method after dynamically changing the client area.
204 * If the options changed is set to <code>true</code>, will also fire a state changed
205 * event to inform the user of unsaved changes.
206 *
207 * @param changed a boolean.
208 */
209 protected void internalUpdateSection(boolean changed){
210 setSectionTitle();
211 destroyDynamicContent();
212 if(isExpanded() || expandSectionWhenContentAvailable()) {
213 renderContent(isExpanded());
214 }
215 if(changed) {
216 firePropertyChangeEvent(this);
217 }
218 }
219
220 /**
221 * Create the elements to be shown in this section client area
222 */
223 private void renderContent(boolean forceExpansion)
224 {
225 Collection<ELEMENT> collection = getCollection(getEntity());
226
227 if(collection == null || collection.isEmpty()){
228 createEmptyContent();
229 }else{
230 List<ELEMENT> elements = new ArrayList<>(collection);
231 Collections.sort(elements, getComparator());
232 createDynamicContents(elements);
233 forceExpansion = true;
234 }
235
236 this.setExpanded(forceExpansion);
237
238 reflow();
239 }
240
241 protected void createEmptyContent(){
242 label_empty = formFactory.createLabel(getLayoutComposite(), getEmptyString());
243 }
244
245 /**
246 * Creates the widgets for the collection
247 *
248 * @param elements a {@link java.util.Collection} object.
249 */
250 protected void createDynamicContents(Collection<ELEMENT> elements)
251 {
252 int i = 0;
253 for(final ELEMENT element : elements){
254 SelectionAdapter removeListener = new SelectionAdapter(){
255 @Override
256 public void widgetSelected(SelectionEvent e) {
257 removeElement(element);
258 internalUpdateSection(true);
259 }
260 };
261 boolean modulo = i++%2 == 0;
262 String colorResource = modulo ? Resources.COLOR_LIST_EVEN : Resources.COLOR_LIST_ODD;
263 createElementComposite(element, removeListener, AbstractUtility.getColor(colorResource));
264 }
265 }
266
267 /**
268 * Create the specific widget for the element
269 *
270 * @param element a ELEMENT object.
271 * @param removeListener a {@link org.eclipse.swt.events.SelectionListener} object.
272 * @param backgroundColor a {@link org.eclipse.swt.graphics.Color} object.
273 */
274 protected void createElementComposite(ELEMENT element, SelectionListener removeListener, Color backgroundColor){
275 entityCollectionElement = formFactory.createEntityCollectionElement(this, element, removeListener, backgroundColor, SWT.NULL);
276 }
277
278 /** {@inheritDoc} */
279 @Override
280 public void setBackground(Color color) {
281 if(label_empty != null && !label_empty.isDisposed()){
282 label_empty.setBackground(color);
283 }
284 super.setBackground(color);
285 }
286
287 /**
288 * <p>getTitleString</p>
289 *
290 * @return a {@link java.lang.String} object.
291 */
292 public String getTitleString() {
293 return CdmUtils.Nz(title);
294 }
295
296 /**
297 * <p>setTitleString</p>
298 *
299 * @param title a {@link java.lang.String} object.
300 */
301 public void setTitleString(String title){
302 this.title = title;
303 setSectionTitle();
304 layout();
305 }
306
307 /** {@inheritDoc} */
308 @Override
309 public void expansionStateChanging(ExpansionEvent e) {
310 // logger.warn("Expansion State Changing");
311 }
312
313 /** {@inheritDoc} */
314 @Override
315 public void expansionStateChanged(ExpansionEvent e) {
316 if(isExpanded()){
317 renderContent(isExpanded());
318 }else{
319 destroyDynamicContent();
320 }
321 }
322
323 private boolean expandSectionWhenContentAvailable(){
324 return PreferencesUtil.getBooleanValue(IPreferenceKeys.SHOULD_EXPAND_SECTION_WHEN_DATA_AVAILABLE, true);
325 }
326
327 /**
328 * Remove an element from the entities collection and update the section
329 *
330 * @param element a ELEMENT object.
331 */
332 public void removeElementAndUpdate(ELEMENT element) {
333 removeElement(element);
334 internalUpdateSection(true);
335 }
336
337 @Override
338 public void update(Observable o, Object arg){
339 if(o instanceof LoginManager){
340 updateToolbar();
341 }
342 }
343
344 protected void updateToolbar() {
345 if(getEntity() != null && CdmStore.currentAuthentiationHasPermission(StoreUtil.getCdmEntity(getEntity()), UPDATE)){
346 showToolbar();
347 } else {
348 removeToolbar();
349 }
350 }
351
352 public AbstractEntityCollectionElement<ENTITY> getEntityCollectionElement() {
353 return entityCollectionElement;
354 }
355
356 /**
357 * Returns the {@link Comparator} specific for the ELEMENTs
358 * which is used to sort the elements
359 * @return the comparator for ELEMENT
360 */
361 public abstract Comparator<ELEMENT> getComparator();
362
363 /**
364 * Get the specific collection of this entity
365 *
366 * @param entity a ENTITY object.
367 * @return a {@link java.util.Collection} object.
368 */
369 public abstract Collection<ELEMENT> getCollection(ENTITY entity);
370
371
372 /**
373 * Create a new Element for this collection
374 *
375 * @return a ELEMENT object.
376 */
377 public abstract ELEMENT createNewElement();
378
379 /**
380 * Add an element to the entities collection
381 *
382 * @param element a ELEMENT object.
383 */
384 public abstract void addElement(ELEMENT element);
385
386 /**
387 * Add an existing element to the entities collection.
388 * @return the existing element
389 */
390 public abstract ELEMENT addExisting();
391
392 /**
393 * If <code>true</code> the section will also display
394 * a browse icon to choose from existing elements.
395 * <br>
396 * <b>Note:</b> when returning true you have to make sure
397 * to implement the {@link #addExisting()} method
398 * @return true if existing entities can be added;
399 * false otherwise
400 */
401 public abstract boolean allowAddExisting();
402
403 /**
404 * Remove an element from the entities collection
405 *
406 * @param element a ELEMENT object.
407 */
408 public abstract void removeElement(ELEMENT element);
409
410 /**
411 * String to display when the collection is empty
412 *
413 * @return a {@link java.lang.String} object.
414 */
415 public abstract String getEmptyString();
416
417 /**
418 * <p>getTooltipString</p>
419 *
420 * @return String to display when hovering the add button
421 */
422 protected abstract String getTooltipString();
423 }