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