001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.ui.preferences;
011:
012: import java.util.ArrayList;
013: import java.util.HashMap;
014: import java.util.HashSet;
015: import java.util.Iterator;
016: import java.util.Map;
017: import java.util.Set;
018:
019: import org.eclipse.core.runtime.Assert;
020: import org.eclipse.core.runtime.IStatus;
021:
022: import org.eclipse.swt.SWT;
023: import org.eclipse.swt.events.ModifyEvent;
024: import org.eclipse.swt.events.ModifyListener;
025: import org.eclipse.swt.events.SelectionEvent;
026: import org.eclipse.swt.events.SelectionListener;
027: import org.eclipse.swt.layout.GridData;
028: import org.eclipse.swt.layout.GridLayout;
029: import org.eclipse.swt.widgets.Button;
030: import org.eclipse.swt.widgets.Composite;
031: import org.eclipse.swt.widgets.Control;
032: import org.eclipse.swt.widgets.Group;
033: import org.eclipse.swt.widgets.Label;
034: import org.eclipse.swt.widgets.Text;
035:
036: import org.eclipse.jface.preference.IPreferenceStore;
037: import org.eclipse.jface.preference.PreferencePage;
038: import org.eclipse.jface.resource.JFaceResources;
039:
040: import org.eclipse.ui.forms.events.ExpansionAdapter;
041: import org.eclipse.ui.forms.events.ExpansionEvent;
042: import org.eclipse.ui.forms.widgets.ExpandableComposite;
043:
044: import org.eclipse.jdt.internal.corext.util.Messages;
045:
046: import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
047: import org.eclipse.jdt.internal.ui.dialogs.StatusUtil;
048: import org.eclipse.jdt.internal.ui.util.PixelConverter;
049:
050: /**
051: * Configures Java Editor typing preferences.
052: *
053: * @since 3.1
054: */
055: abstract class AbstractConfigurationBlock implements
056: IPreferenceConfigurationBlock {
057:
058: /**
059: * Use as follows:
060: *
061: * <pre>
062: * SectionManager manager= new SectionManager();
063: * Composite composite= manager.createSectionComposite(parent);
064: *
065: * Composite xSection= manager.createSection("section X"));
066: * xSection.setLayout(new FillLayout());
067: * new Button(xSection, SWT.PUSH); // add controls to section..
068: *
069: * [...]
070: *
071: * return composite; // return main composite
072: * </pre>
073: */
074: protected final class SectionManager {
075: /** The preference setting for keeping no section open. */
076: private static final String __NONE = "__none"; //$NON-NLS-1$
077: private Set fSections = new HashSet();
078: private boolean fIsBeingManaged = false;
079: private ExpansionAdapter fListener = new ExpansionAdapter() {
080: public void expansionStateChanged(ExpansionEvent e) {
081: ExpandableComposite source = (ExpandableComposite) e
082: .getSource();
083: updateSectionStyle(source);
084: if (fIsBeingManaged)
085: return;
086: if (e.getState()) {
087: try {
088: fIsBeingManaged = true;
089: for (Iterator iter = fSections.iterator(); iter
090: .hasNext();) {
091: ExpandableComposite composite = (ExpandableComposite) iter
092: .next();
093: if (composite != source)
094: composite.setExpanded(false);
095: }
096: } finally {
097: fIsBeingManaged = false;
098: }
099: if (fLastOpenKey != null
100: && fDialogSettingsStore != null)
101: fDialogSettingsStore.setValue(fLastOpenKey,
102: source.getText());
103: } else {
104: if (!fIsBeingManaged && fLastOpenKey != null
105: && fDialogSettingsStore != null)
106: fDialogSettingsStore.setValue(fLastOpenKey,
107: __NONE);
108: }
109: ExpandableComposite exComp = getParentExpandableComposite(source);
110: if (exComp != null)
111: exComp.layout(true, true);
112: ScrolledPageContent parentScrolledComposite = getParentScrolledComposite(source);
113: if (parentScrolledComposite != null) {
114: parentScrolledComposite.reflow(true);
115: }
116: }
117: };
118: private Composite fBody;
119: private final String fLastOpenKey;
120: private final IPreferenceStore fDialogSettingsStore;
121: private ExpandableComposite fFirstChild = null;
122:
123: /**
124: * Creates a new section manager.
125: */
126: public SectionManager() {
127: this (null, null);
128: }
129:
130: /**
131: * Creates a new section manager.
132: */
133: public SectionManager(IPreferenceStore dialogSettingsStore,
134: String lastOpenKey) {
135: fDialogSettingsStore = dialogSettingsStore;
136: fLastOpenKey = lastOpenKey;
137: }
138:
139: private void manage(ExpandableComposite section) {
140: if (section == null)
141: throw new NullPointerException();
142: if (fSections.add(section))
143: section.addExpansionListener(fListener);
144: makeScrollableCompositeAware(section);
145: }
146:
147: /**
148: * Creates a new composite that can contain a set of expandable
149: * sections. A <code>ScrolledPageComposite</code> is created and a new
150: * composite within that, to ensure that expanding the sections will
151: * always have enough space, unless there already is a
152: * <code>ScrolledComposite</code> along the parent chain of
153: * <code>parent</code>, in which case a normal <code>Composite</code>
154: * is created.
155: * <p>
156: * The receiver keeps a reference to the inner body composite, so that
157: * new sections can be added via <code>createSection</code>.
158: * </p>
159: *
160: * @param parent the parent composite
161: * @return the newly created composite
162: */
163: public Composite createSectionComposite(Composite parent) {
164: Assert.isTrue(fBody == null);
165: boolean isNested = isNestedInScrolledComposite(parent);
166: Composite composite;
167: if (isNested) {
168: composite = new Composite(parent, SWT.NONE);
169: fBody = composite;
170: } else {
171: composite = new ScrolledPageContent(parent);
172: fBody = ((ScrolledPageContent) composite).getBody();
173: }
174:
175: fBody.setLayout(new GridLayout());
176:
177: return composite;
178: }
179:
180: /**
181: * Creates an expandable section within the parent created previously by
182: * calling <code>createSectionComposite</code>. Controls can be added
183: * directly to the returned composite, which has no layout initially.
184: *
185: * @param label the display name of the section
186: * @return a composite within the expandable section
187: */
188: public Composite createSection(String label) {
189: Assert.isNotNull(fBody);
190: final ExpandableComposite excomposite = new ExpandableComposite(
191: fBody, SWT.NONE, ExpandableComposite.TWISTIE
192: | ExpandableComposite.CLIENT_INDENT
193: | ExpandableComposite.COMPACT);
194: if (fFirstChild == null)
195: fFirstChild = excomposite;
196: excomposite.setText(label);
197: String last = null;
198: if (fLastOpenKey != null && fDialogSettingsStore != null)
199: last = fDialogSettingsStore.getString(fLastOpenKey);
200:
201: if (fFirstChild == excomposite && !__NONE.equals(last)
202: || label.equals(last)) {
203: excomposite.setExpanded(true);
204: if (fFirstChild != excomposite)
205: fFirstChild.setExpanded(false);
206: } else {
207: excomposite.setExpanded(false);
208: }
209: excomposite.setLayoutData(new GridData(GridData.FILL,
210: GridData.BEGINNING, true, false));
211:
212: updateSectionStyle(excomposite);
213: manage(excomposite);
214:
215: Composite contents = new Composite(excomposite, SWT.NONE);
216: excomposite.setClient(contents);
217:
218: return contents;
219: }
220: }
221:
222: protected static final int INDENT = 20;
223: private OverlayPreferenceStore fStore;
224:
225: private Map fCheckBoxes = new HashMap();
226: private SelectionListener fCheckBoxListener = new SelectionListener() {
227: public void widgetDefaultSelected(SelectionEvent e) {
228: }
229:
230: public void widgetSelected(SelectionEvent e) {
231: Button button = (Button) e.widget;
232: fStore.setValue((String) fCheckBoxes.get(button), button
233: .getSelection());
234: }
235: };
236:
237: private Map fTextFields = new HashMap();
238: private ModifyListener fTextFieldListener = new ModifyListener() {
239: public void modifyText(ModifyEvent e) {
240: Text text = (Text) e.widget;
241: fStore.setValue((String) fTextFields.get(text), text
242: .getText());
243: }
244: };
245:
246: private ArrayList fNumberFields = new ArrayList();
247: private ModifyListener fNumberFieldListener = new ModifyListener() {
248: public void modifyText(ModifyEvent e) {
249: numberFieldChanged((Text) e.widget);
250: }
251: };
252:
253: /**
254: * List of master/slave listeners when there's a dependency.
255: *
256: * @see #createDependency(Button, Control)
257: * @since 3.0
258: */
259: private ArrayList fMasterSlaveListeners = new ArrayList();
260:
261: private StatusInfo fStatus;
262: private final PreferencePage fMainPage;
263:
264: public AbstractConfigurationBlock(OverlayPreferenceStore store) {
265: Assert.isNotNull(store);
266: fStore = store;
267: fMainPage = null;
268: }
269:
270: public AbstractConfigurationBlock(OverlayPreferenceStore store,
271: PreferencePage mainPreferencePage) {
272: Assert.isNotNull(store);
273: Assert.isNotNull(mainPreferencePage);
274: fStore = store;
275: fMainPage = mainPreferencePage;
276: }
277:
278: protected final ScrolledPageContent getParentScrolledComposite(
279: Control control) {
280: Control parent = control.getParent();
281: while (!(parent instanceof ScrolledPageContent)
282: && parent != null) {
283: parent = parent.getParent();
284: }
285: if (parent instanceof ScrolledPageContent) {
286: return (ScrolledPageContent) parent;
287: }
288: return null;
289: }
290:
291: private final ExpandableComposite getParentExpandableComposite(
292: Control control) {
293: Control parent = control.getParent();
294: while (!(parent instanceof ExpandableComposite)
295: && parent != null) {
296: parent = parent.getParent();
297: }
298: if (parent instanceof ExpandableComposite) {
299: return (ExpandableComposite) parent;
300: }
301: return null;
302: }
303:
304: protected void updateSectionStyle(ExpandableComposite excomposite) {
305: excomposite.setFont(JFaceResources.getFontRegistry().getBold(
306: JFaceResources.DIALOG_FONT));
307: }
308:
309: private void makeScrollableCompositeAware(Control control) {
310: ScrolledPageContent parentScrolledComposite = getParentScrolledComposite(control);
311: if (parentScrolledComposite != null) {
312: parentScrolledComposite.adaptChild(control);
313: }
314: }
315:
316: private boolean isNestedInScrolledComposite(Composite parent) {
317: return getParentScrolledComposite(parent) != null;
318: }
319:
320: protected Button addCheckBox(Composite parent, String label,
321: String key, int indentation) {
322: Button checkBox = new Button(parent, SWT.CHECK);
323: checkBox.setText(label);
324:
325: GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
326: gd.horizontalIndent = indentation;
327: gd.horizontalSpan = 2;
328: checkBox.setLayoutData(gd);
329: checkBox.addSelectionListener(fCheckBoxListener);
330: makeScrollableCompositeAware(checkBox);
331:
332: fCheckBoxes.put(checkBox, key);
333:
334: return checkBox;
335: }
336:
337: /**
338: * Returns an array of size 2:
339: * - first element is of type <code>Label</code>
340: * - second element is of type <code>Text</code>
341: * Use <code>getLabelControl</code> and <code>getTextControl</code> to get the 2 controls.
342: *
343: * @param composite the parent composite
344: * @param label the text field's label
345: * @param key the preference key
346: * @param textLimit the text limit
347: * @param indentation the field's indentation
348: * @param isNumber <code>true</code> iff this text field is used to e4dit a number
349: * @return the controls added
350: */
351: protected Control[] addLabelledTextField(Composite composite,
352: String label, String key, int textLimit, int indentation,
353: boolean isNumber) {
354:
355: PixelConverter pixelConverter = new PixelConverter(composite);
356:
357: Label labelControl = new Label(composite, SWT.NONE);
358: labelControl.setText(label);
359: GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
360: gd.horizontalIndent = indentation;
361: labelControl.setLayoutData(gd);
362:
363: Text textControl = new Text(composite, SWT.BORDER | SWT.SINGLE);
364: gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
365: gd.widthHint = pixelConverter
366: .convertWidthInCharsToPixels(textLimit + 1);
367: textControl.setLayoutData(gd);
368: textControl.setTextLimit(textLimit);
369: fTextFields.put(textControl, key);
370: if (isNumber) {
371: fNumberFields.add(textControl);
372: textControl.addModifyListener(fNumberFieldListener);
373: } else {
374: textControl.addModifyListener(fTextFieldListener);
375: }
376:
377: return new Control[] { labelControl, textControl };
378: }
379:
380: protected void createDependency(final Button master,
381: final Control slave) {
382: createDependency(master, new Control[] { slave });
383: }
384:
385: protected void createDependency(final Button master,
386: final Control[] slaves) {
387: Assert.isTrue(slaves.length > 0);
388: indent(slaves[0]);
389: SelectionListener listener = new SelectionListener() {
390: public void widgetSelected(SelectionEvent e) {
391: boolean state = master.getSelection();
392: for (int i = 0; i < slaves.length; i++) {
393: slaves[i].setEnabled(state);
394: }
395: }
396:
397: public void widgetDefaultSelected(SelectionEvent e) {
398: }
399: };
400: master.addSelectionListener(listener);
401: fMasterSlaveListeners.add(listener);
402: }
403:
404: protected static void indent(Control control) {
405: ((GridData) control.getLayoutData()).horizontalIndent += INDENT;
406: }
407:
408: public void initialize() {
409: initializeFields();
410: }
411:
412: private void initializeFields() {
413:
414: Iterator iter = fCheckBoxes.keySet().iterator();
415: while (iter.hasNext()) {
416: Button b = (Button) iter.next();
417: String key = (String) fCheckBoxes.get(b);
418: b.setSelection(fStore.getBoolean(key));
419: }
420:
421: iter = fTextFields.keySet().iterator();
422: while (iter.hasNext()) {
423: Text t = (Text) iter.next();
424: String key = (String) fTextFields.get(t);
425: t.setText(fStore.getString(key));
426: }
427:
428: // Update slaves
429: iter = fMasterSlaveListeners.iterator();
430: while (iter.hasNext()) {
431: SelectionListener listener = (SelectionListener) iter
432: .next();
433: listener.widgetSelected(null);
434: }
435:
436: updateStatus(new StatusInfo());
437: }
438:
439: public void performOk() {
440: }
441:
442: public void performDefaults() {
443: initializeFields();
444: }
445:
446: IStatus getStatus() {
447: if (fStatus == null)
448: fStatus = new StatusInfo();
449: return fStatus;
450: }
451:
452: /*
453: * @see org.eclipse.jdt.internal.ui.preferences.IPreferenceConfigurationBlock#dispose()
454: * @since 3.0
455: */
456: public void dispose() {
457: }
458:
459: private void numberFieldChanged(Text textControl) {
460: String number = textControl.getText();
461: IStatus status = validatePositiveNumber(number);
462: if (!status.matches(IStatus.ERROR))
463: fStore.setValue((String) fTextFields.get(textControl),
464: number);
465: updateStatus(status);
466: }
467:
468: private IStatus validatePositiveNumber(String number) {
469: StatusInfo status = new StatusInfo();
470: if (number.length() == 0) {
471: status
472: .setError(PreferencesMessages.JavaEditorPreferencePage_empty_input);
473: } else {
474: try {
475: int value = Integer.parseInt(number);
476: if (value < 0)
477: status
478: .setError(Messages
479: .format(
480: PreferencesMessages.JavaEditorPreferencePage_invalid_input,
481: number));
482: } catch (NumberFormatException e) {
483: status
484: .setError(Messages
485: .format(
486: PreferencesMessages.JavaEditorPreferencePage_invalid_input,
487: number));
488: }
489: }
490: return status;
491: }
492:
493: protected void updateStatus(IStatus status) {
494: if (fMainPage == null)
495: return;
496: fMainPage.setValid(status.isOK());
497: StatusUtil.applyToStatusLine(fMainPage, status);
498: }
499:
500: protected final OverlayPreferenceStore getPreferenceStore() {
501: return fStore;
502: }
503:
504: protected Composite createSubsection(Composite parent,
505: SectionManager manager, String label) {
506: if (manager != null) {
507: return manager.createSection(label);
508: } else {
509: Group group = new Group(parent, SWT.SHADOW_NONE);
510: group.setText(label);
511: GridData data = new GridData(SWT.FILL, SWT.CENTER, true,
512: false);
513: group.setLayoutData(data);
514: return group;
515: }
516: }
517: }
|