removed NPE in XMLPreferenceInitializer
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / preferences / SpellingConfigurationBlock.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11
12 package net.sourceforge.phpdt.internal.ui.preferences;
13
14 import java.io.File;
15 import java.util.HashMap;
16 import java.util.Iterator;
17 import java.util.Locale;
18 import java.util.Map;
19 import java.util.Set;
20
21 import net.sourceforge.phpdt.core.IJavaProject;
22 import net.sourceforge.phpdt.internal.ui.dialogs.StatusInfo;
23 import net.sourceforge.phpdt.internal.ui.dialogs.StatusUtil;
24 import net.sourceforge.phpdt.internal.ui.text.spelling.SpellCheckEngine;
25 import net.sourceforge.phpdt.internal.ui.util.PixelConverter;
26 import net.sourceforge.phpdt.internal.ui.util.SWTUtil;
27 import net.sourceforge.phpdt.internal.ui.wizards.IStatusChangeListener;
28 import net.sourceforge.phpdt.ui.PreferenceConstants;
29
30 import org.eclipse.core.runtime.IStatus;
31 import org.eclipse.jface.preference.IPreferenceStore;
32 import org.eclipse.swt.SWT;
33 import org.eclipse.swt.events.SelectionAdapter;
34 import org.eclipse.swt.events.SelectionEvent;
35 import org.eclipse.swt.events.SelectionListener;
36 import org.eclipse.swt.layout.GridData;
37 import org.eclipse.swt.layout.GridLayout;
38 import org.eclipse.swt.widgets.Button;
39 import org.eclipse.swt.widgets.Combo;
40 import org.eclipse.swt.widgets.Composite;
41 import org.eclipse.swt.widgets.Control;
42 import org.eclipse.swt.widgets.FileDialog;
43 import org.eclipse.swt.widgets.Group;
44 import org.eclipse.swt.widgets.Label;
45 import org.eclipse.swt.widgets.Text;
46
47 /**
48  * Options configuration block for spell-check related settings.
49  * 
50  * @since 3.0
51  */
52 public class SpellingConfigurationBlock extends OptionsConfigurationBlock {
53
54         /** Preference keys for the preferences in this block */
55         private static final String PREF_SPELLING_CHECK_SPELLING= PreferenceConstants.SPELLING_CHECK_SPELLING;
56         private static final String PREF_SPELLING_IGNORE_DIGITS= PreferenceConstants.SPELLING_IGNORE_DIGITS;
57         private static final String PREF_SPELLING_IGNORE_MIXED= PreferenceConstants.SPELLING_IGNORE_MIXED;
58         private static final String PREF_SPELLING_IGNORE_SENTENCE= PreferenceConstants.SPELLING_IGNORE_SENTENCE;
59         private static final String PREF_SPELLING_IGNORE_UPPER= PreferenceConstants.SPELLING_IGNORE_UPPER;
60         private static final String PREF_SPELLING_IGNORE_URLS= PreferenceConstants.SPELLING_IGNORE_URLS;
61         private static final String PREF_SPELLING_LOCALE= PreferenceConstants.SPELLING_LOCALE;
62         private static final String PREF_SPELLING_PROPOSAL_THRESHOLD= PreferenceConstants.SPELLING_PROPOSAL_THRESHOLD;
63         private static final String PREF_SPELLING_USER_DICTIONARY= PreferenceConstants.SPELLING_USER_DICTIONARY;
64         private static final String PREF_SPELLING_ENABLE_CONTENTASSIST= PreferenceConstants.SPELLING_ENABLE_CONTENTASSIST;
65
66         /**
67          * Creates a selection dependency between a master and a slave control.
68          * 
69          * @param master
70          *                   The master button that controls the state of the slave
71          * @param slave
72          *                   The slave control that is enabled only if the master is
73          *                   selected
74          */
75         protected static void createSelectionDependency(final Button master, final Control slave) {
76
77                 master.addSelectionListener(new SelectionListener() {
78
79                         public void widgetDefaultSelected(SelectionEvent event) {
80                                 // Do nothing
81                         }
82
83                         public void widgetSelected(SelectionEvent event) {
84                                 slave.setEnabled(master.getSelection());
85                         }
86                 });
87                 slave.setEnabled(master.getSelection());
88         }
89
90         /**
91          * Returns the locale codes for the locale list.
92          * 
93          * @param locales
94          *                   The list of locales
95          * @return Array of locale codes for the list
96          */
97         protected static String[] getDictionaryCodes(final Set locales) {
98
99                 int index= 0;
100                 Locale locale= null;
101
102                 final String[] codes= new String[locales.size()];
103                 for (final Iterator iterator= locales.iterator(); iterator.hasNext();) {
104
105                         locale= (Locale)iterator.next();
106                         codes[index++]= locale.toString();
107                 }
108                 return codes;
109         }
110
111         /**
112          * Returns the display labels for the locale list.
113          * 
114          * @param locales
115          *                   The list of locales
116          * @return Array of display labels for the list
117          */
118         protected static String[] getDictionaryLabels(final Set locales) {
119
120                 int index= 0;
121                 Locale locale= null;
122
123                 final String[] labels= new String[locales.size()];
124                 for (final Iterator iterator= locales.iterator(); iterator.hasNext();) {
125
126                         locale= (Locale)iterator.next();
127                         labels[index++]= locale.getDisplayName(SpellCheckEngine.getDefaultLocale());
128                 }
129                 return labels;
130         }
131
132         /**
133          * Validates that the file with the specified absolute path exists and can
134          * be opened.
135          * 
136          * @param path
137          *                   The path of the file to validate
138          * @return <code>true</code> iff the file exists and can be opened,
139          *               <code>false</code> otherwise
140          */
141         protected static IStatus validateAbsoluteFilePath(final String path) {
142
143                 final StatusInfo status= new StatusInfo();
144                 if (path.length() > 0) {
145
146                         final File file= new File(path);
147                         if (!file.isFile() || !file.isAbsolute() || !file.exists() || !file.canRead() || !file.canWrite())
148                                 status.setError(PreferencesMessages.getString("SpellingPreferencePage.dictionary.error")); //$NON-NLS-1$
149
150                 }
151                 return status;
152         }
153
154         /**
155          * Validates that the specified locale is available.
156          * 
157          * @param locale
158          *                   The locale to validate
159          * @return The status of the validation
160          */
161         protected static IStatus validateLocale(final String locale) {
162
163                 final StatusInfo status= new StatusInfo(IStatus.ERROR, PreferencesMessages.getString("SpellingPreferencePage.locale.error")); //$NON-NLS-1$
164                 final Set locales= SpellCheckEngine.getAvailableLocales();
165
166                 Locale current= null;
167                 for (final Iterator iterator= locales.iterator(); iterator.hasNext();) {
168
169                         current= (Locale)iterator.next();
170                         if (current.toString().equals(locale))
171                                 return new StatusInfo();
172                 }
173                 return status;
174         }
175
176         /**
177          * Validates that the specified number is positive.
178          * 
179          * @param number
180          *                   The number to validate
181          * @return The status of the validation
182          */
183         protected static IStatus validatePositiveNumber(final String number) {
184
185                 final StatusInfo status= new StatusInfo();
186                 if (number.length() == 0) {
187                         status.setError(PreferencesMessages.getString("SpellingPreferencePage.empty_threshold")); //$NON-NLS-1$
188                 } else {
189                         try {
190                                 final int value= Integer.parseInt(number);
191                                 if (value < 0) {
192                                         status.setError(PreferencesMessages.getFormattedString("SpellingPreferencePage.invalid_threshold", number)); //$NON-NLS-1$
193                                 }
194                         } catch (NumberFormatException exception) {
195                                 status.setError(PreferencesMessages.getFormattedString("SpellingPreferencePage.invalid_threshold", number)); //$NON-NLS-1$
196                         }
197                 }
198                 return status;
199         }
200
201         /** The dictionary path field */
202         private Text fDictionaryPath= null;
203
204         /** The status for the workspace dictionary file */
205         private IStatus fFileStatus= new StatusInfo();
206
207         /** The status for the platform locale */
208         private IStatus fLocaleStatus= new StatusInfo();
209
210         /** The status for the proposal threshold */
211         private IStatus fThresholdStatus= new StatusInfo();
212
213         /**
214          * Creates a new spelling configuration block.
215          * 
216          * @param context
217          *                   The status change listener
218          * @param project
219          *                   The Java project
220          */
221         public SpellingConfigurationBlock(final IStatusChangeListener context, final IJavaProject project) {
222                 super(context, project, getAllKeys());
223  
224                 IStatus status= validateAbsoluteFilePath((String)fWorkingValues.get(PREF_SPELLING_USER_DICTIONARY));
225                 if (status.getSeverity() != IStatus.OK)
226                         fWorkingValues.put(PREF_SPELLING_USER_DICTIONARY, ""); //$NON-NLS-1$
227
228                 status= validateLocale((String)fWorkingValues.get(PREF_SPELLING_LOCALE));
229                 if (status.getSeverity() != IStatus.OK)
230                         fWorkingValues.put(PREF_SPELLING_LOCALE, SpellCheckEngine.getDefaultLocale().toString());
231         }
232
233         protected Combo addComboBox(Composite parent, String label, String key, String[] values, String[] valueLabels, int indent) {
234                 ControlData data= new ControlData(key, values);
235                 
236                 GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
237                 gd.horizontalIndent= indent;
238                                 
239                 Label labelControl= new Label(parent, SWT.LEFT | SWT.WRAP);
240                 labelControl.setText(label);
241                 labelControl.setLayoutData(gd);
242                 
243                 Combo comboBox= new Combo(parent, SWT.READ_ONLY);
244                 comboBox.setItems(valueLabels);
245                 comboBox.setData(data);
246                 gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
247                 gd.horizontalSpan= 2;
248                 comboBox.setLayoutData(gd);
249                 comboBox.addSelectionListener(getSelectionListener());
250                 
251                 fLabels.put(comboBox, labelControl);
252                 
253                 String currValue= (String)fWorkingValues.get(key);      
254                 comboBox.select(data.getSelection(currValue));
255                 
256                 fComboBoxes.add(comboBox);
257                 return comboBox;
258         }
259
260         /*
261          * @see net.sourceforge.phpdt.internal.ui.preferences.OptionsConfigurationBlock#createContents(org.eclipse.swt.widgets.Composite)
262          */
263         protected Control createContents(final Composite parent) {
264
265                 Composite composite= new Composite(parent, SWT.NONE);
266                 GridLayout layout= new GridLayout(); layout.numColumns= 1;
267                 composite.setLayout(layout);
268
269                 final PixelConverter converter= new PixelConverter(parent);
270
271                 layout= new GridLayout();
272                 layout.numColumns= 3;
273
274                 final String[] trueFalse= new String[] { IPreferenceStore.TRUE, IPreferenceStore.FALSE };
275
276                 Group user= new Group(composite, SWT.NONE);
277                 user.setText(PreferencesMessages.getString("SpellingPreferencePage.preferences.user")); //$NON-NLS-1$
278                 user.setLayout(new GridLayout());               
279                 user.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
280
281                 String label= PreferencesMessages.getString("SpellingPreferencePage.enable.label"); //$NON-NLS-1$
282                 final Button master= addCheckBox(user, label, PREF_SPELLING_CHECK_SPELLING, trueFalse, 0);
283
284                 label= PreferencesMessages.getString("SpellingPreferencePage.ignore.digits.label"); //$NON-NLS-1$
285                 Control slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_DIGITS, trueFalse, 20);
286                 createSelectionDependency(master, slave);
287
288                 label= PreferencesMessages.getString("SpellingPreferencePage.ignore.mixed.label"); //$NON-NLS-1$
289                 slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_MIXED, trueFalse, 20);
290                 createSelectionDependency(master, slave);
291
292                 label= PreferencesMessages.getString("SpellingPreferencePage.ignore.sentence.label"); //$NON-NLS-1$
293                 slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_SENTENCE, trueFalse, 20);
294                 createSelectionDependency(master, slave);
295
296                 label= PreferencesMessages.getString("SpellingPreferencePage.ignore.upper.label"); //$NON-NLS-1$
297                 slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_UPPER, trueFalse, 20);
298                 createSelectionDependency(master, slave);
299
300                 label= PreferencesMessages.getString("SpellingPreferencePage.ignore.url.label"); //$NON-NLS-1$
301                 slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_URLS, trueFalse, 20);
302                 createSelectionDependency(master, slave);
303
304                 final Group engine= new Group(composite, SWT.NONE);
305                 engine.setText(PreferencesMessages.getString("SpellingPreferencePage.preferences.engine")); //$NON-NLS-1$
306                 layout= new GridLayout();
307                 layout.numColumns= 4;
308                 engine.setLayout(layout);
309                 engine.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
310
311                 label= PreferencesMessages.getString("SpellingPreferencePage.dictionary.label"); //$NON-NLS-1$
312                 final Set locales= SpellCheckEngine.getAvailableLocales();
313
314                 Combo combo= addComboBox(engine, label, PREF_SPELLING_LOCALE, getDictionaryCodes(locales), getDictionaryLabels(locales), 0);
315                 combo.setEnabled(locales.size() > 1);
316                 
317                 new Label(engine, SWT.NONE); // placeholder
318
319                 label= PreferencesMessages.getString("SpellingPreferencePage.workspace.dictionary.label"); //$NON-NLS-1$
320                 fDictionaryPath= addTextField(engine, label, PREF_SPELLING_USER_DICTIONARY, 0, 0);
321
322                 
323                 Button button= new Button(engine, SWT.PUSH);
324                 button.setText(PreferencesMessages.getString("SpellingPreferencePage.browse.label")); //$NON-NLS-1$
325                 button.addSelectionListener(new SelectionAdapter() {
326
327                         public void widgetSelected(final SelectionEvent event) {
328                                 handleBrowseButtonSelected();
329                         }
330                 });
331                 button.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END));
332                 SWTUtil.setButtonDimensionHint(button);
333                 
334                 layout= new GridLayout();
335                 layout.numColumns= 3;
336
337                 Group advanced= new Group(composite, SWT.NONE);
338                 advanced.setText(PreferencesMessages.getString("SpellingPreferencePage.preferences.advanced")); //$NON-NLS-1$
339                 layout= new GridLayout();
340                 layout.numColumns= 3;
341                 advanced.setLayout(layout);             
342                 advanced.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
343                 
344                 label= PreferencesMessages.getString("SpellingPreferencePage.proposals.threshold"); //$NON-NLS-1$
345                 Text text= addTextField(advanced, label, PREF_SPELLING_PROPOSAL_THRESHOLD, 0, 0);
346                 text.setTextLimit(3);
347                 GridData data= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
348                 data.widthHint= converter.convertWidthInCharsToPixels(4);
349                 text.setLayoutData(data);
350                 
351                 label= PreferencesMessages.getString("SpellingPreferencePage.enable.contentassist.label"); //$NON-NLS-1$
352                 addCheckBox(advanced, label, PREF_SPELLING_ENABLE_CONTENTASSIST, trueFalse, 0);
353
354                 return composite;
355         }
356
357         private static String[] getAllKeys() {
358                 return new String[] { PREF_SPELLING_USER_DICTIONARY, PREF_SPELLING_CHECK_SPELLING, PREF_SPELLING_IGNORE_DIGITS, PREF_SPELLING_IGNORE_MIXED, PREF_SPELLING_IGNORE_SENTENCE, PREF_SPELLING_IGNORE_UPPER, PREF_SPELLING_IGNORE_URLS, PREF_SPELLING_LOCALE, PREF_SPELLING_PROPOSAL_THRESHOLD, PREF_SPELLING_ENABLE_CONTENTASSIST };
359         }
360
361         /*
362          * @see net.sourceforge.phpdt.internal.ui.preferences.OptionsConfigurationBlock#getDefaultOptions()
363          */
364         protected Map getDefaultOptions() {
365
366                 final String[] keys= fAllKeys;
367                 final Map options= new HashMap();
368                 final IPreferenceStore store= PreferenceConstants.getPreferenceStore();
369
370                 for (int index= 0; index < keys.length; index++)
371                         options.put(keys[index], store.getDefaultString(keys[index]));
372
373                 return options;
374         }
375
376         /*
377          * @see net.sourceforge.phpdt.internal.ui.preferences.OptionsConfigurationBlock#getFullBuildDialogStrings(boolean)
378          */
379         protected final String[] getFullBuildDialogStrings(final boolean workspace) {
380                 return null;
381         }
382
383         /*
384          * @see net.sourceforge.phpdt.internal.ui.preferences.OptionsConfigurationBlock#getOptions(boolean)
385          */
386         protected Map getOptions(final boolean inherit) {
387
388                 final String[] keys= fAllKeys;
389                 final Map options= new HashMap();
390                 final IPreferenceStore store= PreferenceConstants.getPreferenceStore();
391
392                 for (int index= 0; index < keys.length; index++)
393                         options.put(keys[index], store.getString(keys[index]));
394
395                 return options;
396         }
397
398         /**
399          * Handles selections of the browse button.
400          */
401         protected void handleBrowseButtonSelected() {
402
403                 final FileDialog dialog= new FileDialog(fDictionaryPath.getShell(), SWT.OPEN);
404                 dialog.setText(PreferencesMessages.getString("SpellingPreferencePage.filedialog.title")); //$NON-NLS-1$
405                 dialog.setFilterExtensions(new String[] { PreferencesMessages.getString("SpellingPreferencePage.filter.dictionary.extension"), PreferencesMessages.getString("SpellingPreferencePage.filter.all.extension") }); //$NON-NLS-1$ //$NON-NLS-2$
406                 dialog.setFilterNames(new String[] { PreferencesMessages.getString("SpellingPreferencePage.filter.dictionary.label"), PreferencesMessages.getString("SpellingPreferencePage.filter.all.label") }); //$NON-NLS-1$ //$NON-NLS-2$
407
408                 final String path= dialog.open();
409                 if (path != null)
410                         fDictionaryPath.setText(path);
411         }
412
413         /*
414          * @see net.sourceforge.phpdt.internal.ui.preferences.OptionsConfigurationBlock#setOptions(java.util.Map)
415          */
416         protected void setOptions(final Map options) {
417
418                 final String[] keys= fAllKeys;
419                 final IPreferenceStore store= PreferenceConstants.getPreferenceStore();
420
421                 for (int index= 0; index < keys.length; index++)
422                         store.setValue(keys[index], (String)fWorkingValues.get(keys[index]));
423         }
424
425         /*
426          * @see net.sourceforge.phpdt.internal.ui.preferences.OptionsConfigurationBlock#validateSettings(java.lang.String,java.lang.String)
427          */
428         protected void validateSettings(final String key, final String value) {
429
430                 if (key == null || PREF_SPELLING_PROPOSAL_THRESHOLD.equals(key))
431                         fThresholdStatus= validatePositiveNumber((String)fWorkingValues.get(PREF_SPELLING_PROPOSAL_THRESHOLD));
432
433                 if (key == null || PREF_SPELLING_USER_DICTIONARY.equals(key))
434                         fFileStatus= validateAbsoluteFilePath((String)fWorkingValues.get(PREF_SPELLING_USER_DICTIONARY));
435
436                 if (key == null || PREF_SPELLING_LOCALE.equals(key))
437                         fLocaleStatus= validateLocale((String)fWorkingValues.get(PREF_SPELLING_LOCALE));
438
439                 fContext.statusChanged(StatusUtil.getMostSevere(new IStatus[] { fThresholdStatus, fFileStatus, fLocaleStatus }));
440         }
441 }