View Javadoc

1   /* ====================================================================
2    *   Copyright 2003-2004 Fabrizio Giustina.
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
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 = ";"; //$NON-NLS-1$
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         // Create a grid data that takes up the extra space in the dialog and spans both columns.
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         // Create a composite for the add and remove buttons and the input text field.
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         // Create a composite for the add and remove buttons.
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         // Create the add button.
196         this.add = new Button(buttonGroup, SWT.NONE);
197         this.add.setText(CCMessages.getString("preferences.button.add")); //$NON-NLS-1$
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         // Create the remove button.
212         this.remove = new Button(buttonGroup, SWT.NONE);
213         this.remove.setEnabled(false);
214         this.remove.setText(CCMessages.getString("preferences.button.remove")); //$NON-NLS-1$
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         // Create the text field.
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         // The button composite and the text field.
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(//$NON-NLS-1$
302                         CCMessages.getString("preferences.invalidfieldmessage"), //$NON-NLS-1$
303                         new Object[]{tag}));
304                 return;
305             }
306 
307             this.list.add(tag);
308         }
309         this.textField.setText(""); //$NON-NLS-1$
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 }