/******************************************************************************* * Copyright (c) 2011, 2015 Willink Transformations 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: * 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"); } }