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