Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/eclipse/jface/preference/PreferencePage.java


1   /*******************************************************************************
2    * Copyright (c) 2000, 2003 IBM Corporation and others.
3    * All rights reserved. This program and the accompanying materials 
4    * are made available under the terms of the Common Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/cpl-v10.html
7    * 
8    * Contributors:
9    *     IBM Corporation - initial API and implementation
10   *      Sebastian Davids <sdavids@gmx.de> - Fix for bug 38729 - [Preferences]
11   *        NPE PreferencePage isValid.
12   *******************************************************************************/
13  package org.eclipse.jface.preference;
14  
15  import org.eclipse.jface.dialogs.*;
16  import org.eclipse.jface.dialogs.Dialog;
17  import org.eclipse.jface.resource.ImageDescriptor;
18  import org.eclipse.jface.resource.JFaceResources;
19  import org.eclipse.jface.util.IPropertyChangeListener;
20  import org.eclipse.jface.util.PropertyChangeEvent;
21  import org.eclipse.swt.SWT;
22  import org.eclipse.swt.events.*;
23  import org.eclipse.swt.graphics.Font;
24  import org.eclipse.swt.graphics.Point;
25  import org.eclipse.swt.layout.GridData;
26  import org.eclipse.swt.layout.GridLayout;
27  import org.eclipse.swt.widgets.*;
28  
29  /**
30   * Abstract base implementation for all preference page implementations.
31   * <p>
32   * Subclasses must implement the <code>createControl</code> framework
33   * method to supply the page's main control.
34   * </p>
35   * <p>
36   * Subclasses should extend the <code>doComputeSize</code> framework
37   * method to compute the size of the page's control.
38   * </p>
39   * <p>
40   * Subclasses may override the <code>performOk</code>, <code>performApply</code>, 
41   * <code>performDefaults</code>, <code>performCancel</code>, and <code>performHelp</code>
42   * framework methods to react to the standard button events.
43   * </p>
44   * <p>
45   * Subclasses may call the <code>noDefaultAndApplyButton</code> framework
46   * method before the page's control has been created to suppress
47   * the standard Apply and Defaults buttons.
48   * </p>
49   */
50  public abstract class PreferencePage
51    extends DialogPage
52    implements IPreferencePage {
53  
54    /**
55     * Preference store, or <code>null</code>.
56     */
57    private IPreferenceStore preferenceStore;
58  
59    /**
60     * Valid state for this page; <code>true</code> by default.
61     *
62     * @see #isValid
63     */
64    private boolean isValid = true;
65  
66    /**
67     * Body of page.
68     */
69    private Control body;
70  
71    /**
72     * Whether this page has the standard Apply and Defaults buttons; 
73     * <code>true</code> by default.
74     *
75     * @see #noDefaultAndApplyButton
76     */
77    private boolean createDefaultAndApplyButton = true;
78  
79    /**
80     * Standard Defaults button, or <code>null</code> if none.
81     * This button has id <code>DEFAULTS_ID</code>.
82     */
83    private Button defaultsButton = null;
84    /**
85     * The container this preference page belongs to; <code>null</code>
86     * if none.
87     */
88    private IPreferencePageContainer container = null;
89  
90    /**
91     * Standard Apply button, or <code>null</code> if none.
92     * This button has id <code>APPLY_ID</code>.
93     */
94    private Button applyButton = null;
95  
96    /**
97     * Description label.
98     * 
99     * @see #createDescriptionLabel.
100    */
101   private Label descriptionLabel;
102 
103   /**
104    * Caches size of page.
105    */
106   private Point size = null;
107 
108   /**
109    * Creates a new preference page with an empty title and no image.
110    */
111   protected PreferencePage() {
112     this(""); //$NON-NLS-1$
113   }
114   /**
115    * Creates a new preference page with the given title and no image.
116    *
117    * @param title the title of this preference page
118    */
119   protected PreferencePage(String title) {
120     super(title);
121   }
122   /**
123    * Creates a new abstract preference page with the given title and image.
124    *
125    * @param title the title of this preference page
126    * @param image the image for this preference page,
127    *  or <code>null</code> if none
128    */
129   protected PreferencePage(String title, ImageDescriptor image) {
130     super(title, image);
131   }
132   /**
133    * Computes the size for this page's UI control.
134    * <p>
135    * The default implementation of this <code>IPreferencePage</code>
136    * method returns the size set by <code>setSize</code>; if no size
137    * has been set, but the page has a UI control, the framework
138    * method <code>doComputeSize</code> is called to compute the size.
139    * </p>
140    *
141    * @return the size of the preference page encoded as
142    *   <code>new Point(width,height)</code>, or 
143    *   <code>(0,0)</code> if the page doesn't currently have any UI component
144    */
145   public Point computeSize() {
146     if (size != null)
147       return size;
148     Control control = getControl();
149     if (control != null) {
150       size = doComputeSize();
151       return size;
152     }
153     return new Point(0, 0);
154   }
155   /**
156    * Contributes additional buttons to the given composite.
157    * <p>
158    * The default implementation of this framework hook method does
159    * nothing. Subclasses should override this method to contribute buttons 
160    * to this page's button bar. For each button a subclass contributes,
161    * it must also increase the parent's grid layout number of columns
162    * by one; that is,
163    * <pre>
164    * ((GridLayout) parent.getLayout()).numColumns++);
165    * </pre>
166    * </p>
167    *
168    * @param parent the button bar
169    */
170   protected void contributeButtons(Composite parent) {
171   }
172   /**
173    * Creates and returns the SWT control for the customized body 
174    * of this preference page under the given parent composite.
175    * <p>
176    * This framework method must be implemented by concrete subclasses. Any
177    * subclass returning a <code>Composite</code> object whose <code>Layout</code>
178    * has default margins (for example, a <code>GridLayout</code>) are expected to
179    * set the margins of this <code>Layout</code> to 0 pixels. 
180    * </p>
181    *
182    * @param parent the parent composite
183    * @return the new control
184    */
185   protected abstract Control createContents(Composite parent);
186   /**
187    * The <code>PreferencePage</code> implementation of this 
188    * <code>IDialogPage</code> method creates a description label
189    * and button bar for the page. It calls <code>createContents</code>
190    * to create the custom contents of the page.
191    * <p>
192    * If a subclass that overrides this method creates a <code>Composite</code>
193    * that has a layout with default margins (for example, a <code>GridLayout</code>)
194    * it is expected to set the margins of this <code>Layout</code> to 0 pixels.
195    */
196   public void createControl(Composite parent) {
197 
198     GridData gd;
199     Composite content = new Composite(parent, SWT.NULL);
200     setControl(content);
201     GridLayout layout = new GridLayout();
202     layout.marginWidth = 0;
203     layout.marginHeight = 0;
204     content.setLayout(layout);
205     //Apply the font on creation for backward compatibility
206     applyDialogFont(content);
207 
208     // initialize the dialog units
209     initializeDialogUnits(content);
210 
211     descriptionLabel = createDescriptionLabel(content);
212     if (descriptionLabel != null) {
213       descriptionLabel.setLayoutData(
214         new GridData(GridData.FILL_HORIZONTAL));
215     }
216 
217     body = createContents(content);
218     if (body != null)
219       // null is not a valid return value but support graceful failure
220       body.setLayoutData(new GridData(GridData.FILL_BOTH));
221 
222     Composite buttonBar = new Composite(content, SWT.NULL);
223     layout = new GridLayout();
224     layout.numColumns = 0;
225     layout.marginHeight = 0;
226     layout.marginWidth = 0;
227     buttonBar.setLayout(layout);
228     gd = new GridData();
229     gd.horizontalAlignment = GridData.END;
230     buttonBar.setLayoutData(gd);
231 
232     contributeButtons(buttonBar);
233 
234     if (createDefaultAndApplyButton) {
235       layout.numColumns = layout.numColumns + 2;
236       String[] labels = JFaceResources.getStrings(new String[] { "defaults", "apply" }); //$NON-NLS-2$//$NON-NLS-1$
237       int heightHint =
238         convertVerticalDLUsToPixels(IDialogConstants.BUTTON_HEIGHT);
239       int widthHint =
240         convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
241       defaultsButton = new Button(buttonBar, SWT.PUSH);
242       defaultsButton.setText(labels[0]);
243       Dialog.applyDialogFont(defaultsButton);
244       GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
245       data.heightHint = heightHint;
246       data.widthHint =
247         Math.max(
248           widthHint,
249           defaultsButton.computeSize(
250             SWT.DEFAULT,
251             SWT.DEFAULT,
252             true).x);
253       defaultsButton.setLayoutData(data);
254       defaultsButton.addSelectionListener(new SelectionAdapter() {
255         public void widgetSelected(SelectionEvent e) {
256           performDefaults();
257         }
258       });
259 
260       applyButton = new Button(buttonBar, SWT.PUSH);
261       applyButton.setText(labels[1]);
262       Dialog.applyDialogFont(applyButton);
263       data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
264       data.heightHint = heightHint;
265       data.widthHint =
266         Math.max(
267           widthHint,
268           applyButton.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
269       applyButton.setLayoutData(data);
270       applyButton.addSelectionListener(new SelectionAdapter() {
271         public void widgetSelected(SelectionEvent e) {
272           performApply();
273         }
274       });
275       applyButton.setEnabled(isValid());
276       applyDialogFont(buttonBar);
277     } else {
278       /* Check if there are any other buttons on the button bar.
279        * If not, throw away the button bar composite.  Otherwise
280        * there is an unusually large button bar.
281        */
282       if (buttonBar.getChildren().length < 1)
283         buttonBar.dispose();
284     }
285   }
286   
287   /**
288    * Apply the dialog font to the composite and it's children
289    * if it is set. Subclasses may override if they wish to
290    * set the font themselves.
291    * @param composite
292    */
293   protected void applyDialogFont(Composite composite){
294     Dialog.applyDialogFont(composite);
295   }
296   
297   /**
298    * Creates and returns an SWT label under the given composite.
299    *
300    * @param parent the parent composite
301    * @return the new label
302    */
303   protected Label createDescriptionLabel(Composite parent) {
304     Label result = null;
305     String description = getDescription();
306     if (description != null) {
307       result = new Label(parent, SWT.WRAP);
308       result.setFont(parent.getFont());
309       result.setText(description);
310     }
311     return result;
312   }
313   /**
314    * Computes the size needed by this page's UI control.
315    * <p>
316    * All pages should override this method and set the appropriate sizes
317    * of their widgets, and then call <code>super.doComputeSize</code>.
318    * </p>
319    *
320    * @return the size of the preference page encoded as
321    *   <code>new Point(width,height)</code>
322    */
323   protected Point doComputeSize() {
324     if (descriptionLabel != null && body != null) {
325       Point bodySize = body.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
326       GridData gd = (GridData) descriptionLabel.getLayoutData();
327       gd.widthHint = bodySize.x;
328       descriptionLabel.getParent().layout(true);
329     }
330     return getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
331   }
332   /**
333    * Returns the preference store of this preference page.
334    * <p>
335    * This is a framework hook method for subclasses to return a
336    * page-specific preference store. The default implementation
337    * returns <code>null</code>.
338    * </p>
339    *
340    * @return the preference store, or <code>null</code> if none
341    */
342   protected IPreferenceStore doGetPreferenceStore() {
343     return null;
344   }
345   /**
346    * Returns the container of this page.
347    *
348    * @return the preference page container, or <code>null</code> if this
349    *   page has yet to be added to a container
350    */
351   public IPreferencePageContainer getContainer() {
352     return container;
353   }
354   /**
355    * Returns the preference store of this preference page.
356    *
357    * @return the preference store , or <code>null</code> if none
358    */
359   public IPreferenceStore getPreferenceStore() {
360     if (preferenceStore == null)
361       preferenceStore = doGetPreferenceStore();
362     if (preferenceStore != null)
363       return preferenceStore;
364     else if (container != null)
365       return container.getPreferenceStore();
366     return null;
367   }
368   /**  
369    * The preference page implementation of an <code>IPreferencePage</code>
370    * method returns whether this preference page is valid. Preference
371    * pages are considered valid by default; call <code>setValid(false)</code>
372    * to make a page invalid.
373    */
374   public boolean isValid() {
375     return isValid;
376   }
377   /**
378    * Suppresses creation of the standard Default and Apply buttons
379    * for this page.
380    * <p>
381    * Subclasses wishing a preference page wihthout these buttons
382    * should call this framework method before the page's control
383    * has been created.
384    * </p>
385    */
386   protected void noDefaultAndApplyButton() {
387     createDefaultAndApplyButton = false;
388   }
389   /**
390    * The <code>PreferencePage</code> implementation of this 
391    * <code>IPreferencePage</code> method returns <code>true</code>
392    * if the page is valid.
393    */
394   public boolean okToLeave() {
395     return isValid();
396   }
397   /**
398    * Performs special processing when this page's Apply button has been pressed.
399    * <p>
400    * This is a framework hook method for sublcasses to do special things when
401    * the Apply button has been pressed.
402    * The default implementation of this framework method simply calls
403    * <code>performOk</code> to simulate the pressing of the page's OK button.
404    * </p>
405    * 
406    * @see #performOk
407    */
408   protected void performApply() {
409     performOk();
410   }
411   /**  
412    * The preference page implementation of an <code>IPreferencePage</code>
413    * method performs special processing when this page's Cancel button has
414    * been pressed.
415    * <p>
416    * This is a framework hook method for sublcasses to do special things when
417    * the Cancel button has been pressed. The default implementation of this
418    * framework method does nothing and returns <code>true</code>.
419    */
420   public boolean performCancel() {
421     return true;
422   }
423   /**
424    * Performs special processing when this page's Defaults button has been pressed.
425    * <p>
426    * This is a framework hook method for subclasses to do special things when
427    * the Defaults button has been pressed.
428    * Subclasses may override, but should call <code>super.performDefaults</code>.
429    * </p>
430    */
431   protected void performDefaults() {
432     updateApplyButton();
433   }
434   /** 
435    * Method declared on IPreferencePage.
436    * Subclasses should override
437    */
438   public boolean performOk() {
439     return true;
440   }
441   /** (non-Javadoc)
442    * Method declared on IPreferencePage.
443    */
444   public void setContainer(IPreferencePageContainer container) {
445     this.container = container;
446   }
447   /**
448    * The <code>PreferencePage</code> implementation of this method 
449    * declared on <code>DialogPage</code> updates the container.
450    */
451   public void setErrorMessage(String newMessage) {
452     super.setErrorMessage(newMessage);
453     if (getContainer() != null) {
454       getContainer().updateMessage();
455     }
456   }
457   /**
458    * The <code>PreferencePage</code> implementation of this method 
459    * declared on <code>DialogPage</code> updates the container.
460    */
461   public void setMessage(String newMessage, int newType) {
462     super.setMessage(newMessage, newType);
463     if (getContainer() != null) {
464       getContainer().updateMessage();
465     }
466   }
467   /**
468    * Sets the preference store for this preference page.
469    * <p>
470    * If preferenceStore is set to null, getPreferenceStore
471    * will invoke doGetPreferenceStore the next time it is called.
472    * </p>
473    *
474    * @param store the preference store, or <code>null</code>
475    * @see #getPreferenceStore
476    */
477   public void setPreferenceStore(IPreferenceStore store) {
478     preferenceStore = store;
479   }
480   /* (non-Javadoc)
481    * Method declared on IPreferencePage.
482    */
483   public void setSize(Point uiSize) {
484     Control control = getControl();
485     if (control != null) {
486       control.setSize(uiSize);
487       size = uiSize;
488     }
489   }
490   /**
491    * The <code>PreferencePage</code> implementation of this <code>IDialogPage</code>
492    * method extends the <code>DialogPage</code> implementation to update
493    * the preference page container title. Subclasses may extend.
494    */
495   public void setTitle(String title) {
496     super.setTitle(title);
497     if (getContainer() != null)
498       getContainer().updateTitle();
499   }
500   /**
501    * Sets whether this page is valid.
502    * The enable state of the container buttons and the
503    * apply button is updated when a page's valid state 
504    * changes.
505    * <p>
506    *
507    * @param b the new valid state
508    */
509   public void setValid(boolean b) {
510     boolean oldValue = isValid;
511     isValid = b;
512     if (oldValue != isValid) {
513       // update container state
514       if (getContainer() != null)
515         getContainer().updateButtons();
516       // update page state
517       updateApplyButton();
518     }
519   }
520   /**
521    * Returns a string suitable for debugging purpose only.
522    */
523   public String toString() {
524     return getTitle();
525   }
526   /**
527    * Updates the enabled state of the Apply button to reflect whether 
528    * this page is valid.
529    */
530   protected void updateApplyButton() {
531     if (applyButton != null)
532       applyButton.setEnabled(isValid());
533   }
534 
535   /**
536    * Creates a composite with a highlighted Note entry and a message text.
537    * This is designed to take up the full width of the page.
538    * 
539    * @param font the font to use
540    * @param composite the parent composite
541    * @param title the title of the note
542    * @param message the message for the note
543    * @return the composite for the note
544    */
545   protected Composite createNoteComposite(
546     Font font,
547     Composite composite,
548     String title,
549     String message) {
550     Composite messageComposite = new Composite(composite, SWT.NONE);
551     GridLayout messageLayout = new GridLayout();
552     messageLayout.numColumns = 2;
553     messageLayout.marginWidth = 0;
554     messageLayout.marginHeight = 0;
555     messageComposite.setLayout(messageLayout);
556     messageComposite.setLayoutData(
557       new GridData(GridData.HORIZONTAL_ALIGN_FILL));
558     messageComposite.setFont(font);
559 
560     final Label noteLabel = new Label(messageComposite, SWT.BOLD);
561     noteLabel.setText(title);
562     noteLabel.setFont(JFaceResources.getBannerFont());
563     noteLabel.setLayoutData(
564       new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
565 
566     final IPropertyChangeListener fontListener =
567       new IPropertyChangeListener() {
568       public void propertyChange(PropertyChangeEvent event) {
569         if (JFaceResources.BANNER_FONT.equals(event.getProperty())) {
570           noteLabel.setFont(
571             JFaceResources.getFont(JFaceResources.BANNER_FONT));
572         }
573       }
574     };
575     JFaceResources.getFontRegistry().addListener(fontListener);
576     noteLabel.addDisposeListener(new DisposeListener() {
577       public void widgetDisposed(DisposeEvent event) {
578         JFaceResources.getFontRegistry().removeListener(fontListener);
579       }
580     });
581 
582     Label messageLabel = new Label(messageComposite, SWT.WRAP);
583     messageLabel.setText(message);
584     messageLabel.setFont(font);
585     return messageComposite;
586   }
587 
588   /**
589    * Returns the Apply button.
590    * 
591    * @return the Apply button
592    */
593   protected Button getApplyButton() {
594     return applyButton;
595   }
596 
597   /**
598    * Returns the Restore Defaults button.
599    * 
600    * @return the Restore Defaults button
601    */
602   protected Button getDefaultsButton() {
603     return defaultsButton;
604   }
605 
606   /* (non-Javadoc)
607    * @see org.eclipse.jface.dialogs.IDialogPage#performHelp()
608    */
609   public void performHelp() {
610     getControl().notifyListeners(SWT.Help,new Event());
611   }
612 
613 }