Adding two small options for code formatter (zhil's patch). These features are being...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / preferences / CodeFormatterPreferencePage.java
1 /*
2  * (c) Copyright IBM Corp. 2000, 2001.
3  * All Rights Reserved.
4  */
5 package net.sourceforge.phpdt.internal.ui.preferences;
6
7 import java.io.BufferedReader;
8 import java.io.IOException;
9 import java.io.InputStreamReader;
10 import java.util.ArrayList;
11 import java.util.Hashtable;
12
13 import net.sourceforge.phpdt.core.ICodeFormatter;
14 import net.sourceforge.phpdt.core.JavaCore;
15 import net.sourceforge.phpdt.core.ToolFactory;
16 import net.sourceforge.phpdt.internal.ui.PHPUIMessages;
17 import net.sourceforge.phpdt.internal.ui.dialogs.StatusInfo;
18 import net.sourceforge.phpdt.internal.ui.dialogs.StatusUtil;
19 import net.sourceforge.phpdt.internal.ui.util.TabFolderLayout;
20 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
21
22 import org.eclipse.core.runtime.IStatus;
23 import org.eclipse.jface.preference.PreferencePage;
24 import org.eclipse.jface.text.Document;
25 import org.eclipse.jface.text.IDocument;
26 import org.eclipse.swt.SWT;
27 import org.eclipse.swt.events.ModifyEvent;
28 import org.eclipse.swt.events.ModifyListener;
29 import org.eclipse.swt.events.SelectionEvent;
30 import org.eclipse.swt.events.SelectionListener;
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.Control;
36 import org.eclipse.swt.widgets.Label;
37 import org.eclipse.swt.widgets.TabFolder;
38 import org.eclipse.swt.widgets.TabItem;
39 import org.eclipse.swt.widgets.Text;
40 import org.eclipse.ui.IWorkbench;
41 import org.eclipse.ui.IWorkbenchPreferencePage;
42
43 /*
44  * The page for setting code formatter options
45  */
46 public class CodeFormatterPreferencePage extends PreferencePage implements
47                 IWorkbenchPreferencePage {
48
49         // Preference store keys, see PHPCore.getOptions
50         private static final String PREF_NEWLINE_OPENING_BRACES = JavaCore.FORMATTER_NEWLINE_OPENING_BRACE;
51
52         private static final String PREF_NEWLINE_CONTROL_STATEMENT = JavaCore.FORMATTER_NEWLINE_CONTROL;
53
54         private static final String PREF_NEWLINE_CLEAR_ALL = JavaCore.FORMATTER_CLEAR_BLANK_LINES;
55
56         // private static final String PREF_NEWLINE_ELSE_IF=
57         // PHPCore.FORMATTER_NEWLINE_ELSE_IF;
58         private static final String PREF_NEWLINE_EMPTY_BLOCK = JavaCore.FORMATTER_NEWLINE_EMPTY_BLOCK;
59
60         private static final String PREF_LINE_SPLIT = JavaCore.FORMATTER_LINE_SPLIT;
61
62         private static final String PREF_STYLE_COMPACT_ASSIGNEMENT = JavaCore.FORMATTER_COMPACT_ASSIGNMENT;
63
64         private static final String PREF_STYLE_COMPACT_STRING_CONCATENATION = JavaCore.FORMATTER_COMPACT_STRING_CONCATENATION;
65
66         private static final String PREF_STYLE_COMPACT_ARRAYS = JavaCore.FORMATTER_COMPACT_ARRAYS;
67         
68         private static final String PREF_TAB_CHAR = JavaCore.FORMATTER_TAB_CHAR;
69
70         private static final String PREF_TAB_SIZE = JavaCore.FORMATTER_TAB_SIZE;
71
72         // values
73         private static final String INSERT = JavaCore.INSERT;
74
75         private static final String DO_NOT_INSERT = JavaCore.DO_NOT_INSERT;
76
77         private static final String COMPACT = JavaCore.COMPACT;
78
79         private static final String NORMAL = JavaCore.NORMAL;
80
81         private static final String TAB = JavaCore.TAB;
82
83         private static final String SPACE = JavaCore.SPACE;
84
85         private static final String CLEAR_ALL = JavaCore.CLEAR_ALL;
86
87         private static final String PRESERVE_ONE = JavaCore.PRESERVE_ONE;
88
89         private static String[] getAllKeys() {
90                 return new String[] { PREF_NEWLINE_OPENING_BRACES,
91                                 PREF_NEWLINE_CONTROL_STATEMENT, PREF_NEWLINE_CLEAR_ALL,
92                                 // PREF_NEWLINE_ELSE_IF,
93                                 PREF_NEWLINE_EMPTY_BLOCK, PREF_LINE_SPLIT,
94                                 PREF_STYLE_COMPACT_ASSIGNEMENT, PREF_STYLE_COMPACT_STRING_CONCATENATION,
95                                 PREF_STYLE_COMPACT_ARRAYS,
96                                 PREF_TAB_CHAR, PREF_TAB_SIZE };
97         }
98
99         /**
100          * Gets the currently configured tab size
101          * 
102          * @deprecated Inline to avoid reference to preference page
103          */
104         public static int getTabSize() {
105                 String string = (String) JavaCore.getOptions().get(PREF_TAB_SIZE);
106                 return getPositiveIntValue(string, 4);
107         }
108
109         /**
110          * Gets the current compating assignement configuration
111          * 
112          * @deprecated Inline to avoid reference to preference page
113          */
114         public static boolean isCompactingAssignment() {
115                 return COMPACT.equals(JavaCore.getOptions().get(
116                                 PREF_STYLE_COMPACT_ASSIGNEMENT));
117         }
118
119         /**
120          * Gets the current compating assignement configuration
121          * 
122          * @deprecated Inline to avoid reference to preference page
123          */
124         public static boolean useSpaces() {
125                 return SPACE.equals(JavaCore.getOptions().get(PREF_TAB_CHAR));
126         }
127
128         private static int getPositiveIntValue(String string, int dflt) {
129                 try {
130                         int i = Integer.parseInt(string);
131                         if (i >= 0) {
132                                 return i;
133                         }
134                 } catch (NumberFormatException e) {
135                 }
136                 return dflt;
137         }
138
139         private static class ControlData {
140                 private String fKey;
141
142                 private String[] fValues;
143
144                 public ControlData(String key, String[] values) {
145                         fKey = key;
146                         fValues = values;
147                 }
148
149                 public String getKey() {
150                         return fKey;
151                 }
152
153                 public String getValue(boolean selection) {
154                         int index = selection ? 0 : 1;
155                         return fValues[index];
156                 }
157
158                 public String getValue(int index) {
159                         return fValues[index];
160                 }
161
162                 public int getSelection(String value) {
163                         for (int i = 0; i < fValues.length; i++) {
164                                 if (value.equals(fValues[i])) {
165                                         return i;
166                                 }
167                         }
168                         throw new IllegalArgumentException();
169                 }
170         }
171
172         private Hashtable fWorkingValues;
173
174         private ArrayList fCheckBoxes;
175
176         private ArrayList fTextBoxes;
177
178         private SelectionListener fButtonSelectionListener;
179
180         private ModifyListener fTextModifyListener;
181
182         private String fPreviewText;
183
184         private IDocument fPreviewDocument;
185
186         private Text fTabSizeTextBox;
187
188         // private SourceViewer fSourceViewer;
189
190         public CodeFormatterPreferencePage() {
191                 setPreferenceStore(PHPeclipsePlugin.getDefault().getPreferenceStore());
192                 setDescription(PHPUIMessages
193                                 .getString("CodeFormatterPreferencePage.description")); //$NON-NLS-1$
194
195                 fWorkingValues = JavaCore.getOptions();
196                 fCheckBoxes = new ArrayList();
197                 fTextBoxes = new ArrayList();
198
199                 fButtonSelectionListener = new SelectionListener() {
200                         public void widgetDefaultSelected(SelectionEvent e) {
201                         }
202
203                         public void widgetSelected(SelectionEvent e) {
204                                 if (!e.widget.isDisposed()) {
205                                         controlChanged((Button) e.widget);
206                                 }
207                         }
208                 };
209
210                 fTextModifyListener = new ModifyListener() {
211                         public void modifyText(ModifyEvent e) {
212                                 if (!e.widget.isDisposed()) {
213                                         textChanged((Text) e.widget);
214                                 }
215                         }
216                 };
217
218                 fPreviewDocument = new Document();
219                 fPreviewText = loadPreviewFile("CodeFormatterPreviewCode.txt"); //$NON-NLS-1$   
220         }
221
222         /*
223          * @see IWorkbenchPreferencePage#init()
224          */
225         public void init(IWorkbench workbench) {
226         }
227
228         /*
229          * @see PreferencePage#createControl(Composite)
230          */
231         public void createControl(Composite parent) {
232                 super.createControl(parent);
233                 // WorkbenchHelp.setHelp(getControl(),
234                 // IJavaHelpContextIds.CODEFORMATTER_PREFERENCE_PAGE);
235         }
236
237         /*
238          * @see PreferencePage#createContents(Composite)
239          */
240         protected Control createContents(Composite parent) {
241
242                 GridLayout layout = new GridLayout();
243                 layout.marginHeight = 0;
244                 layout.marginWidth = 0;
245
246                 Composite composite = new Composite(parent, SWT.NONE);
247                 composite.setLayout(layout);
248
249                 TabFolder folder = new TabFolder(composite, SWT.NONE);
250                 folder.setLayout(new TabFolderLayout());
251                 folder.setLayoutData(new GridData(GridData.FILL_BOTH));
252
253                 String[] insertNotInsert = new String[] { INSERT, DO_NOT_INSERT };
254
255                 layout = new GridLayout();
256                 layout.numColumns = 2;
257
258                 Composite newlineComposite = new Composite(folder, SWT.NULL);
259                 newlineComposite.setLayout(layout);
260
261                 String label = PHPUIMessages
262                                 .getString("CodeFormatterPreferencePage.newline_opening_braces.label"); //$NON-NLS-1$
263                 addCheckBox(newlineComposite, label, PREF_NEWLINE_OPENING_BRACES,
264                                 insertNotInsert);
265
266                 label = PHPUIMessages
267                                 .getString("CodeFormatterPreferencePage.newline_control_statement.label"); //$NON-NLS-1$
268                 addCheckBox(newlineComposite, label, PREF_NEWLINE_CONTROL_STATEMENT,
269                                 insertNotInsert);
270
271                 label = PHPUIMessages
272                                 .getString("CodeFormatterPreferencePage.newline_clear_lines"); //$NON-NLS-1$
273                 addCheckBox(newlineComposite, label, PREF_NEWLINE_CLEAR_ALL,
274                                 new String[] { CLEAR_ALL, PRESERVE_ONE });
275
276                 // label=
277                 // PHPUIMessages.getString("CodeFormatterPreferencePage.newline_else_if.label");
278                 // //$NON-NLS-1$
279                 // addCheckBox(newlineComposite, label, PREF_NEWLINE_ELSE_IF,
280                 // insertNotInsert);
281
282                 label = PHPUIMessages
283                                 .getString("CodeFormatterPreferencePage.newline_empty_block.label"); //$NON-NLS-1$
284                 addCheckBox(newlineComposite, label, PREF_NEWLINE_EMPTY_BLOCK,
285                                 insertNotInsert);
286
287                 layout = new GridLayout();
288                 layout.numColumns = 2;
289
290                 Composite lineSplittingComposite = new Composite(folder, SWT.NULL);
291                 lineSplittingComposite.setLayout(layout);
292
293                 label = PHPUIMessages
294                                 .getString("CodeFormatterPreferencePage.split_line.label"); //$NON-NLS-1$
295                 addTextField(lineSplittingComposite, label, PREF_LINE_SPLIT);
296
297                 layout = new GridLayout();
298                 layout.numColumns = 2;
299
300                 Composite styleComposite = new Composite(folder, SWT.NULL);
301                 styleComposite.setLayout(layout);
302
303                 label = PHPUIMessages
304                                 .getString("CodeFormatterPreferencePage.style_compact_assignement.label"); //$NON-NLS-1$
305                 addCheckBox(styleComposite, label, PREF_STYLE_COMPACT_ASSIGNEMENT,
306                                 new String[] { COMPACT, NORMAL });
307                 
308                 label = PHPUIMessages
309                 .getString("CodeFormatterPreferencePage.style_compact_string_concatenation.label"); //$NON-NLS-1$
310                 addCheckBox(styleComposite, label, PREF_STYLE_COMPACT_STRING_CONCATENATION,
311                 new String[] { COMPACT, NORMAL });
312                 
313                 label = PHPUIMessages
314                 .getString("CodeFormatterPreferencePage.style_compact_arrays.label"); //$NON-NLS-1$
315                 addCheckBox(styleComposite, label, PREF_STYLE_COMPACT_ARRAYS,
316                 new String[] { COMPACT, NORMAL });              
317
318                 label = PHPUIMessages
319                                 .getString("CodeFormatterPreferencePage.tab_char.label"); //$NON-NLS-1$
320                 addCheckBox(styleComposite, label, PREF_TAB_CHAR, new String[] { TAB,
321                                 SPACE });
322
323                 label = PHPUIMessages
324                                 .getString("CodeFormatterPreferencePage.tab_size.label"); //$NON-NLS-1$
325                 fTabSizeTextBox = addTextField(styleComposite, label, PREF_TAB_SIZE);
326
327                 TabItem item = new TabItem(folder, SWT.NONE);
328                 item.setText(PHPUIMessages
329                                 .getString("CodeFormatterPreferencePage.tab.newline.tabtitle")); //$NON-NLS-1$
330                 item.setControl(newlineComposite);
331
332                 item = new TabItem(folder, SWT.NONE);
333                 item
334                                 .setText(PHPUIMessages
335                                                 .getString("CodeFormatterPreferencePage.tab.linesplit.tabtitle")); //$NON-NLS-1$
336                 item.setControl(lineSplittingComposite);
337
338                 item = new TabItem(folder, SWT.NONE);
339                 item.setText(PHPUIMessages
340                                 .getString("CodeFormatterPreferencePage.tab.style.tabtitle")); //$NON-NLS-1$
341                 item.setControl(styleComposite);
342
343                 // fSourceViewer= createPreview(parent);
344
345                 updatePreview();
346
347                 return composite;
348         }
349
350         // private SourceViewer createPreview(Composite parent) {
351         // SourceViewer previewViewer= new SourceViewer(parent, null, SWT.V_SCROLL |
352         // SWT.H_SCROLL | SWT.BORDER);
353         // JavaTextTools tools= JavaPlugin.getDefault().getJavaTextTools();
354         // previewViewer.configure(new PHPSourceViewerConfiguration(tools, null));
355         // previewViewer.getTextWidget().setFont(JFaceResources.getFontRegistry().get(JFaceResources.TEXT_FONT));
356         // previewViewer.getTextWidget().setTabs(getPositiveIntValue((String)
357         // fWorkingValues.get(PREF_TAB_SIZE), 0));
358         // previewViewer.setEditable(false);
359         // previewViewer.setDocument(fPreviewDocument);
360         // Control control= previewViewer.getControl();
361         // GridData gdata= new GridData(GridData.FILL_BOTH);
362         // gdata.widthHint= convertWidthInCharsToPixels(30);
363         // gdata.heightHint= convertHeightInCharsToPixels(5);
364         // control.setLayoutData(gdata);
365         // return previewViewer;
366         // }
367
368         private Button addCheckBox(Composite parent, String label, String key,
369                         String[] values) {
370                 ControlData data = new ControlData(key, values);
371
372                 GridData gd = new GridData(GridData.FILL_HORIZONTAL);
373                 gd.horizontalSpan = 2;
374
375                 Button checkBox = new Button(parent, SWT.CHECK);
376                 checkBox.setText(label);
377                 checkBox.setData(data);
378                 checkBox.setLayoutData(gd);
379
380                 String currValue = (String) fWorkingValues.get(key);
381                 checkBox.setSelection(data.getSelection(currValue) == 0);
382                 checkBox.addSelectionListener(fButtonSelectionListener);
383
384                 fCheckBoxes.add(checkBox);
385                 return checkBox;
386         }
387
388         private Text addTextField(Composite parent, String label, String key) {
389                 Label labelControl = new Label(parent, SWT.NONE);
390                 labelControl.setText(label);
391                 labelControl.setLayoutData(new GridData());
392
393                 Text textBox = new Text(parent, SWT.BORDER | SWT.SINGLE);
394                 textBox.setData(key);
395                 textBox.setLayoutData(new GridData());
396
397                 String currValue = (String) fWorkingValues.get(key);
398                 textBox.setText(String.valueOf(getPositiveIntValue(currValue, 1)));
399                 textBox.setTextLimit(3);
400                 textBox.addModifyListener(fTextModifyListener);
401
402                 GridData gd = new GridData();
403                 gd.widthHint = convertWidthInCharsToPixels(5);
404                 textBox.setLayoutData(gd);
405
406                 fTextBoxes.add(textBox);
407                 return textBox;
408         }
409
410         private void controlChanged(Button button) {
411                 ControlData data = (ControlData) button.getData();
412                 boolean selection = button.getSelection();
413                 String newValue = data.getValue(selection);
414                 fWorkingValues.put(data.getKey(), newValue);
415                 updatePreview();
416
417                 if (PREF_TAB_CHAR.equals(data.getKey())) {
418                         updateStatus(new StatusInfo());
419                         if (selection) {
420                                 fTabSizeTextBox.setText((String) fWorkingValues
421                                                 .get(PREF_TAB_SIZE));
422                         }
423                 }
424         }
425
426         private void textChanged(Text textControl) {
427                 String key = (String) textControl.getData();
428                 String number = textControl.getText();
429                 IStatus status = validatePositiveNumber(number);
430                 if (!status.matches(IStatus.ERROR)) {
431                         fWorkingValues.put(key, number);
432                 }
433                 // if (PREF_TAB_SIZE.equals(key)) {
434                 // fSourceViewer.getTextWidget().setTabs(getPositiveIntValue(number,
435                 // 0));
436                 // }
437                 updateStatus(status);
438                 updatePreview();
439         }
440
441         /*
442          * @see IPreferencePage#performOk()
443          */
444         public boolean performOk() {
445                 String[] allKeys = getAllKeys();
446                 // preserve other options
447                 // store in JCore
448                 Hashtable actualOptions = JavaCore.getOptions();
449                 for (int i = 0; i < allKeys.length; i++) {
450                         String key = allKeys[i];
451                         String val = (String) fWorkingValues.get(key);
452                         actualOptions.put(key, val);
453                 }
454                 JavaCore.setOptions(actualOptions);
455                 PHPeclipsePlugin.getDefault().savePluginPreferences();
456                 return super.performOk();
457         }
458
459         /*
460          * @see PreferencePage#performDefaults()
461          */
462         protected void performDefaults() {
463                 fWorkingValues = JavaCore.getDefaultOptions();
464                 updateControls();
465                 super.performDefaults();
466         }
467
468         private String loadPreviewFile(String filename) {
469                 String separator = System.getProperty("line.separator"); //$NON-NLS-1$
470                 StringBuffer btxt = new StringBuffer(512);
471                 BufferedReader rin = null;
472                 try {
473                         rin = new BufferedReader(new InputStreamReader(getClass()
474                                         .getResourceAsStream(filename)));
475                         String line;
476                         while ((line = rin.readLine()) != null) {
477                                 btxt.append(line);
478                                 btxt.append(separator);
479                         }
480                 } catch (IOException io) {
481                         PHPeclipsePlugin.log(io);
482                 } finally {
483                         if (rin != null) {
484                                 try {
485                                         rin.close();
486                                 } catch (IOException e) {
487                                 }
488                         }
489                 }
490                 return btxt.toString();
491         }
492
493         private void updatePreview() {
494                 ICodeFormatter formatter = ToolFactory
495                                 .createDefaultCodeFormatter(fWorkingValues);
496                 fPreviewDocument.set(formatter.format(fPreviewText, 0, null, "\n")); //$NON-NLS-1$
497         }
498
499         private void updateControls() {
500                 // update the UI
501                 for (int i = fCheckBoxes.size() - 1; i >= 0; i--) {
502                         Button curr = (Button) fCheckBoxes.get(i);
503                         ControlData data = (ControlData) curr.getData();
504
505                         String currValue = (String) fWorkingValues.get(data.getKey());
506                         curr.setSelection(data.getSelection(currValue) == 0);
507                 }
508                 for (int i = fTextBoxes.size() - 1; i >= 0; i--) {
509                         Text curr = (Text) fTextBoxes.get(i);
510                         String key = (String) curr.getData();
511                         String currValue = (String) fWorkingValues.get(key);
512                         curr.setText(currValue);
513                 }
514         }
515
516         private IStatus validatePositiveNumber(String number) {
517                 StatusInfo status = new StatusInfo();
518                 if (number.length() == 0) {
519                         status.setError(PHPUIMessages
520                                         .getString("CodeFormatterPreferencePage.empty_input")); //$NON-NLS-1$
521                 } else {
522                         try {
523                                 int value = Integer.parseInt(number);
524                                 if (value < 0) {
525                                         status
526                                                         .setError(PHPUIMessages
527                                                                         .getFormattedString(
528                                                                                         "CodeFormatterPreferencePage.invalid_input", number)); //$NON-NLS-1$
529                                 }
530                         } catch (NumberFormatException e) {
531                                 status.setError(PHPUIMessages.getFormattedString(
532                                                 "CodeFormatterPreferencePage.invalid_input", number)); //$NON-NLS-1$
533                         }
534                 }
535                 return status;
536         }
537
538         private void updateStatus(IStatus status) {
539                 if (!status.matches(IStatus.ERROR)) {
540                         // look if there are more severe errors
541                         for (int i = 0; i < fTextBoxes.size(); i++) {
542                                 Text curr = (Text) fTextBoxes.get(i);
543                                 if (!(curr == fTabSizeTextBox && usesTabs())) {
544                                         IStatus currStatus = validatePositiveNumber(curr.getText());
545                                         status = StatusUtil.getMoreSevere(currStatus, status);
546                                 }
547                         }
548                 }
549                 setValid(!status.matches(IStatus.ERROR));
550                 StatusUtil.applyToStatusLine(this, status);
551         }
552
553         private boolean usesTabs() {
554                 return TAB.equals(fWorkingValues.get(PREF_TAB_CHAR));
555         }
556
557 }