diff options
author | Walter Harley | 2005-11-15 00:47:56 +0000 |
---|---|---|
committer | Walter Harley | 2005-11-15 00:47:56 +0000 |
commit | d7ed279f5216d32337e674d05a19fd9c56f52744 (patch) | |
tree | 0c57ed57ab05f81f8a9115699accae9d29a4e663 /org.eclipse.jdt.apt.ui/src/org | |
parent | 790443c8ea7a17aa25cb0c35c01196da99d52e04 (diff) | |
download | eclipse.jdt.core-d7ed279f5216d32337e674d05a19fd9c56f52744.tar.gz eclipse.jdt.core-d7ed279f5216d32337e674d05a19fd9c56f52744.tar.xz eclipse.jdt.core-d7ed279f5216d32337e674d05a19fd9c56f52744.zip |
wharley - support for spaces embedded in processor options.
Diffstat (limited to 'org.eclipse.jdt.apt.ui/src/org')
4 files changed, 935 insertions, 140 deletions
diff --git a/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/AptConfigurationBlock.java b/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/AptConfigurationBlock.java index ea91019332..c0f3cb47af 100644 --- a/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/AptConfigurationBlock.java +++ b/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/AptConfigurationBlock.java @@ -12,6 +12,8 @@ package org.eclipse.jdt.apt.ui.internal.preferences; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -19,9 +21,10 @@ import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.preferences.IScopeContext; +import org.eclipse.jdt.apt.core.AptPlugin; import org.eclipse.jdt.apt.core.util.AptConfig; import org.eclipse.jdt.apt.core.util.AptPreferenceConstants; -import org.eclipse.jdt.apt.core.util.AptConfig.ProcessorOptionsParser; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.internal.ui.dialogs.StatusInfo; @@ -53,16 +56,17 @@ import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; * Preference pane for most APT (Java annotation processing) settings. * @see org.eclipse.jdt.ui.internal.preferences.TodoTaskConfigurationBlock * for the conceptual source of some of this code. + * <p> + * */ public class AptConfigurationBlock extends BaseConfigurationBlock { - private static final Key KEY_APTENABLED= getAptCoreKey(AptPreferenceConstants.APT_ENABLED); - private static final Key KEY_GENSRCDIR= getAptCoreKey(AptPreferenceConstants.APT_GENSRCDIR); - private static final Key KEY_PROCESSOROPTIONS= getAptCoreKey(AptPreferenceConstants.APT_PROCESSOROPTIONS); + private static final Key KEY_APTENABLED= getKey(AptPlugin.PLUGIN_ID, AptPreferenceConstants.APT_ENABLED); + private static final Key KEY_GENSRCDIR= getKey(AptPlugin.PLUGIN_ID, AptPreferenceConstants.APT_GENSRCDIR); private static Key[] getAllKeys() { return new Key[] { - KEY_APTENABLED, KEY_GENSRCDIR, KEY_PROCESSOROPTIONS + KEY_APTENABLED, KEY_GENSRCDIR }; } @@ -79,6 +83,8 @@ public class AptConfigurationBlock extends BaseConfigurationBlock { private PixelConverter fPixelConverter; private Composite fBlockControl; + private Map<String, String> fOriginalProcOptions; // cache of saved values + /** * Event handler for Processor Options list control. */ @@ -264,7 +270,7 @@ public class AptConfigurationBlock extends BaseConfigurationBlock { gdLabel.horizontalSpan= 2; gdLabel.widthHint= fPixelConverter.convertWidthInCharsToPixels(60); description.setLayoutData(gdLabel); - + Dialog.applyDialogFont(fBlockControl); validateSettings(null, null, null); @@ -272,6 +278,55 @@ public class AptConfigurationBlock extends BaseConfigurationBlock { return fBlockControl; } + @Override + protected void cacheOriginalValues() { + fOriginalProcOptions= AptConfig.getRawProcessorOptions(fJProj); + } + + protected void initContents() { + loadProcessorOptions(fJProj); + } + + @Override + protected void saveSettings() { + List<ProcessorOption> elements; + if ((fJProj != null) && !fBlockControl.isEnabled()) { + // We're in a project properties pane but the entire configuration + // block control is disabled. That means the per-project settings checkbox + // is unchecked. To save that state, we'll clear the proc options map. + elements = Collections.<ProcessorOption>emptyList(); + } + else { + elements = getListElements(); + } + saveProcessorOptions(elements); + super.saveSettings(); + } + + /** + * Check whether any processor options have changed, as well as + * any of the settings tracked in the "normal" way (as Keys). + */ + @Override + protected boolean settingsChanged(IScopeContext currContext) { + Map<String, String> savedProcOptions = new HashMap<String, String>(fOriginalProcOptions); + for (ProcessorOption o : getListElements()) { + final String savedVal = savedProcOptions.get(o.key); + if (savedVal != null && savedVal.equals(o.value)) { + savedProcOptions.remove(o.key); + } + else { + // found an unsaved option in the list + return true; + } + } + if (!savedProcOptions.isEmpty()) { + // found a saved option that has been removed + return true; + } + return super.settingsChanged(currContext); + } + /** * Call after updating key values, to warn user if new values are invalid. * @param changedKey may be null, e.g. if called from createContents. @@ -339,66 +394,62 @@ public class AptConfigurationBlock extends BaseConfigurationBlock { fAptEnabledField.setSelection(aptEnabled); String str= getValue(KEY_GENSRCDIR); fGenSrcDirField.setText(str == null ? "" : str); //$NON-NLS-1$ - str= getValue(KEY_PROCESSOROPTIONS); - unpackProcessorOptions(str); } /** * Update the values stored in the keys based on the UI. */ protected final void updateModel(DialogField field) { - String newVal = null; - Key key = null; if (field == fAptEnabledField) { - key = KEY_APTENABLED; - newVal = String.valueOf(fAptEnabledField.isSelected()); + String newVal = String.valueOf(fAptEnabledField.isSelected()); + setValue(KEY_APTENABLED, newVal); } else if (field == fGenSrcDirField) { - key = KEY_GENSRCDIR; - newVal = fGenSrcDirField.getText(); - } else if (field == fProcessorOptionsField) { - key = KEY_PROCESSOROPTIONS; - newVal = packProcessorOptions(); - } - if (key != null) { - String oldVal = setValue(key, newVal); - validateSettings(key, oldVal, newVal); - } + String newVal = fGenSrcDirField.getText(); + setValue(KEY_GENSRCDIR, newVal); + } + validateSettings(null, null, null); // params are ignored } /** - * @return all the options, packaged as an apt-style command line ("-Afoo=bar -Abaz=quux"). - * If there are no options, return null. + * Save the contents of the options list. */ - private String packProcessorOptions() { - List<ProcessorOption> elements = getListElements(); + private void saveProcessorOptions(List<ProcessorOption> elements) { Map<String, String> map = new LinkedHashMap<String, String>(elements.size()); for (ProcessorOption o : elements) { map.put(o.key, o.value); } - return AptConfig.serializeProcessorOptions(map); + AptConfig.setProcessorOptions(map, fJProj); } /** - * Set the processor options list contents by parsing an apt-style - * command line ("-Afoo=bar -Abaz=quux") - * @param str may be null + * Set the processor options list contents */ - private void unpackProcessorOptions(String str) { + private void loadProcessorOptions(IJavaProject jproj) { List<ProcessorOption> options= new ArrayList<ProcessorOption>(); - if (str != null) { - ProcessorOptionsParser parser = new ProcessorOptionsParser(str); - Map<String, String> parsedOptions = parser.parse(); - for (Map.Entry<String, String> entry : parsedOptions.entrySet()) { - ProcessorOption o = new ProcessorOption(); - o.key = entry.getKey(); - o.value = entry.getValue(); - options.add(o); - } + Map<String, String> parsedOptions = AptConfig.getRawProcessorOptions(jproj); + for (Map.Entry<String, String> entry : parsedOptions.entrySet()) { + ProcessorOption o = new ProcessorOption(); + o.key = entry.getKey(); + o.value = entry.getValue(); + options.add(o); } fProcessorOptionsField.setElements(options); } + @Override + public void performDefaults() { + if (fJProj != null) { + // If project-specific, load workspace settings + loadProcessorOptions(null); + } + else { + // If workspace, load "factory default," which is empty. + fProcessorOptionsField.removeAllElements(); + } + super.performDefaults(); + } + } diff --git a/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/BaseConfigurationBlock.java b/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/BaseConfigurationBlock.java index 6eee8e28ea..40293a90b6 100644 --- a/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/BaseConfigurationBlock.java +++ b/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/BaseConfigurationBlock.java @@ -11,27 +11,155 @@ package org.eclipse.jdt.apt.ui.internal.preferences; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.StringTokenizer; + import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.runtime.preferences.DefaultScope; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.core.runtime.preferences.InstanceScope; -import org.eclipse.jdt.apt.core.AptPlugin; -import org.eclipse.jdt.internal.ui.preferences.OptionsConfigurationBlock; +import org.eclipse.jdt.apt.ui.internal.util.ExceptionHandler; +import org.eclipse.jdt.internal.ui.JavaPlugin; +import org.eclipse.jdt.internal.ui.preferences.ScrolledPageContent; +import org.eclipse.jdt.internal.ui.util.CoreUtility; import org.eclipse.jdt.internal.ui.wizards.IStatusChangeListener; import org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField; import org.eclipse.jdt.internal.ui.wizards.dialogfields.IDialogFieldListener; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Scrollable; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Widget; +import org.eclipse.ui.forms.events.ExpansionAdapter; +import org.eclipse.ui.forms.events.ExpansionEvent; +import org.eclipse.ui.forms.widgets.ExpandableComposite; +import org.eclipse.ui.internal.preferences.WorkingCopyManager; import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; +import org.eclipse.ui.preferences.IWorkingCopyManager; +import org.osgi.service.prefs.BackingStoreException; /** * The ConfigurationBlock hierarchy is used to organize controls and keys * within a property/preference page. The implementor derives from this * class and creates dialog controls, layout, and response code. + * <p> + * This code is largely a copy of OptionsConfigurationBlock (JDT UI), modified + * to fix bugs and to improve extensibility for preference pages that contain + * a mix of preference-based and externally serialized data. */ -public abstract class BaseConfigurationBlock extends OptionsConfigurationBlock { +public abstract class BaseConfigurationBlock { + protected static class ControlData { + private Key fKey; + private String[] fValues; + + public ControlData(Key key, String[] values) { + fKey= key; + fValues= values; + } + + public Key getKey() { + return fKey; + } + + public int getSelection(String value) { + if (value != null) { + for (int i= 0; i < fValues.length; i++) { + if (value.equals(fValues[i])) { + return i; + } + } + } + return fValues.length -1; // assume the last option is the least severe + } + + public String getValue(boolean selection) { + int index= selection ? 0 : 1; + return fValues[index]; + } + + public String getValue(int index) { + return fValues[index]; + } + } + + public static final class Key { + + private String fKey; + private String fQualifier; + + public Key(String qualifier, String key) { + fQualifier= qualifier; + fKey= key; + } + + public String getName() { + return fKey; + } + + private IEclipsePreferences getNode(IScopeContext context, IWorkingCopyManager manager) { + IEclipsePreferences node= context.getNode(fQualifier); + if (manager != null) { + return manager.getWorkingCopy(node); + } + return node; + } + + public String getQualifier() { + return fQualifier; + } + + public String getStoredValue(IScopeContext context, IWorkingCopyManager manager) { + return getNode(context, manager).get(fKey, null); + } + + public String getStoredValue(IScopeContext[] lookupOrder, boolean ignoreTopScope, IWorkingCopyManager manager) { + for (int i= ignoreTopScope ? 1 : 0; i < lookupOrder.length; i++) { + String value= getStoredValue(lookupOrder[i], manager); + if (value != null) { + return value; + } + } + return null; + } + + public void setStoredValue(IScopeContext context, String value, IWorkingCopyManager manager) { + if (value != null) { + getNode(context, manager).put(fKey, value); + } else { + getNode(context, manager).remove(fKey); + } + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + return fQualifier + '/' + fKey; + } + + } + protected class UpdateAdapter implements IDialogFieldListener { public void dialogFieldChanged(DialogField field) { @@ -39,11 +167,309 @@ public abstract class BaseConfigurationBlock extends OptionsConfigurationBlock { } } - protected static Key getAptCoreKey(String name) { - return getKey(AptPlugin.PLUGIN_ID, name); + private static final String SETTINGS_EXPANDED= "expanded"; //$NON-NLS-1$ + + protected final Key[] fAllKeys; + private Map<Key, String> fDisabledProjectSettings; // null when project specific settings are turned off + protected IScopeContext[] fLookupOrder; + protected final IWorkingCopyManager fManager; + + protected final ArrayList<Button> fCheckBoxes; + protected final ArrayList<Combo> fComboBoxes; + protected final ArrayList<ExpandableComposite> fExpandedComposites; + protected final HashMap<Scrollable, Label> fLabels; + protected final ArrayList<Text> fTextBoxes; + + private ModifyListener fTextModifyListener; + protected IStatusChangeListener fContext; + private SelectionListener fSelectionListener; + + protected final IProject fProject; // project or null + + private IWorkbenchPreferenceContainer fContainer; + private Shell fShell; + + protected static Key getKey(String plugin, String name) { + return new Key(plugin, name); + } + + public BaseConfigurationBlock(IStatusChangeListener context, IProject project, Key[] keys, IWorkbenchPreferenceContainer container) { + fContext= context; + fProject= project; + fAllKeys= keys; + fContainer= container; + /* + if (container == null) { + fManager= new WorkingCopyManager(); + } else { + fManager= container.getWorkingCopyManager(); + } + */ + // Workaround for Bugzilla 115731 - always use our own WCM. + fManager = new WorkingCopyManager(); + + if (fProject != null) { + fLookupOrder= new IScopeContext[] { + new ProjectScope(fProject), + new InstanceScope(), + new DefaultScope() + }; + } else { + fLookupOrder= new IScopeContext[] { + new InstanceScope(), + new DefaultScope() + }; + } + + testIfOptionsComplete(keys); + if (fProject == null || hasProjectSpecificOptions(fProject)) { + fDisabledProjectSettings= null; + } else { + fDisabledProjectSettings= new IdentityHashMap<Key, String>(); + for (int i= 0; i < keys.length; i++) { + Key curr= keys[i]; + fDisabledProjectSettings.put(curr, curr.getStoredValue(fLookupOrder, false, fManager)); + } + } + + settingsUpdated(); + + fCheckBoxes= new ArrayList<Button>(); + fComboBoxes= new ArrayList<Combo>(); + fTextBoxes= new ArrayList<Text>(2); + fLabels= new HashMap<Scrollable, Label>(); + fExpandedComposites= new ArrayList<ExpandableComposite>(); + } + + protected Button addCheckBox(Composite parent, String label, Key key, String[] values, int indent) { + ControlData data= new ControlData(key, values); + + GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL); + gd.horizontalSpan= 3; + gd.horizontalIndent= indent; + + Button checkBox= new Button(parent, SWT.CHECK); + checkBox.setFont(JFaceResources.getDialogFont()); + checkBox.setText(label); + checkBox.setData(data); + checkBox.setLayoutData(gd); + checkBox.addSelectionListener(getSelectionListener()); + + makeScrollableCompositeAware(checkBox); + + String currValue= getValue(key); + checkBox.setSelection(data.getSelection(currValue) == 0); + + fCheckBoxes.add(checkBox); + + return checkBox; + } + + protected Combo addComboBox(Composite parent, String label, Key key, String[] values, String[] valueLabels, int indent) { + GridData gd= new GridData(GridData.FILL, GridData.CENTER, true, false, 2, 1); + gd.horizontalIndent= indent; + + Label labelControl= new Label(parent, SWT.LEFT); + labelControl.setFont(JFaceResources.getDialogFont()); + labelControl.setText(label); + labelControl.setLayoutData(gd); + + Combo comboBox= newComboControl(parent, key, values, valueLabels); + comboBox.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); + + fLabels.put(comboBox, labelControl); + + return comboBox; + } + + protected Combo addInversedComboBox(Composite parent, String label, Key key, String[] values, String[] valueLabels, int indent) { + GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + gd.horizontalIndent= indent; + gd.horizontalSpan= 3; + + Composite composite= new Composite(parent, SWT.NONE); + GridLayout layout= new GridLayout(); + layout.marginHeight= 0; + layout.marginWidth= 0; + layout.numColumns= 2; + composite.setLayout(layout); + composite.setLayoutData(gd); + + Combo comboBox= newComboControl(composite, key, values, valueLabels); + comboBox.setFont(JFaceResources.getDialogFont()); + comboBox.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); + + Label labelControl= new Label(composite, SWT.LEFT | SWT.WRAP); + labelControl.setText(label); + labelControl.setLayoutData(new GridData()); + + fLabels.put(comboBox, labelControl); + return comboBox; + } + + protected Text addTextField(Composite parent, String label, Key key, int indent, int widthHint) { + Label labelControl= new Label(parent, SWT.WRAP); + labelControl.setText(label); + labelControl.setFont(JFaceResources.getDialogFont()); + labelControl.setLayoutData(new GridData()); + + Text textBox= new Text(parent, SWT.BORDER | SWT.SINGLE); + textBox.setData(key); + textBox.setLayoutData(new GridData()); + + makeScrollableCompositeAware(textBox); + + fLabels.put(textBox, labelControl); + + String currValue= getValue(key); + if (currValue != null) { + textBox.setText(currValue); + } + textBox.addModifyListener(getTextModifyListener()); + + GridData data= new GridData(GridData.HORIZONTAL_ALIGN_FILL); + if (widthHint != 0) { + data.widthHint= widthHint; + } + data.horizontalIndent= indent; + data.horizontalSpan= 2; + textBox.setLayoutData(data); + + fTextBoxes.add(textBox); + return textBox; + } + + protected boolean checkValue(Key key, String value) { + return value.equals(getValue(key)); + } + + protected void controlChanged(Widget widget) { + ControlData data= (ControlData) widget.getData(); + String newValue= null; + if (widget instanceof Button) { + newValue= data.getValue(((Button)widget).getSelection()); + } else if (widget instanceof Combo) { + newValue= data.getValue(((Combo)widget).getSelectionIndex()); + } else { + return; + } + String oldValue= setValue(data.getKey(), newValue); + validateSettings(data.getKey(), oldValue, newValue); + } + + /** + * Called from BasePreferencePage#createPreferenceContent. + */ + public final Control createPreferenceContent(Composite parent) { + Control control = createContents(parent); + if (control != null) { + cacheOriginalValues(); + initContents(); + } + return control; + } + + /** + * Derived classes must override this in order to create + * their visual content. After this is called, initContents() + * will be called. + * @return a Composite representing the entire pane. + */ + protected abstract Control createContents(Composite parent); + + /** + * This will be called when settings are first loaded and + * whenever changes are applied. + * Derived classes may use this to cache the saved settings + * values, for later comparison to see if anything changed. + */ + protected void cacheOriginalValues() { + // Base method does nothing. } /** + * This will be called exactly once during initialization, after + * createContents() and cacheOriginalValues(). + * Derived classes may override this to initialize any fields + * that are not based on a Key. + */ + protected void initContents() { + // Base method does nothing. + } + + protected ExpandableComposite createStyleSection(Composite parent, String label, int nColumns) { + ExpandableComposite excomposite= new ExpandableComposite(parent, SWT.NONE, ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT); + excomposite.setText(label); + excomposite.setExpanded(false); + excomposite.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT)); + excomposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false, nColumns, 1)); + excomposite.addExpansionListener(new ExpansionAdapter() { + public void expansionStateChanged(ExpansionEvent e) { + expandedStateChanged((ExpandableComposite) e.getSource()); + } + }); + fExpandedComposites.add(excomposite); + makeScrollableCompositeAware(excomposite); + return excomposite; + } + + /** + * Called from BasePreferencePage#dispose(). + * Derived classes may override. + */ + public void dispose() { + } + + protected final void expandedStateChanged(ExpandableComposite expandable) { + ScrolledPageContent parentScrolledComposite= getParentScrolledComposite(expandable); + if (parentScrolledComposite != null) { + parentScrolledComposite.reflow(true); + } + } + + protected Control findControl(Key key) { + Combo comboBox= getComboBox(key); + if (comboBox != null) { + return comboBox; + } + Button checkBox= getCheckBox(key); + if (checkBox != null) { + return checkBox; + } + Text text= getTextControl(key); + if (text != null) { + return text; + } + return null; + } + + protected boolean getBooleanValue(Key key) { + return Boolean.valueOf(getValue(key)).booleanValue(); + } + + protected Button getCheckBox(Key key) { + for (int i= fCheckBoxes.size() - 1; i >= 0; i--) { + Button curr= fCheckBoxes.get(i); + ControlData data= (ControlData) curr.getData(); + if (key.equals(data.getKey())) { + return curr; + } + } + return null; + } + + protected Combo getComboBox(Key key) { + for (int i= fComboBoxes.size() - 1; i >= 0; i--) { + Combo curr= fComboBoxes.get(i); + ControlData data= (ControlData) curr.getData(); + if (key.equals(data.getKey())) { + return curr; + } + } + return null; + } + + /** * Provide the strings needed to ask the user whether to rebuild. * Derived classes can override this to change the strings, or to * return null, in which case the dialog will not be shown and the @@ -64,40 +490,111 @@ public abstract class BaseConfigurationBlock extends OptionsConfigurationBlock { } return strings; } - - public BaseConfigurationBlock(IStatusChangeListener context, IProject project, Key[] keys, IWorkbenchPreferenceContainer container) { - super(context, project, keys, container); + + protected ExpandableComposite getParentExpandableComposite(Control control) { + Control parent= control.getParent(); + while (!(parent instanceof ExpandableComposite) && parent != null) { + parent= parent.getParent(); + } + if (parent instanceof ExpandableComposite) { + return (ExpandableComposite) parent; + } + return null; } - /* TODO: temporary fix for Bugzilla 105623. When OptionsConfigurationBlock#performDefaults() - * is fixed, remove this overriding method. */ - @Override - public void performDefaults() { - IScopeContext defaultScope= (fProject == null) ? new DefaultScope() : new InstanceScope(); - for (int i= 0; i < fAllKeys.length; i++) { - Key curr= fAllKeys[i]; - String defValue= curr.getStoredValue(defaultScope, null); - setValue(curr, defValue); + protected ScrolledPageContent getParentScrolledComposite(Control control) { + Control parent= control.getParent(); + while (!(parent instanceof ScrolledPageContent) && parent != null) { + parent= parent.getParent(); } - - settingsUpdated(); - updateControls(); - validateSettings(null, null, null); + if (parent instanceof ScrolledPageContent) { + return (ScrolledPageContent) parent; + } + return null; } - - - /* Increase visibility from declaration in superclass */ - @Override - protected abstract Control createContents(Composite parent); - - protected abstract void updateModel(DialogField field); - /* (non-javadoc) - * Update fields and validate. - * @param changedKey Key that changed, or null, if all changed. - */ - protected abstract void validateSettings(Key changedKey, String oldValue, String newValue); + protected final IWorkbenchPreferenceContainer getPreferenceContainer() { + return fContainer; + } + + protected SelectionListener getSelectionListener() { + if (fSelectionListener == null) { + fSelectionListener= new SelectionListener() { + public void widgetDefaultSelected(SelectionEvent e) {} + + public void widgetSelected(SelectionEvent e) { + controlChanged(e.widget); + } + }; + } + return fSelectionListener; + } + + protected Shell getShell() { + return fShell; + } + + /** + * Retuens the value as actually stored in the preference store. + * @param key + * @return the value as actually stored in the preference store. + */ + protected String getStoredValue(Key key) { + return key.getStoredValue(fLookupOrder, false, fManager); + } + + protected Text getTextControl(Key key) { + for (int i= fTextBoxes.size() - 1; i >= 0; i--) { + Text curr= fTextBoxes.get(i); + ControlData data= (ControlData) curr.getData(); + if (key.equals(data.getKey())) { + return curr; + } + } + return null; + } + protected ModifyListener getTextModifyListener() { + if (fTextModifyListener == null) { + fTextModifyListener= new ModifyListener() { + public void modifyText(ModifyEvent e) { + textChanged((Text) e.widget); + } + }; + } + return fTextModifyListener; + } + + protected String[] getTokens(String text, String separator) { + StringTokenizer tok= new StringTokenizer(text, separator); //$NON-NLS-1$ + int nTokens= tok.countTokens(); + String[] res= new String[nTokens]; + for (int i= 0; i < res.length; i++) { + res[i]= tok.nextToken().trim(); + } + return res; + } + + protected String getValue(Key key) { + if (fDisabledProjectSettings != null) { + return fDisabledProjectSettings.get(key); + } + return key.getStoredValue(fLookupOrder, false, fManager); + } + + public final boolean hasProjectSpecificOptions(IProject project) { + if (project != null) { + IScopeContext projectContext= new ProjectScope(project); + Key[] allKeys= fAllKeys; + for (int i= 0; i < allKeys.length; i++) { + if (allKeys[i].getStoredValue(projectContext, fManager) != null) { + return true; + } + } + } + return false; + } + /** * TODO: this method is a workaround for Bugzilla 111144 and 106111. When * 111144 is fixed, remove this method and call hasProjectSpecificOptions() @@ -117,4 +614,304 @@ public abstract class BaseConfigurationBlock extends OptionsConfigurationBlock { } return false; } + + private void makeScrollableCompositeAware(Control control) { + ScrolledPageContent parentScrolledComposite= getParentScrolledComposite(control); + if (parentScrolledComposite != null) { + parentScrolledComposite.adaptChild(control); + } + } + + protected Combo newComboControl(Composite composite, Key key, String[] values, String[] valueLabels) { + ControlData data= new ControlData(key, values); + + Combo comboBox= new Combo(composite, SWT.READ_ONLY); + comboBox.setItems(valueLabels); + comboBox.setData(data); + comboBox.addSelectionListener(getSelectionListener()); + comboBox.setFont(JFaceResources.getDialogFont()); + + makeScrollableCompositeAware(comboBox); + + String currValue= getValue(key); + comboBox.select(data.getSelection(currValue)); + + fComboBoxes.add(comboBox); + return comboBox; + } + + public boolean performApply() { + return processChanges(null); // apply directly + } + + + public void performDefaults() { + IScopeContext defaultScope= (fProject == null) ? new DefaultScope() : new InstanceScope(); + for (int i= 0; i < fAllKeys.length; i++) { + Key curr= fAllKeys[i]; + String defValue= curr.getStoredValue(defaultScope, null); + setValue(curr, defValue); + } + + settingsUpdated(); + updateControls(); + validateSettings(null, null, null); + } + + public boolean performOk() { + return processChanges(fContainer); + } + + /** + * @since 3.1 + */ + public void performRevert() { + for (int i= 0; i < fAllKeys.length; i++) { + Key curr= fAllKeys[i]; + String origValue= curr.getStoredValue(fLookupOrder, false, null); + setValue(curr, origValue); + } + + settingsUpdated(); + updateControls(); + validateSettings(null, null, null); + } + + /** + * If there are changed settings, save them and ask user whether to rebuild. + * This is called by performOk() and performApply(). + * @param container null when called from performApply(). + */ + protected boolean processChanges(IWorkbenchPreferenceContainer container) { + IScopeContext currContext= fLookupOrder[0]; + if (!settingsChanged(currContext)) { + return true; + } + + int response= 1; // "NO" rebuild unless we put up the dialog. + String[] strings= getFullBuildDialogStrings(fProject == null); + if (strings != null) { + MessageDialog dialog= new MessageDialog( + getShell(), + strings[0], + null, + strings[1], + MessageDialog.QUESTION, + new String[] { + IDialogConstants.YES_LABEL, + IDialogConstants.NO_LABEL, + IDialogConstants.CANCEL_LABEL + }, + 2); + response= dialog.open(); + } + if (response == 0 || response == 1) { // "YES" or "NO" - either way, save. + saveSettings(); + if (container == null) { + // we're doing an Apply, so update the reference values. + cacheOriginalValues(); + } + } + if (response == 0) { // "YES", rebuild + if (container != null) { + // build after dialog exits + container.registerUpdateJob(CoreUtility.getBuildJob(fProject)); + } else { + // build immediately + CoreUtility.getBuildJob(fProject).schedule(); + } + } else if (response != 1) { // "CANCEL" - no save, no rebuild. + return false; + } + return true; + } + + /** + * Save dialog information to persistent storage. + * Derived classes should override this if they have settings + * that are managed using means other than the Key infrastructure. + */ + protected void saveSettings() { + try { + fManager.applyChanges(); + } catch (BackingStoreException e) { + ExceptionHandler.log(e, "Unable to save preferences"); //$NON-NLS-1$ + } + } + + protected void restoreSectionExpansionStates(IDialogSettings settings) { + for (int i= 0; i < fExpandedComposites.size(); i++) { + ExpandableComposite excomposite= fExpandedComposites.get(i); + if (settings == null) { + excomposite.setExpanded(i == 0); // only expand the first node by default + } else { + excomposite.setExpanded(settings.getBoolean(SETTINGS_EXPANDED + String.valueOf(i))); + } + } + } + + public void selectOption(Key key) { + Control control= findControl(key); + if (control != null) { + if (!fExpandedComposites.isEmpty()) { + ExpandableComposite expandable= getParentExpandableComposite(control); + if (expandable != null) { + for (int i= 0; i < fExpandedComposites.size(); i++) { + ExpandableComposite curr= fExpandedComposites.get(i); + curr.setExpanded(curr == expandable); + } + expandedStateChanged(expandable); + } + } + control.setFocus(); + } + } + + public void selectOption(String key, String qualifier) { + for (int i= 0; i < fAllKeys.length; i++) { + Key curr= fAllKeys[i]; + if (curr.getName().equals(key) && curr.getQualifier().equals(qualifier)) { + selectOption(curr); + } + } + } + + protected void setComboEnabled(Key key, boolean enabled) { + Combo combo= getComboBox(key); + Label label= fLabels.get(combo); + combo.setEnabled(enabled); + label.setEnabled(enabled); + } + + protected void setShell(Shell shell) { + fShell= shell; + } + + /** + * Checks the state of all Keys in the dialog to see whether there have been changes. + * Derived classes which include settings managed outside of the Key infrastructure + * should override this method, in order to check whether the additional settings have changed. + * @return true if there is anything that needs to be saved. + */ + protected boolean settingsChanged(IScopeContext currContext) { + boolean needsBuild= false; + for (int i= 0; i < fAllKeys.length; i++) { + Key key= fAllKeys[i]; + String oldVal= key.getStoredValue(currContext, null); + String val= key.getStoredValue(currContext, fManager); + if (val == null) { + if (oldVal != null) { + needsBuild |= !oldVal.equals(key.getStoredValue(fLookupOrder, true, fManager)); + } + } else if (!val.equals(oldVal)) { + needsBuild |= oldVal != null || !val.equals(key.getStoredValue(fLookupOrder, true, fManager)); + } + } + return needsBuild; + } + + protected void settingsUpdated() { + } + + protected String setValue(Key key, boolean value) { + return setValue(key, String.valueOf(value)); + } + + protected String setValue(Key key, String value) { + if (fDisabledProjectSettings != null) { + return fDisabledProjectSettings.put(key, value); + } + String oldValue= getValue(key); + key.setStoredValue(fLookupOrder[0], value, fManager); + return oldValue; + } + + protected void storeSectionExpansionStates(IDialogSettings settings) { + for (int i= 0; i < fExpandedComposites.size(); i++) { + ExpandableComposite curr= fExpandedComposites.get(i); + settings.put(SETTINGS_EXPANDED + String.valueOf(i), curr.isExpanded()); + } + } + + private void testIfOptionsComplete(Key[] allKeys) { + for (int i= 0; i < allKeys.length; i++) { + if (allKeys[i].getStoredValue(fLookupOrder, false, fManager) == null) { + JavaPlugin.logErrorMessage("preference option missing: " + allKeys[i] + " (" + this.getClass().getName() +')'); //$NON-NLS-1$//$NON-NLS-2$ + } + } + } + + protected void textChanged(Text textControl) { + Key key= (Key) textControl.getData(); + String number= textControl.getText(); + String oldValue= setValue(key, number); + validateSettings(key, oldValue, number); + } + + protected void updateCheckBox(Button curr) { + ControlData data= (ControlData) curr.getData(); + + String currValue= getValue(data.getKey()); + curr.setSelection(data.getSelection(currValue) == 0); + } + + protected void updateCombo(Combo curr) { + ControlData data= (ControlData) curr.getData(); + + String currValue= getValue(data.getKey()); + curr.select(data.getSelection(currValue)); + } + + protected void updateControls() { + // update the UI + for (int i= fCheckBoxes.size() - 1; i >= 0; i--) { + updateCheckBox(fCheckBoxes.get(i)); + } + for (int i= fComboBoxes.size() - 1; i >= 0; i--) { + updateCombo(fComboBoxes.get(i)); + } + for (int i= fTextBoxes.size() - 1; i >= 0; i--) { + updateText(fTextBoxes.get(i)); + } + } + + protected abstract void updateModel(DialogField field); + + protected void updateText(Text curr) { + Key key= (Key) curr.getData(); + + String currValue= getValue(key); + if (currValue != null) { + curr.setText(currValue); + } + } + + public void useProjectSpecificSettings(boolean enable) { + boolean hasProjectSpecificOption= fDisabledProjectSettings == null; + if (enable != hasProjectSpecificOption && fProject != null) { + if (enable) { + for (int i= 0; i < fAllKeys.length; i++) { + Key curr= fAllKeys[i]; + String val= fDisabledProjectSettings.get(curr); + curr.setStoredValue(fLookupOrder[0], val, fManager); + } + fDisabledProjectSettings= null; + updateControls(); + } else { + fDisabledProjectSettings= new IdentityHashMap<Key, String>(); + for (int i= 0; i < fAllKeys.length; i++) { + Key curr= fAllKeys[i]; + String oldSetting= curr.getStoredValue(fLookupOrder, false, fManager); + fDisabledProjectSettings.put(curr, oldSetting); + curr.setStoredValue(fLookupOrder[0], null, fManager); // clear project settings + } + } + } + } + + /* (non-javadoc) + * Update fields and validate. + * @param changedKey Key that changed, or null, if all changed. + */ + protected abstract void validateSettings(Key changedKey, String oldValue, String newValue); } diff --git a/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/BasePreferencePage.java b/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/BasePreferencePage.java index 6f0c391483..6cebaa5afb 100644 --- a/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/BasePreferencePage.java +++ b/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/BasePreferencePage.java @@ -25,7 +25,7 @@ public abstract class BasePreferencePage extends PropertyAndPreferencePage { private BaseConfigurationBlock fConfigurationBlock; protected Control createPreferenceContent(Composite composite) { - return getConfigurationBlock().createContents(composite); + return getConfigurationBlock().createPreferenceContent(composite); } @Override diff --git a/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/FactoryPathConfigurationBlock.java b/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/FactoryPathConfigurationBlock.java index 8abb414acc..c1b1fe1d00 100644 --- a/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/FactoryPathConfigurationBlock.java +++ b/org.eclipse.jdt.apt.ui/src/org/eclipse/jdt/apt/ui/internal/preferences/FactoryPathConfigurationBlock.java @@ -23,6 +23,7 @@ import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.jdt.apt.core.internal.util.FactoryContainer; import org.eclipse.jdt.apt.core.internal.util.FactoryPath; import org.eclipse.jdt.apt.core.internal.util.FactoryPathUtil; @@ -32,7 +33,6 @@ import org.eclipse.jdt.apt.core.util.IFactoryPath; import org.eclipse.jdt.apt.ui.internal.util.ExceptionHandler; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.internal.ui.util.CoreUtility; import org.eclipse.jdt.internal.ui.util.PixelConverter; import org.eclipse.jdt.internal.ui.wizards.IStatusChangeListener; import org.eclipse.jdt.internal.ui.wizards.dialogfields.CheckedListDialogField; @@ -43,8 +43,6 @@ import org.eclipse.jdt.internal.ui.wizards.dialogfields.LayoutUtil; import org.eclipse.jdt.internal.ui.wizards.dialogfields.ListDialogField; import org.eclipse.jdt.ui.wizards.BuildPathDialogAccess; import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.jface.dialogs.IDialogConstants; -import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ICheckStateListener; @@ -396,9 +394,6 @@ public class FactoryPathConfigurationBlock extends BaseConfigurationBlock { int buttonBarWidth= fPixelConverter.convertWidthInCharsToPixels(24); fFactoryPathList.setButtonsMinWidth(buttonBarWidth); - cacheOriginalValues(); - initListContents(); - // Register a change listener on the checkboxes CheckboxTableViewer tableViewer = (CheckboxTableViewer)fFactoryPathList.getTableViewer(); tableViewer.addCheckStateListener(new ICheckStateListener() { @@ -407,16 +402,11 @@ public class FactoryPathConfigurationBlock extends BaseConfigurationBlock { } }); - //TODO: enable help - //PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, IJavaHelpContextIds.BUILD_PATH_BLOCK); return fBlockControl; } - - /** - * (Re-)initialize the contents of the list control to the currently saved factory path. - * This relies on the cached values being correct (@see #cacheOriginalValues()). - */ - private void initListContents() { + + @Override + protected void initContents() { fFactoryPathList.removeAllElements(); for (FactoryPathEntry originalFpe : fOriginalPath) { // clone, because we may later modify it and we want to compare with the original. @@ -425,19 +415,21 @@ public class FactoryPathConfigurationBlock extends BaseConfigurationBlock { fFactoryPathList.setChecked(fpe, fpe._attr.isEnabled()); } } - + /** * Save reference copies of the settings, so we can see if anything changed. * This must stay in sync with the actual saved values for the rebuild logic * to work; so be sure to call this any time you save (eg in performApply()). */ - private void cacheOriginalValues() { + @Override + protected void cacheOriginalValues() { IFactoryPath ifp = AptConfig.getFactoryPath(fJProj); // we'll risk this downcast because we're such good buddies with apt.core. FactoryPath fp = (FactoryPath)ifp; Map<FactoryContainer, FactoryPath.Attributes> path = fp.getAllContainers(); fOriginalPath = FactoryPathEntry.pathListFromMap(path); fOriginallyProjectSpecific = AptConfig.hasProjectSpecificFactoryPath(fJProj); + super.cacheOriginalValues(); } /* @@ -655,7 +647,7 @@ public class FactoryPathConfigurationBlock extends BaseConfigurationBlock { // TODO: validate that all the specified factory containers exist? } - private void saveSettings() { + protected void saveSettings() { List<FactoryPathEntry> containers; if ((fJProj != null) && !fBlockControl.isEnabled()) { // We're in a project properties pane but the entire configuration @@ -679,6 +671,8 @@ public class FactoryPathConfigurationBlock extends BaseConfigurationBlock { final String message = Messages.FactoryPathConfigurationBlock_unableToSaveFactorypath_message; ExceptionHandler.handle(e, fBlockControl.getShell(), title, message); } + + super.saveSettings(); } /** @@ -701,58 +695,11 @@ public class FactoryPathConfigurationBlock extends BaseConfigurationBlock { } /** - * If there are changed settings, save them and ask user whether to rebuild. - * This is called by performOk() and performApply(). - * @param container null when called from performApply(). - */ - protected boolean processChanges(IWorkbenchPreferenceContainer container) { - if (!settingsChanged()) { - return true; - } - - int response= 1; // "NO" rebuild unless we put up the dialog. - String[] strings= getFullBuildDialogStrings(fProject == null); - if (strings != null) { - MessageDialog dialog= new MessageDialog( - getShell(), - strings[0], - null, - strings[1], - MessageDialog.QUESTION, - new String[] { - IDialogConstants.YES_LABEL, - IDialogConstants.NO_LABEL, - IDialogConstants.CANCEL_LABEL - }, - 2); - response= dialog.open(); - } - if (response == 0 || response == 1) { // "YES" or "NO" - either way, save. - saveSettings(); - if (container == null) { - // we're doing an Apply, so update the reference values. - cacheOriginalValues(); - } - } - if (response == 0) { // "YES", rebuild - if (container != null) { - // build after dialog exits - container.registerUpdateJob(CoreUtility.getBuildJob(fProject)); - } else { - // build immediately - CoreUtility.getBuildJob(fProject).schedule(); - } - } else if (response != 1) { // "CANCEL" - no save, no rebuild. - return false; - } - return true; - } - - /** * @return true if settings or project-specificness changed since * the pane was launched - that is, if there is anything to save. */ - private boolean settingsChanged() { + @Override + protected boolean settingsChanged(IScopeContext currContext) { boolean isProjectSpecific= (fJProj != null) && fBlockControl.getEnabled(); if (fOriginallyProjectSpecific ^ isProjectSpecific) { // the project-specificness changed. |