blob: 010dfb520a0b8500a216daabb459c4c98294f18d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2018 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.common.ui.internal.preferences;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.preference.ComboFieldEditor;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.preference.IPersistentPreferenceStore;
import org.eclipse.jface.preference.IPreferenceNode;
import org.eclipse.jface.preference.IPreferencePage;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.preference.PreferenceManager;
import org.eclipse.jface.preference.PreferenceNode;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.ocl.common.preferences.PreferenceableOption;
import org.eclipse.ocl.common.ui.internal.messages.CommonUIMessages;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.SelectionAdapter;
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.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Link;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.IWorkbenchPropertyPage;
import org.eclipse.ui.preferences.ScopedPreferenceStore;
/**
* An abstract Project/Property preference page providing support for use as a global
* preference page or as a project-specific property page.
*/
public abstract class AbstractProjectPreferencePage extends PreferencePage
implements IPropertyChangeListener, IWorkbenchPreferencePage, IWorkbenchPropertyPage
{
protected static interface IFieldEditor
{
void adjustForNumColumns(int numColumns);
int getNumberOfControls();
String getPreferenceName();
boolean isValid();
void load();
void loadDefault();
void setEnabled(boolean enabled, Composite fieldEditorParent);
void setFocus();
void setPage(DialogPage dialogPage);
void setPreferenceStore(IPreferenceStore store);
void setPresentsDefaultValue(boolean booleanValue);
void setPropertyChangeListener(IPropertyChangeListener listener);
void store();
}
protected static class MyComboFieldEditor extends ComboFieldEditor implements IFieldEditor
{
public MyComboFieldEditor(PreferenceableOption<?> preference, String labelText,
String[][] entryNamesAndValues, Composite parent) {
super(preference.getKey(), labelText, entryNamesAndValues, parent);
}
@Override
public void adjustForNumColumns(int numColumns) {
super.adjustForNumColumns(numColumns);
}
@Override
public void setPresentsDefaultValue(boolean booleanValue) {
super.setPresentsDefaultValue(booleanValue);
}
}
// Name of the preferences store: /project/.setting/STORE_ID.prefs
protected static final String[][] BOOLEANS = new String[][] {
{ CommonUIMessages.Preference_False, Boolean.FALSE.toString() },
{ CommonUIMessages.Preference_True, Boolean.TRUE.toString() }
};
protected static final String[][] ANY_LESS_VALUES = new String[][] {
{ CommonUIMessages.Preference_Null, Boolean.FALSE.toString() },
{ CommonUIMessages.Preference_Invalid, Boolean.TRUE.toString() }
};
@SuppressWarnings("deprecation")
private static final InstanceScope INSTANCE_SCOPE_INSTANCE = new InstanceScope(); // InstanceScope.INSTANCE not available for Galileo
private String pluginId;
/**
* The field editors, or <code>null</code> if not created yet.
*/
private List<IFieldEditor> fields = new ArrayList<IFieldEditor>();
/**
* The first invalid field editor, or <code>null</code>
* if all field editors are valid.
*/
private IFieldEditor invalidFieldEditor = null;
/**
* The parent composite for field editors
*/
private Composite fieldEditorParent;
private IProject project; // Non-null for a project page.
private IPersistentPreferenceStore projectStore; // Non-null store for a project page
private Button projectSpecificSettingsButton;
private Link configureLink;
private boolean initialized = false; // Set true once field editors initialized
public AbstractProjectPreferencePage(String pluginId, String pageTitle) {
this.pluginId = pluginId;
if (pluginId != null) {
setPreferenceStore(new ScopedPreferenceStore(INSTANCE_SCOPE_INSTANCE, pluginId));
}
setDescription(pageTitle);
}
/**
* Adjust the layout of the field editors so that
* they are properly aligned.
*/
protected void adjustGridLayout() {
int numColumns = calcNumberOfColumns();
((GridLayout) fieldEditorParent.getLayout()).numColumns = numColumns;
for (IFieldEditor field : fields) {
field.adjustForNumColumns(numColumns);
}
}
/**
* Calculates the number of columns needed to host all field editors.
*
* @return the number of columns
*/
private int calcNumberOfColumns() {
int result = 0;
for (IFieldEditor field : fields) {
result = Math.max(result, field.getNumberOfControls());
}
return result;
}
/**
* Recomputes the page's error state by calling <code>isValid</code> for
* every field editor.
*/
protected void checkState() {
boolean valid = true;
invalidFieldEditor = null;
// The state can only be set to true if all
// field editors contain a valid value. So we must check them all
for (IFieldEditor field : fields) {
valid = valid && field.isValid();
if (!valid) {
invalidFieldEditor = field;
break;
}
}
setValid(valid);
}
protected abstract AbstractProjectPreferencePage createClonePage();
/**
* Insert the project-specific button and link on project-specific pages.
*/
@Override
protected Control createContents(Composite parent) {
if (project != null) {
Composite comp = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(2, false);
layout.marginHeight = 0;
layout.marginWidth = 0;
comp.setLayout(layout);
comp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
projectSpecificSettingsButton = new Button(comp, SWT.CHECK);
projectSpecificSettingsButton.setText(CommonUIMessages.EnableProjectSpecificSettings);
projectSpecificSettingsButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
updateFieldEditors();
}
});
projectSpecificSettingsButton.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
if (true) {
configureLink = createLink(comp, CommonUIMessages.ConfigureWorkspaceSettings);
configureLink.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false));
}
}
fieldEditorParent = new Composite(parent, SWT.NULL);
GridLayout layout = new GridLayout();
layout.numColumns = 1;
layout.marginHeight = 0;
layout.marginWidth = 0;
fieldEditorParent.setLayout(layout);
fieldEditorParent.setFont(parent.getFont());
createFieldEditors(fieldEditorParent);
adjustGridLayout();
initialize();
checkState();
return fieldEditorParent;
}
/**
* Creates the field editors. Field editors are abstractions of
* the common GUI blocks needed to manipulate various types
* of preferences. Each field editor knows how to save and
* restore itself.
*/
public void createFieldEditors(Composite fieldEditorParent) {
createFieldEditors(fieldEditorParent, fields);
}
protected abstract void createFieldEditors(Composite fieldEditorParent, List<IFieldEditor> fields);
private Link createLink(Composite composite, String text) {
Link link= new Link(composite, SWT.NONE);
link.setFont(composite.getFont());
link.setText("<A>" + text + "</A>"); //$NON-NLS-1$//$NON-NLS-2$
link.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
doLinkActivated((Link) e.widget);
}
public void widgetDefaultSelected(SelectionEvent e) {
doLinkActivated((Link) e.widget);
}
});
return link;
}
/**
* The field editor preference page implementation of an <code>IDialogPage</code>
* method disposes of this page's controls and images.
* Subclasses may override to release their own allocated SWT
* resources, but must call <code>super.dispose</code>.
*/
@Override
public void dispose() {
super.dispose();
for (IFieldEditor field : fields) {
field.setPage(null);
field.setPropertyChangeListener(null);
field.setPreferenceStore(null);
}
}
/**
* When the project-specific link is activated, install the project-specific property page.
*/
final void doLinkActivated(Link link) {
IPreferencePage page = createClonePage();
page.setTitle(getTitle());
final IPreferenceNode targetNode = new PreferenceNode(pluginId, page);
PreferenceManager manager = new PreferenceManager();
manager.addToRoot(targetNode);
final PreferenceDialog dialog = new PreferenceDialog(getControl().getShell(), manager);
BusyIndicator.showWhile(getControl().getDisplay(), new Runnable() {
public void run() {
dialog.create();
dialog.setMessage(targetNode.getLabelText());
dialog.open();
}
});
}
/**
* Return the object that owns the properties shown in this property page, which is
* a non-null IProject for a project Property page and null for a global preference page.
*/
public final IProject getElement() {
return project;
}
/**
* Returns the prevailing project or workspace preference store.
*/
@Override
public IPreferenceStore getPreferenceStore() {
if (project == null) {
return getWorkspaceStore();
}
if (!initialized) {
return projectStore;
}
if (!projectSpecificSettingsButton.getSelection()) {
return getWorkspaceStore();
}
return projectStore;
}
public IPreferenceStore getWorkspaceStore() {
return super.getPreferenceStore();
}
public void init(IWorkbench workbench) {
}
protected void initialize() {
if (project != null) {
IPreferenceStore workspaceStore = getWorkspaceStore();
IEclipsePreferences preferenceNode = new ProjectScope(project).getNode(pluginId);
boolean hasProjectSpecificSettings = false;
for (IFieldEditor field : fields) {
String preferenceName = field.getPreferenceName();
String prefValue = preferenceNode.get(preferenceName, null);
if (prefValue != null) {
hasProjectSpecificSettings = true;
}
else { // Missing preference has null-default to force write
projectStore.setValue(preferenceName, workspaceStore.getString(preferenceName));
}
}
projectSpecificSettingsButton.setSelection(hasProjectSpecificSettings);
}
// printPreferences("super.initialize");
// super.initialize();
for (IFieldEditor field : fields) {
field.setPage(this);
field.setPropertyChangeListener(this);
field.setPreferenceStore(getPreferenceStore());
field.load();
}
initialized = true;
if (project != null) {
updateFieldEditors();
}
}
/**
* Performing defaults reverts workspace settings to built-in defaults or
* project settings to workspace settings.
*/
@Override
protected void performDefaults() {
IPreferenceStore workspaceStore = getWorkspaceStore();
if (project == null) {
for (IFieldEditor field : fields) {
String preferenceName = field.getPreferenceName();
workspaceStore.setValue(preferenceName, workspaceStore.getDefaultString(preferenceName));
}
}
else {
for (IFieldEditor field : fields) {
String preferenceName = field.getPreferenceName();
projectStore.setValue(preferenceName, workspaceStore.getString(preferenceName));
}
}
for (IFieldEditor field : fields) {
field.loadDefault();
}
// Force a recalculation of my error state.
checkState();
super.performDefaults();
// printPreferences("super.performDefaults()");
}
/**
*
*/
@Override
public boolean performOk() {
if ((project != null) && !projectSpecificSettingsButton.getSelection()) {
for (IFieldEditor field : fields) {
String preferenceName = field.getPreferenceName();
projectStore.setValue(preferenceName, projectStore.getDefaultString(preferenceName));
field.loadDefault();
}
}
// printPreferences("pre super.performOk");
// boolean result = super.performOk();
for (IFieldEditor field : fields) {
field.store();
field.setPresentsDefaultValue(false);
}
// printPreferences("post super.performOk");
if (project != null) {
try {
if (projectStore.needsSaving()) {
projectStore.save();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return true;
}
/* public void printPreferences(String title) {
System.out.println("--------" + title + "--------");
IPreferenceStore workspaceStore = getWorkspaceStore();
List<String> names = new ArrayList<String>();
for (FieldEditor editor : editors) {
names.add(editor.getPreferenceName());
}
Collections.sort(names);
for (String preferenceName : names) {
String workValue = workspaceStore.getString(preferenceName);
String workDefaultValue = workspaceStore.getDefaultString(preferenceName);
if (projectStore == null) {
System.out.println(preferenceName + " " + workValue + "[" + workDefaultValue + "]");
}
else {
String projValue = projectStore.getString(preferenceName);
String projDefaultValue = projectStore.getDefaultString(preferenceName);
System.out.println(preferenceName + " " + projValue + "[" + projDefaultValue + "]" + " [" + workValue + "[" + workDefaultValue + "]]");
}
}
} */
/**
* The field editor preference page implementation of this <code>IPreferencePage</code>
* (and <code>IPropertyChangeListener</code>) method intercepts <code>IS_VALID</code>
* events but passes other events on to its superclass.
*/
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(FieldEditor.IS_VALID)) {
boolean newValue = ((Boolean) event.getNewValue()).booleanValue();
// If the new value is true then we must check all field editors.
// If it is false, then the page is invalid in any case.
if (newValue) {
checkState();
} else {
invalidFieldEditor = (IFieldEditor) event.getSource();
setValid(newValue);
}
}
}
/**
* Receives the object that owns the properties shown in this property page.
* @see org.eclipse.ui.IWorkbenchPropertyPage#setElement(org.eclipse.core.runtime.IAdaptable)
*/
public void setElement(IAdaptable element) {
IProject adapter = element.getAdapter(IProject.class);
this.project = adapter;
if (project != null) {
projectStore = new ScopedPreferenceStore(new ProjectScope(project), pluginId);
}
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if (visible && invalidFieldEditor != null) {
invalidFieldEditor.setFocus();
}
}
/*
* Enables or disables the editors and buttons depending on whether
* the project-specific settings are applicable.
*/
protected void updateFieldEditors() {
boolean enabled = projectSpecificSettingsButton.getSelection();
for (IFieldEditor field : fields) {
field.setEnabled(enabled, fieldEditorParent);
}
configureLink.setEnabled(enabled);
// printPreferences("updateFieldEditors");
}
}