upload tizen1.0 source
[sdk/ide/product.git] / org.eclipse.cdt.ui / src / org / eclipse / cdt / internal / ui / preferences / AbstractConfigurationBlock.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2010 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *     Sergey Prigogin, Google
11  *     Anton Leherbauer (Wind River Systems)
12  *******************************************************************************/
13
14 package org.eclipse.cdt.internal.ui.preferences;
15
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.Iterator;
20 import java.util.Map;
21 import java.util.Set;
22
23 import org.eclipse.core.runtime.Assert;
24 import org.eclipse.core.runtime.IStatus;
25 import org.eclipse.jface.layout.PixelConverter;
26 import org.eclipse.jface.preference.IPreferenceStore;
27 import org.eclipse.jface.preference.PreferencePage;
28 import org.eclipse.jface.resource.JFaceResources;
29 import org.eclipse.osgi.util.NLS;
30 import org.eclipse.swt.SWT;
31 import org.eclipse.swt.events.ModifyEvent;
32 import org.eclipse.swt.events.ModifyListener;
33 import org.eclipse.swt.events.SelectionEvent;
34 import org.eclipse.swt.events.SelectionListener;
35 import org.eclipse.swt.layout.GridData;
36 import org.eclipse.swt.layout.GridLayout;
37 import org.eclipse.swt.widgets.Button;
38 import org.eclipse.swt.widgets.Composite;
39 import org.eclipse.swt.widgets.Control;
40 import org.eclipse.swt.widgets.Group;
41 import org.eclipse.swt.widgets.Label;
42 import org.eclipse.swt.widgets.Text;
43 import org.eclipse.ui.forms.events.ExpansionAdapter;
44 import org.eclipse.ui.forms.events.ExpansionEvent;
45 import org.eclipse.ui.forms.widgets.ExpandableComposite;
46
47 import org.eclipse.cdt.internal.ui.dialogs.StatusInfo;
48 import org.eclipse.cdt.internal.ui.dialogs.StatusUtil;
49
50 /**
51  * Abstract implementation of a generic {@link IPreferenceConfigurationBlock}.
52  * 
53  * @since 4.0
54  */
55 abstract class AbstractConfigurationBlock implements IPreferenceConfigurationBlock {
56
57         /**
58          * Use as follows:
59          * 
60          * <pre>
61          * SectionManager manager= new SectionManager();
62          * Composite composite= manager.createSectionComposite(parent);
63          * 
64          * Composite xSection= manager.createSection("section X"));
65          * xSection.setLayout(new FillLayout());
66          * new Button(xSection, SWT.PUSH); // add controls to section..
67          * 
68          * [...]
69          * 
70          * return composite; // return main composite
71          * </pre>
72          */
73         protected final class SectionManager {
74                 /** The preference setting for keeping no section open. */
75                 private static final String __NONE= "__none"; //$NON-NLS-1$
76                 private Set<ExpandableComposite> fSections= new HashSet<ExpandableComposite>();
77                 private boolean fIsBeingManaged= false;
78                 private ExpansionAdapter fListener= new ExpansionAdapter() {
79                         @Override
80                         public void expansionStateChanged(ExpansionEvent e) {
81                                 ExpandableComposite source= (ExpandableComposite) e.getSource();
82                                 updateSectionStyle(source);
83                                 if (fIsBeingManaged)
84                                         return;
85                                 if (e.getState()) {
86                                         try {
87                                                 fIsBeingManaged= true;
88                                                 for (ExpandableComposite composite : fSections) {
89                                                         if (composite != source)
90                                                                 composite.setExpanded(false);
91                                                 }
92                                         } finally {
93                                                 fIsBeingManaged= false;
94                                         }
95                                         if (fLastOpenKey != null && fDialogSettingsStore != null)
96                                                 fDialogSettingsStore.setValue(fLastOpenKey, source.getText());
97                                 } else {
98                                         if (!fIsBeingManaged && fLastOpenKey != null && fDialogSettingsStore != null)
99                                                 fDialogSettingsStore.setValue(fLastOpenKey, __NONE);
100                                 }
101                                 ExpandableComposite exComp= getParentExpandableComposite(source);
102                                 if (exComp != null)
103                                         exComp.layout(true, true);
104                                 ScrolledPageContent parentScrolledComposite= getParentScrolledComposite(source);
105                                 if (parentScrolledComposite != null) {
106                                         parentScrolledComposite.reflow(true);
107                                 }
108                         }
109                 };
110                 private Composite fBody;
111                 private final String fLastOpenKey;
112                 private final IPreferenceStore fDialogSettingsStore;
113                 private ExpandableComposite fFirstChild= null;
114                 /**
115                  * Creates a new section manager.
116                  */
117                 public SectionManager() {
118                         this(null, null);
119                 }
120                 /**
121                  * Creates a new section manager.
122                  */
123                 public SectionManager(IPreferenceStore dialogSettingsStore, String lastOpenKey) {
124                         fDialogSettingsStore= dialogSettingsStore;
125                         fLastOpenKey= lastOpenKey;
126                 }
127                 private void manage(ExpandableComposite section) {
128                         if (section == null)
129                                 throw new NullPointerException();
130                         if (fSections.add(section))
131                                 section.addExpansionListener(fListener);
132                         makeScrollableCompositeAware(section);
133                 }
134                 
135                 /**
136                  * Creates a new composite that can contain a set of expandable
137                  * sections. A <code>ScrolledPageComposite</code> is created and a new
138                  * composite within that, to ensure that expanding the sections will
139                  * always have enough space, unless there already is a
140                  * <code>ScrolledComposite</code> along the parent chain of
141                  * <code>parent</code>, in which case a normal <code>Composite</code>
142                  * is created.
143                  * <p>
144                  * The receiver keeps a reference to the inner body composite, so that
145                  * new sections can be added via <code>createSection</code>.
146                  * </p>
147                  * 
148                  * @param parent the parent composite
149                  * @return the newly created composite
150                  */
151                 public Composite createSectionComposite(Composite parent) {
152                         Assert.isTrue(fBody == null);
153                         boolean isNested= isNestedInScrolledComposite(parent);
154                         Composite composite;
155                         if (isNested) {
156                                 composite= new Composite(parent, SWT.NONE);
157                                 fBody= composite;
158                         } else {
159                                 composite= new ScrolledPageContent(parent);
160                                 fBody= ((ScrolledPageContent) composite).getBody();
161                         }
162                         
163                         fBody.setLayout(new GridLayout());
164                         
165                         return composite;
166                 }
167                 
168                 /**
169                  * Creates an expandable section within the parent created previously by
170                  * calling <code>createSectionComposite</code>. Controls can be added 
171                  * directly to the returned composite, which has no layout initially.
172                  * 
173                  * @param label the display name of the section
174                  * @return a composite within the expandable section
175                  */
176                 public Composite createSection(String label) {
177                         Assert.isNotNull(fBody);
178                         final ExpandableComposite excomposite= new ExpandableComposite(fBody, SWT.NONE, ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT | ExpandableComposite.COMPACT);
179                         if (fFirstChild == null)
180                                 fFirstChild= excomposite;
181                         excomposite.setText(label);
182                         String last= null;
183                         if (fLastOpenKey != null && fDialogSettingsStore != null)
184                                 last= fDialogSettingsStore.getString(fLastOpenKey);
185                         
186                         if (fFirstChild == excomposite && !__NONE.equals(last) || label.equals(last)) {
187                                 excomposite.setExpanded(true);
188                                 if (fFirstChild != excomposite)
189                                         fFirstChild.setExpanded(false);
190                         } else {
191                                 excomposite.setExpanded(false);
192                         }
193                         excomposite.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false));
194                         
195                         updateSectionStyle(excomposite);
196                         manage(excomposite);
197                         
198                         Composite contents= new Composite(excomposite, SWT.NONE);
199                         excomposite.setClient(contents);
200                         
201                         return contents;
202                 }
203         }
204
205         protected static final int INDENT= 20;
206         private OverlayPreferenceStore fStore;
207         
208         private Map<Object, String> fCheckBoxes= new HashMap<Object, String>();
209         private SelectionListener fCheckBoxListener= new SelectionListener() {
210                 public void widgetDefaultSelected(SelectionEvent e) {
211                 }
212                 public void widgetSelected(SelectionEvent e) {
213                         Button button= (Button) e.widget;
214                         fStore.setValue(fCheckBoxes.get(button), button.getSelection());
215                 }
216         };
217         
218         
219         private Map<Object, String> fTextFields= new HashMap<Object, String>();
220         private ModifyListener fTextFieldListener= new ModifyListener() {
221                 public void modifyText(ModifyEvent e) {
222                         Text text= (Text) e.widget;
223                         fStore.setValue(fTextFields.get(text), text.getText());
224                 }
225         };
226
227         private ArrayList<Text> fNumberFields= new ArrayList<Text>();
228         private ModifyListener fNumberFieldListener= new ModifyListener() {
229                 public void modifyText(ModifyEvent e) {
230                         numberFieldChanged((Text) e.widget);
231                 }
232         };
233         
234         /**
235          * List of master/slave listeners when there's a dependency.
236          * 
237          * @see #createDependency(Button, Control)
238          * @since 3.0
239          */
240         private ArrayList<Object> fMasterSlaveListeners= new ArrayList<Object>();
241         
242         private StatusInfo fStatus;
243         private final PreferencePage fMainPage;
244
245         public AbstractConfigurationBlock(OverlayPreferenceStore store) {
246                 Assert.isNotNull(store);
247                 fStore= store;
248                 fMainPage= null;
249         }
250         
251         public AbstractConfigurationBlock(OverlayPreferenceStore store, PreferencePage mainPreferencePage) {
252                 Assert.isNotNull(store);
253                 Assert.isNotNull(mainPreferencePage);
254                 fStore= store;
255                 fMainPage= mainPreferencePage;
256         }
257
258         protected final ScrolledPageContent getParentScrolledComposite(Control control) {
259                 Control parent= control.getParent();
260                 while (!(parent instanceof ScrolledPageContent) && parent != null) {
261                         parent= parent.getParent();
262                 }
263                 if (parent instanceof ScrolledPageContent) {
264                         return (ScrolledPageContent) parent;
265                 }
266                 return null;
267         }
268
269         private final ExpandableComposite getParentExpandableComposite(Control control) {
270                 Control parent= control.getParent();
271                 while (!(parent instanceof ExpandableComposite) && parent != null) {
272                         parent= parent.getParent();
273                 }
274                 if (parent instanceof ExpandableComposite) {
275                         return (ExpandableComposite) parent;
276                 }
277                 return null;
278         }
279
280         protected void updateSectionStyle(ExpandableComposite excomposite) {
281                 excomposite.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT));
282         }
283         
284         private void makeScrollableCompositeAware(Control control) {
285                 ScrolledPageContent parentScrolledComposite= getParentScrolledComposite(control);
286                 if (parentScrolledComposite != null) {
287                         parentScrolledComposite.adaptChild(control);
288                 }
289         }
290         
291         private boolean isNestedInScrolledComposite(Composite parent) {
292                 return getParentScrolledComposite(parent) != null;
293         }
294         
295         protected Button addCheckBox(Composite parent, String label, String key, int indentation) {             
296                 Button checkBox= new Button(parent, SWT.CHECK);
297                 checkBox.setText(label);
298                 
299                 GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
300                 gd.horizontalIndent= indentation;
301                 gd.horizontalSpan= 2;
302                 checkBox.setLayoutData(gd);
303                 checkBox.addSelectionListener(fCheckBoxListener);
304                 makeScrollableCompositeAware(checkBox);
305
306                 fCheckBoxes.put(checkBox, key);
307                 
308                 return checkBox;
309         }
310
311         /**
312          * Returns an array of size 2:
313          *  - first element is of type <code>Label</code>
314          *  - second element is of type <code>Text</code>
315          * Use <code>getLabelControl</code> and <code>getTextControl</code> to get the 2 controls.
316          * 
317          * @param composite     the parent composite
318          * @param label                 the text field's label
319          * @param key                   the preference key
320          * @param textLimit             the text limit
321          * @param indentation   the field's indentation
322          * @param isNumber              <code>true</code> iff this text field is used to e4dit a number
323          * @return the controls added
324          */
325         protected Control[] addLabelledTextField(Composite composite, String label, String key, int textLimit, int indentation, boolean isNumber) {
326                 
327                 PixelConverter pixelConverter= new PixelConverter(composite);
328                 
329                 Label labelControl= new Label(composite, SWT.NONE);
330                 labelControl.setText(label);
331                 GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
332                 gd.horizontalIndent= indentation;
333                 labelControl.setLayoutData(gd);
334                 
335                 Text textControl= new Text(composite, SWT.BORDER | SWT.SINGLE);         
336                 gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
337                 gd.widthHint= pixelConverter.convertWidthInCharsToPixels(textLimit + 1);
338                 textControl.setLayoutData(gd);
339                 textControl.setTextLimit(textLimit);
340                 fTextFields.put(textControl, key);
341                 if (isNumber) {
342                         fNumberFields.add(textControl);
343                         textControl.addModifyListener(fNumberFieldListener);
344                 } else {
345                         textControl.addModifyListener(fTextFieldListener);
346                 }
347                         
348                 return new Control[]{labelControl, textControl};
349         }
350
351         protected void createDependency(final Button master, final Control slave) {
352                 createDependency(master, new Control[] {slave});
353         }
354         
355         protected void createDependency(final Button master, final Control[] slaves) {
356                 Assert.isTrue(slaves.length > 0);
357                 indent(slaves[0]);
358                 SelectionListener listener= new SelectionListener() {
359                         public void widgetSelected(SelectionEvent e) {
360                                 boolean state= master.getSelection();
361                                 for (Control slave : slaves) {
362                                         slave.setEnabled(state);
363                                 }
364                         }
365
366                         public void widgetDefaultSelected(SelectionEvent e) {}
367                 };
368                 master.addSelectionListener(listener);
369                 fMasterSlaveListeners.add(listener);
370         }
371
372         protected static void indent(Control control) {
373                 ((GridData) control.getLayoutData()).horizontalIndent+= INDENT;
374         }
375
376         public void initialize() {
377                 initializeFields();
378         }
379
380         private void initializeFields() {
381                 
382                 Iterator<Object> iter= fCheckBoxes.keySet().iterator();
383                 while (iter.hasNext()) {
384                         Button b= (Button) iter.next();
385                         String key= fCheckBoxes.get(b);
386                         b.setSelection(fStore.getBoolean(key));
387                 }
388                 
389                 iter= fTextFields.keySet().iterator();
390                 while (iter.hasNext()) {
391                         Text t= (Text) iter.next();
392                         String key= fTextFields.get(t);
393                         t.setText(fStore.getString(key));
394                 }
395                 
396         // Update slaves
397         iter= fMasterSlaveListeners.iterator();
398         while (iter.hasNext()) {
399             SelectionListener listener= (SelectionListener)iter.next();
400             listener.widgetSelected(null);
401         }
402      
403         updateStatus(new StatusInfo());
404         }
405
406         public void performOk() {
407         }
408
409         public void performDefaults() {
410                 initializeFields();
411         }
412
413         IStatus getStatus() {
414                 if (fStatus == null)
415                         fStatus= new StatusInfo();
416                 return fStatus;
417         }
418
419         /*
420          * @see org.eclipse.cdt.internal.ui.preferences.IPreferenceConfigurationBlock#dispose()
421          */
422         public void dispose() {
423         }
424         
425         private void numberFieldChanged(Text textControl) {
426                 String number= textControl.getText();
427                 IStatus status= validatePositiveNumber(number);
428                 if (!status.matches(IStatus.ERROR))
429                         fStore.setValue(fTextFields.get(textControl), number);
430                 updateStatus(status);
431         }
432         
433         private IStatus validatePositiveNumber(String number) {
434                 StatusInfo status= new StatusInfo();
435                 if (number.length() == 0) {
436                         status.setError(PreferencesMessages.CEditorPreferencePage_empty_input); 
437                 } else {
438                         try {
439                                 int value= Integer.parseInt(number);
440                                 if (value < 0)
441                                         status.setError(NLS.bind(PreferencesMessages.CEditorPreferencePage_invalid_input, number)); 
442                         } catch (NumberFormatException e) {
443                                 status.setError(NLS.bind(PreferencesMessages.CEditorPreferencePage_invalid_input, number)); 
444                         }
445                 }
446                 return status;
447         }
448
449         protected void updateStatus(IStatus status) {
450                 if (fMainPage == null)
451                         return;
452                 fMainPage.setValid(status.isOK());
453                 StatusUtil.applyToStatusLine(fMainPage, status);
454         }
455         
456         protected final OverlayPreferenceStore getPreferenceStore() {
457                 return fStore;
458         }
459
460         protected Composite createSubsection(Composite parent, SectionManager manager, String label) {
461                 if (manager != null) {
462                         return manager.createSection(label);
463                 }
464                 Group group= new Group(parent, SWT.SHADOW_NONE);
465                 group.setText(label);
466                 GridData data= new GridData(SWT.FILL, SWT.CENTER, true, false);
467                 group.setLayoutData(data);
468                 return group;
469         }
470 }