Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 04bd011276ea19db4cbb9e6229467460686247f7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
/*****************************************************************************
 * Copyright (c) 2010, 2014 CEA LIST and others.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
 *  Christian W. Damus (CEA) - bug 402525
 *  
 *****************************************************************************/
package org.eclipse.papyrus.infra.widgets.editors;

import java.util.LinkedHashSet;
import java.util.Set;

import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.conversion.IConverter;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.papyrus.infra.widgets.creation.IAtomicOperationExecutor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;

/**
 * An Abstract class to represent Editors.
 * An editor is a Composite, containing a label and one
 * or more controls. The label may be null.
 * The controls are specified by the implementations
 * of this abstract class.
 * 
 * @author Camille Letavernier
 */
//FIXME: The composite widget hides access to the encapsulated widget(s). 
//Thus, it is not possible to add custom listeners on the editors
//We should forward the listeners to the encapsulated (this.addListener(int, Listener) -> getMainWidget().addListener(int, Listener))
//Problem: some widgets have more than one "main widget" (e.g. EnumRadio).
public abstract class AbstractEditor extends Composite implements DisposeListener {

	/**
	 * The label for this editor. May be null.
	 */
	protected Label label;

	/**
	 * The label value for this editor
	 */
	protected String labelText;

	/**
	 * The set of elements listening on changes from this editor
	 */
	protected Set<ICommitListener> commitListeners = new LinkedHashSet<ICommitListener>();

	/**
	 * The binding between the model object and the widget
	 */
	protected Binding binding;

	/**
	 * The toolTipText associated to this editor
	 */
	protected String toolTipText;

	/**
	 * The factory for creating all the editors with a common style
	 */
	public static TabbedPropertySheetWidgetFactory factory = new TabbedPropertySheetWidgetFactory();

	static {
		factory.setBackground(null);
		factory.setBorderStyle(SWT.BORDER); //This seems to be used only by the FormToolKit factory, we still need to force it for the CLabel or CCombo widgets
	}

	/**
	 * 
	 * Constructor. Constructs an editor without a label
	 * 
	 * @param parent
	 *        The composite in which this editor should be created
	 */
	protected AbstractEditor(Composite parent) {
		this(parent, SWT.NONE, null);
	}

	/**
	 * 
	 * Constructor. Constructs an editor without a label
	 * 
	 * @param parent
	 *        The composite in which this editor should be created
	 * @param style
	 *        The style of this editor's main composite
	 */
	protected AbstractEditor(Composite parent, int style) {
		this(parent, style, null);
	}

	/**
	 * 
	 * Constructor. Constructs an editor with a label
	 * 
	 * @param parent
	 *        The composite in which this editor should be created
	 * @param label
	 *        The label that will be displayed for this editor, or null
	 *        if no label should be displayed
	 */
	protected AbstractEditor(Composite parent, String label) {
		this(parent, SWT.NONE, label);
	}

	/**
	 * 
	 * Constructor. Constructs an editor with a label
	 * 
	 * @param parent
	 *        The composite in which this editor should be created
	 * @param style
	 *        The style of this editor's main composite
	 * @param label
	 *        The label that will be displayed for this editor, or null
	 *        if no label should be displayed
	 */
	protected AbstractEditor(Composite parent, int style, String label) {
		super(parent, style);
		GridLayout layout = new GridLayout(1, false);
		setLayout(layout);
		if(label != null) {
			createLabel(label);
		}
		parent.addDisposeListener(this);
	}

	/**
	 * Creates the label widget with the given text
	 * 
	 * @param text
	 *        The text to be displayed on the label
	 */
	protected void createLabel(String text) {
		label = factory.createLabel(this, text);
		label.setLayoutData(getLabelLayoutData());
		if(toolTipText != null) {
			label.setToolTipText(toolTipText);
		}
		((GridLayout)getLayout()).numColumns++;
	}

	/**
	 * @return The default layoutData for the label
	 */
	protected GridData getLabelLayoutData() {
		GridData data = new GridData();
		data.widthHint = 120;
		data.verticalAlignment = SWT.CENTER;
		return data;
	}

	/**
	 * This method should be called by subclasses to get the default layoutData
	 * for a control in this editor.
	 * 
	 * @return The default layoutData for the main control
	 */
	protected GridData getDefaultLayoutData() {
		GridData data = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
		return data;
	}

	/**
	 * Changes the text label for this editor. This method is available
	 * only when the editor has been constructed with a label.
	 * 
	 * @param label
	 *        The new text for this editor's label
	 */
	public void setLabel(String label) {
		this.labelText = label;

		if(this.label != null) {
			this.label.setText(label);
		} else {
			createLabel(label);
			this.label.moveAbove(getChildren()[0]);
		}
	}

	/**
	 * Show or delete the Label Widget.
	 * 
	 * @param displayLabel
	 */
	public void setDisplayLabel(boolean displayLabel) {
		if(displayLabel) {
			setLabel(labelText);
		} else {
			if(this.label != null) {
				this.label.dispose();
				((GridLayout)getLayout()).numColumns--;
			}
		}
	}

	/**
	 * Adds a commit listener to this editor. A Commit event is
	 * fired when a modification occures on this editor.
	 * 
	 * @param listener
	 *        The commit listener to add to this editor
	 */
	public void addCommitListener(ICommitListener listener) {
		commitListeners.add(listener);
	}

	/**
	 * Removes a commit listener from this editor.
	 * 
	 * @param listener
	 *        The commit listener to remove from this editor
	 */
	public void removeCommitListener(ICommitListener listener) {
		commitListeners.remove(listener);
	}

	/**
	 * Informs the commit listeners that a modification occured
	 */
	protected void commit() {
		for(ICommitListener listener : commitListeners) {
			listener.commit(this);
		}
	}

	/**
	 * Gets the BindingContext associated to the editors
	 * 
	 * @return
	 */
	protected DataBindingContext getBindingContext() {
		return new DataBindingContext();
	}

	/**
	 * Sets the converters to convert data from Model to Target (Widget),
	 * and from Widget to Model
	 * 
	 * @param targetToModel
	 * @param modelToTarget
	 */
	abstract public void setConverters(IConverter targetToModel, IConverter modelToTarget);


	/**
	 * Binds the Widget Observable to the Model observable property,
	 * using the specified converters when available
	 */
	abstract protected void doBinding();

	/**
	 * @return the type of objects that this widget can edit
	 */
	public abstract Object getEditableType();

	/**
	 * Marks this editor as being read-only. The value of a read-only
	 * editor cannot be changed by the editor itself.
	 * 
	 * @param readOnly
	 */
	public abstract void setReadOnly(boolean readOnly);

	/**
	 * Tests whether this editor is read-only or not
	 * 
	 * @return
	 *         True if the editor is read-only
	 */
	public abstract boolean isReadOnly();

	/**
	 * Indicates that this editor should notify its commit listeners
	 * when the given control looses the Focus
	 * 
	 * @param control
	 *        The control on which a FocusListener should be added,
	 *        to notify the CommitListeners
	 */
	protected void setCommitOnFocusLost(Control control) {
		control.addFocusListener(new FocusListener() {

			public void focusGained(FocusEvent e) {
				// Nothing
			}

			public void focusLost(FocusEvent e) {
				commit();
			}

		});
	}

	/**
	 * Forces the refresh of the widget's value
	 */
	public void refreshValue() {
		if(binding != null) {
			binding.updateModelToTarget();
		}
	}

	/**
	 * Sets the given toolTip to the label
	 * 
	 * @param text
	 *        The new label's tooltip
	 */
	protected void setLabelToolTipText(String text) {
		toolTipText = text;
		if(label != null && !label.isDisposed()) {
			label.setToolTipText(text);
		}
	}

	/**
	 * Excludes or includes the given control from the layout
	 * 
	 * @param control
	 *        The control to exclude or include
	 * @param exclude
	 *        If true, the control will be excluded ; otherwise, it will be included
	 */
	protected void setExclusion(Control control, boolean exclude) {
		if(control.getLayoutData() == null) {
			GridData data = new GridData();
			control.setLayoutData(data);
		}

		GridData data = (GridData)control.getLayoutData();

		if(data.exclude != exclude) {
			data.exclude = exclude;
			GridLayout layout = (GridLayout)control.getParent().getLayout();
			if(exclude) {
				layout.numColumns--;
			} else {
				layout.numColumns++;
			}
		}
	}

	@Override
	public abstract void setToolTipText(String text);

	public void widgetDisposed(DisposeEvent e) {
		dispose();
	}

	/**
	 * Obtains the most appropriate operation executor for the object being edited.
	 * 
	 * @param context the object being edited
	 * @return the executor to use to run operations (never {@code null})
	 */
	public IAtomicOperationExecutor getOperationExecutor(Object context) {
		IAtomicOperationExecutor result;
		if(context instanceof IAdaptable) {
			result = (IAtomicOperationExecutor)((IAdaptable)context).getAdapter(IAtomicOperationExecutor.class);
		} else {
			result = (IAtomicOperationExecutor)Platform.getAdapterManager().getAdapter(context, IAtomicOperationExecutor.class);
		}

		if (result == null) {
			result = IAtomicOperationExecutor.DEFAULT;
		}
		
		return result;
	}
}

Back to the top