/*******************************************************************************
* 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 java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.imp.preferences.PreferencesUtilities.PreferenceChangeListener;
import org.eclipse.imp.preferences.fields.BooleanFieldEditor;
import org.eclipse.imp.preferences.fields.ColorFieldEditor;
import org.eclipse.imp.preferences.fields.ComboFieldEditor;
import org.eclipse.imp.preferences.fields.FontFieldEditor;
import org.eclipse.imp.preferences.fields.RadioGroupFieldEditor;
import org.eclipse.imp.preferences.fields.StringFieldEditor;
import org.eclipse.swt.SWT;
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.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.ui.dialogs.ContainerSelectionDialog;
import org.osgi.service.prefs.Preferences;
public abstract class ProjectPreferencesTab extends PreferencesTab {
protected List<Link> detailsLinks = new ArrayList<Link>();
protected IProject fProject = null;
protected Combo selectedProjectCombo;
public ProjectPreferencesTab(IPreferencesService prefService, boolean noDetails) {
super(IPreferencesService.PROJECT_LEVEL, noDetails);
this.fPrefService = prefService;
fPrefUtils = new PreferencesUtilities(prefService);
}
@Override
public Composite createTabContents(TabbedPreferencesPage page, final TabFolder tabFolder) {
fPrefPage = page;
int numColumns= getNoDetails() ? 1 : 2;
GridLayout layout = null;
final Composite composite= new Composite(tabFolder, SWT.NONE);
composite.setFont(tabFolder.getFont());
final GridData gd= new GridData(SWT.FILL, SWT.CENTER, true, false);
gd.widthHint= 0;
gd.heightHint= SWT.DEFAULT;
gd.horizontalSpan= 1;
composite.setLayoutData(gd);
layout = new GridLayout();
layout.numColumns = numColumns;
composite.setLayout(layout);
// The "tab" on the tab folder
fTabItem = new TabItem(tabFolder, SWT.NONE);
fTabItem.setText("Project");
fTabItem.setControl(composite);
PreferencesTab.TabSelectionListener listener =
new PreferencesTab.TabSelectionListener(fPrefPage, fTabItem);
tabFolder.addSelectionListener(listener);
/*
* Add the elements relating to preferences fields and their associated "details" links.
*/
fFields = createFields(page, composite);
// Clear some space
PreferencesUtilities.fillGridPlace(composite, numColumns);
// Disable the details links since no project is selected at the start
for (int i = 0; i < detailsLinks.size(); i++) {
((Link)detailsLinks.get(i)).setEnabled(false);
}
PreferencesUtilities.fillGridPlace(composite, 2);
// Being newly loaded, the fields may be displayed with some
// indication that they have been modified. This should reset
// that marking.
clearModifiedMarksOnLabels();
/*
* Put in the elements related to selecting a project
*/
Group groupHolder = new Group(composite, SWT.SHADOW_ETCHED_IN);
groupHolder.setText("Project selection");
groupHolder.setLayout(new GridLayout(2, false));
groupHolder.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Label projLabel= new Label(groupHolder, SWT.LEFT);
projLabel.setText("Project:");
selectedProjectCombo= new Combo(groupHolder, SWT.DROP_DOWN | SWT.READ_ONLY);
final Combo projCombo= selectedProjectCombo;
IProject[] pluginProjects= ResourcesPlugin.getWorkspace().getRoot().getProjects();
projCombo.add("none selected");
for(int i= 0; i < pluginProjects.length; i++) {
projCombo.add(pluginProjects[i].getName());
}
projCombo.addSelectionListener(new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent e) { }
public void widgetSelected(SelectionEvent e) {
String projName= projCombo.getText();
ProjectPreferencesTab.this.fPrefService.setProjectName(projName);
}
});
projCombo.setSize(150, 24);
Label pad = new Label(groupHolder, SWT.NONE);
// Clear these here in case there are any saved from a previous interaction with the page
// (assuming that we should start each new page with no project selected)
fPrefService.clearPreferencesAtLevel(IPreferencesService.PROJECT_LEVEL);
addProjectSelectionListener(composite);
PreferencesUtilities.fillGridPlace(composite, 3);
/*
* Put explanatory notes toward the bottom
* (not sure whether WRAP is helpful here; can manually
* wrap text in labels with '\n')
*/
final Composite bottom = new Composite(composite, SWT.BOTTOM | SWT.WRAP);
layout = new GridLayout();
bottom.setLayout(layout);
bottom.setLayoutData(new GridData(SWT.BOTTOM));
Label bar = new Label(bottom, SWT.WRAP);
GridData data = new GridData();
data.verticalAlignment = SWT.WRAP;
bar.setLayoutData(data);
bar.setText("Preferences are shown here only when a project is selected.\n\n" +
"Preferences shown with a white background are set on this level.\n\n" +
"Preferences shown with a colored background are inherited from a higher level.\n\n" +
Markings.MODIFIED_NOTE + "\n\n" +
Markings.TAB_ERROR_NOTE);
PreferencesUtilities.fillGridPlace(bottom, 1);
/*
* Put Restore Defaults and Apply buttons at the very bottom,
* disabled if (as expected) there is no project selected and
* the tab is otherwise mainly disabled
*/
fButtons = fPrefUtils.createDefaultAndApplyButtons(composite, this);
if (fPrefService.getProject() == null) {
for (int i = 0; i < fButtons.length; i++)
fButtons[i].setEnabled(false);
}
return composite;
}
private void addProjectSelectionListener(Composite composite)
{
fPrefService.addProjectSelectionListener(new ProjectSelectionListener(composite));
}
private class ProjectSelectionListener implements PreferencesService.IProjectSelectionListener
{
Composite composite = null;
IEclipsePreferences.IPreferenceChangeListener currentListener = null;
ProjectSelectionListener(Composite composite) {
this.composite = composite;
}
/**
* Notification that a project was selected for inclusion in the preferences hierarchy.
* The given event must not be <code>null</code>.
*
* @param event an event specifying the details about the new node
* @see IEclipsePreferences.NodeChangeEvent
* @see IEclipsePreferences#addNodeChangeListener(IEclipsePreferences.INodeChangeListener)
* @see IEclipsePreferences#removeNodeChangeListener(IEclipsePreferences.INodeChangeListener)
*/
public void selection(IPreferencesService.ProjectSelectionEvent event) {
addressProjectSelection(event, composite);
}
}
protected List<PreferenceChangeListener> currentListeners = new ArrayList<PreferenceChangeListener>();
protected List<IEclipsePreferences> currentListenerNodes = new ArrayList<IEclipsePreferences>();
protected void addressProjectSelection(IPreferencesService.ProjectSelectionEvent event, Composite composite) {
// TODO: Override in subtype with a real implementation
// System.err.println("ProjectPreferencesTab.addressProjectSelection(..): unimplemented");
// SMS 20 Jun 2007
// Adding code from JikesPG project tab implementation to try
// to flesh out a skeleton (field-independent) implementation)
// just to see what there may be of that
boolean haveCurrentListeners = false;
Preferences oldeNode = event.getPrevious();
Preferences newNode = event.getNew();
if (oldeNode == null && newNode == null) {
// This is what happens for some reason when you clear the project selection.
// Nothing, really, to do in this case ...
return;
}
// If oldeNode is not null, we want to remove any preference-change listeners from it
if (oldeNode != null && oldeNode instanceof IEclipsePreferences && haveCurrentListeners) {
removeProjectPreferenceChangeListeners();
haveCurrentListeners = false;
} else {
// Print an advisory message if you want to
}
// If we have a new project preferences node, then do various things
// to set up the project's preferences
if (newNode != null && newNode instanceof IEclipsePreferences) {
// If the containing composite is not disposed, then set the fields' values
// and make them enabled and editable (as appropriate to the type of field)
if (!composite.isDisposed()) {
// Note: Where there are toggles between fields, it is a good idea to set the
// properties of the dependent field here according to the values they should have
// based on the independent field. There should be listeners to take care of
// that sort of adjustment once the tab is established, but when properties are
// first initialized here, the properties may not always be set correctly through
// the toggle. I'm not entirely sure why that happens, except that there may be
// a race condition between the setting of the dependent values by the listener
// and the setting of those values here. If the values are set by the listener
// first (which might be surprising, but may be possible) then they will be
// overwritten by values set here--so the values set here should be consistent
// with what the listener would set.
// Used in setting enabled and editable status
// boolean enabledState = false;
// Example:
// // Pretend field1 is a boolean field (checkbox)
// prefUtils.setField(field1, field1.getHolder());
// field1.getChangeControl().setEnabled(true);
//
// // Pretend field2 is a text-based field
// prefUtils.setField(field2, field2.getHolder());
// // field2 enabled iff field1 not checked
// enabledState = !field1.getBooleanValue();
// field2.getTextControl().setEditable(enabledState);
// field2.getTextControl().setEnabled(enabledState);
// field2.setEnabled(enabledState, field2.getParent());
// And so on for other fields
clearModifiedMarksOnLabels();
}
// Add property change listeners
// Example
// if (field1.getHolder() != null) addProjectPreferenceChangeListeners(field1, PreferenceConstants.P_FIELD_1, field1.getHolder());
// if (field2.getHolder() != null) addProjectPreferenceChangeListeners(fieldw, PreferenceConstants.P_FIELD_2, field2.getHolder());
// And so on for other fields ...
haveCurrentListeners = true;
}
// Or if we don't have a new project preferences node ...
if (newNode == null || !(newNode instanceof IEclipsePreferences)) {
// May happen when the preferences page is first brought up, or
// if we allow the project to be deselected
// Clear the preferences from the store
fPrefService.clearPreferencesAtLevel(IPreferencesService.PROJECT_LEVEL);
// Disable fields and make them non-editable
if (!composite.isDisposed()) {
// Example:
// field1.getChangeControl().setEnabled(false);
//
// field2.getTextControl(field2.getHolder()).setEnabled(false);
// field2.getTextControl(field2.getHolder()).setEditable(false);
}
// Remove listeners
removeProjectPreferenceChangeListeners();
haveCurrentListeners = false;
}
}
protected void addProjectPreferenceChangeListeners(BooleanFieldEditor field, String key, Composite composite)
{
IEclipsePreferences[] nodes = fPrefService.getNodesForLevels();
for (int i = IPreferencesService.PROJECT_INDEX; i < nodes.length; i++) {
if (nodes[i] != null) {
PreferencesUtilities.BooleanPreferenceChangeListener listener =
fPrefUtils.new BooleanPreferenceChangeListener(field, key, composite);
nodes[i].addPreferenceChangeListener(listener);
currentListeners.add(listener);
currentListenerNodes.add(nodes[i]);
} else {
//System.err.println("ProjectPreferencesTab.addPropertyChangeListeners(..): no listener added at level = " + i + "; node at that level is null");
}
}
}
protected void addProjectPreferenceChangeListeners(FontFieldEditor field, String key, Composite composite)
{
IEclipsePreferences[] nodes = fPrefService.getNodesForLevels();
for (int i = IPreferencesService.PROJECT_INDEX; i < nodes.length; i++) {
if (nodes[i] != null) {
PreferencesUtilities.FontPreferenceChangeListener listener =
fPrefUtils.new FontPreferenceChangeListener(field, key, composite);
nodes[i].addPreferenceChangeListener(listener);
currentListeners.add(listener);
currentListenerNodes.add(nodes[i]);
} else {
//System.err.println("ProjectPreferencesTab.addPropertyChangeListeners(..): no listener added at level = " + i + "; node at that level is null");
}
}
}
protected void addProjectPreferenceChangeListeners(ColorFieldEditor field, String key, Composite composite)
{
IEclipsePreferences[] nodes = fPrefService.getNodesForLevels();
for (int i = IPreferencesService.PROJECT_INDEX; i < nodes.length; i++) {
if (nodes[i] != null) {
PreferencesUtilities.ColorPreferenceChangeListener listener =
fPrefUtils.new ColorPreferenceChangeListener(field, key, composite);
nodes[i].addPreferenceChangeListener(listener);
currentListeners.add(listener);
currentListenerNodes.add(nodes[i]);
} else {
//System.err.println("ProjectPreferencesTab.addPropertyChangeListeners(..): no listener added at level = " + i + "; node at that level is null");
}
}
}
protected void addProjectPreferenceChangeListeners(ComboFieldEditor field, String key, Composite composite)
{
IEclipsePreferences[] nodes = fPrefService.getNodesForLevels();
for (int i = IPreferencesService.PROJECT_INDEX; i < nodes.length; i++) {
if (nodes[i] != null) {
PreferencesUtilities.ComboPreferenceChangeListener listener =
fPrefUtils.new ComboPreferenceChangeListener(field, key, composite);
nodes[i].addPreferenceChangeListener(listener);
currentListeners.add(listener);
currentListenerNodes.add(nodes[i]);
} else {
//System.err.println("ProjectPreferencesTab.addPropertyChangeListeners(..): no listener added at level = " + i + "; node at that level is null");
}
}
}
protected void addProjectPreferenceChangeListeners(RadioGroupFieldEditor field, String key, Composite composite)
{
IEclipsePreferences[] nodes = fPrefService.getNodesForLevels();
for (int i = IPreferencesService.PROJECT_INDEX; i < nodes.length; i++) {
if (nodes[i] != null) {
PreferencesUtilities.RadioGroupPreferenceChangeListener listener =
fPrefUtils.new RadioGroupPreferenceChangeListener(field, key, composite);
nodes[i].addPreferenceChangeListener(listener);
currentListeners.add(listener);
currentListenerNodes.add(nodes[i]);
} else {
//System.err.println("ProjectPreferencesTab.addPropertyChangeListeners(..): no listener added at level = " + i + "; node at that level is null");
}
}
}
protected void addProjectPreferenceChangeListeners(StringFieldEditor field, String key, Composite composite)
{
IEclipsePreferences[] nodes = fPrefService.getNodesForLevels();
for (int i = IPreferencesService.PROJECT_INDEX; i < nodes.length; i++) {
if (nodes[i] != null) {
// SMS 31 Oct 2006
//ProjectPreferenceChangeListener listener = new ProjectPreferenceChangeListener(field, key, composite);
PreferencesUtilities.StringPreferenceChangeListener listener =
fPrefUtils.new StringPreferenceChangeListener(field, key, composite);
nodes[i].addPreferenceChangeListener(listener);
currentListeners.add(listener);
currentListenerNodes.add(nodes[i]);
} else {
//System.err.println("ProjectPreferencesTab.addPropertyChangeListeners(..): no listener added at level = " + i + "; node at that level is null");
}
}
}
protected void removeProjectPreferenceChangeListeners()
{
// Remove all listeners from their respective nodes
for (int i = 0; i < currentListeners.size(); i++) {
((IEclipsePreferences) currentListenerNodes.get(i)).removePreferenceChangeListener(
((IEclipsePreferences.IPreferenceChangeListener)currentListeners.get(i)));
}
// Clear the lists
currentListeners = new ArrayList<PreferenceChangeListener>();
currentListenerNodes = new ArrayList<IEclipsePreferences>();
}
// SMS 20 Nov 2007:
protected void setProjectSelectionValidator(
ContainerSelectionDialog dialog, boolean validateForPluginProject, boolean validateForIDEProject)
{
// dialog.setValidator(new ValidationUtils.ProjectSelectionValidator());
}
public void performApply()
{
if (fPrefService.getProject() == null) {
// No preferences node into which to store anything
clearModifiedMarksOnLabels(); // just in case fields still show modified
return;
}
for (int i = 0; i < fFields.length; i++) {
fFields[i].store();
fFields[i].clearModifiedMarkOnLabel();
}
}
public boolean performCancel() {
// Nullify the project in any case
fPrefService.setProject(null);
return true;
}
public void performDefaults() {
if (fPrefService.getProject() == null) {
// If no project set then there's no preferences
// file from which to load anything
return;
}
// Clear all preferences for this page at this level;
// "default" values will be set by inheritance from a higher level
PreferencesInitializer initializer = fPrefPage.getPreferenceInitializer();
initializer.clearPreferencesOnLevel(IPreferencesService.PROJECT_LEVEL);
for (int i = 0; i < fFields.length; i++) {
fFields[i].loadWithInheritance();
}
}
public boolean performOk()
{
if (fPrefService.getProject() != null) {
// Store each field
for (int i = 0; i < fFields.length; i++) {
fFields[i].store();
}
} else {
// Clear preferences because we're closing up dialog;
// note that a project preferences node will exist, if only
// in a leftover state, even when no project is selected
fPrefService.clearPreferencesAtLevel(IPreferencesService.PROJECT_LEVEL);
//return true;
}
// Nullify the project in any case
fPrefService.setProject(null);
return true;
}
}