001: /*******************************************************************************
002: * Copyright (c) 2005, 2007 BEA Systems, Inc.
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: * wharley@bea.com - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.apt.ui.internal.preferences;
011:
012: import java.util.ArrayList;
013: import java.util.HashMap;
014: import java.util.IdentityHashMap;
015: import java.util.Map;
016: import java.util.StringTokenizer;
017:
018: import org.eclipse.core.resources.IProject;
019: import org.eclipse.core.resources.ProjectScope;
020: import org.eclipse.core.runtime.preferences.DefaultScope;
021: import org.eclipse.core.runtime.preferences.IEclipsePreferences;
022: import org.eclipse.core.runtime.preferences.IScopeContext;
023: import org.eclipse.core.runtime.preferences.InstanceScope;
024: import org.eclipse.jdt.apt.core.internal.AptPlugin;
025: import org.eclipse.jdt.apt.core.internal.AptProject;
026: import org.eclipse.jdt.apt.ui.internal.util.ExceptionHandler;
027: import org.eclipse.jdt.core.JavaCore;
028: import org.eclipse.jdt.internal.ui.JavaPlugin;
029: import org.eclipse.jdt.internal.ui.preferences.ScrolledPageContent;
030: import org.eclipse.jdt.internal.ui.util.CoreUtility;
031: import org.eclipse.jdt.internal.ui.wizards.IStatusChangeListener;
032: import org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField;
033: import org.eclipse.jdt.internal.ui.wizards.dialogfields.IDialogFieldListener;
034: import org.eclipse.jface.dialogs.IDialogConstants;
035: import org.eclipse.jface.dialogs.IDialogSettings;
036: import org.eclipse.jface.dialogs.MessageDialog;
037: import org.eclipse.jface.resource.JFaceResources;
038: import org.eclipse.swt.SWT;
039: import org.eclipse.swt.events.ModifyEvent;
040: import org.eclipse.swt.events.ModifyListener;
041: import org.eclipse.swt.events.SelectionEvent;
042: import org.eclipse.swt.events.SelectionListener;
043: import org.eclipse.swt.layout.GridData;
044: import org.eclipse.swt.layout.GridLayout;
045: import org.eclipse.swt.widgets.Button;
046: import org.eclipse.swt.widgets.Combo;
047: import org.eclipse.swt.widgets.Composite;
048: import org.eclipse.swt.widgets.Control;
049: import org.eclipse.swt.widgets.Label;
050: import org.eclipse.swt.widgets.Scrollable;
051: import org.eclipse.swt.widgets.Shell;
052: import org.eclipse.swt.widgets.Text;
053: import org.eclipse.swt.widgets.Widget;
054: import org.eclipse.ui.forms.events.ExpansionAdapter;
055: import org.eclipse.ui.forms.events.ExpansionEvent;
056: import org.eclipse.ui.forms.widgets.ExpandableComposite;
057: import org.eclipse.ui.preferences.WorkingCopyManager;
058: import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;
059: import org.eclipse.ui.preferences.IWorkingCopyManager;
060: import org.osgi.service.prefs.BackingStoreException;
061:
062: /**
063: * The ConfigurationBlock hierarchy is used to organize controls and keys
064: * within a property/preference page. The implementor derives from this
065: * class and creates dialog controls, layout, and response code.
066: * <p>
067: * This code is largely a copy of OptionsConfigurationBlock (JDT UI), modified
068: * to fix bugs and to improve extensibility for preference pages that contain
069: * a mix of preference-based and externally serialized data.
070: */
071: public abstract class BaseConfigurationBlock {
072:
073: protected static class ControlData {
074: private Key fKey;
075: private String[] fValues;
076:
077: public ControlData(Key key, String[] values) {
078: fKey = key;
079: fValues = values;
080: }
081:
082: public Key getKey() {
083: return fKey;
084: }
085:
086: public int getSelection(String value) {
087: if (value != null) {
088: for (int i = 0; i < fValues.length; i++) {
089: if (value.equals(fValues[i])) {
090: return i;
091: }
092: }
093: }
094: return fValues.length - 1; // assume the last option is the least severe
095: }
096:
097: public String getValue(boolean selection) {
098: int index = selection ? 0 : 1;
099: return fValues[index];
100: }
101:
102: public String getValue(int index) {
103: return fValues[index];
104: }
105: }
106:
107: public static final class Key {
108:
109: private String fKey;
110: private String fQualifier;
111:
112: public Key(String qualifier, String key) {
113: fQualifier = qualifier;
114: fKey = key;
115: }
116:
117: public String getName() {
118: return fKey;
119: }
120:
121: private IEclipsePreferences getNode(IScopeContext context,
122: IWorkingCopyManager manager) {
123: IEclipsePreferences node = context.getNode(fQualifier);
124: if (manager != null) {
125: return manager.getWorkingCopy(node);
126: }
127: return node;
128: }
129:
130: public String getQualifier() {
131: return fQualifier;
132: }
133:
134: public String getStoredValue(IScopeContext context,
135: IWorkingCopyManager manager) {
136: return getNode(context, manager).get(fKey, null);
137: }
138:
139: public String getStoredValue(IScopeContext[] lookupOrder,
140: boolean ignoreTopScope, IWorkingCopyManager manager) {
141: for (int i = ignoreTopScope ? 1 : 0; i < lookupOrder.length; i++) {
142: String value = getStoredValue(lookupOrder[i], manager);
143: if (value != null) {
144: return value;
145: }
146: }
147: return null;
148: }
149:
150: public void setStoredValue(IScopeContext context, String value,
151: IWorkingCopyManager manager) {
152: if (value != null) {
153: getNode(context, manager).put(fKey, value);
154: } else {
155: getNode(context, manager).remove(fKey);
156: }
157: }
158:
159: /* (non-Javadoc)
160: * @see java.lang.Object#toString()
161: */
162: public String toString() {
163: return fQualifier + '/' + fKey;
164: }
165:
166: }
167:
168: protected class UpdateAdapter implements IDialogFieldListener {
169:
170: public void dialogFieldChanged(DialogField field) {
171: updateModel(field);
172: }
173: }
174:
175: private static final String SETTINGS_EXPANDED = "expanded"; //$NON-NLS-1$
176:
177: protected final Key[] fAllKeys;
178: private boolean fOriginallyHadProjectSettings; // updated in cacheOriginalValues
179: private Map<Key, String> fDisabledProjectSettings; // null when project specific settings are turned off
180: protected IScopeContext[] fLookupOrder;
181: protected final IWorkingCopyManager fManager;
182:
183: protected final ArrayList<Button> fCheckBoxes;
184: protected final ArrayList<Combo> fComboBoxes;
185: protected final ArrayList<ExpandableComposite> fExpandedComposites;
186: protected final HashMap<Scrollable, Label> fLabels;
187: protected final ArrayList<Text> fTextBoxes;
188:
189: private ModifyListener fTextModifyListener;
190: protected IStatusChangeListener fContext;
191: private SelectionListener fSelectionListener;
192:
193: protected final IProject fProject; // project or null
194: protected final AptProject fAptProject; // null for workspace prefs
195:
196: private IWorkbenchPreferenceContainer fContainer;
197: private Shell fShell;
198:
199: private Control fBlockControl;
200:
201: protected static Key getKey(String plugin, String name) {
202: return new Key(plugin, name);
203: }
204:
205: public BaseConfigurationBlock(IStatusChangeListener context,
206: IProject project, Key[] keys,
207: IWorkbenchPreferenceContainer container) {
208: fContext = context;
209: fProject = project;
210: fAllKeys = keys;
211: fContainer = container;
212: /*
213: if (container == null) {
214: fManager= new WorkingCopyManager();
215: } else {
216: fManager= container.getWorkingCopyManager();
217: }
218: */
219: // Workaround for Bugzilla 115731 - always use our own WCM.
220: fManager = new WorkingCopyManager();
221:
222: if (fProject != null) {
223: fLookupOrder = new IScopeContext[] {
224: new ProjectScope(fProject), new InstanceScope(),
225: new DefaultScope() };
226: fAptProject = AptPlugin.getAptProject(JavaCore
227: .create(fProject));
228: } else {
229: fLookupOrder = new IScopeContext[] { new InstanceScope(),
230: new DefaultScope() };
231: fAptProject = null;
232: }
233:
234: testIfOptionsComplete(keys);
235: if (fProject == null
236: || hasProjectSpecificOptionsNoCache(fProject)) {
237: fDisabledProjectSettings = null;
238: } else {
239: fDisabledProjectSettings = new IdentityHashMap<Key, String>();
240: for (int i = 0; i < keys.length; i++) {
241: Key curr = keys[i];
242: fDisabledProjectSettings.put(curr, curr.getStoredValue(
243: fLookupOrder, false, fManager));
244: }
245: }
246:
247: settingsUpdated();
248:
249: fCheckBoxes = new ArrayList<Button>();
250: fComboBoxes = new ArrayList<Combo>();
251: fTextBoxes = new ArrayList<Text>(2);
252: fLabels = new HashMap<Scrollable, Label>();
253: fExpandedComposites = new ArrayList<ExpandableComposite>();
254: }
255:
256: protected Button addCheckBox(Composite parent, String label,
257: Key key, String[] values, int indent) {
258: ControlData data = new ControlData(key, values);
259:
260: GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
261: gd.horizontalSpan = 3;
262: gd.horizontalIndent = indent;
263:
264: Button checkBox = new Button(parent, SWT.CHECK);
265: checkBox.setFont(JFaceResources.getDialogFont());
266: checkBox.setText(label);
267: checkBox.setData(data);
268: checkBox.setLayoutData(gd);
269: checkBox.addSelectionListener(getSelectionListener());
270:
271: makeScrollableCompositeAware(checkBox);
272:
273: String currValue = getValue(key);
274: checkBox.setSelection(data.getSelection(currValue) == 0);
275:
276: fCheckBoxes.add(checkBox);
277:
278: return checkBox;
279: }
280:
281: protected Combo addComboBox(Composite parent, String label,
282: Key key, String[] values, String[] valueLabels, int indent) {
283: GridData gd = new GridData(GridData.FILL, GridData.CENTER,
284: true, false, 2, 1);
285: gd.horizontalIndent = indent;
286:
287: Label labelControl = new Label(parent, SWT.LEFT);
288: labelControl.setFont(JFaceResources.getDialogFont());
289: labelControl.setText(label);
290: labelControl.setLayoutData(gd);
291:
292: Combo comboBox = newComboControl(parent, key, values,
293: valueLabels);
294: comboBox.setLayoutData(new GridData(
295: GridData.HORIZONTAL_ALIGN_FILL));
296:
297: fLabels.put(comboBox, labelControl);
298:
299: return comboBox;
300: }
301:
302: protected Combo addInversedComboBox(Composite parent, String label,
303: Key key, String[] values, String[] valueLabels, int indent) {
304: GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
305: gd.horizontalIndent = indent;
306: gd.horizontalSpan = 3;
307:
308: Composite composite = new Composite(parent, SWT.NONE);
309: GridLayout layout = new GridLayout();
310: layout.marginHeight = 0;
311: layout.marginWidth = 0;
312: layout.numColumns = 2;
313: composite.setLayout(layout);
314: composite.setLayoutData(gd);
315:
316: Combo comboBox = newComboControl(composite, key, values,
317: valueLabels);
318: comboBox.setFont(JFaceResources.getDialogFont());
319: comboBox.setLayoutData(new GridData(
320: GridData.HORIZONTAL_ALIGN_FILL));
321:
322: Label labelControl = new Label(composite, SWT.LEFT | SWT.WRAP);
323: labelControl.setText(label);
324: labelControl.setLayoutData(new GridData());
325:
326: fLabels.put(comboBox, labelControl);
327: return comboBox;
328: }
329:
330: protected Text addTextField(Composite parent, String label,
331: Key key, int indent, int widthHint) {
332: Label labelControl = new Label(parent, SWT.WRAP);
333: labelControl.setText(label);
334: labelControl.setFont(JFaceResources.getDialogFont());
335: labelControl.setLayoutData(new GridData());
336:
337: Text textBox = new Text(parent, SWT.BORDER | SWT.SINGLE);
338: textBox.setData(key);
339: textBox.setLayoutData(new GridData());
340:
341: makeScrollableCompositeAware(textBox);
342:
343: fLabels.put(textBox, labelControl);
344:
345: String currValue = getValue(key);
346: if (currValue != null) {
347: textBox.setText(currValue);
348: }
349: textBox.addModifyListener(getTextModifyListener());
350:
351: GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
352: if (widthHint != 0) {
353: data.widthHint = widthHint;
354: }
355: data.horizontalIndent = indent;
356: data.horizontalSpan = 2;
357: textBox.setLayoutData(data);
358:
359: fTextBoxes.add(textBox);
360: return textBox;
361: }
362:
363: protected boolean checkValue(Key key, String value) {
364: return value.equals(getValue(key));
365: }
366:
367: protected void controlChanged(Widget widget) {
368: ControlData data = (ControlData) widget.getData();
369: String newValue = null;
370: if (widget instanceof Button) {
371: newValue = data.getValue(((Button) widget).getSelection());
372: } else if (widget instanceof Combo) {
373: newValue = data.getValue(((Combo) widget)
374: .getSelectionIndex());
375: } else {
376: return;
377: }
378: String oldValue = setValue(data.getKey(), newValue);
379: validateSettings(data.getKey(), oldValue, newValue);
380: }
381:
382: /**
383: * Called from BasePreferencePage#createPreferenceContent.
384: */
385: public final Control createPreferenceContent(Composite parent) {
386: fBlockControl = createContents(parent);
387: if (fBlockControl != null) {
388: cacheOriginalValues();
389: initContents();
390: }
391: return fBlockControl;
392: }
393:
394: /**
395: * Derived classes must override this in order to create
396: * their visual content. After this is called, initContents()
397: * will be called.
398: * @return a Composite representing the entire pane.
399: */
400: protected abstract Control createContents(Composite parent);
401:
402: /**
403: * This will be called when settings are first loaded and
404: * whenever changes are applied.
405: * Derived classes may use this to cache the saved settings
406: * values, for later comparison to see if anything changed.
407: */
408: protected void cacheOriginalValues() {
409: fOriginallyHadProjectSettings = hasProjectSpecificOptionsNoCache(fProject);
410: }
411:
412: /**
413: * This will be called exactly once during initialization, after
414: * createContents() and cacheOriginalValues().
415: * Derived classes may override this to initialize any fields
416: * that are not based on a Key.
417: */
418: protected void initContents() {
419: // Base method does nothing.
420: }
421:
422: protected ExpandableComposite createStyleSection(Composite parent,
423: String label, int nColumns) {
424: ExpandableComposite excomposite = new ExpandableComposite(
425: parent, SWT.NONE, ExpandableComposite.TWISTIE
426: | ExpandableComposite.CLIENT_INDENT);
427: excomposite.setText(label);
428: excomposite.setExpanded(false);
429: excomposite.setFont(JFaceResources.getFontRegistry().getBold(
430: JFaceResources.DIALOG_FONT));
431: excomposite.setLayoutData(new GridData(GridData.FILL,
432: GridData.FILL, true, false, nColumns, 1));
433: excomposite.addExpansionListener(new ExpansionAdapter() {
434: public void expansionStateChanged(ExpansionEvent e) {
435: expandedStateChanged((ExpandableComposite) e
436: .getSource());
437: }
438: });
439: fExpandedComposites.add(excomposite);
440: makeScrollableCompositeAware(excomposite);
441: return excomposite;
442: }
443:
444: /**
445: * Called from BasePreferencePage#dispose().
446: * Derived classes may override.
447: */
448: public void dispose() {
449: }
450:
451: protected final void expandedStateChanged(
452: ExpandableComposite expandable) {
453: ScrolledPageContent parentScrolledComposite = getParentScrolledComposite(expandable);
454: if (parentScrolledComposite != null) {
455: parentScrolledComposite.reflow(true);
456: }
457: }
458:
459: protected Control findControl(Key key) {
460: Combo comboBox = getComboBox(key);
461: if (comboBox != null) {
462: return comboBox;
463: }
464: Button checkBox = getCheckBox(key);
465: if (checkBox != null) {
466: return checkBox;
467: }
468: Text text = getTextControl(key);
469: if (text != null) {
470: return text;
471: }
472: return null;
473: }
474:
475: protected boolean getBooleanValue(Key key) {
476: return Boolean.valueOf(getValue(key)).booleanValue();
477: }
478:
479: protected Button getCheckBox(Key key) {
480: for (int i = fCheckBoxes.size() - 1; i >= 0; i--) {
481: Button curr = fCheckBoxes.get(i);
482: ControlData data = (ControlData) curr.getData();
483: if (key.equals(data.getKey())) {
484: return curr;
485: }
486: }
487: return null;
488: }
489:
490: protected Combo getComboBox(Key key) {
491: for (int i = fComboBoxes.size() - 1; i >= 0; i--) {
492: Combo curr = fComboBoxes.get(i);
493: ControlData data = (ControlData) curr.getData();
494: if (key.equals(data.getKey())) {
495: return curr;
496: }
497: }
498: return null;
499: }
500:
501: /**
502: * Provide the strings needed to ask the user whether to rebuild.
503: * Derived classes can override this to change the strings, or to
504: * return null, in which case the dialog will not be shown and the
505: * rebuild will not be triggered.
506: * @param workspaceSettings true if workspace settings have changed,
507: * false if only project-specific settings have changed.
508: * @return an array whose first entry is the dialog title, and whose
509: * second entry is a query asking the user whether to rebuild.
510: */
511: protected String[] getFullBuildDialogStrings(
512: boolean workspaceSettings) {
513: String[] strings = new String[2];
514: strings[0] = Messages.BaseConfigurationBlock_settingsChanged;
515: if (workspaceSettings) {
516: strings[1] = Messages.BaseConfigurationBlock_fullRebuildRequired;
517: } else {
518: strings[1] = Messages.BaseConfigurationBlock_rebuildRequired;
519: }
520: return strings;
521: }
522:
523: protected ExpandableComposite getParentExpandableComposite(
524: Control control) {
525: Control parent = control.getParent();
526: while (!(parent instanceof ExpandableComposite)
527: && parent != null) {
528: parent = parent.getParent();
529: }
530: if (parent instanceof ExpandableComposite) {
531: return (ExpandableComposite) parent;
532: }
533: return null;
534: }
535:
536: protected ScrolledPageContent getParentScrolledComposite(
537: Control control) {
538: Control parent = control.getParent();
539: while (!(parent instanceof ScrolledPageContent)
540: && parent != null) {
541: parent = parent.getParent();
542: }
543: if (parent instanceof ScrolledPageContent) {
544: return (ScrolledPageContent) parent;
545: }
546: return null;
547: }
548:
549: protected final IWorkbenchPreferenceContainer getPreferenceContainer() {
550: return fContainer;
551: }
552:
553: protected SelectionListener getSelectionListener() {
554: if (fSelectionListener == null) {
555: fSelectionListener = new SelectionListener() {
556: public void widgetDefaultSelected(SelectionEvent e) {
557: }
558:
559: public void widgetSelected(SelectionEvent e) {
560: controlChanged(e.widget);
561: }
562: };
563: }
564: return fSelectionListener;
565: }
566:
567: protected Shell getShell() {
568: return fShell;
569: }
570:
571: /**
572: * Retuens the value as actually stored in the preference store.
573: * @param key
574: * @return the value as actually stored in the preference store.
575: */
576: protected String getStoredValue(Key key) {
577: return key.getStoredValue(fLookupOrder, false, fManager);
578: }
579:
580: protected Text getTextControl(Key key) {
581: for (int i = fTextBoxes.size() - 1; i >= 0; i--) {
582: Text curr = fTextBoxes.get(i);
583: ControlData data = (ControlData) curr.getData();
584: if (key.equals(data.getKey())) {
585: return curr;
586: }
587: }
588: return null;
589: }
590:
591: protected ModifyListener getTextModifyListener() {
592: if (fTextModifyListener == null) {
593: fTextModifyListener = new ModifyListener() {
594: public void modifyText(ModifyEvent e) {
595: textChanged((Text) e.widget);
596: }
597: };
598: }
599: return fTextModifyListener;
600: }
601:
602: protected String[] getTokens(String text, String separator) {
603: StringTokenizer tok = new StringTokenizer(text, separator);
604: int nTokens = tok.countTokens();
605: String[] res = new String[nTokens];
606: for (int i = 0; i < res.length; i++) {
607: res[i] = tok.nextToken().trim();
608: }
609: return res;
610: }
611:
612: protected String getValue(Key key) {
613: if (fDisabledProjectSettings != null) {
614: return fDisabledProjectSettings.get(key);
615: }
616: return key.getStoredValue(fLookupOrder, false, fManager);
617: }
618:
619: /**
620: * TODO: this method is a workaround for Bugzilla 111144 and 106111. When
621: * 111144 is fixed, remove this method and call hasProjectSpecificOptions()
622: * instead. The difference is that this one does not cause project prefs nodes
623: * to be cached in the WorkingCopyManager.
624: * @return true if the project has project-specific options.
625: */
626: public boolean hasProjectSpecificOptionsNoCache(IProject project) {
627: if (project != null) {
628: IScopeContext projectContext = new ProjectScope(project);
629: Key[] allKeys = fAllKeys;
630: for (int i = 0; i < allKeys.length; i++) {
631: if (allKeys[i].getStoredValue(projectContext, null) != null) {
632: return true;
633: }
634: }
635: }
636: return false;
637: }
638:
639: private void makeScrollableCompositeAware(Control control) {
640: ScrolledPageContent parentScrolledComposite = getParentScrolledComposite(control);
641: if (parentScrolledComposite != null) {
642: parentScrolledComposite.adaptChild(control);
643: }
644: }
645:
646: protected Combo newComboControl(Composite composite, Key key,
647: String[] values, String[] valueLabels) {
648: ControlData data = new ControlData(key, values);
649:
650: Combo comboBox = new Combo(composite, SWT.READ_ONLY);
651: comboBox.setItems(valueLabels);
652: comboBox.setData(data);
653: comboBox.addSelectionListener(getSelectionListener());
654: comboBox.setFont(JFaceResources.getDialogFont());
655:
656: makeScrollableCompositeAware(comboBox);
657:
658: String currValue = getValue(key);
659: comboBox.select(data.getSelection(currValue));
660:
661: fComboBoxes.add(comboBox);
662: return comboBox;
663: }
664:
665: public boolean performApply() {
666: return processChanges(null); // apply directly
667: }
668:
669: public void performDefaults() {
670: IScopeContext[] lookupOrder; // not same as fLookupOrder! Starts one layer deeper.
671: if (fProject != null) {
672: lookupOrder = new IScopeContext[] { new InstanceScope(),
673: new DefaultScope() };
674: } else {
675: lookupOrder = new IScopeContext[] { new DefaultScope() };
676: }
677:
678: for (int i = 0; i < fAllKeys.length; i++) {
679: Key curr = fAllKeys[i];
680: String defValue = curr.getStoredValue(lookupOrder, false,
681: null);
682: setValue(curr, defValue);
683: }
684:
685: settingsUpdated();
686: updateControls();
687: validateSettings(null, null, null);
688: }
689:
690: public boolean performOk() {
691: return processChanges(fContainer);
692: }
693:
694: /**
695: * @since 3.1
696: */
697: public void performRevert() {
698: for (int i = 0; i < fAllKeys.length; i++) {
699: Key curr = fAllKeys[i];
700: String origValue = curr.getStoredValue(fLookupOrder, false,
701: null);
702: setValue(curr, origValue);
703: }
704:
705: settingsUpdated();
706: updateControls();
707: validateSettings(null, null, null);
708: }
709:
710: /**
711: * If there are changed settings, save them and ask user whether to rebuild.
712: * This is called by performOk() and performApply().
713: * @param container null when called from performApply().
714: * @return false to abort exiting the preference pane.
715: */
716: protected boolean processChanges(
717: IWorkbenchPreferenceContainer container) {
718:
719: boolean projectSpecificnessChanged = false;
720: boolean isProjectSpecific = (fProject != null)
721: && fBlockControl.getEnabled();
722: if (fOriginallyHadProjectSettings ^ isProjectSpecific) {
723: // the project-specificness changed.
724: projectSpecificnessChanged = true;
725: } else if ((fProject != null) && !isProjectSpecific) {
726: // no project specific data, and there never was, and this
727: // is a project preferences pane, so nothing could have changed.
728: return true;
729: }
730:
731: if (!projectSpecificnessChanged
732: && !settingsChanged(fLookupOrder[0])) {
733: return true;
734: }
735:
736: int response = 1; // "NO" rebuild unless we put up the dialog.
737: String[] strings = getFullBuildDialogStrings(fProject == null);
738: if (strings != null) {
739: MessageDialog dialog = new MessageDialog(getShell(),
740: strings[0], null, strings[1],
741: MessageDialog.QUESTION, new String[] {
742: IDialogConstants.YES_LABEL,
743: IDialogConstants.NO_LABEL,
744: IDialogConstants.CANCEL_LABEL }, 2);
745: response = dialog.open();
746: }
747: if (response == 0 || response == 1) { // "YES" or "NO" - either way, save.
748: saveSettings();
749: if (container == null) {
750: // we're doing an Apply, so update the reference values.
751: cacheOriginalValues();
752: }
753: }
754: if (response == 0) { // "YES", rebuild
755: if (container != null) {
756: // build after dialog exits
757: container.registerUpdateJob(CoreUtility
758: .getBuildJob(fProject));
759: } else {
760: // build immediately
761: CoreUtility.getBuildJob(fProject).schedule();
762: }
763: } else if (response != 1) { // "CANCEL" - no save, no rebuild.
764: return false;
765: }
766: return true;
767: }
768:
769: /**
770: * Save dialog information to persistent storage.
771: * Derived classes should override this if they have settings
772: * that are managed using means other than the Key infrastructure.
773: */
774: protected void saveSettings() {
775: try {
776: fManager.applyChanges();
777: } catch (BackingStoreException e) {
778: ExceptionHandler.log(e, "Unable to save preferences"); //$NON-NLS-1$
779: }
780: }
781:
782: protected void restoreSectionExpansionStates(
783: IDialogSettings settings) {
784: for (int i = 0; i < fExpandedComposites.size(); i++) {
785: ExpandableComposite excomposite = fExpandedComposites
786: .get(i);
787: if (settings == null) {
788: excomposite.setExpanded(i == 0); // only expand the first node by default
789: } else {
790: excomposite.setExpanded(settings
791: .getBoolean(SETTINGS_EXPANDED
792: + String.valueOf(i)));
793: }
794: }
795: }
796:
797: public void selectOption(Key key) {
798: Control control = findControl(key);
799: if (control != null) {
800: if (!fExpandedComposites.isEmpty()) {
801: ExpandableComposite expandable = getParentExpandableComposite(control);
802: if (expandable != null) {
803: for (int i = 0; i < fExpandedComposites.size(); i++) {
804: ExpandableComposite curr = fExpandedComposites
805: .get(i);
806: curr.setExpanded(curr == expandable);
807: }
808: expandedStateChanged(expandable);
809: }
810: }
811: control.setFocus();
812: }
813: }
814:
815: public void selectOption(String key, String qualifier) {
816: for (int i = 0; i < fAllKeys.length; i++) {
817: Key curr = fAllKeys[i];
818: if (curr.getName().equals(key)
819: && curr.getQualifier().equals(qualifier)) {
820: selectOption(curr);
821: }
822: }
823: }
824:
825: protected void setComboEnabled(Key key, boolean enabled) {
826: Combo combo = getComboBox(key);
827: Label label = fLabels.get(combo);
828: combo.setEnabled(enabled);
829: label.setEnabled(enabled);
830: }
831:
832: protected void setShell(Shell shell) {
833: fShell = shell;
834: }
835:
836: /**
837: * Checks the state of all Keys in the dialog to see whether there have been changes.
838: * Derived classes which include settings managed outside of the Key infrastructure
839: * should override this method, in order to check whether the additional settings have changed.
840: * @return true if there is anything that needs to be saved.
841: */
842: protected boolean settingsChanged(IScopeContext currContext) {
843: boolean needsBuild = false;
844: for (int i = 0; i < fAllKeys.length; i++) {
845: Key key = fAllKeys[i];
846: String oldVal = key.getStoredValue(currContext, null);
847: String val = key.getStoredValue(currContext, fManager);
848: if (val == null) {
849: if (oldVal != null) {
850: needsBuild |= !oldVal.equals(key.getStoredValue(
851: fLookupOrder, true, fManager));
852: }
853: } else if (!val.equals(oldVal)) {
854: needsBuild |= oldVal != null
855: || !val.equals(key.getStoredValue(fLookupOrder,
856: true, fManager));
857: }
858: }
859: return needsBuild;
860: }
861:
862: protected void settingsUpdated() {
863: }
864:
865: protected String setValue(Key key, boolean value) {
866: return setValue(key, String.valueOf(value));
867: }
868:
869: protected String setValue(Key key, String value) {
870: if (fDisabledProjectSettings != null) {
871: return fDisabledProjectSettings.put(key, value);
872: }
873: String oldValue = getValue(key);
874: key.setStoredValue(fLookupOrder[0], value, fManager);
875: return oldValue;
876: }
877:
878: protected void storeSectionExpansionStates(IDialogSettings settings) {
879: for (int i = 0; i < fExpandedComposites.size(); i++) {
880: ExpandableComposite curr = fExpandedComposites.get(i);
881: settings.put(SETTINGS_EXPANDED + String.valueOf(i), curr
882: .isExpanded());
883: }
884: }
885:
886: private void testIfOptionsComplete(Key[] allKeys) {
887: for (int i = 0; i < allKeys.length; i++) {
888: if (allKeys[i]
889: .getStoredValue(fLookupOrder, false, fManager) == null) {
890: JavaPlugin
891: .logErrorMessage("preference option missing: " + allKeys[i] + " (" + this .getClass().getName() + ')'); //$NON-NLS-1$//$NON-NLS-2$
892: }
893: }
894: }
895:
896: protected void textChanged(Text textControl) {
897: Key key = (Key) textControl.getData();
898: String number = textControl.getText();
899: String oldValue = setValue(key, number);
900: validateSettings(key, oldValue, number);
901: }
902:
903: protected void updateCheckBox(Button curr) {
904: ControlData data = (ControlData) curr.getData();
905:
906: String currValue = getValue(data.getKey());
907: curr.setSelection(data.getSelection(currValue) == 0);
908: }
909:
910: protected void updateCombo(Combo curr) {
911: ControlData data = (ControlData) curr.getData();
912:
913: String currValue = getValue(data.getKey());
914: curr.select(data.getSelection(currValue));
915: }
916:
917: protected void updateControls() {
918: // update the UI
919: for (int i = fCheckBoxes.size() - 1; i >= 0; i--) {
920: updateCheckBox(fCheckBoxes.get(i));
921: }
922: for (int i = fComboBoxes.size() - 1; i >= 0; i--) {
923: updateCombo(fComboBoxes.get(i));
924: }
925: for (int i = fTextBoxes.size() - 1; i >= 0; i--) {
926: updateText(fTextBoxes.get(i));
927: }
928: }
929:
930: protected abstract void updateModel(DialogField field);
931:
932: protected void updateText(Text curr) {
933: Key key = (Key) curr.getData();
934:
935: String currValue = getValue(key);
936: if (currValue != null) {
937: curr.setText(currValue);
938: }
939: }
940:
941: public void useProjectSpecificSettings(boolean enable) {
942: boolean hasProjectSpecificOption = fDisabledProjectSettings == null;
943: if (enable != hasProjectSpecificOption && fProject != null) {
944: if (enable) {
945: for (int i = 0; i < fAllKeys.length; i++) {
946: Key curr = fAllKeys[i];
947: String val = fDisabledProjectSettings.get(curr);
948: curr.setStoredValue(fLookupOrder[0], val, fManager);
949: }
950: fDisabledProjectSettings = null;
951: updateControls();
952: } else {
953: fDisabledProjectSettings = new IdentityHashMap<Key, String>();
954: for (int i = 0; i < fAllKeys.length; i++) {
955: Key curr = fAllKeys[i];
956: String oldSetting = curr.getStoredValue(
957: fLookupOrder, false, fManager);
958: fDisabledProjectSettings.put(curr, oldSetting);
959: curr
960: .setStoredValue(fLookupOrder[0], null,
961: fManager); // clear project settings
962: }
963: }
964: }
965: }
966:
967: /* (non-javadoc)
968: * Update fields and validate.
969: * @param changedKey Key that changed, or null, if all changed.
970: */
971: protected abstract void validateSettings(Key changedKey,
972: String oldValue, String newValue);
973: }
|