1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package net.sf.commonclipse.preferences;
18
19 import java.text.MessageFormat;
20 import java.util.ArrayList;
21 import java.util.StringTokenizer;
22
23 import net.sf.commonclipse.CCMessages;
24
25 import org.eclipse.jface.dialogs.IDialogConstants;
26 import org.eclipse.jface.dialogs.MessageDialog;
27 import org.eclipse.jface.preference.FieldEditor;
28 import org.eclipse.swt.SWT;
29 import org.eclipse.swt.events.SelectionAdapter;
30 import org.eclipse.swt.events.SelectionEvent;
31 import org.eclipse.swt.layout.GridData;
32 import org.eclipse.swt.layout.GridLayout;
33 import org.eclipse.swt.widgets.Button;
34 import org.eclipse.swt.widgets.Composite;
35 import org.eclipse.swt.widgets.Label;
36 import org.eclipse.swt.widgets.List;
37 import org.eclipse.swt.widgets.Shell;
38 import org.eclipse.swt.widgets.Text;
39
40
41 /***
42 * A field editor for displaying and storing a list of strings. Buttons are provided for adding items to the list and
43 * removing items from the list. Implementation from
44 * http://www.eclipse.org/articles/Article-Field-Editors/field_editors.html
45 * @author fgiust
46 * @version $Revision: 1.5 $ ($Author: fgiust $)
47 */
48 public class AddRemoveListFieldEditor extends FieldEditor
49 {
50
51 /***
52 * default separator for list items.
53 */
54 private static final String DEFAULT_SEPARATOR = ";";
55
56 /***
57 * vertical dialog units per char.
58 */
59 private static final int VERTICAL_DIALOG_UNITS_PER_CHAR = 8;
60
61 /***
62 * list height.
63 */
64 private static final int LIST_HEIGHT_IN_CHARS = 10;
65
66 /***
67 * list height in units.
68 */
69 private static final int LIST_HEIGHT_IN_DLUS = LIST_HEIGHT_IN_CHARS * VERTICAL_DIALOG_UNITS_PER_CHAR;
70
71 /***
72 * The list of items.
73 */
74 List list;
75
76 /***
77 * The top-level control for the field editor.
78 */
79 private Composite top;
80
81 /***
82 * The text field for inputting new list items.
83 */
84 private Text textField;
85
86 /***
87 * The button for adding the contents of the text field to the list.
88 */
89 private Button add;
90
91 /***
92 * The button for removing the currently-selected list item.
93 */
94 private Button remove;
95
96 /***
97 * The string used to separate list items in a single String representation.
98 */
99 private String separator = DEFAULT_SEPARATOR;
100
101 /***
102 * Creates a string field editor of unlimited width. Use the method <code>setTextLimit</code> to limit the text.
103 * @param name the name of the preference this field editor works on
104 * @param labelText the label text of the field editor
105 * @param parent the parent of the field editor's control
106 */
107 public AddRemoveListFieldEditor(String name, String labelText, Composite parent)
108 {
109 super(name, labelText, parent);
110 }
111
112 /***
113 * Creates a string field editor of unlimited width. Use the method <code>setTextLimit</code> to limit the text.
114 * @param name the name of the preference this field editor works on
115 * @param labelText the label text of the field editor
116 * @param addButtonText text for the "add" button
117 * @param removeButtonText text for the "remove" buttom
118 * @param parent the parent of the field editor's control
119 */
120 public AddRemoveListFieldEditor(
121 String name,
122 String labelText,
123 String addButtonText,
124 String removeButtonText,
125 Composite parent)
126 {
127 super(name, labelText, parent);
128 setAddButtonText(addButtonText);
129 setRemoveButtonText(removeButtonText);
130 }
131
132 /***
133 * @see org.eclipse.jface.preference.FieldEditor#adjustForNumColumns(int)
134 */
135 protected void adjustForNumColumns(int numColumns)
136 {
137 ((GridData) this.top.getLayoutData()).horizontalSpan = numColumns;
138 }
139
140 /***
141 * @see org.eclipse.jface.preference.FieldEditor#doFillIntoGrid (Composite, int)
142 */
143 protected void doFillIntoGrid(Composite parent, int numColumns)
144 {
145 this.top = parent;
146
147 GridData gd = new GridData(GridData.FILL_HORIZONTAL);
148 gd.horizontalSpan = numColumns;
149 this.top.setLayoutData(gd);
150
151 Label label = getLabelControl(this.top);
152 GridData labelData = new GridData();
153 labelData.horizontalSpan = numColumns;
154 label.setLayoutData(labelData);
155
156 this.list = new List(this.top, SWT.BORDER);
157
158
159 GridData listData = new GridData(GridData.FILL_HORIZONTAL);
160 listData.heightHint = convertVerticalDLUsToPixels(this.list, LIST_HEIGHT_IN_DLUS);
161 listData.horizontalSpan = numColumns;
162
163 this.list.setLayoutData(listData);
164 this.list.addSelectionListener(new SelectionAdapter()
165 {
166
167 public void widgetSelected(SelectionEvent e)
168 {
169 selectionChanged();
170 }
171 });
172
173
174 Composite addRemoveGroup = new Composite(this.top, SWT.NONE);
175
176 GridData addRemoveData = new GridData(GridData.FILL_HORIZONTAL);
177 addRemoveData.horizontalSpan = numColumns;
178 addRemoveGroup.setLayoutData(addRemoveData);
179
180 GridLayout addRemoveLayout = new GridLayout();
181 addRemoveLayout.numColumns = numColumns;
182 addRemoveLayout.marginHeight = 0;
183 addRemoveLayout.marginWidth = 0;
184 addRemoveGroup.setLayout(addRemoveLayout);
185
186
187 Composite buttonGroup = new Composite(addRemoveGroup, SWT.NONE);
188 buttonGroup.setLayoutData(new GridData());
189
190 GridLayout buttonLayout = new GridLayout();
191 buttonLayout.marginHeight = 0;
192 buttonLayout.marginWidth = 0;
193 buttonGroup.setLayout(buttonLayout);
194
195
196 this.add = new Button(buttonGroup, SWT.NONE);
197 this.add.setText(CCMessages.getString("preferences.button.add"));
198 this.add.addSelectionListener(new SelectionAdapter()
199 {
200
201 public void widgetSelected(SelectionEvent e)
202 {
203 add();
204 }
205 });
206 GridData addData = new GridData(GridData.FILL_HORIZONTAL);
207 addData.heightHint = convertVerticalDLUsToPixels(this.add, 14);
208 addData.widthHint = convertHorizontalDLUsToPixels(this.add, IDialogConstants.BUTTON_WIDTH);
209 this.add.setLayoutData(addData);
210
211
212 this.remove = new Button(buttonGroup, SWT.NONE);
213 this.remove.setEnabled(false);
214 this.remove.setText(CCMessages.getString("preferences.button.remove"));
215 this.remove.addSelectionListener(new SelectionAdapter()
216 {
217
218 public void widgetSelected(SelectionEvent e)
219 {
220 AddRemoveListFieldEditor.this.list.remove(AddRemoveListFieldEditor.this.list.getSelectionIndex());
221 selectionChanged();
222 }
223 });
224 GridData removeData = new GridData(GridData.FILL_HORIZONTAL);
225 removeData.heightHint = convertVerticalDLUsToPixels(this.remove, 14);
226 removeData.widthHint = convertHorizontalDLUsToPixels(this.remove, IDialogConstants.BUTTON_WIDTH);
227 this.remove.setLayoutData(removeData);
228
229
230 this.textField = new Text(addRemoveGroup, SWT.BORDER);
231
232 GridData textData = new GridData(GridData.FILL_HORIZONTAL);
233 textData.horizontalSpan = numColumns - 1;
234 textData.verticalAlignment = GridData.BEGINNING;
235 this.textField.setLayoutData(textData);
236 }
237
238 /***
239 * @see org.eclipse.jface.preference.FieldEditor#doLoad()
240 */
241 protected void doLoad()
242 {
243 String items = getPreferenceStore().getString(getPreferenceName());
244 setList(items);
245 }
246
247 /***
248 * @see org.eclipse.jface.preference.FieldEditor#doLoadDefault()
249 */
250 protected void doLoadDefault()
251 {
252 String items = getPreferenceStore().getDefaultString(getPreferenceName());
253 setList(items);
254 }
255
256 /***
257 * Parses the string into separate list items and adds them to the list.
258 * @param items String to be splitted
259 */
260 private void setList(String items)
261 {
262 String[] itemArray = parseString(items);
263 this.list.setItems(itemArray);
264 }
265
266 /***
267 * @see org.eclipse.jface.preference.FieldEditor#doStore()
268 */
269 protected void doStore()
270 {
271 String s = createListString(this.list.getItems());
272 if (s != null)
273 {
274
275 getPreferenceStore().setValue(getPreferenceName(), s);
276 }
277 }
278
279 /***
280 * @see org.eclipse.jface.preference.FieldEditor#getNumberOfControls()
281 */
282 public int getNumberOfControls()
283 {
284
285 return 2;
286 }
287
288 /***
289 * Adds the string in the text field to the list.
290 */
291 void add()
292 {
293 String tag = this.textField.getText();
294 if (tag != null && tag.length() > 0)
295 {
296
297 if (!containsOnlyValidChars(tag))
298 {
299 MessageDialog.openError(
300 new Shell(),
301 CCMessages.getString("preferences.invalidfieldtitle"), MessageFormat.format(
302 CCMessages.getString("preferences.invalidfieldmessage"),
303 new Object[]{tag}));
304 return;
305 }
306
307 this.list.add(tag);
308 }
309 this.textField.setText("");
310 }
311
312 /***
313 * Checks the the given String doesn't contains invalid chars.
314 * @param str input String
315 * @return <code>true</code> if the string doesn't contains invalid chars.
316 */
317 public static boolean containsOnlyValidChars(String str)
318 {
319
320 char[] invalidChars = new char[]{'(', ')', '[', ']', '{', '}', '.', '/', '//', '^', '$', '.', '&', '|', '+'};
321
322 int strSize = str.length();
323 int validSize = invalidChars.length;
324 for (int i = 0; i < strSize; i++)
325 {
326 char ch = str.charAt(i);
327 for (int j = 0; j < validSize; j++)
328 {
329 if (invalidChars[j] == ch)
330 {
331 return false;
332 }
333 }
334 }
335 return true;
336 }
337
338 /***
339 * Sets the label for the button that adds the contents of the text field to the list.
340 * @param text "add" button text
341 */
342 public void setAddButtonText(String text)
343 {
344 this.add.setText(text);
345 }
346
347 /***
348 * Sets the label for the button that removes the selected item from the list.
349 * @param text "remove" button text
350 */
351 public void setRemoveButtonText(String text)
352 {
353 this.remove.setText(text);
354 }
355
356 /***
357 * Sets the string that separates items in the list when the list is stored as a single String in the preference
358 * store.
359 * @param listSeparator token used as a delimiter when converting the array in a single string
360 */
361 public void setSeparator(String listSeparator)
362 {
363 this.separator = listSeparator;
364 }
365
366 /***
367 * Creates the single String representation of the list that is stored in the preference store.
368 * @param items String array
369 * @return String created adding items elements separated by "separator"
370 */
371 private String createListString(String[] items)
372 {
373 StringBuffer path = new StringBuffer();
374
375 for (int i = 0; i < items.length; i++)
376 {
377 path.append(items[i]);
378 path.append(this.separator);
379 }
380 return path.toString();
381 }
382
383 /***
384 * Parses the single String representation of the list into an array of list items.
385 * @param stringList String to be splitted in array
386 * @return String[] splitted string
387 */
388 private String[] parseString(String stringList)
389 {
390 StringTokenizer st = new StringTokenizer(stringList, this.separator);
391 java.util.List v = new ArrayList();
392 while (st.hasMoreElements())
393 {
394 v.add(st.nextElement());
395 }
396 return (String[]) v.toArray(new String[v.size()]);
397 }
398
399 /***
400 * Sets the enablement of the remove button depending on the selection in the list.
401 */
402 void selectionChanged()
403 {
404 int index = this.list.getSelectionIndex();
405 this.remove.setEnabled(index >= 0);
406 }
407
408 }