Project

General

Profile

Download (12 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2018 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.section;
10

    
11
import java.util.ArrayList;
12
import java.util.Collection;
13
import java.util.Collections;
14
import java.util.Comparator;
15
import java.util.EnumSet;
16
import java.util.List;
17
import java.util.Observable;
18
import java.util.Observer;
19

    
20
import org.eclipse.jface.action.Action;
21
import org.eclipse.jface.action.IAction;
22
import org.eclipse.jface.action.ToolBarManager;
23
import org.eclipse.jface.resource.ImageDescriptor;
24
import org.eclipse.swt.SWT;
25
import org.eclipse.swt.events.DisposeEvent;
26
import org.eclipse.swt.events.DisposeListener;
27
import org.eclipse.swt.events.SelectionAdapter;
28
import org.eclipse.swt.events.SelectionEvent;
29
import org.eclipse.swt.events.SelectionListener;
30
import org.eclipse.swt.graphics.Color;
31
import org.eclipse.swt.graphics.ImageData;
32
import org.eclipse.swt.widgets.Composite;
33
import org.eclipse.swt.widgets.Control;
34
import org.eclipse.swt.widgets.Label;
35
import org.eclipse.ui.forms.events.ExpansionEvent;
36
import org.eclipse.ui.forms.events.IExpansionListener;
37
import org.eclipse.ui.forms.widgets.ExpandableComposite;
38

    
39
import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
40
import eu.etaxonomy.cdm.common.CdmUtils;
41
import eu.etaxonomy.cdm.model.common.CdmBase;
42
import eu.etaxonomy.cdm.model.permission.CRUD;
43
import eu.etaxonomy.taxeditor.model.AbstractUtility;
44
import eu.etaxonomy.taxeditor.model.ImageResources;
45
import eu.etaxonomy.taxeditor.preference.IPreferenceKeys;
46
import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
47
import eu.etaxonomy.taxeditor.preference.Resources;
48
import eu.etaxonomy.taxeditor.store.CdmStore;
49
import eu.etaxonomy.taxeditor.store.LoginManager;
50
import eu.etaxonomy.taxeditor.store.StoreUtil;
51
import eu.etaxonomy.taxeditor.ui.element.AbstractFormSection;
52
import eu.etaxonomy.taxeditor.ui.element.CdmFormFactory;
53
import eu.etaxonomy.taxeditor.ui.element.ICdmFormElement;
54

    
55
/**
56
 * This class visualizes an CDM entity of type ENTITY and additionally provides the functionality to add
57
 * other elements of type ELEMENT to them.
58
 *
59
 * @param <ENTITY> A CDM entity which should be visualized by this section.
60
 * @param <ELEMENT> An element that can be added (multiple times) to this entity.
61
 *
62
 * @author n.hoffmann
63
 */
64
public abstract class AbstractEntityCollectionSection<ENTITY, ELEMENT> extends AbstractFormSection<ENTITY> implements IExpansionListener, Observer {
65

    
66
    private static final EnumSet<CRUD> UPDATE = EnumSet.of(CRUD.UPDATE);
67

    
68
    protected Composite container;
69

    
70
	private Label label_empty;
71

    
72
	private String title;
73

    
74
    private AbstractEntityCollectionElement<ENTITY> entityCollectionElement;
75

    
76
	public AbstractEntityCollectionSection(CdmFormFactory formFactory, ConversationHolder conversation, ICdmFormElement parentElement, String title, int style) {
77
		super(formFactory, parentElement, ExpandableComposite.CLIENT_INDENT | style);
78
		this.title = title;
79
		this.setText(getTitleString());
80
		updateToolbar();
81

    
82
		addExpansionListener(this);
83

    
84
		CdmStore.getLoginManager().addObserver(this);
85
		addDisposeListener(new DisposeListener() {
86
            @Override
87
            public void widgetDisposed(DisposeEvent e) {
88
                CdmStore.getLoginManager().deleteObserver(AbstractEntityCollectionSection.this);
89
            }
90
        });
91
	}
92

    
93
	protected Control createToolbar() {
94
		ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
95

    
96
		Action addAction = new Action("Add", IAction.AS_PUSH_BUTTON){
97
			@Override
98
			public void run() {
99
			    ELEMENT element = createNewElement();
100
			    if(element != null){
101
			        addElement(element);
102
			        if(! getSection().isExpanded()) {
103
			            getSection().setExpanded(true);
104
			        }
105
			        internalUpdateSection(true);
106
			    }
107
			}
108
		};
109
		addAction.setImageDescriptor(new ImageDescriptor() {
110

    
111
			@Override
112
			public ImageData getImageData() {
113
				return ImageResources.getImage(ImageResources.ADD_ICON).getImageData();
114
			}
115
		});
116
		addAction.setToolTipText(getTooltipString());
117

    
118
		Action browseAction = null;
119
		if(allowAddExisting()){
120
		    browseAction = new Action("Browse", IAction.AS_PUSH_BUTTON){
121
		        @Override
122
		        public void run() {
123
		            ELEMENT element = addExisting();
124
		            if(element != null){
125
		                addElement(element);
126
		                if(! getSection().isExpanded()) {
127
		                    getSection().setExpanded(true);
128
		                }
129
		                internalUpdateSection(true);
130
		            }
131
		        }
132
		    };
133
		    browseAction.setImageDescriptor(new ImageDescriptor() {
134

    
135
		        @Override
136
		        public ImageData getImageData() {
137
		            return ImageResources.getImage(ImageResources.BROWSE_ICON).getImageData();
138
		        }
139
		    });
140
		    browseAction.setToolTipText("Browse");
141
		}
142

    
143
		toolBarManager.add(addAction);
144
		if(browseAction!=null){
145
		    toolBarManager.add(browseAction);
146
		}
147

    
148
		addAction(toolBarManager);
149

    
150
		return toolBarManager.createControl(this);
151
	}
152

    
153
    protected void addAction(ToolBarManager toolBarManager) {
154
        // default implementation empty
155
    }
156

    
157
    /**
158
	 * using this method is discouraged, use updateToolBar() instead
159
	 */
160
	public void showToolbar(){
161
		setTextClient(createToolbar());
162
	}
163

    
164
    /**
165
     * using this method is discouraged, use updateToolBar() instead
166
     */
167
	public void removeToolbar(){
168
		setTextClient(null);
169
	}
170

    
171
	@Override
172
	public void setEntity(ENTITY entity) {
173
		if(entity != null){
174
			super.setEntity(entity);
175
			internalUpdateSection(false);
176
		}
177
		setSectionTitle();
178
		updateToolbar();
179
		layout();
180
	}
181

    
182
	/**
183
	 * Sets the title for the section. Adds a "+" sign if the collection is not empty for this section.
184
	 * Override in subclasses if you want to have a different behaviour.
185
	 */
186
	protected void setSectionTitle() {
187
		Collection<ELEMENT> collection = getCollection(getEntity());
188
		if(collection != null && collection.size() > 0){
189
			this.setText(getTitleString() + " +");
190
		}else{
191
			this.setText(getTitleString());
192
		}
193
	}
194

    
195
	/**
196
	 * Removes all content from the container
197
	 */
198
	private void destroyDynamicContent(){
199
		if(label_empty != null){
200
			label_empty.dispose();
201
			label_empty = null;
202
		}
203
		removeElements();
204
	}
205

    
206
	/**
207
	 * Call this method after dynamically changing the client area.
208
	 * If the options changed is set to <code>true</code>, will also fire a state changed
209
	 * event to inform the user of unsaved changes.
210
	 *
211
	 * @param changed a boolean.
212
	 */
213
	protected void internalUpdateSection(boolean changed){
214
	    setSectionTitle();
215
		destroyDynamicContent();
216
		if(isExpanded() || expandSectionWhenContentAvailable()) {
217
            renderContent(isExpanded());
218
        }
219
		if(changed) {
220
            firePropertyChangeEvent(this);
221
        }
222
	}
223

    
224
	/**
225
	 * Create the elements to be shown in this section client area
226
	 */
227
	private void renderContent(boolean forceExpansion)
228
	{
229
		Collection<ELEMENT> collection = getCollection(getEntity());
230

    
231
		if(collection == null || collection.isEmpty()){
232
			createEmptyContent();
233
		}else{
234
		    List<ELEMENT> elements = new ArrayList<>(collection);
235
		    Collections.sort(elements, getComparator());
236
			createDynamicContents(elements);
237
			forceExpansion = true;
238
		}
239

    
240
		this.setExpanded(forceExpansion);
241

    
242
		reflow();
243
	}
244

    
245
	protected void createEmptyContent(){
246
		label_empty = formFactory.createLabel(getLayoutComposite(), getEmptyString());
247
	}
248

    
249
	/**
250
	 * Creates the widgets for the collection
251
	 *
252
	 * @param elements a {@link java.util.Collection} object.
253
	 */
254
	protected void createDynamicContents(Collection<ELEMENT> elements)
255
	{
256
		int i = 0;
257
		for(final ELEMENT element : elements){
258
			SelectionAdapter removeListener = new SelectionAdapter(){
259
				@Override
260
				public void widgetSelected(SelectionEvent e) {
261
					removeElement(element);
262
					internalUpdateSection(true);
263
				}
264
			};
265
			boolean modulo = i++%2 == 0;
266
			String colorResource = modulo ? Resources.COLOR_LIST_EVEN : Resources.COLOR_LIST_ODD;
267
			createElementComposite(element, removeListener, AbstractUtility.getColor(colorResource));
268
		}
269
	}
270

    
271
	/**
272
	 * Create the specific widget for the element
273
	 *
274
	 * @param element a ELEMENT object.
275
	 * @param removeListener a {@link org.eclipse.swt.events.SelectionListener} object.
276
	 * @param backgroundColor a {@link org.eclipse.swt.graphics.Color} object.
277
	 */
278
	protected void createElementComposite(ELEMENT element, SelectionListener removeListener, Color backgroundColor){
279
		entityCollectionElement = formFactory.createEntityCollectionElement(this, element, removeListener, backgroundColor, SWT.NULL);
280
	}
281

    
282
	/** {@inheritDoc} */
283
	@Override
284
	public void setBackground(Color color) {
285
		if(label_empty != null && !label_empty.isDisposed()){
286
			label_empty.setBackground(color);
287
		}
288
		super.setBackground(color);
289
	}
290

    
291
	/**
292
	 * <p>getTitleString</p>
293
	 *
294
	 * @return a {@link java.lang.String} object.
295
	 */
296
	public String getTitleString() {
297
		return CdmUtils.Nz(title);
298
	}
299

    
300
	/**
301
	 * <p>setTitleString</p>
302
	 *
303
	 * @param title a {@link java.lang.String} object.
304
	 */
305
	public void setTitleString(String title){
306
		this.title = title;
307
		setSectionTitle();
308
		layout();
309
	}
310

    
311
	/** {@inheritDoc} */
312
	@Override
313
    public void expansionStateChanging(ExpansionEvent e) {
314
//		logger.warn("Expansion State Changing");
315
	}
316

    
317
	/** {@inheritDoc} */
318
	@Override
319
    public void expansionStateChanged(ExpansionEvent e) {
320
		if(isExpanded()){
321
			renderContent(isExpanded());
322
		}else{
323
			destroyDynamicContent();
324
		}
325
	}
326

    
327
	private boolean expandSectionWhenContentAvailable(){
328
		return PreferencesUtil.getBooleanValue(IPreferenceKeys.SHOULD_EXPAND_SECTION_WHEN_DATA_AVAILABLE, true);
329
	}
330

    
331
	/**
332
	 * Remove an element from the entities collection and update the section
333
	 *
334
	 * @param element a ELEMENT object.
335
	 */
336
	public void removeElementAndUpdate(ELEMENT element) {
337
		removeElement(element);
338
		internalUpdateSection(true);
339
	}
340

    
341
	@Override
342
    public void update(Observable o, Object arg){
343
	    if(o instanceof LoginManager){
344
	        updateToolbar();
345
	    }
346
	}
347

    
348
    protected void updateToolbar() {
349
        if( !(getEntity() instanceof CdmBase) || (getEntity() != null && CdmStore.currentAuthentiationHasPermission(StoreUtil.getCdmEntity(getEntity()), UPDATE)) ){
350
            showToolbar();
351
        } else {
352
            removeToolbar();
353
        }
354
    }
355

    
356
    public AbstractEntityCollectionElement<ENTITY> getEntityCollectionElement() {
357
        return entityCollectionElement;
358
    }
359

    
360
    /**
361
     * Returns the {@link Comparator} specific for the ELEMENTs
362
     * which is used to sort the elements
363
     * @return the comparator for ELEMENT
364
     */
365
    public abstract Comparator<ELEMENT> getComparator();
366

    
367
	/**
368
	 * Get the specific collection of this entity
369
	 *
370
	 * @param entity a ENTITY object.
371
	 * @return a {@link java.util.Collection} object.
372
	 */
373
	public abstract Collection<ELEMENT> getCollection(ENTITY entity);
374

    
375

    
376
	/**
377
	 * Create a new Element for this collection
378
	 *
379
	 * @return a ELEMENT object.
380
	 */
381
	public abstract ELEMENT createNewElement();
382

    
383
	/**
384
	 * Add an element to the entities collection
385
	 *
386
	 * @param element a ELEMENT object.
387
	 */
388
	public abstract void addElement(ELEMENT element);
389

    
390
	/**
391
	 * Add an existing element to the entities collection.
392
	 * @return the existing element
393
	 */
394
	public abstract ELEMENT addExisting();
395

    
396
	/**
397
	 * If <code>true</code> the section will also display
398
	 * a browse icon to choose from existing elements.
399
	 * <br>
400
	 * <b>Note:</b> when returning true you have to make sure
401
	 * to implement the {@link #addExisting()} method
402
	 * @return true if existing entities can be added;
403
	 * false otherwise
404
	 */
405
	public abstract boolean allowAddExisting();
406

    
407
	/**
408
	 * Remove an element from the entities collection
409
	 *
410
	 * @param element a ELEMENT object.
411
	 */
412
	public abstract void removeElement(ELEMENT element);
413

    
414
	/**
415
	 * String to display when the collection is empty
416
	 *
417
	 * @return a {@link java.lang.String} object.
418
	 */
419
	public abstract String getEmptyString();
420

    
421
	/**
422
	 * <p>getTooltipString</p>
423
	 *
424
	 * @return String to display when hovering the add button
425
	 */
426
	protected abstract String getTooltipString();
427
}
(4-4/9)