Project

General

Profile

Download (11.8 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
57
 * the functionality to add 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>
65
        extends AbstractFormSection<ENTITY>
66
        implements IExpansionListener, Observer {
67

    
68
    private static final EnumSet<CRUD> UPDATE = EnumSet.of(CRUD.UPDATE);
69

    
70
    protected Composite container;
71

    
72
	private Label label_empty;
73

    
74
	private String title;
75

    
76
    private AbstractEntityCollectionElement<ENTITY> entityCollectionElement;
77

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

    
84
		addExpansionListener(this);
85

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

    
95
	protected Control createToolbar() {
96
		ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
97

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

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

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

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

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

    
150
		addAction(toolBarManager);
151

    
152
		return toolBarManager.createControl(this);
153
	}
154

    
155
    protected void addAction(ToolBarManager toolBarManager) {
156
        // default implementation empty
157
    }
158

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

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

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

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

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

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

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

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

    
242
		this.setExpanded(forceExpansion);
243

    
244
		reflow();
245
	}
246

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

    
251
	/**
252
	 * Creates the widgets for the collection
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
	@Override
283
	public void setBackground(Color color) {
284
		if(label_empty != null && !label_empty.isDisposed()){
285
			label_empty.setBackground(color);
286
		}
287
		super.setBackground(color);
288
	}
289

    
290
	/**
291
	 * getTitleString
292
	 */
293
	public String getTitleString() {
294
		return CdmUtils.Nz(title);
295
	}
296

    
297
	/**
298
	 * setTitleString
299
	 */
300
	public void setTitleString(String title){
301
		this.title = title;
302
		setSectionTitle();
303
		layout();
304
	}
305

    
306
	@Override
307
    public void expansionStateChanging(ExpansionEvent e) {
308
//		logger.warn("Expansion State Changing");
309
	}
310

    
311
	@Override
312
    public void expansionStateChanged(ExpansionEvent e) {
313
		if(isExpanded()){
314
			renderContent(isExpanded());
315
		}else{
316
			destroyDynamicContent();
317
		}
318
	}
319

    
320
	private boolean expandSectionWhenContentAvailable(){
321
		return PreferencesUtil.getBooleanValue(IPreferenceKeys.SHOULD_EXPAND_SECTION_WHEN_DATA_AVAILABLE, true);
322
	}
323

    
324
	/**
325
	 * Remove an element from the entities collection and update the section
326
	 *
327
	 * @param element a ELEMENT object.
328
	 */
329
	public void removeElementAndUpdate(ELEMENT element) {
330
		removeElement(element);
331
		internalUpdateSection(true);
332
	}
333

    
334
	@Override
335
    public void update(Observable o, Object arg){
336
	    if(o instanceof LoginManager){
337
	        updateToolbar();
338
	    }
339
	}
340

    
341
    protected void updateToolbar() {
342
        if( !(getEntity() instanceof CdmBase) || (getEntity() != null && CdmStore.currentAuthentiationHasPermission(StoreUtil.getCdmEntity(getEntity()), UPDATE)) ){
343
            showToolbar();
344
        } else {
345
            removeToolbar();
346
        }
347
    }
348

    
349
    public AbstractEntityCollectionElement<ENTITY> getEntityCollectionElement() {
350
        return entityCollectionElement;
351
    }
352

    
353
    /**
354
     * Returns the {@link Comparator} specific for the ELEMENTs
355
     * which is used to sort the elements
356
     * @return the comparator for ELEMENT
357
     */
358
    public abstract Comparator<ELEMENT> getComparator();
359

    
360
	/**
361
	 * Get the specific collection of this entity
362
	 *
363
	 * @param entity a ENTITY object.
364
	 * @return a {@link java.util.Collection} object.
365
	 */
366
	public abstract Collection<ELEMENT> getCollection(ENTITY entity);
367

    
368

    
369
	/**
370
	 * Create a new Element for this collection
371
	 *
372
	 * @return a ELEMENT object.
373
	 */
374
	public abstract ELEMENT createNewElement();
375

    
376
	/**
377
	 * Add an element to the entities collection
378
	 *
379
	 * @param element a ELEMENT object.
380
	 */
381
	public abstract void addElement(ELEMENT element);
382

    
383
	/**
384
	 * Add an existing element to the entities collection.
385
	 * @return the existing element
386
	 */
387
	public abstract ELEMENT addExisting();
388

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

    
400
	/**
401
	 * Remove an element from the entities collection
402
	 *
403
	 * @param element a ELEMENT object.
404
	 */
405
	public abstract void removeElement(ELEMENT element);
406

    
407
	/**
408
	 * String to display when the collection is empty
409
	 *
410
	 * @return a {@link java.lang.String} object.
411
	 */
412
	public abstract String getEmptyString();
413

    
414
	/**
415
	 * <p>getTooltipString</p>
416
	 *
417
	 * @return String to display when hovering the add button
418
	 */
419
	protected abstract String getTooltipString();
420
}
(4-4/9)