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
9 * IBM Corporation - initial API and implementation
10 * Sergey Prigogin, Google
11 * Anton Leherbauer (Wind River Systems)
12 *******************************************************************************/
14 package org.eclipse.cdt.internal.ui.preferences;
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.Iterator;
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;
47 import org.eclipse.cdt.internal.ui.dialogs.StatusInfo;
48 import org.eclipse.cdt.internal.ui.dialogs.StatusUtil;
51 * Abstract implementation of a generic {@link IPreferenceConfigurationBlock}.
55 abstract class AbstractConfigurationBlock implements IPreferenceConfigurationBlock {
61 * SectionManager manager= new SectionManager();
62 * Composite composite= manager.createSectionComposite(parent);
64 * Composite xSection= manager.createSection("section X"));
65 * xSection.setLayout(new FillLayout());
66 * new Button(xSection, SWT.PUSH); // add controls to section..
70 * return composite; // return main composite
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() {
80 public void expansionStateChanged(ExpansionEvent e) {
81 ExpandableComposite source= (ExpandableComposite) e.getSource();
82 updateSectionStyle(source);
87 fIsBeingManaged= true;
88 for (ExpandableComposite composite : fSections) {
89 if (composite != source)
90 composite.setExpanded(false);
93 fIsBeingManaged= false;
95 if (fLastOpenKey != null && fDialogSettingsStore != null)
96 fDialogSettingsStore.setValue(fLastOpenKey, source.getText());
98 if (!fIsBeingManaged && fLastOpenKey != null && fDialogSettingsStore != null)
99 fDialogSettingsStore.setValue(fLastOpenKey, __NONE);
101 ExpandableComposite exComp= getParentExpandableComposite(source);
103 exComp.layout(true, true);
104 ScrolledPageContent parentScrolledComposite= getParentScrolledComposite(source);
105 if (parentScrolledComposite != null) {
106 parentScrolledComposite.reflow(true);
110 private Composite fBody;
111 private final String fLastOpenKey;
112 private final IPreferenceStore fDialogSettingsStore;
113 private ExpandableComposite fFirstChild= null;
115 * Creates a new section manager.
117 public SectionManager() {
121 * Creates a new section manager.
123 public SectionManager(IPreferenceStore dialogSettingsStore, String lastOpenKey) {
124 fDialogSettingsStore= dialogSettingsStore;
125 fLastOpenKey= lastOpenKey;
127 private void manage(ExpandableComposite section) {
129 throw new NullPointerException();
130 if (fSections.add(section))
131 section.addExpansionListener(fListener);
132 makeScrollableCompositeAware(section);
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>
144 * The receiver keeps a reference to the inner body composite, so that
145 * new sections can be added via <code>createSection</code>.
148 * @param parent the parent composite
149 * @return the newly created composite
151 public Composite createSectionComposite(Composite parent) {
152 Assert.isTrue(fBody == null);
153 boolean isNested= isNestedInScrolledComposite(parent);
156 composite= new Composite(parent, SWT.NONE);
159 composite= new ScrolledPageContent(parent);
160 fBody= ((ScrolledPageContent) composite).getBody();
163 fBody.setLayout(new GridLayout());
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.
173 * @param label the display name of the section
174 * @return a composite within the expandable section
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);
183 if (fLastOpenKey != null && fDialogSettingsStore != null)
184 last= fDialogSettingsStore.getString(fLastOpenKey);
186 if (fFirstChild == excomposite && !__NONE.equals(last) || label.equals(last)) {
187 excomposite.setExpanded(true);
188 if (fFirstChild != excomposite)
189 fFirstChild.setExpanded(false);
191 excomposite.setExpanded(false);
193 excomposite.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false));
195 updateSectionStyle(excomposite);
198 Composite contents= new Composite(excomposite, SWT.NONE);
199 excomposite.setClient(contents);
205 protected static final int INDENT= 20;
206 private OverlayPreferenceStore fStore;
208 private Map<Object, String> fCheckBoxes= new HashMap<Object, String>();
209 private SelectionListener fCheckBoxListener= new SelectionListener() {
210 public void widgetDefaultSelected(SelectionEvent e) {
212 public void widgetSelected(SelectionEvent e) {
213 Button button= (Button) e.widget;
214 fStore.setValue(fCheckBoxes.get(button), button.getSelection());
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());
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);
235 * List of master/slave listeners when there's a dependency.
237 * @see #createDependency(Button, Control)
240 private ArrayList<Object> fMasterSlaveListeners= new ArrayList<Object>();
242 private StatusInfo fStatus;
243 private final PreferencePage fMainPage;
245 public AbstractConfigurationBlock(OverlayPreferenceStore store) {
246 Assert.isNotNull(store);
251 public AbstractConfigurationBlock(OverlayPreferenceStore store, PreferencePage mainPreferencePage) {
252 Assert.isNotNull(store);
253 Assert.isNotNull(mainPreferencePage);
255 fMainPage= mainPreferencePage;
258 protected final ScrolledPageContent getParentScrolledComposite(Control control) {
259 Control parent= control.getParent();
260 while (!(parent instanceof ScrolledPageContent) && parent != null) {
261 parent= parent.getParent();
263 if (parent instanceof ScrolledPageContent) {
264 return (ScrolledPageContent) parent;
269 private final ExpandableComposite getParentExpandableComposite(Control control) {
270 Control parent= control.getParent();
271 while (!(parent instanceof ExpandableComposite) && parent != null) {
272 parent= parent.getParent();
274 if (parent instanceof ExpandableComposite) {
275 return (ExpandableComposite) parent;
280 protected void updateSectionStyle(ExpandableComposite excomposite) {
281 excomposite.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT));
284 private void makeScrollableCompositeAware(Control control) {
285 ScrolledPageContent parentScrolledComposite= getParentScrolledComposite(control);
286 if (parentScrolledComposite != null) {
287 parentScrolledComposite.adaptChild(control);
291 private boolean isNestedInScrolledComposite(Composite parent) {
292 return getParentScrolledComposite(parent) != null;
295 protected Button addCheckBox(Composite parent, String label, String key, int indentation) {
296 Button checkBox= new Button(parent, SWT.CHECK);
297 checkBox.setText(label);
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);
306 fCheckBoxes.put(checkBox, key);
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.
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
325 protected Control[] addLabelledTextField(Composite composite, String label, String key, int textLimit, int indentation, boolean isNumber) {
327 PixelConverter pixelConverter= new PixelConverter(composite);
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);
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);
342 fNumberFields.add(textControl);
343 textControl.addModifyListener(fNumberFieldListener);
345 textControl.addModifyListener(fTextFieldListener);
348 return new Control[]{labelControl, textControl};
351 protected void createDependency(final Button master, final Control slave) {
352 createDependency(master, new Control[] {slave});
355 protected void createDependency(final Button master, final Control[] slaves) {
356 Assert.isTrue(slaves.length > 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);
366 public void widgetDefaultSelected(SelectionEvent e) {}
368 master.addSelectionListener(listener);
369 fMasterSlaveListeners.add(listener);
372 protected static void indent(Control control) {
373 ((GridData) control.getLayoutData()).horizontalIndent+= INDENT;
376 public void initialize() {
380 private void initializeFields() {
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));
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));
397 iter= fMasterSlaveListeners.iterator();
398 while (iter.hasNext()) {
399 SelectionListener listener= (SelectionListener)iter.next();
400 listener.widgetSelected(null);
403 updateStatus(new StatusInfo());
406 public void performOk() {
409 public void performDefaults() {
413 IStatus getStatus() {
415 fStatus= new StatusInfo();
420 * @see org.eclipse.cdt.internal.ui.preferences.IPreferenceConfigurationBlock#dispose()
422 public void dispose() {
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);
433 private IStatus validatePositiveNumber(String number) {
434 StatusInfo status= new StatusInfo();
435 if (number.length() == 0) {
436 status.setError(PreferencesMessages.CEditorPreferencePage_empty_input);
439 int value= Integer.parseInt(number);
441 status.setError(NLS.bind(PreferencesMessages.CEditorPreferencePage_invalid_input, number));
442 } catch (NumberFormatException e) {
443 status.setError(NLS.bind(PreferencesMessages.CEditorPreferencePage_invalid_input, number));
449 protected void updateStatus(IStatus status) {
450 if (fMainPage == null)
452 fMainPage.setValid(status.isOK());
453 StatusUtil.applyToStatusLine(fMainPage, status);
456 protected final OverlayPreferenceStore getPreferenceStore() {
460 protected Composite createSubsection(Composite parent, SectionManager manager, String label) {
461 if (manager != null) {
462 return manager.createSection(label);
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);