org.eclipse.jface.preference.PreferencePage Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Sebastian Davids - Fix for bug 38729 - [Preferences]
* NPE PreferencePage isValid.
*******************************************************************************/
package org.eclipse.jface.preference;
import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogPage;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Point;
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.Event;
import org.eclipse.swt.widgets.Label;
/**
* Abstract base implementation for all preference page implementations.
*
* Subclasses must implement the createContents
framework
* method to supply the page's main control.
*
*
* Subclasses should extend the doComputeSize
framework
* method to compute the size of the page's control.
*
*
* Subclasses may override the performOk
, performApply
,
* performDefaults
, performCancel
, and performHelp
* framework methods to react to the standard button events.
*
*
* Subclasses may call the noDefaultAndApplyButton
framework
* method before the page's control has been created to suppress
* the standard Apply and Defaults buttons.
*
*/
public abstract class PreferencePage extends DialogPage implements
IPreferencePage {
/**
* Preference store, or null
.
*/
private IPreferenceStore preferenceStore;
/**
* Valid state for this page; true
by default.
*
* @see #isValid
*/
private boolean isValid = true;
/**
* Body of page.
*/
private Control body;
/**
* Whether this page has the standard Apply button; true
by
* default.
*
* @see #noDefaultAndApplyButton
*/
private boolean createApplyButton = true;
/**
* Whether this page has the standard Default button; true
by
* default.
*
* @see #noDefaultButton
*/
private boolean createDefaultButton = true;
/**
* Standard Defaults button, or null
if none.
* This button has id DEFAULTS_ID
.
*/
private Button defaultsButton = null;
/**
* The container this preference page belongs to; null
* if none.
*/
private IPreferencePageContainer container = null;
/**
* Standard Apply button, or null
if none.
* This button has id APPLY_ID
.
*/
private Button applyButton = null;
/**
* Description label.
*
* @see #createDescriptionLabel(Composite)
*/
private Label descriptionLabel;
/**
* Caches size of page.
*/
private Point size = null;
/**
* Creates a new preference page with an empty title and no image.
*/
protected PreferencePage() {
this(""); //$NON-NLS-1$
}
/**
* Creates a new preference page with the given title and no image.
*
* @param title the title of this preference page
*/
protected PreferencePage(String title) {
super(title);
}
/**
* Creates a new abstract preference page with the given title and image.
*
* @param title the title of this preference page
* @param image the image for this preference page,
* or null
if none
*/
protected PreferencePage(String title, ImageDescriptor image) {
super(title, image);
}
/**
* Computes the size for this page's UI control.
*
* The default implementation of this IPreferencePage
* method returns the size set by setSize
; if no size
* has been set, but the page has a UI control, the framework
* method doComputeSize
is called to compute the size.
*
*
* @return the size of the preference page encoded as
* new Point(width,height)
, or
* (0,0)
if the page doesn't currently have any UI component
*/
@Override
public Point computeSize() {
if (size != null) {
return size;
}
Control control = getControl();
if (control != null) {
size = doComputeSize();
return size;
}
return new Point(0, 0);
}
/**
* Contributes additional buttons to the given composite.
*
* The default implementation of this framework hook method does nothing.
* Subclasses should override this method to contribute buttons to this page's
* button bar. For each button a subclass contributes, it must also increase the
* parent's grid layout number of columns by one; that is,
*
*
*
* ((GridLayout) parent.getLayout()).numColumns++);
*
*
* @param parent the button bar
*/
protected void contributeButtons(Composite parent) {
}
/**
* Creates and returns the SWT control for the customized body
* of this preference page under the given parent composite.
*
* This framework method must be implemented by concrete subclasses. Any
* subclass returning a Composite
object whose Layout
* has default margins (for example, a GridLayout
) is expected to
* set the margins of this Layout
to 0 pixels.
*
*
* @param parent the parent composite
* @return the new control
*/
protected abstract Control createContents(Composite parent);
/**
* The PreferencePage
implementation of this
* IDialogPage
method creates a description label
* and button bar for the page. It calls createContents
* to create the custom contents of the page.
*
* If a subclass that overrides this method creates a Composite
* that has a layout with default margins (for example, a GridLayout
)
* it is expected to set the margins of this Layout
to 0 pixels.
* @see IDialogPage#createControl(Composite)
*/
@Override
public void createControl(Composite parent){
GridData gd;
Composite content = new Composite(parent, SWT.NONE);
setControl(content);
GridLayout layout = new GridLayout();
layout.marginWidth = 0;
layout.marginHeight = 0;
content.setLayout(layout);
//Apply the font on creation for backward compatibility
applyDialogFont(content);
// initialize the dialog units
initializeDialogUnits(content);
descriptionLabel = createDescriptionLabel(content);
if (descriptionLabel != null) {
descriptionLabel.setLayoutData(new GridData(
GridData.FILL_HORIZONTAL));
}
body = createContents(content);
if (body != null) {
// null is not a valid return value but support graceful failure
body.setLayoutData(new GridData(GridData.FILL_BOTH));
}
Composite buttonBar = new Composite(content, SWT.NONE);
layout = new GridLayout();
layout.numColumns = 0;
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.makeColumnsEqualWidth = false;
buttonBar.setLayout(layout);
gd = new GridData(GridData.HORIZONTAL_ALIGN_END);
buttonBar.setLayoutData(gd);
contributeButtons(buttonBar);
if (createApplyButton || createDefaultButton) {
layout.numColumns += 1 + (createApplyButton && createDefaultButton ? 1 : 0);
int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
if (createDefaultButton) {
String label = JFaceResources.getString("defaults"); //$NON-NLS-1$
defaultsButton = new Button(buttonBar, SWT.PUSH);
defaultsButton.setText(label);
Dialog.applyDialogFont(defaultsButton);
Point minButtonSize = defaultsButton.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
data.widthHint = Math.max(widthHint, minButtonSize.x);
defaultsButton.setLayoutData(data);
defaultsButton.addSelectionListener(widgetSelectedAdapter(e -> performDefaults()));
}
if (createApplyButton) {
String label = JFaceResources.getString("apply"); //$NON-NLS-1$
applyButton = new Button(buttonBar, SWT.PUSH);
applyButton.setText(label);
Dialog.applyDialogFont(applyButton);
Point minButtonSize = applyButton.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
data.widthHint = Math.max(widthHint, minButtonSize.x);
applyButton.setLayoutData(data);
applyButton.addSelectionListener(widgetSelectedAdapter(e -> performApply()));
applyButton.setEnabled(isValid());
}
applyDialogFont(buttonBar);
} else /* Check if there are any other buttons on the button bar.
* If not, throw away the button bar composite. Otherwise
* there is an unusually large button bar.
*/
if (buttonBar.getChildren().length < 1) {
buttonBar.dispose();
}
}
/**
* Apply the dialog font to the composite and it's children if it is set.
* Subclasses may override if they wish to set the font themselves.
*
* @param composite the composite to apply the font to. Font will also be
* applied to its children. If the control is null
* nothing happens.
*/
protected void applyDialogFont(Composite composite) {
Dialog.applyDialogFont(composite);
}
/**
* Creates and returns an SWT label under the given composite.
*
* @param parent the parent composite
* @return the new label
*/
protected Label createDescriptionLabel(Composite parent) {
Label result = null;
String description = getDescription();
if (description != null) {
result = new Label(parent, SWT.WRAP);
result.setFont(parent.getFont());
result.setText(description);
}
return result;
}
/**
* Computes the size needed by this page's UI control.
*
* All pages should override this method and set the appropriate sizes
* of their widgets, and then call super.doComputeSize
.
*
*
* @return the size of the preference page encoded as
* new Point(width,height)
*/
protected Point doComputeSize() {
if (descriptionLabel != null && body != null) {
Point bodySize = body.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
GridData gd = (GridData) descriptionLabel.getLayoutData();
gd.widthHint = bodySize.x;
}
return getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
}
/**
* Returns the preference store of this preference page.
*
* This is a framework hook method for subclasses to return a
* page-specific preference store. The default implementation
* returns null
.
*
*
* @return the preference store, or null
if none
*/
protected IPreferenceStore doGetPreferenceStore() {
return null;
}
/**
* Returns the container of this page.
*
* @return the preference page container, or null
if this
* page has yet to be added to a container
*/
public IPreferencePageContainer getContainer() {
return container;
}
/**
* Returns the preference store of this preference page.
*
* @return the preference store , or null
if none
*/
public IPreferenceStore getPreferenceStore() {
if (preferenceStore == null) {
preferenceStore = doGetPreferenceStore();
}
if (preferenceStore != null) {
return preferenceStore;
} else if (container != null) {
return container.getPreferenceStore();
}
return null;
}
/**
* The preference page implementation of an IPreferencePage
* method returns whether this preference page is valid. Preference
* pages are considered valid by default; call setValid(false)
* to make a page invalid.
* @see IPreferencePage#isValid()
*/
@Override
public boolean isValid() {
return isValid;
}
/**
* Suppresses creation of the standard Default and Apply buttons
* for this page.
*
* Subclasses wishing a preference page without these buttons
* should call this framework method before the page's control
* has been created.
*
*/
protected void noDefaultAndApplyButton() {
createApplyButton = false;
createDefaultButton = false;
}
/**
* Suppress creation of the standard Default button for this page.
*
* Subclasses wishing a preference page with this button should call this
* framework method before the page's control has been created.
*
*
* @since 3.11
*/
protected void noDefaultButton() {
createDefaultButton = false;
}
/**
* The PreferencePage
implementation of this
* IPreferencePage
method returns true
if the page
* is valid.
*
* @see IPreferencePage#okToLeave()
*/
@Override
public boolean okToLeave() {
return isValid();
}
/**
* Performs special processing when this page's Apply button has been pressed.
*
* This is a framework hook method for subclasses to do special things when
* the Apply button has been pressed.
* The default implementation of this framework method simply calls
* performOk
to simulate the pressing of the page's OK button.
*
*
* @see #performOk()
* @see #performCancel()
*/
protected void performApply() {
performOk();
}
/**
* The preference page implementation of an IPreferencePage
method
* performs special processing when this page's Cancel button has been pressed.
*
* This is a framework hook method for subclasses to do special things when the
* Cancel button has been pressed. The default implementation of this framework
* method does nothing and returns true
.
*
*
* Note that UI guidelines on different platforms disagree on whether Cancel
* should revert changes that have been applied with the Apply button. Windows
* wants applied changes to persist on Cancel, whereas Mac
* and GTK
* consider Apply a preview that should not be saved on Cancel. Eclipse
* applications typically adhere to the Windows guidelines and just override
* {@link #performOk()} and save preferences there.
*
*
* @see IPreferencePage#performCancel()
*/
@Override
public boolean performCancel() {
return true;
}
/**
* Performs special processing when this page's Defaults button has been pressed.
*
* This is a framework hook method for subclasses to do special things when
* the Defaults button has been pressed.
* Subclasses may override, but should call super.performDefaults
.
*
*/
protected void performDefaults() {
updateApplyButton();
}
@Override
public boolean performOk() {
return true;
}
@Override
public void setContainer(IPreferencePageContainer container) {
this.container = container;
}
/**
* Sets the preference store for this preference page.
*
* If preferenceStore is set to null, getPreferenceStore
* will invoke doGetPreferenceStore the next time it is called.
*
*
* @param store the preference store, or null
* @see #getPreferenceStore
*/
public void setPreferenceStore(IPreferenceStore store) {
preferenceStore = store;
}
@Override
public void setSize(Point uiSize) {
Control control = getControl();
if (control != null) {
control.setSize(uiSize);
size = uiSize;
}
}
/**
* The PreferencePage
implementation of this IDialogPage
* method extends the DialogPage
implementation to update
* the preference page container title. Subclasses may extend.
* @see IDialogPage#setTitle(String)
*/
@Override
public void setTitle(String title) {
super.setTitle(title);
if (getContainer() != null) {
getContainer().updateTitle();
}
}
/**
* Sets whether this page is valid.
* The enable state of the container buttons and the
* apply button is updated when a page's valid state
* changes.
*
* @param b the new valid state
*/
public void setValid(boolean b) {
boolean oldValue = isValid;
isValid = b;
if (oldValue != isValid) {
// update container state
if (getContainer() != null) {
getContainer().updateButtons();
}
// update page state
updateApplyButton();
}
}
@Override
public String toString() {
return getTitle();
}
/**
* Updates the enabled state of the Apply button to reflect whether
* this page is valid.
*/
protected void updateApplyButton() {
if (applyButton != null) {
applyButton.setEnabled(isValid());
}
}
/**
* Creates a composite with a highlighted Note entry and a message text.
* This is designed to take up the full width of the page.
*
* @param font the font to use
* @param composite the parent composite
* @param title the title of the note
* @param message the message for the note
* @return the composite for the note
*/
protected Composite createNoteComposite(Font font, Composite composite,
String title, String message) {
Composite messageComposite = new Composite(composite, SWT.NONE);
GridLayout messageLayout = new GridLayout();
messageLayout.numColumns = 2;
messageLayout.marginWidth = 0;
messageLayout.marginHeight = 0;
messageComposite.setLayout(messageLayout);
messageComposite.setLayoutData(new GridData(
GridData.HORIZONTAL_ALIGN_FILL));
messageComposite.setFont(font);
final Label noteLabel = new Label(messageComposite, SWT.BOLD);
noteLabel.setText(title);
noteLabel.setFont(JFaceResources.getFontRegistry().getBold(
JFaceResources.DIALOG_FONT));
noteLabel
.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
Label messageLabel = new Label(messageComposite, SWT.WRAP);
messageLabel.setText(message);
messageLabel.setFont(font);
return messageComposite;
}
/**
* Returns the Apply button.
*
* @return the Apply button
*/
protected Button getApplyButton() {
return applyButton;
}
/**
* Returns the Restore Defaults button.
*
* @return the Restore Defaults button
*/
protected Button getDefaultsButton() {
return defaultsButton;
}
@Override
public void performHelp() {
getControl().notifyListeners(SWT.Help, new Event());
}
/**
* Applies the given data to this page.
*
* It is up to the subclasses to specify the contract and the data format. The contract is not
* guaranteed if the subclass is in an internal package.
*
*
* Note: The implementation must silently ignore all unknown data.
*
*
* The default implementation does nothing.
*
*
* @param data the data as specified by the subclass
* @since 3.1
*/
public void applyData(Object data) {
}
@Override
public void setErrorMessage(String newMessage) {
super.setErrorMessage(newMessage);
if (getContainer() != null) {
getContainer().updateMessage();
}
}
@Override
public void setMessage(String newMessage, int newType) {
super.setMessage(newMessage, newType);
if (getContainer() != null) {
getContainer().updateMessage();
}
}
}