blob: e8c84224963e7e92b3a4c788a1c3767bda295aee [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.preferences;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.osgi.service.prefs.BackingStoreException;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
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.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
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.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jface.action.LegacyActionTools;
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.jface.util.Util;
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.preferences.IWorkbenchPreferenceContainer;
import org.eclipse.ui.preferences.IWorkingCopyManager;
import org.eclipse.ui.preferences.WorkingCopyManager;
import org.eclipse.ui.progress.WorkbenchJob;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.util.CoreUtility;
import org.eclipse.jdt.internal.ui.util.SWTUtil;
import org.eclipse.jdt.internal.ui.util.StringMatcher;
import org.eclipse.jdt.internal.ui.wizards.IStatusChangeListener;
/**
* Abstract options configuration block providing a general implementation for setting up
* an options configuration page.
*
* @since 2.1
*/
public abstract class OptionsConfigurationBlock {
public static class Key {
private final String fQualifier;
private final String fKey;
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 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);
}
}
@Override
public String toString() {
return fQualifier + '/' + fKey;
}
public String getQualifier() {
return fQualifier;
}
}
/**
* Key that is only managed locally and not part of preference store.
*/
private static class LocalKey extends Key {
private final HashMap<IScopeContext, String> fValues;
private LocalKey(String key) {
super("local", key); //$NON-NLS-1$
fValues= new HashMap<>();
}
@Override
public String getStoredValue(IScopeContext context, IWorkingCopyManager manager) {
return fValues.get(context);
}
@Override
public void setStoredValue(IScopeContext context, String value, IWorkingCopyManager manager) {
if (value != null) {
fValues.put(context, value);
} else {
fValues.remove(context);
}
}
}
protected static class ControlData {
private final Key fKey;
private final String[] fValues;
public ControlData(Key key, String[] values) {
fKey= key;
fValues= values;
}
public Key getKey() {
return fKey;
}
public String getValue(boolean selection) {
int index= selection ? 0 : 1;
return fValues[index];
}
public String getValue(int index) {
return fValues[index];
}
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
}
}
protected static class LinkControlData extends ControlData {
private Link fLink;
public LinkControlData(Key key, String[] values) {
super(key, values);
}
public void setLink(Link link) {
fLink= link;
}
public Link getLink() {
return fLink;
}
}
/**
* A node in <code>FilteredPreferenceTree</code>.
*/
protected static class PreferenceTreeNode {
public static final int NONE= 0;
public static final int CHECKBOX= 1;
public static final int COMBO= 2;
public static final int EXPANDABLE_COMPOSITE= 3;
public static final int TEXT_CONTROL= 4;
public static final int LINK= 5;
/**
* Tells the type of UI control corresponding to this node. One of
* <ul>
* <li> <code>NONE</code></li>
* <li> <code>CHECKBOX</code></li>
* <li> <code>COMBO</code></li>
* <li> <code>EXPANDABLE_COMPOSITE</code></li>
* <li> <code>TEXT_CONTROL</code></li>
* <li> <code>LINK</code></li>
* </ul>
*/
private final int fControlType;
/**
* Label text of the preference which is used for filtering. This text does not contain
* <code>&</code> which is used to indicate mnemonics.
*/
private final String fLabel;
/**
* The preference key or the local key to uniquely identify a node's corresponding UI
* control. Can be <code>null</code>.
*/
private final Key fKey;
/**
* Tells whether all children should be shown even if just one child matches the filter.
*/
private final boolean fShowAllChildren;
/**
* Tells whether this node's UI control is visible in the UI for the current filter text.
*/
private boolean fVisible;
/**
* List of children nodes.
*/
private List<PreferenceTreeNode> fChildren;
/**
* Constructs a new instance of PreferenceTreeNode according to the parameters.
* <p>
* The <code>label</code> and the <code>key</code> must not be <code>null</code> if the node
* has a corresponding UI control.
* </p>
*
* @param label the label text
* @param key the key
* @param controlType the type of UI control.
* @param showAllChildren tells whether all children should be shown even if just one child
* matches the filter.
*/
public PreferenceTreeNode(String label, Key key, int controlType, boolean showAllChildren) {
super();
if (controlType != NONE && (label == null || key == null)) {
throw new IllegalArgumentException();
}
if (label == null) {
label= ""; //$NON-NLS-1$
}
fLabel= LegacyActionTools.removeMnemonics(label);
fKey= key;
fControlType= controlType;
fShowAllChildren= showAllChildren;
}
public String getLabel() {
return fLabel;
}
public Key getKey() {
return fKey;
}
public int getControlType() {
return fControlType;
}
public List<PreferenceTreeNode> getChildren() {
return fChildren;
}
public boolean isShowAllChildren() {
return fShowAllChildren;
}
public boolean isVisible() {
return fVisible;
}
private void setVisible(boolean visible, boolean recursive) {
fVisible= visible;
if (!recursive)
return;
if (fChildren != null) {
for (int i= 0; i < fChildren.size(); i++) {
fChildren.get(i).setVisible(visible, recursive);
}
}
}
public PreferenceTreeNode addChild(String label, Key key, int controlType, boolean showAllChildren) {
if (fChildren == null) {
fChildren= new ArrayList<>();
}
PreferenceTreeNode n= new PreferenceTreeNode(label, key, controlType, showAllChildren);
fChildren.add(n);
return n;
}
public boolean hasValue() {
if (fControlType == COMBO || fControlType == CHECKBOX || fControlType == TEXT_CONTROL) {
return true;
}
return false;
}
}
/**
* The preference page modeled as a filtered tree.
* <p>
* The tree consists of an optional description label, a filter text input box, and a scrolled
* area. The scrolled content contains all the UI controls which participate in filtering.
* </p>
* <p>
* Supports '*' and '?' wildcards. A word in filter text preceded by '~' is used to filter on
* preference values, e.g. ~ignore or ~off. Supported filter formats are
* <ul>
* <li>pattern</li>
* <li>~valueFilter</li>
* <li>pattern ~valueFilter</li>
* <li>~valueFilter pattern</li>
* </ul>
* </p>
*/
protected static class FilteredPreferenceTree {
/**
* Root node for the tree. It does not have a corresponding UI control.
*/
private final PreferenceTreeNode fRoot;
/**
* The Options Configuration block.
*/
private final OptionsConfigurationBlock fConfigBlock;
/**
* The parent composite of <code>FilteredPreferenceTree</code>.
*/
private final Composite fParentComposite;
/**
* The scrolled area of the tree.
*/
private ScrolledPageContent fScrolledPageContent;
/**
* Job to update the UI in a separate thread.
*/
private final WorkbenchJob fRefreshJob;
/**
* Tells whether the filter text matched at least one element.
*/
private boolean fMatchFound;
/**
* Label to indicate that no option matched the filter text.
*/
private Label fNoMatchFoundLabel;
/**
* Constructs a new instance of FilteredPreferenceTree according to the parameters.
*
* @param configBlock the Options Configuration block
* @param parentComposite the parent composite
* @param label the label, or <code>null</code> if none
*/
public FilteredPreferenceTree(OptionsConfigurationBlock configBlock, Composite parentComposite, String label) {
fRoot= new PreferenceTreeNode(null, null, PreferenceTreeNode.NONE, false);
fConfigBlock= configBlock;
fParentComposite= parentComposite;
createDescription(label);
createFilterBox();
createScrolledArea();
createNoMatchFoundLabel();
fRefreshJob= doCreateRefreshJob();
fRefreshJob.setSystem(true);
fParentComposite.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
fRefreshJob.cancel();
}
});
}
private void createDescription(String label) {
if (label == null)
return;
Label description= new Label(fParentComposite, SWT.LEFT | SWT.WRAP);
description.setFont(fParentComposite.getFont());
description.setText(label);
description.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false));
}
private void createFilterBox() {
//TODO: Directly use the hint flags once Bug 293230 is fixed
FilterTextControl filterTextControl= new FilterTextControl(fParentComposite);
final Text filterBox= filterTextControl.getFilterControl();
filterBox.setMessage(PreferencesMessages.OptionsConfigurationBlock_TypeFilterText);
filterBox.addModifyListener(new ModifyListener() {
private String fPrevFilterText;
@Override
public void modifyText(ModifyEvent e) {
String input= filterBox.getText();
if (input != null && input.equalsIgnoreCase(fPrevFilterText))
return;
fPrevFilterText= input;
doFilter(input);
}
});
}
private void createScrolledArea() {
fScrolledPageContent= new ScrolledPageContent(fParentComposite);
fScrolledPageContent.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
fScrolledPageContent.getVerticalBar().setVisible(true);
}
});
}
public ScrolledPageContent getScrolledPageContent() {
return fScrolledPageContent;
}
private void createNoMatchFoundLabel() {
fNoMatchFoundLabel= new Label(fScrolledPageContent.getBody(), SWT.NONE);
GridData gd= new GridData(SWT.BEGINNING, SWT.CENTER, true, false);
gd.horizontalSpan= 3;
fNoMatchFoundLabel.setLayoutData(gd);
fNoMatchFoundLabel.setFont(fScrolledPageContent.getFont());
fNoMatchFoundLabel.setText(PreferencesMessages.OptionsConfigurationBlock_NoOptionMatchesTheFilter);
setVisible(fNoMatchFoundLabel, false);
}
public PreferenceTreeNode addChild(PreferenceTreeNode parent, String label, Key key, int controlType, boolean showAllChildren) {
parent= (parent == null) ? fRoot : parent;
return parent.addChild(label, key, controlType, showAllChildren);
}
public PreferenceTreeNode addCheckBox(Composite parentComposite, String label, Key key, String[] values, int indent, PreferenceTreeNode parentNode, boolean showAllChildren) {
fConfigBlock.addCheckBox(parentComposite, label, key, values, indent);
return addChild(parentNode, label, key, PreferenceTreeNode.CHECKBOX, showAllChildren);
}
public PreferenceTreeNode addCheckBox(Composite parentComposite, String label, Key key, String[] values, int indent, PreferenceTreeNode parentNode) {
return addCheckBox(parentComposite, label, key, values, indent, parentNode, true);
}
public PreferenceTreeNode addCheckBoxWithLink(Composite parentComposite, String label, Key key, String[] values, int indent, PreferenceTreeNode parentNode, boolean showAllChildren, int widthHint, SelectionListener listener) {
fConfigBlock.addCheckBoxWithLink(parentComposite, label, key, values, indent, widthHint, listener);
addChild(parentNode, label, key, PreferenceTreeNode.LINK, false);
return addChild(parentNode, label, key, PreferenceTreeNode.CHECKBOX, showAllChildren);
}
public PreferenceTreeNode addComboBox(Composite parentComposite, String label, Key key, String[] values, String[] valueLabels, int indent, PreferenceTreeNode parentNode, boolean showAllChildren) {
fConfigBlock.addComboBox(parentComposite, label, key, values, valueLabels, indent);
return addChild(parentNode, label, key, PreferenceTreeNode.COMBO, showAllChildren);
}
public PreferenceTreeNode addComboBox(Composite parentComposite, String label, Key key, String[] values, String[] valueLabels, int indent, PreferenceTreeNode parentNode) {
return addComboBox(parentComposite, label, key, values, valueLabels, indent, parentNode, true);
}
public PreferenceTreeNode addTextField(Composite parentComposite, String label, Key key, int indent, int widthHint, PreferenceTreeNode parentNode) {
fConfigBlock.addTextField(parentComposite, label, key, indent, widthHint);
return addChild(parentNode, label, key, PreferenceTreeNode.TEXT_CONTROL, true);
}
public PreferenceTreeNode addExpandableComposite(Composite parentComposite, String label, int nColumns, Key key, PreferenceTreeNode parentNode, boolean showAllChildren) {
fConfigBlock.createStyleSection(parentComposite, label, nColumns, key);
return addChild(parentNode, label, key, PreferenceTreeNode.EXPANDABLE_COMPOSITE, showAllChildren);
}
public PreferenceTreeNode addLink(Composite parentComposite, String label, Key key, SelectionListener linkListener, int indent, int widthHint, PreferenceTreeNode parentNode) {
fConfigBlock.addLink(parentComposite, label, key, linkListener, indent, widthHint);
return addChild(parentNode, label, key, PreferenceTreeNode.LINK, false);
}
private boolean match(PreferenceTreeNode node, StringMatcher labelMatcher, StringMatcher valueMatcher) {
if (node.getKey() == null) {
return false;
}
boolean valueMatched= true;
boolean labelMatched= true;
if (labelMatcher != null) {
labelMatched= labelMatcher.match(node.getLabel());
}
if (valueMatcher != null) {
if (node.getControlType() == PreferenceTreeNode.COMBO) {
valueMatched= valueMatcher.match(fConfigBlock.getComboBox(node.getKey()).getText());
} else if (node.getControlType() == PreferenceTreeNode.CHECKBOX) {
boolean checked= fConfigBlock.getCheckBox(node.getKey()).getSelection();
if (checked) {
valueMatched= valueMatcher.match(PreferencesMessages.OptionsConfigurationBlock_On) || valueMatcher.match(PreferencesMessages.OptionsConfigurationBlock_Enabled);
} else {
valueMatched= valueMatcher.match(PreferencesMessages.OptionsConfigurationBlock_Off) || valueMatcher.match(PreferencesMessages.OptionsConfigurationBlock_Disabled);
}
}
}
return labelMatched && valueMatched;
}
public boolean filter(PreferenceTreeNode node, StringMatcher labelMatcher, StringMatcher valueMatcher) {
//check this node
boolean visible= match(node, labelMatcher, valueMatcher);
if (visible) {
if (valueMatcher != null && !node.hasValue()) { //see bug 321818
labelMatcher= null;
visible= false;
} else {
node.setVisible(visible, true);
fMatchFound= true;
return visible;
}
}
//check children
List<PreferenceTreeNode> children= node.getChildren();
if (children != null) {
for (int i= 0; i < children.size(); i++) {
visible|= filter(children.get(i), labelMatcher, valueMatcher);
}
if (node.isShowAllChildren()) {
for (int i= 0; i < children.size(); i++) {
children.get(i).setVisible(visible, false);
}
}
}
node.setVisible(visible, false);
return visible;
}
public void doFilter(String filterText) {
fRefreshJob.cancel();
fRefreshJob.schedule(getRefreshJobDelay());
filterText= filterText.trim();
int index= filterText.indexOf("~"); //$NON-NLS-1$
StringMatcher labelMatcher= null;
StringMatcher valueMatcher= null;
if (index == -1) {
labelMatcher= createStringMatcher(filterText);
} else {
if (index == 0) {
int i= 0;
for (; i < filterText.length(); i++) {
char ch= filterText.charAt(i);
if (ch == ' ' || ch == '\t') {
break;
}
}
valueMatcher= createStringMatcher(filterText.substring(1, i));
labelMatcher= createStringMatcher(filterText.substring(i));
} else {
labelMatcher= createStringMatcher(filterText.substring(0, index));
if (index < filterText.length())
valueMatcher= createStringMatcher(filterText.substring(index + 1));
}
}
fMatchFound= false;
filter(fRoot, labelMatcher, valueMatcher);
}
private StringMatcher createStringMatcher(String filterText) {
filterText= filterText.trim();
if (filterText.length() > 0)
return new StringMatcher("*" + filterText + "*", true, false); //$NON-NLS-1$ //$NON-NLS-2$
return null;
}
/**
* Return the time delay that should be used when scheduling the filter refresh job.
*
* @return a time delay in milliseconds before the job should run
*/
private long getRefreshJobDelay() {
return 200;
}
private void updateUI(PreferenceTreeNode node) {
//update node
int controlType= node.getControlType();
Control control= null;
if (controlType == PreferenceTreeNode.CHECKBOX) {
control= fConfigBlock.getCheckBox(node.getKey());
} else if (controlType == PreferenceTreeNode.COMBO) {
control= fConfigBlock.getComboBox(node.getKey());
} else if (controlType == PreferenceTreeNode.TEXT_CONTROL) {
control= fConfigBlock.getTextControl(node.getKey());
} else if (controlType == PreferenceTreeNode.EXPANDABLE_COMPOSITE) {
control= fConfigBlock.getExpandableComposite(node.getKey());
} else if (controlType == PreferenceTreeNode.LINK) {
control= fConfigBlock.getLink(node.getKey());
}
if (control != null) {
boolean visible= node.isVisible();
setVisible(control, visible);
if (control instanceof Combo || control instanceof Text) {
Label label= (fConfigBlock.fLabels.get(control));
setVisible(label, visible);
}
if (control instanceof ExpandableComposite) {
((ExpandableComposite)control).setExpanded(visible);
}
}
//update children
List<PreferenceTreeNode> children= node.getChildren();
if (children != null) {
for (int i= 0; i < children.size(); i++) {
updateUI(children.get(i));
}
}
}
private WorkbenchJob doCreateRefreshJob() {
return new WorkbenchJob(PreferencesMessages.OptionsConfigurationBlock_RefreshFilter) {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
updateUI(fRoot);
fParentComposite.layout(true, true); //relayout
if (fScrolledPageContent != null) {
setVisible(fNoMatchFoundLabel, !fMatchFound);
fScrolledPageContent.reflow(true);
}
return Status.OK_STATUS;
}
};
}
private void setVisible(Control control, boolean visible) {
control.setVisible(visible);
((GridData)control.getLayoutData()).exclude= !visible;
}
}
private static class HighlightPainter implements PaintListener {
private final Composite fParent;
private final Label fLabelControl;
private final Combo fComboBox;
int fColor;
public HighlightPainter(Composite parent, Label labelControl, Combo comboBox, int color) {
fParent= parent;
fLabelControl= labelControl;
fComboBox= comboBox;
fColor= color;
}
@Override
public void paintControl(PaintEvent e) {
if (((GridData) fLabelControl.getLayoutData()).exclude) {
fParent.removePaintListener(this);
fLabelControl.setData(null);
return;
}
int GAP= 7;
int ARROW= 3;
Rectangle l= fLabelControl.getBounds();
Point c= fComboBox.getLocation();
e.gc.setForeground(e.display.getSystemColor(fColor));
int x2= c.x - GAP;
int y= l.y + l.height / 2 + 1;
e.gc.drawLine(l.x + l.width + GAP, y, x2, y);
e.gc.drawLine(x2 - ARROW, y - ARROW, x2, y);
e.gc.drawLine(x2 - ARROW, y + ARROW, x2, y);
}
}
private static final String REBUILD_COUNT_KEY= "preferences_build_requested"; //$NON-NLS-1$
private static final String SETTINGS_EXPANDED= "expanded"; //$NON-NLS-1$
protected final ArrayList<Button> fCheckBoxes;
protected final ArrayList<Combo> fComboBoxes;
protected final ArrayList<Text> fTextBoxes;
protected final ArrayList<Link> fLinks;
protected final HashMap<Control, Label> fLabels;
protected final ArrayList<ExpandableComposite> fExpandableComposites;
private SelectionListener fSelectionListener;
private ModifyListener fTextModifyListener;
protected IStatusChangeListener fContext;
protected final IProject fProject; // project or null
protected final Key[] fAllKeys;
private IScopeContext[] fLookupOrder;
private Shell fShell;
private final IWorkingCopyManager fManager;
private final IWorkbenchPreferenceContainer fContainer;
private Map<Key, String> fDisabledProjectSettings; // null when project specific settings are turned off
private int fRebuildCount; /// used to prevent multiple dialogs that ask for a rebuild
public OptionsConfigurationBlock(IStatusChangeListener context, IProject project, Key[] allKeys, IWorkbenchPreferenceContainer container) {
fContext= context;
fProject= project;
fAllKeys= allKeys;
fContainer= container;
if (container == null) {
fManager= new WorkingCopyManager();
} else {
fManager= container.getWorkingCopyManager();
}
if (fProject != null) {
fLookupOrder= new IScopeContext[] {
new ProjectScope(fProject),
InstanceScope.INSTANCE,
DefaultScope.INSTANCE
};
} else {
fLookupOrder= new IScopeContext[] {
InstanceScope.INSTANCE,
DefaultScope.INSTANCE
};
}
testIfOptionsComplete(allKeys);
if (fProject == null || hasProjectSpecificOptions(fProject)) {
fDisabledProjectSettings= null;
} else {
fDisabledProjectSettings= new IdentityHashMap<>();
for (int i= 0; i < allKeys.length; i++) {
Key curr= allKeys[i];
fDisabledProjectSettings.put(curr, curr.getStoredValue(fLookupOrder, false, fManager));
}
}
settingsUpdated();
fCheckBoxes= new ArrayList<>();
fComboBoxes= new ArrayList<>();
fTextBoxes= new ArrayList<>(2);
fLinks= new ArrayList<>(2);
fLabels= new HashMap<>();
fExpandableComposites= new ArrayList<>();
fRebuildCount= getRebuildCount();
}
protected final IWorkbenchPreferenceContainer getPreferenceContainer() {
return fContainer;
}
protected static Key getKey(String plugin, String key) {
return new Key(plugin, key);
}
protected final static Key getJDTLaunchingKey(String key) {
return getKey(JavaRuntime.ID_PLUGIN, key);
}
protected final static Key getJDTCoreKey(String key) {
return getKey(JavaCore.PLUGIN_ID, key);
}
protected final static Key getJDTUIKey(String key) {
return getKey(JavaUI.ID_PLUGIN, key);
}
protected final static Key getLocalKey(String key) {
return new LocalKey(key);
}
private void testIfOptionsComplete(Key[] allKeys) {
for (int i= 0; i < allKeys.length; i++) {
Key key= allKeys[i];
if (!(key instanceof LocalKey)) {
if (key.getStoredValue(fLookupOrder, false, fManager) == null) {
JavaPlugin.logErrorMessage("preference option missing: " + key + " (" + this.getClass().getName() + ')'); //$NON-NLS-1$//$NON-NLS-2$
}
}
}
}
private int getRebuildCount() {
return fManager.getWorkingCopy(DefaultScope.INSTANCE.getNode(JavaUI.ID_PLUGIN)).getInt(REBUILD_COUNT_KEY, 0);
}
private void incrementRebuildCount() {
fRebuildCount++;
fManager.getWorkingCopy(DefaultScope.INSTANCE.getNode(JavaUI.ID_PLUGIN)).putInt(REBUILD_COUNT_KEY, fRebuildCount);
}
protected void settingsUpdated() {
// hook for subclasses
}
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);
}
}
}
public void selectOption(Key key) {
Control control= findControl(key);
if (control != null) {
if (!fExpandableComposites.isEmpty()) {
ExpandableComposite expandable= getParentExpandableComposite(control);
if (expandable != null) {
for (int i= 0; i < fExpandableComposites.size(); i++) {
ExpandableComposite curr= fExpandableComposites.get(i);
curr.setExpanded(curr == expandable);
}
expandedStateChanged(expandable);
}
}
control.setFocus();
}
}
public boolean hasProjectSpecificOptions(IProject project) {
return hasProjectSpecificOptions(project, fAllKeys, fManager);
}
public static boolean hasProjectSpecificOptions(IProject project, Key[] allKeys, IWorkingCopyManager manager) {
if (project != null) {
IScopeContext projectContext= new ProjectScope(project);
for (int i= 0; i < allKeys.length; i++) {
if (allKeys[i].getStoredValue(projectContext, manager) != null) {
return true;
}
}
}
return false;
}
protected Shell getShell() {
return fShell;
}
protected void setShell(Shell shell) {
fShell= shell;
}
protected abstract Control createContents(Composite parent);
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);
updateCheckBox(checkBox);
fCheckBoxes.add(checkBox);
return checkBox;
}
protected Button addCheckBoxWithLink(Composite parent, final String label, Key key, String[] values, int indent, int widthHint, final SelectionListener listener) {
LinkControlData data= new LinkControlData(key, values);
GridData gd= new GridData(GridData.FILL, GridData.FILL, true, false);
gd.horizontalSpan= 3;
gd.horizontalIndent= indent;
Composite composite= new Composite(parent, SWT.NONE);
GridLayout layout= new GridLayout();
layout.marginHeight= 0;
layout.marginWidth= 0;
layout.horizontalSpacing= 0;
layout.numColumns= 2;
composite.setLayout(layout);
composite.setLayoutData(gd);
final Button checkBox= new Button(composite, SWT.CHECK);
checkBox.setFont(JFaceResources.getDialogFont());
gd= new GridData(GridData.FILL, GridData.CENTER, false, false);
int offset= Util.isMac() ? -4 : Util.isLinux() ? -2 : /* Windows et al. */ 3;
gd.widthHint= checkBox.computeSize(SWT.DEFAULT, SWT.DEFAULT).x + offset;
checkBox.setLayoutData(gd);
checkBox.setData(data);
checkBox.addSelectionListener(getSelectionListener());
checkBox.getAccessible().addAccessibleListener(new AccessibleAdapter() {
@Override
public void getName(AccessibleEvent e) {
e.result = LegacyActionTools.removeMnemonics(label.replaceAll("</?[aA][^>]*>", "")); //$NON-NLS-1$ //$NON-NLS-2$
}
});
gd= new GridData(GridData.FILL, GridData.CENTER, true, false);
gd.widthHint= widthHint;
Link link= new Link(composite, SWT.NONE);
link.setText(label);
link.setLayoutData(gd);
link.setData(key);
data.setLink(link);
// toggle checkbox when user clicks unlinked text in link:
final boolean[] linkSelected= { false };
link.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
linkSelected[0]= true;
if (listener != null) {
listener.widgetSelected(e);
}
}
});
link.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
linkSelected[0]= false;
}
@Override
public void mouseUp(MouseEvent e) {
if (!linkSelected[0]) {
checkBox.setSelection(!checkBox.getSelection());
checkBox.setFocus();
linkSelected[0]= false;
controlChanged(checkBox);
}
}
});
link.addTraverseListener(new TraverseListener() {
@Override
public void keyTraversed(TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit == true) {
e.detail= SWT.TRAVERSE_NONE;
checkBox.setSelection(!checkBox.getSelection());
checkBox.setFocus();
linkSelected[0]= false;
controlChanged(checkBox);
}
}
});
makeScrollableCompositeAware(link);
makeScrollableCompositeAware(checkBox);
updateCheckBox(checkBox);
fCheckBoxes.add(checkBox);
fLinks.add(link);
return checkBox;
}
protected Combo addComboBox(Composite parent, String label, Key key, String[] values, String[] valueLabels, int indent) {
GridData gd= new GridData(GridData.BEGINNING, 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);
addHighlight(parent, labelControl, comboBox);
return comboBox;
}
protected static final int HIGHLIGHT_FOCUS = SWT.COLOR_WIDGET_DARK_SHADOW;
protected static final int HIGHLIGHT_MOUSE = SWT.COLOR_WIDGET_NORMAL_SHADOW;
protected static final int HIGHLIGHT_NONE = SWT.NONE;
private void addHighlight(final Composite parent, final Label labelControl, final Combo comboBox) {
comboBox.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
highlight(parent, labelControl, comboBox, HIGHLIGHT_NONE);
}
@Override
public void focusGained(FocusEvent e) {
highlight(parent, labelControl, comboBox, HIGHLIGHT_FOCUS);
}
});
MouseTrackAdapter labelComboListener= new MouseTrackAdapter() {
@Override
public void mouseEnter(MouseEvent e) {
highlight(parent, labelControl, comboBox, comboBox.isEnabled() ? comboBox.isFocusControl() ? HIGHLIGHT_FOCUS : HIGHLIGHT_MOUSE : HIGHLIGHT_NONE);
}
@Override
public void mouseExit(MouseEvent e) {
if (! comboBox.isFocusControl())
highlight(parent, labelControl, comboBox, HIGHLIGHT_NONE);
}
};
comboBox.addMouseTrackListener(labelComboListener);
labelControl.addMouseTrackListener(labelComboListener);
class MouseMoveTrackListener extends MouseTrackAdapter implements MouseMoveListener, MouseListener {
@Override
public void mouseExit(MouseEvent e) {
if (! comboBox.isFocusControl())
highlight(parent, labelControl, comboBox, HIGHLIGHT_NONE);
}
@Override
public void mouseMove(MouseEvent e) {
int color= comboBox.isEnabled() ? comboBox.isFocusControl() ? HIGHLIGHT_FOCUS : isAroundLabel(e) ? HIGHLIGHT_MOUSE : HIGHLIGHT_NONE : HIGHLIGHT_NONE;
highlight(parent, labelControl, comboBox, color);
}
@Override
public void mouseDown(MouseEvent e) {
if (isAroundLabel(e))
comboBox.setFocus();
}
@Override
public void mouseDoubleClick(MouseEvent e) {
// not used
}
@Override
public void mouseUp(MouseEvent e) {
// not used
}
private boolean isAroundLabel(MouseEvent e) {
int lx= labelControl.getLocation().x;
Rectangle c= comboBox.getBounds();
int x= e.x;
int y= e.y;
boolean isAroundLabel= lx - 5 < x && x < c.x && c.y - 2 < y && y < c.y + c.height + 2;
return isAroundLabel;
}
}
MouseMoveTrackListener parentListener= new MouseMoveTrackListener();
parent.addMouseMoveListener(parentListener);
parent.addMouseTrackListener(parentListener);
parent.addMouseListener(parentListener);
MouseAdapter labelClickListener= new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
comboBox.setFocus();
}
};
labelControl.addMouseListener(labelClickListener);
}
protected void highlight(final Composite parent, final Label labelControl, final Combo comboBox, final int color) {
Object data= labelControl.getData();
if (data == null) {
if (color != HIGHLIGHT_NONE) {
PaintListener painter= new HighlightPainter(parent, labelControl, comboBox, color);
parent.addPaintListener(painter);
labelControl.setData(painter);
} else {
return;
}
} else {
if (color == HIGHLIGHT_NONE) {
parent.removePaintListener((PaintListener) data);
labelControl.setData(null);
} else if (color != ((HighlightPainter) data).fColor){
((HighlightPainter) data).fColor= color;
} else {
return;
}
}
parent.redraw();
}
protected int getHighlight(Label labelControl) {
Object data= labelControl.getData();
if (data == null)
return HIGHLIGHT_NONE;
return ((HighlightPainter) data).fColor;
}
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 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());
SWTUtil.setDefaultVisibleItemCount(comboBox);
makeScrollableCompositeAware(comboBox);
updateCombo(comboBox);
fComboBoxes.add(comboBox);
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());
GridData gd= new GridData();
gd.horizontalIndent= indent;
labelControl.setLayoutData(gd);
Text textBox= new Text(parent, SWT.BORDER | SWT.SINGLE);
textBox.setData(key);
makeScrollableCompositeAware(textBox);
fLabels.put(textBox, labelControl);
updateText(textBox);
textBox.addModifyListener(getTextModifyListener());
GridData data= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
if (widthHint != 0) {
data.widthHint= widthHint;
}
data.horizontalSpan= 2;
textBox.setLayoutData(data);
fTextBoxes.add(textBox);
return textBox;
}
protected Link addLink(Composite parent, String label, Key key, SelectionListener linkListener, int indent, int widthHint) {
GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gd.horizontalSpan= 3;
gd.horizontalIndent= indent;
gd.widthHint= widthHint;
Link link= new Link(parent, SWT.NONE);
link.setFont(JFaceResources.getDialogFont());
link.setText(label);
link.setData(key);
link.setLayoutData(gd);
link.addSelectionListener(linkListener);
makeScrollableCompositeAware(link);
fLinks.add(link);
return link;
}
protected Link createIgnoreOptionalProblemsLink(Composite parent) {
final IClasspathEntry sourceFolderEntry= getSourceFolderIgnoringOptionalProblems();
if (sourceFolderEntry != null) {
Link link= new Link(parent, SWT.NONE);
link.setText(PreferencesMessages.OptionsConfigurationBlock_IgnoreOptionalProblemsLink);
link.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
HashMap<Object, Object> data= new HashMap<>(1);
data.put(BuildPathsPropertyPage.DATA_REVEAL_ENTRY, sourceFolderEntry);
data.put(BuildPathsPropertyPage.DATA_REVEAL_ATTRIBUTE_KEY, IClasspathAttribute.IGNORE_OPTIONAL_PROBLEMS);
getPreferenceContainer().openPage(BuildPathsPropertyPage.PROP_ID, data);
}
});
return link;
}
return null;
}
private IClasspathEntry getSourceFolderIgnoringOptionalProblems() {
if (fProject == null) {
return null;
}
IJavaProject javaProject= JavaCore.create(fProject);
if (javaProject == null) {
return null;
}
try {
IClasspathEntry[] classpathEntries= javaProject.getRawClasspath();
for (int i= 0; i < classpathEntries.length; i++) {
IClasspathEntry entry= classpathEntries[i];
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
IClasspathAttribute[] extraAttributes= entry.getExtraAttributes();
for (int j= 0; j < extraAttributes.length; j++) {
IClasspathAttribute attrib= extraAttributes[j];
if (IClasspathAttribute.IGNORE_OPTIONAL_PROBLEMS.equals(attrib.getName())) {
if ("true".equals(attrib.getValue())) { //$NON-NLS-1$
return entry;
} else {
break;
}
}
}
}
}
} catch (JavaModelException e) {
JavaPlugin.log(e);
}
return null;
}
protected ScrolledPageContent getParentScrolledComposite(Control control) {
Control parent= control.getParent();
while (!(parent instanceof ScrolledPageContent) && parent != null) {
parent= parent.getParent();
}
if (parent instanceof ScrolledPageContent) {
return (ScrolledPageContent) parent;
}
return null;
}
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;
}
private void makeScrollableCompositeAware(Control control) {
ScrolledPageContent parentScrolledComposite= getParentScrolledComposite(control);
if (parentScrolledComposite != null) {
parentScrolledComposite.adaptChild(control);
}
}
protected ExpandableComposite createStyleSection(Composite parent, String label, int nColumns) {
return createStyleSection(parent, label, nColumns, null);
}
protected ExpandableComposite createStyleSection(Composite parent, String label, int nColumns, Key key) {
ExpandableComposite excomposite= new ExpandableComposite(parent, SWT.NONE, ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT);
excomposite.setText(label);
if (key != null) {
excomposite.setData(key);
}
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() {
@Override
public void expansionStateChanged(ExpansionEvent e) {
expandedStateChanged((ExpandableComposite) e.getSource());
}
});
fExpandableComposites.add(excomposite);
makeScrollableCompositeAware(excomposite);
return excomposite;
}
protected final void expandedStateChanged(ExpandableComposite expandable) {
ScrolledPageContent parentScrolledComposite= getParentScrolledComposite(expandable);
if (parentScrolledComposite != null) {
parentScrolledComposite.reflow(true);
}
}
protected void restoreSectionExpansionStates(IDialogSettings settings) {
for (int i= 0; i < fExpandableComposites.size(); i++) {
ExpandableComposite excomposite= fExpandableComposites.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)));
}
}
}
protected void storeSectionExpansionStates(IDialogSettings settings) {
for (int i= 0; i < fExpandableComposites.size(); i++) {
ExpandableComposite curr= fExpandableComposites.get(i);
settings.put(SETTINGS_EXPANDED + String.valueOf(i), curr.isExpanded());
}
}
protected SelectionListener getSelectionListener() {
if (fSelectionListener == null) {
fSelectionListener= new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
controlChanged(e.widget);
}
};
}
return fSelectionListener;
}
protected ModifyListener getTextModifyListener() {
if (fTextModifyListener == null) {
fTextModifyListener= new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
textChanged((Text) e.widget);
}
};
}
return fTextModifyListener;
}
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);
}
protected void textChanged(Text textControl) {
Key key= (Key) textControl.getData();
String number= textControl.getText();
String oldValue= setValue(key, number);
validateSettings(key, oldValue, number);
}
/**
* Checks a setting.
*
* @param key a key
* @param value an assumed value for the key
* @return <code>true</code> iff the given key's value is equal to the given value
*/
protected boolean checkValue(Key key, String value) {
return value.equals(getValue(key));
}
/**
* Returns the value for the key.
*
* @param key the key
* @return the stored value
*/
protected String getValue(Key key) {
if (fDisabledProjectSettings != null) {
return fDisabledProjectSettings.get(key);
}
return key.getStoredValue(fLookupOrder, false, fManager);
}
protected boolean getBooleanValue(Key key) {
return Boolean.valueOf(getValue(key)).booleanValue();
}
/**
* Sets the option <code>key</code> to the value <code>value</code>.
* Note that callers have to make sure the corresponding controls are updated afterwards.
*
* @param key the option key
* @param value the new value
* @return the old value
*
* @see #updateControls()
* @see #updateCheckBox(Button)
* @see #updateCombo(Combo)
* @see #updateText(Text)
*/
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;
}
/**
* Sets the option <code>key</code> to the value <code>value</code>.
* Note that callers have to make sure the corresponding controls are updated afterwards.
*
* @param key the option key
* @param value the new value
* @return the old value
*
* @see #updateControls()
* @see #updateCheckBox(Button)
* @see #updateCombo(Combo)
* @see #updateText(Text)
*/
protected String setValue(Key key, boolean value) {
return setValue(key, String.valueOf(value));
}
protected final void setDefaultValue(Key key, String value) {
IScopeContext instanceScope= fLookupOrder[fLookupOrder.length - 1];
key.setStoredValue(instanceScope, value, fManager);
}
/**
* Returns the value as stored in the preference store.
*
* @param key the key
* @return the value
*/
protected String getStoredValue(Key key) {
return key.getStoredValue(fLookupOrder, false, fManager);
}
/**
* Returns the value as actually stored in the preference store, without considering
* the working copy store.
*
* @param key the key
* @return the value as actually stored in the preference store
*/
protected String getOriginalStoredValue(Key key) {
return key.getStoredValue(fLookupOrder, false, null);
}
/**
* Reverts the given options to the stored values.
*
* @param keys the options to revert
* @since 3.5
*/
protected void revertValues(Key[] keys) {
for (int i= 0; i < keys.length; i++) {
Key curr= keys[i];
String origValue= curr.getStoredValue(fLookupOrder, false, null);
setValue(curr, origValue);
}
}
/**
* Updates fields and validates settings.
*
* @param changedKey key that changed, or <code>null</code>, if all changed.
* @param oldValue old value or <code>null</code>
* @param newValue new value or <code>null</code>
*/
protected abstract void validateSettings(Key changedKey, String oldValue, String newValue);
protected String[] getTokens(String text, String separator) {
StringTokenizer tok= new StringTokenizer(text, separator);
int nTokens= tok.countTokens();
String[] res= new String[nTokens];
for (int i= 0; i < res.length; i++) {
res[i]= tok.nextToken().trim();
}
return res;
}
private boolean getChanges(IScopeContext currContext, List<Key> changedSettings) {
boolean completeSettings= fProject != null && fDisabledProjectSettings == null; // complete when project settings are enabled
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) {
changedSettings.add(key);
needsBuild |= !oldVal.equals(key.getStoredValue(fLookupOrder, true, fManager));
} else if (completeSettings) {
key.setStoredValue(currContext, key.getStoredValue(fLookupOrder, true, fManager), fManager);
changedSettings.add(key);
// no build needed
}
} else if (!val.equals(oldVal)) {
changedSettings.add(key);
needsBuild |= oldVal != null || !val.equals(key.getStoredValue(fLookupOrder, true, fManager));
}
}
return needsBuild;
}
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();
validateSettings(null, null, null);
} else {
fDisabledProjectSettings= new IdentityHashMap<>();
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
}
}
}
}
public boolean areSettingsEnabled() {
return fDisabledProjectSettings == null || fProject == null;
}
public boolean performOk() {
return processChanges(fContainer);
}
public boolean performApply() {
return processChanges(null); // apply directly
}
protected boolean processChanges(IWorkbenchPreferenceContainer container) {
IScopeContext currContext= fLookupOrder[0];
List<Key> changedOptions= new ArrayList<>();
boolean needsBuild= getChanges(currContext, changedOptions);
if (changedOptions.isEmpty()) {
return true;
}
if (needsBuild) {
int count= getRebuildCount();
if (count > fRebuildCount) {
needsBuild= false; // build already requested
fRebuildCount= count;
}
}
boolean doBuild= false;
if (needsBuild) {
String[] strings= getFullBuildDialogStrings(fProject == null);
if (strings != null) {
if (ResourcesPlugin.getWorkspace().getRoot().getProjects().length == 0) {
doBuild= true; // don't bother the user
} else {
MessageDialog dialog= new MessageDialog(getShell(), strings[0], null, strings[1], MessageDialog.QUESTION, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL, IDialogConstants.CANCEL_LABEL }, 2);
int res= dialog.open();
if (res == 0) {
doBuild= true;
} else if (res != 1) {
return false; // cancel pressed
}
}
}
}
if (container != null) {
// no need to apply the changes to the original store: will be done by the page container
if (doBuild) { // post build
incrementRebuildCount();
container.registerUpdateJob(CoreUtility.getBuildJob(fProject));
}
} else {
// apply changes right away
try {
fManager.applyChanges();
} catch (BackingStoreException e) {
JavaPlugin.log(e);
return false;
}
if (doBuild) {
CoreUtility.getBuildJob(fProject).schedule();
}
}
return true;
}
protected abstract String[] getFullBuildDialogStrings(boolean workspaceSettings);
public void performDefaults() {
for (int i= 0; i < fAllKeys.length; i++) {
Key curr= fAllKeys[i];
String defValue= curr.getStoredValue(fLookupOrder, true, fManager);
setValue(curr, defValue);
}
settingsUpdated();
updateControls();
validateSettings(null, null, null);
}
/**
* @since 3.1
*/
public void performRevert() {
revertValues(fAllKeys);
settingsUpdated();
updateControls();
validateSettings(null, null, null);
}
public void dispose() {
// hook for subclasses
}
/**
* Updates the UI from the current settings. Must be called whenever a setting has been changed
* by code.
*/
protected void updateControls() {
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 void updateCombo(Combo curr) {
ControlData data= (ControlData) curr.getData();
String currValue= getValue(data.getKey());
curr.select(data.getSelection(currValue));
}
protected void updateCheckBox(Button curr) {
ControlData data= (ControlData) curr.getData();
String currValue= getValue(data.getKey());
curr.setSelection(data.getSelection(currValue) == 0);
}
protected void updateText(Text curr) {
Key key= (Key) curr.getData();
String currValue= getValue(key);
if (currValue != null) {
curr.setText(currValue);
}
}
protected ExpandableComposite getExpandableComposite(Key key) {
for (int i= fExpandableComposites.size() - 1; i >= 0; i--) {
ExpandableComposite curr= fExpandableComposites.get(i);
Key data= (Key)curr.getData();
if (key.equals(data)) {
return curr;
}
}
return null;
}
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 Link getCheckBoxLink(Key key) {
if (fCheckBoxes == null)
return null;
for (int i= fCheckBoxes.size() - 1; i >= 0; i--) {
Button curr= fCheckBoxes.get(i);
ControlData data= (ControlData) curr.getData();
if (key.equals(data.getKey()) && data instanceof LinkControlData) {
return ((LinkControlData)data).getLink();
}
}
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;
}
protected Text getTextControl(Key key) {
for (int i= fTextBoxes.size() - 1; i >= 0; i--) {
Text curr= fTextBoxes.get(i);
Key data= (Key)curr.getData();
if (key.equals(data)) {
return curr;
}
}
return null;
}
protected Link getLink(Key key) {
for (int i= fLinks.size() - 1; i >= 0; i--) {
Link curr= fLinks.get(i);
Key data= (Key)curr.getData();
if (key.equals(data)) {
return curr;
}
}
return null;
}
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;
}
Link link= getLink(key);
if (link != null) {
return link;
}
return null;
}
protected void setComboEnabled(Key key, boolean enabled) {
Combo combo= getComboBox(key);
Label label= fLabels.get(combo);
combo.setEnabled(enabled);
label.setEnabled(enabled);
if (!enabled) {
highlight(combo.getParent(), label, combo, HIGHLIGHT_NONE);
}
}
protected void setTextFieldEnabled(Key key, boolean enabled) {
Text text= getTextControl(key);
Label label= fLabels.get(text);
text.setEnabled(enabled);
label.setEnabled(enabled);
}
}