-
Notifications
You must be signed in to change notification settings - Fork 96
/
FormattedText.java
287 lines (264 loc) · 9.19 KB
/
FormattedText.java
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
/*******************************************************************************
* Copyright (c) 2005, 2009 Eric Wuillai.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eric Wuillai (eric@wdev91.com) - initial API and implementation
* Peter Schulz (eclipse-ps@kurzepost.de) - fix for bug 459484
*******************************************************************************/
package org.eclipse.nebula.widgets.formattedtext;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
/**
* Formatted text viewer. Adds formatting capabilities to the <code>Text</code>
* widget of SWT. This control works on the same principle than the JFace
* viewers. The embedded text widget is accessible by the getControl() method,
* allowing to apply to it all necessary behaviors (layout, listeners...).<p>
*
* Formatting is delegated to formatter objects implementing the <code>ITextFormatter</code>
* interface. Each formatter class manages a base class of values (date, number...).<br>
* Formatters are associated by 2 different means :
* <ul>
* <li>By the <code>setFormatter()</code> method.</li>
* <li>When <code>setValue()</code> is called and there is currently no formatter,
* a new one is automatically created based on the class of the value.</li>
* </ul>
*
* <h4>Styles:</h4>
* <blockquote>
* CENTER, LEFT, RIGHT, READ_ONLY
* </blockquote>
*/
public class FormattedText {
/** The key used to register the FormattedText in its Text widget data */
public static final String TEXT_DATA_KEY = "formattedText";
/** Encapsulated Text widget */
protected Text text;
/** Formatter */
protected ITextFormatter formatter = null;
/** Save position of cursor when the focus is lost */
protected int caretPos;
/** Layout */
protected GridLayout layout;
/** Filter for modify events */
protected Listener modifyFilter;
/** Flag to activate or not beep sound on input errors */
protected static boolean beepSound = true;
protected static int count = 0;
protected int id = ++count;
/**
* Creates a formatted text on a newly-created text control under the given
* parent. The text control is created using the SWT style bits
* <code>BORDER</code>.
*
* @param parent the parent control
*/
public FormattedText(Composite parent) {
this(parent, SWT.BORDER);
}
/**
* Creates a formatted text on a newly-created text control under the given
* parent. The text control is created using the given SWT style bits.
*
* @param parent the parent control
* @param style the SWT style bits used to create the text
*/
public FormattedText(Composite parent, int style) {
this(new Text(parent, style & (~ (SWT.MULTI | SWT.PASSWORD | SWT.WRAP))));
}
/**
* Creates a formatted text on the given text control.
*
* @param t the text control
*/
public FormattedText(Text t) {
text = t;
text.setData(TEXT_DATA_KEY, this);
text.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
if ( formatter != null && text.getEditable() ) {
formatter.setIgnore(true);
setText(formatter.getEditString());
text.setSelection(caretPos);
formatter.setIgnore(false);
}
}
public void focusLost(FocusEvent e) {
if ( formatter != null && text.getEditable() ) {
formatter.setIgnore(true);
caretPos = text.getCaretPosition();
String editString = formatter.getEditString();
String displayString = formatter.getDisplayString();
// Detect inconsistency between internal representation,
// for example, a date, and contents of the text control
if (!editString.equals(displayString)) {
// Update the formatter (caches) so it has a consistent state
formatter.setValue(formatter.getValue());
}
setText(displayString);
formatter.setIgnore(false);
}
}
});
modifyFilter = new Listener() {
public void handleEvent(Event event) {
event.type = SWT.None;
}
};
text.addListener(SWT.Dispose, e -> {
text = null;
modifyFilter = null;
formatter = null;
});
}
/**
* Returns the primary <code>Text</code> control associated with this viewer.
*
* @return the SWT text control which displays this viewer's content
*/
public Text getControl() {
return text;
}
/**
* Returns the formatter associated to the <code>Text</code> widget.
*
* @return Formatter, or <code>null</code> if no formatter is currently associated
*/
public ITextFormatter getFormatter() {
return formatter;
}
/**
* Returns the current value of the widget.<p>
*
* The returned value is provided by the formatter and is of the type managed
* by the formatter. For example a <code>DateFormatter</code> will return a
* <code>Date</code> value.<br>
* If no formatter is associated, the <code>String</code> contained in the
* <code>Text</code> widget is returned.
*
* @return Current value
*/
public Object getValue() {
return formatter != null ? formatter.getValue() : text.getText();
}
/**
* Returns the type of value the {@link ITextFormatter} associated with this
* FormattedText handles,
* i.e. returns in {@link #getValue()}.
*
* @return The value type.
*/
public Class<?> getValueType() {
return formatter != null ? formatter.getValueType() : String.class;
}
/**
* Returns true if beep sound must be produced on input errors, else false.
*
* @return true / false
*/
public static boolean isBeepSound() {
return beepSound;
}
/**
* Returns <code>true</code> if the current value is empty, else
* <code>false</code>.<br>
* An empty value depends of the formatter applied on the Text widget
* and is not just an empty String in the widget. Formatters can use special
* formatting characters to format the value. These characters are not
* always considered as part of the value. For example, in a DateFormatter
* the pattern uses "/" separator and always displays it in the input field.
*
* @return <code>true</code> if empty.
*/
public boolean isEmpty() {
return formatter != null ? formatter.isEmpty() : text.getText().length() == 0;
}
/**
* Returns <code>true</code> if the current value is valid, else <code>false</code>.
*
* @return <code>true</code> if valid.
*/
public boolean isValid() {
return formatter != null ? formatter.isValid() : true;
}
/**
* Set the beep sound to ON or OFF for all the FormattedText fields. Beep
* sound are produced by formatters on every input error. Formatter
* implementation must check this flag on before to emit a beep sound.
*
* @param beepSound true to emit beep sound on errors, else false
*/
public static void setBeepSound(boolean beepSound) {
FormattedText.beepSound = beepSound;
}
/**
* Associates a formatter to the widget.<br>
* Parameter can not be null. In some situations, the FormattedText component
* must not do formatting (eg. when reusing the same object for editing of
* different types of values). In this case, use a StringFormatter. This
* formatter do no formatting.
*
* @param formatter formatter
*/
public void setFormatter(ITextFormatter formatter) {
if ( formatter == null ) SWT.error(SWT.ERROR_NULL_ARGUMENT);
if ( this.formatter != null ) {
text.removeVerifyListener(this.formatter);
this.formatter.detach();
}
this.formatter = formatter;
this.formatter.setText(text);
text.addVerifyListener(this.formatter);
formatter.setIgnore(true);
text.setText(formatter.getDisplayString());
formatter.setIgnore(false);
}
/**
* Sets the Text widget value, preventing fire of Modify events.
*
* @param value The String value to display in the widget
*/
private void setText(String value) {
Display display = text.getDisplay();
try {
display.addFilter(SWT.Modify, modifyFilter);
text.setText(value);
} finally {
display.removeFilter(SWT.Modify, modifyFilter);
}
}
/**
* Sets a new value.<p>
*
* If no formatter is currently associated to he widget, a new one is created
* by the factory based on the value's class.<br>
* If the value is incompatible with the formatter, an <code>IllegalArgumentException</code>
* is returned.
*
* @param value new value
*/
public void setValue(Object value) {
if ( formatter == null ) {
setFormatter(DefaultFormatterFactory.createFormatter(value));
}
formatter.setValue(value);
formatter.setIgnore(true);
text.setText(text.isFocusControl()
? formatter.getEditString()
: formatter.getDisplayString());
formatter.setIgnore(false);
}
}