/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* 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:
* Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
*******************************************************************************/
package org.eclipse.imp.preferences;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
/**
* A multi-tab preferences page for IMP-supported languages.
* The various tabs nominally represent the same sets of preferences
* as set on different levels (default, workspace configuration,
* workspace instance, and project).
*
* @author suttons@us.ibm.com
*/
public abstract class TabbedPreferencesPage extends PreferencePage implements IWorkbenchPreferencePage {
// To hold tabs created by specializations of this class;
// used below in methods that respond to buttons
PreferencesTab[] tabs = new PreferencesTab[4];
// To be provided by a language-specific preferences page
// that is specialized from this one
protected IPreferencesService prefService = null;
public TabbedPreferencesPage() {
this.noDefaultAndApplyButton();
}
protected Control createContents(Composite parent) {
// Create a tab folder to put onto the page
final TabFolder tabFolder = new TabFolder(parent, SWT.NONE);
final GridData gd= new GridData(SWT.FILL, SWT.CENTER, true, false);
gd.widthHint= 0;
gd.heightHint= SWT.DEFAULT;
gd.horizontalSpan= 1;
tabFolder.setLayoutData(gd);
// Create the tabs that go into the tab folder
tabs = createTabs(prefService, this, tabFolder);
setInitialStateForTabs();
// The validity of the page depends on the validity of its tabs,
// so refresh the valid state of the page now that all of the
// tabs have been created
notifyState(true);
// Set the font on the page
Dialog.applyDialogFont(parent);
// If there's more than 1 tab, start out by selecting the 2nd tab
// (usually the "Workspace" tab), rather than the "Project" tab,
// which doesn't even show values until the user selects a project.
if (tabs.length > 1) {
int idx= -1;
for(int i= 0; i < tabs.length; i++) {
if (tabs[i].getLevel().equals(IPreferencesService.INSTANCE_LEVEL)) {
idx= i;
}
}
if (idx > 0) {
tabFolder.setSelection(idx);
}
}
return tabFolder;
}
/**
* Assure that the enabled state of conditionally enabled
* tabs is set appropriately.
*
* When a tab is newly created with some fields that are supposed to
* be enabled depending on the value of other (boolean) fields, it
* seems that the enabled state of the conditional fields does not
* get set properly. This happens despite explicit attempts to set
* the appropriate enabled state (perhaps due to race conditions?).
* Toggling of the condition fields once the tab has been created
* does cause the enabled state of conditional fields to be set
* correctly. This method automates that activity, which can be
* emulated on the default tab by restoring and applying the default
* field values. However, changes on the default tab can affect
* lower levels in the preferences hierarchy--values inherited from
* the default level do not change, but the fact that the default
* values have been restored causes inherited values to be marked as
* modified. To clear the modified marks from lower levels of the
* preferences hierarchy, the tabs for those levels are also applied,
* in a top-down order. On the project level default values are
* restored before being applied; this assures that the fields are
* empty, as they should be, because there should be no project
* selected at this point.
*
*/
protected void setInitialStateForTabs() {
// Assure that all tabs are initialized properly
// and reflect an unmodified state
for (int i = 0; i < tabs.length; i++) {
if (tabs[i] instanceof DefaultPreferencesTab) {
tabs[i].performDefaults();
tabs[i].performApply();
break;
}
}
for (int i = 0; i < tabs.length; i++) {
if (tabs[i] instanceof ConfigurationPreferencesTab) {
tabs[i].performApply();
break;
}
}
for (int i = 0; i < tabs.length; i++) {
if (tabs[i] instanceof InstancePreferencesTab) {
tabs[i].performApply();
break;
}
}
for (int i = 0; i < tabs.length; i++) {
if (tabs[i] instanceof ProjectPreferencesTab) {
tabs[i].performDefaults();
tabs[i].performApply();
break;
}
}
}
/**
* Create the tabs that represent the different levels of preferences
* shown on this page. Nominally these are the default, workspace configuraiton,
* workspace instance, and project levels.
*
* @param prefService The service that manages the preferences by level
* @param page The page on which the tabs are to be created (that is, this page)
* @param tabFolder The tab folder, on this page, that will contain the created tabs
* @return An array containing the created tabs
*/
protected abstract PreferencesTab[] createTabs(
IPreferencesService prefService, TabbedPreferencesPage page, TabFolder tabFolder);
/**
*
*/
public boolean notifyState(boolean state) {
boolean allValid = true;
for (int i = 0; i < tabs.length && allValid; i++) {
allValid = allValid && tabs[i] != null && tabs[i].isValid();
}
setValid(allValid);
return allValid;
}
/*
* The following four operations provide a page-level response to the pressing of
* buttons on the page. Note, though, that a preference page may not have all of
* these buttons--buttons not present on the page may instead be present on individual
* tabs on the page.
*/
/**
* Respond to pressing of the Apply button by saving the prevailing preferences.
*
* Note: In a system of multiple preference levels with preference-value
* inheritance, this may only save values on the levels on which they
* are stored, i.e., not on levels where they apply through inheritance only.
*
* @see org.eclipse.jface.preference.PreferencePage#performApply()
*/
public void performApply()
{
for (int i = 0; i < tabs.length; i++) {
tabs[i].performApply();
}
}
/**
* Respond to pressing of Cancel button by cancelling in-progress
* preference updates on each level.
*
* @see org.eclipse.jface.preference.IPreferencePage#performCancel()
*/
public boolean performCancel()
{
// SMS 4 Dec 2006
// Previously just used to return true; now trying to
// allow for a negative return
boolean result = true;
for (int i = 0; i < tabs.length; i++) {
// If there was a problem initializing the tabs, one or more might be null now.
if (tabs[i] != null) {
result = result && tabs[i].performCancel();
}
}
return result;
}
/**
* Respond to pressing of Restore Defaults button by restoring default
* values on each level.
*
* Note: In a system of multiple preference levels with preference-value
* inheritance, the default value on levels other than the default level
* may be considered to be the level inherited from the next higher level,
* so this may entail removing the preferences stored on each level other
* than the default level. On the default level, the programmed default
* values should be restored.
*
* @see org.eclipse.jface.preference.PreferencePage#performDefaults()
*/
public void performDefaults()
{
// SMS 4 Dec 2006
// Need to check visibility of a composite that contains the tab;
// be sure that the right one is checked here ...
for (int i = 0; i < tabs.length; i++) {
if (tabs[i].getTabItem().getControl().isVisible())
tabs[i].performDefaults();
}
}
/**
* Respond to pressing of the Save button by saving the prevailing preferences.
*
* Note: In a system of multiple preference levels with preference-value
* inheritance, this may only save values on the levels on which they
* are stored, i.e., not on levels where they apply through inheritance only.
*
* @see org.eclipse.jface.preference.PreferencePage#performDefaults()
*/
public boolean performOk()
{
// SMS 4 Dec 2006
// Not sure of the effect of returning false, but
// should probably allow for that
boolean result = true;
for (int i = tabs.length-1; i >= 0; i--) {
result = result && tabs[i].performOk();
}
return result;
}
/**
* For IWorkbenchPreferencePage
*
* @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
*/
public void init(IWorkbench workbench) {
}
/**
* Get the tabs used on this preference page
*
* @return The tabs used on this preference page
*/
protected PreferencesTab[] getTabs() {
return tabs;
}
/**
* Should be overridden in language-specific tabbed preferences page
* to make use of language-specific preference initializer.
*
* @return The preference initializer to be used to initialize
* preferences in this tab
*/
// TODO: Probably should make this an abstract method
public PreferencesInitializer getPreferenceInitializer() {
// TODO: Override in subclass where the language-specific
// initializer should be known
System.out.println("TabbedPreferencesPage.getPreferenceInitializar(): unimplemented; should be overridden with language-specific implementation");
return null;
}
}