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