/******************************************************************************* * Copyright (c) 2008, 2012 itemis AG (http://www.itemis.eu) 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: * itemis AG - initial API and implementation * Cloudsmith Inc - changes to store 'use project settings' in project * *******************************************************************************/ package org.cloudsmith.geppetto.pp.dsl.ui.preferences.editors; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.preferences.ConfigurationScope; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jface.preference.FieldEditor; import org.eclipse.jface.preference.FieldEditorPreferencePage; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; 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.Label; 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.dialogs.PreferencesUtil; import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess; import com.google.inject.Inject; import com.google.inject.name.Named; /** * This is a reworked implementation the class with the same name in org.eclipse.xtext.ui.editor.preferences. * This implementation works with a {@link ProjectAwareScopedPreferenceStore} as oppoes to the * ScopedPreferenceStore used by Xtext < 2.3, and FixedScopedPreferenceStore used in Xtext > 2.3. * * <p> * The following changes have been made: * <ul> * <li>'useProjectSettings' is now stored in the project preference store as opposed to the project meta-data stored in the workspace. This is done so * that import of a project automatically gets the intended 'useProjectSettings'.</li> * </ul> */ public abstract class AbstractPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage, IWorkbenchPropertyPage { private static final Logger log = Logger.getLogger(AbstractPreferencePage.class); private static final String USE_PROJECT_SETTINGS = "useProjectSettings"; //$NON-NLS-1$ private IWorkbench workbench; private IProject project; private Button useProjectSettingsButton; private final List<FieldEditor> editors = new ArrayList<FieldEditor>(); private Link link; @Inject private IPreferenceStoreAccess preferenceStoreAccess; @Inject @Named("languageName") private String languageName; public AbstractPreferencePage() { super(GRID); } @Override protected void addField(FieldEditor editor) { editors.add(editor); super.addField(editor); } @Override protected Control createContents(Composite parent) { if(isPropertyPage()) createUseProjectSettingsControls(parent); return super.createContents(parent); } @Override public void createControl(Composite parent) { super.createControl(parent); if(isPropertyPage()) handleUseProjectSettings(); } private void createUseProjectSettingsControls(Composite parent) { Composite projectSettingsParent = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(2, false); layout.marginHeight = 0; layout.marginWidth = 0; projectSettingsParent.setLayout(layout); projectSettingsParent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); // use project settings button useProjectSettingsButton = new Button(projectSettingsParent, SWT.CHECK); useProjectSettingsButton.setText("Enable project specific settings"); useProjectSettingsButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { handleUseProjectSettings(); log.debug("AbstractPreferencePage.widgetSelected()"); //$NON-NLS-1$ } }); // configure ws settings link link = new Link(projectSettingsParent, SWT.NONE); link.setFont(projectSettingsParent.getFont()); link.setText("<A>" + "Configure Workspace Settings..." + "</A>"); //$NON-NLS-1$ link.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { String id = qualifiedName(); PreferencesUtil.createPreferenceDialogOn(getShell(), id, new String[] { id }, null).open(); updateFieldEditors(false); } }); link.setLayoutData(new GridData(SWT.END, SWT.CENTER, true, false)); // separator line Label horizontalLine = new Label(projectSettingsParent, SWT.SEPARATOR | SWT.HORIZONTAL); horizontalLine.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false, 2, 1)); horizontalLine.setFont(projectSettingsParent.getFont()); useProjectSettingsButton.setSelection(getUseProjectSettings()); } private IProject currentProject() { if(project == null) throw new IllegalStateException("Not a property page case, but current project was requested."); //$NON-NLS-1$ return project; } @Override protected IPreferenceStore doGetPreferenceStore() { if(isPropertyPage()) { return preferenceStoreAccess.getWritablePreferenceStore(currentProject()); } return preferenceStoreAccess.getWritablePreferenceStore(); } public IAdaptable getElement() { return project; } protected String getLanguageName() { return this.languageName; } /** * Gets the 'useProjectSettings' flag in the project preferences. * * @return true if the settings on this page are project specific */ private Boolean getUseProjectSettings() { return Boolean.valueOf(getPreferenceStore().getBoolean(getUseProjectSettingsName())); } /** * Produces the preference key to use for the 'use project settings' flag when this preference page * is is used as a properties page. * * @return the concatenation of {@link #qualifiedName()}, "." and {@link #USE_PROJECT_SETTINGS}. */ protected String getUseProjectSettingsName() { return qualifiedName() + "." + USE_PROJECT_SETTINGS; } protected IWorkbench getWorkbench() { return workbench; } /** * Switches the search scope of the preference store to use [Project, Instance, Configuration] if * values are project specific, and [Instance, Configuration] otherwise. This implementation requires that the * given preference store is based on the Project preference store when the page is used as * a Properties page. (This is done in {@link #doGetPreferenceStore()}). */ @SuppressWarnings("deprecation") private void handleUseProjectSettings() { // Note: uses the pre Eclipse 3.6 way of specifying search scopes (deprecated since 3.6) boolean isUseProjectSettings = useProjectSettingsButton.getSelection(); link.setEnabled(!isUseProjectSettings); if(!isUseProjectSettings) { setSearchContexts( getPreferenceStore(), new IScopeContext[] { new InstanceScope(), new ConfigurationScope() }); } else { // copy instance values to project specific values setSearchContexts(getPreferenceStore(), new IScopeContext[] { new ProjectScope(currentProject()), new InstanceScope(), new ConfigurationScope() }); setProjectSpecificValues(); } updateFieldEditors(isUseProjectSettings); } public void init(IWorkbench workbench) { this.workbench = workbench; } public boolean isPropertyPage() { return project != null; } @Override public boolean performOk() { boolean retVal = super.performOk(); if(retVal && isPropertyPage()) { try { saveUseProjectSettings(useProjectSettingsButton.getSelection()); } catch(Exception e) { log.error("Error", e); //$NON-NLS-1$ retVal = false; } } return retVal; } /** * @return prefix for preference keys */ protected String qualifiedName() { return languageName; } /** * Saves the 'use project settings' as a preference in the store using * the {@link #getUseProjectSettingsName()} as the key. * If not project specific, all affected keys are removed from the project preferences. */ private void saveUseProjectSettings(boolean isProjectSpecific) throws Exception { final ProjectAwareScopedPreferenceStore store = (ProjectAwareScopedPreferenceStore) getPreferenceStore(); if(!isProjectSpecific) { // remove all the keys (written by various field editors) IEclipsePreferences storePreferences = store.getStorePreferences(); for(FieldEditor field : editors) { storePreferences.remove(field.getPreferenceName()); } // Also remove the master key (i.e. use default/default == false when later reading). storePreferences.remove(getUseProjectSettingsName()); } else { store.setValue(getUseProjectSettingsName(), Boolean.toString(isProjectSpecific)); } store.save(); } public void setElement(IAdaptable element) { this.project = (IProject) element.getAdapter(IProject.class); } /** * Copies all the project specific values (except master key) to the values of the instance preference * store. */ private void setProjectSpecificValues() { ProjectAwareScopedPreferenceStore store = (ProjectAwareScopedPreferenceStore) getPreferenceStore(); IEclipsePreferences storePreferences = store.getStorePreferences(); for(FieldEditor field : editors) { String key = field.getPreferenceName(); storePreferences.put(key, store.getString(key)); } } /** * Sets the given search scopes in the preference store. * This implementation requires use of {@link ProjectAwareScopedPreferenceStore} - see {@link PPPreferenceStoreAccess} and its binding in the UI * module. * * @param store * @param scopes */ private void setSearchContexts(IPreferenceStore store, IScopeContext[] scopes) { // Note: Eclipse default uses ScopedPreferenceStore, Xtext >= 2.3 uses FixedScopedPreferenceStore ((ProjectAwareScopedPreferenceStore) store).setSearchContexts(scopes); } /** * Loads values of all field editors using current search scopes in the preference store. * Also updates fields enabled status. (The effect is that fields show project specific values * when enabled, and instance scoped/default values when disabled). * * @param enabled */ protected void updateFieldEditors(boolean enabled) { Composite parent = getFieldEditorParent(); for(FieldEditor editor : editors) { editor.load(); editor.setEnabled(enabled, parent); } getDefaultsButton().setEnabled(enabled); } }