blob: 27f387509e823a1293466b6e87c91b5ba58cfa7b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 Oracle. 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:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.ui.internal.widgets;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jpt.ui.internal.Tracing;
import org.eclipse.jpt.ui.internal.listeners.SWTPropertyChangeListenerWrapper;
import org.eclipse.jpt.ui.internal.swt.BooleanButtonModelAdapter;
import org.eclipse.jpt.ui.internal.swt.TextFieldModelAdapter;
import org.eclipse.jpt.ui.internal.swt.TriStateCheckBoxModelAdapter;
import org.eclipse.jpt.ui.internal.util.ControlAligner;
import org.eclipse.jpt.ui.internal.util.LabeledButton;
import org.eclipse.jpt.ui.internal.util.LabeledControlUpdater;
import org.eclipse.jpt.ui.internal.util.SWTUtil;
import org.eclipse.jpt.utility.internal.ClassTools;
import org.eclipse.jpt.utility.internal.model.value.SimplePropertyValueModel;
import org.eclipse.jpt.utility.model.Model;
import org.eclipse.jpt.utility.model.event.PropertyChangeEvent;
import org.eclipse.jpt.utility.model.listener.PropertyChangeListener;
import org.eclipse.jpt.utility.model.value.PropertyValueModel;
import org.eclipse.jpt.utility.model.value.WritablePropertyValueModel;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.help.IWorkbenchHelpSystem;
import org.eclipse.ui.part.PageBook;
/**
* The abstract definition of a pane which holds onto a <code>PropertyValueModel</code>
* that contains the subject of this pane.
* <p>
* It also contains convenience methods for building buttons, labels, check
* boxes, and radio buttons, etc.
* <p>
* It is possible to easily listen to any property changes coming from the
* subject, {@link #addPropertyNames(Collection)} is specify which properties
* are of interest and {@link #propertyChanged(String)} is used to notify the
* pane when the property has changed.
*
* @see AbstractFormPane
* @see AbstractDialogPane
*
* @version 2.0
* @since 2.0
*/
@SuppressWarnings("nls")
public abstract class AbstractPane<T extends Model>
{
/**
* The listener registered with the subject in order to be notified when a
* property has changed, the property names are determined by
* {@link #propertyNames()}.
*/
private PropertyChangeListener aspectChangeListener;
/**
* The container of this composite.
*/
private Composite container;
/**
* The aligner responsible to align the left controls.
*/
private ControlAligner leftControlAligner;
/**
* Flag used to stop the circular population of widgets.
*/
private boolean populating;
/**
* The aligner responsible to align the left controls.
*/
private ControlAligner rightControlAligner;
/**
* This listener is registered with the subject holder in order to
* automatically repopulate this pane with the new subject.
*/
private PropertyChangeListener subjectChangeListener;
/**
* The subject of this pane.
*/
private PropertyValueModel<T> subjectHolder;
/**
* The collection of registered sub-panes will be automatically notified
* when listeners need to be engaged or disengaged or when to populate its
* widgets.
*/
private Collection<AbstractPane<?>> subPanes;
/**
* The factory used to create various common widgets.
*/
private WidgetFactory widgetFactory;
/**
* Creates a new <code>AbstractSubjectPane</code>.
*
* @category Constructor
*/
@SuppressWarnings("unused")
private AbstractPane() {
super();
}
/**
* Creates a new <code>AbstractSubjectPane</code>.
*
* @param parentPane The parent pane of this one
* @param parent The parent container
*
* @category Constructor
*/
protected AbstractPane(AbstractPane<? extends T> parentPane,
Composite parent) {
this(parentPane, parent, true);
}
/**
* Creates a new <code>AbstractSubjectPane</code>.
*
* @param parentPane The parent container of this one
* @param parent The parent container
* @param widgetFactory The factory used to create various widgets
* @param automaticallyAlignWidgets <code>true</code> to make the widgets
* this pane aligned with the widgets of the given parent pane;
* <code>false</code> to not align them
*
* @category Constructor
*/
protected AbstractPane(AbstractPane<? extends T> parentPane,
Composite parent,
boolean automaticallyAlignWidgets) {
this(parentPane,
parentPane.getSubjectHolder(),
parent,
automaticallyAlignWidgets);
}
/**
* Creates a new <code>AbstractSubjectPane</code>.
*
* @param parentPane The parent container of this one
* @param subjectHolder The holder of this pane's subject
* @param parent The parent container
*
* @category Constructor
*/
protected AbstractPane(AbstractPane<?> parentPane,
PropertyValueModel<? extends T> subjectHolder,
Composite parent) {
this(parentPane, subjectHolder, parent, true);
}
/**
* Creates a new <code>AbstractSubjectPane</code>.
*
* @param parentPane The parent container of this one
* @param subjectHolder The holder of this pane's subject
* @param parent The parent container
* @param widgetFactory The factory used to create various widgets
* @param automaticallyAlignWidgets <code>true</code> to make the widgets
* this pane aligned with the widgets of the given parent pane;
* <code>false</code> to not align them
*
* @category Constructor
*/
protected AbstractPane(AbstractPane<?> parentPane,
PropertyValueModel<? extends T> subjectHolder,
Composite parent,
boolean automaticallyAlignWidgets) {
this(subjectHolder,
parent,
parentPane.getWidgetFactory());
initialize(parentPane, automaticallyAlignWidgets);
}
/**
* Creates a new <code>AbstractSubjectPane</code>.
*
* @param subjectHolder The holder of this pane's subject
* @param parent The parent container
* @param widgetFactory The factory used to create various common widgets
*
* @category Constructor
*/
protected AbstractPane(PropertyValueModel<? extends T> subjectHolder,
Composite parent,
WidgetFactory widgetFactory) {
super();
this.initialize(subjectHolder, widgetFactory);
try {
this.populating = true;
this.container = this.buildContainer(parent);
this.initializeLayout(this.container);
}
finally {
this.populating = false;
}
}
/**
* Adds the given pane's widgets (those that were registered with its
* left <code>ControlAligner</code>) to this pane's left
* <code>ControlAligner</code> so that their width can be adjusted to have
* the width of the widest widget.
*
* @param pane The pane containing the widgets to add
*
* @category Layout
*/
protected final void addAlignLeft(AbstractPane<?> container) {
this.leftControlAligner.add(container.leftControlAligner);
}
/**
* Adds the given control to the collection of widgets that have their width
* adjust with the width of the widest widget. The left alignment is usually
* used for labels.
*
* @param pane The pane to add
*
* @category Layout
*/
protected final void addAlignLeft(Control control) {
this.leftControlAligner.add(control);
}
/**
* Adds the given pane's widgets (those that were registered with its
* right <code>ControlAligner</code>) to this pane's right
* <code>ControlAligner</code> so that their width can be adjusted to have
* the width of the widest widget.
*
* @param pane The pane containing the widgets to add
*
* @category Layout
*/
protected final void addAlignRight(AbstractPane<?> container) {
this.rightControlAligner.add(container.rightControlAligner);
}
/**
* Adds the given control to the collection of widgets that have their width
* adjust with the width of the widest widget. The left alignment is usually
* used for buttons.
*
* @param pane The pane to add
*
* @category Layout
*/
protected final void addAlignRight(Control control) {
this.rightControlAligner.add(control);
}
/**
* Adds the given pane's controls (those that were registered for
* alignment) from this pane.
*
* @param pane The pane containing the widgets to add for
* alignment
*
* @category Layout
*/
protected final void addPaneForAlignment(AbstractPane<?> container) {
addAlignLeft(container);
addAlignRight(container);
}
/**
* Adds any property names to the given collection in order to be notified
* when the actual property changes in the subject.
*
* @param propertyNames The collection of property names to register with the
* subject
*/
protected void addPropertyNames(Collection<String> propertyNames) {
}
private PropertyChangeListener buildAspectChangeListener() {
return new SWTPropertyChangeListenerWrapper(buildAspectChangeListener_());
}
private PropertyChangeListener buildAspectChangeListener_() {
return new PropertyChangeListener() {
public void propertyChanged(PropertyChangeEvent e) {
updatePane(e.propertyName());
}
};
}
/**
* Creates a new button using the given information.
*
* @param parent The parent container
* @param buttonText The button's text
* @param buttonAction The action to be invoked when the button is pressed
* @return The newly created <code>Button</code>
*
* @category Layout
*/
protected final Button buildButton(Composite container,
String text,
final Runnable buttonAction) {
return this.buildButton(container, text, null, buttonAction);
}
/**
* Creates a new button using the given information.
*
* @param parent The parent container
* @param buttonText The button's text
* @param helpId The topic help ID to be registered for the new check box
* @param buttonAction The action to be invoked when the button is pressed
* @return The newly created <code>Button</code>
*
* @category Layout
*/
protected final Button buildButton(Composite container,
String text,
String helpId,
final Runnable buttonAction) {
Button button = this.widgetFactory.createButton(container, text);
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
SWTUtil.asyncExec(buttonAction);
}
});
if (helpId != null) {
helpSystem().setHelp(button, helpId);
}
return button;
}
/**
* Creates a new non-editable <code>CCombo</code>.
*
* @param container The parent container
* @return The newly created <code>Combo</code>
*
* @category Layout
*/
protected final CCombo buildCCombo(Composite container) {
CCombo combo = this.widgetFactory.createCCombo(container);
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
combo.setLayoutData(gridData);
return combo;
}
/**
* Creates a new <code>ComboViewer</code> using a <code>CCombo</code>.
*
* @param container The parent container
* @param labelProvider The provider responsible to convert the combo's items
* into human readable strings
* @return The newly created <code>ComboViewer</code>
*
* @category Layout
*/
protected final ComboViewer buildCComboViewer(Composite container,
IBaseLabelProvider labelProvider) {
CCombo combo = this.buildCCombo(container);
ComboViewer viewer = new ComboViewer(combo);
viewer.setLabelProvider(labelProvider);
return viewer;
}
/**
* Creates a new check box using the given information.
*
* @param parent The parent container
* @param buttonText The button's text
* @param booleanHolder The holder of the selection state
* @return The newly created <code>Button</code>
*
* @category Layout
*/
protected final Button buildCheckBox(Composite parent,
String buttonText,
WritablePropertyValueModel<Boolean> booleanHolder) {
return this.buildCheckBox(parent, buttonText, booleanHolder, null);
}
/**
* Creates a new check box using the given information.
*
* @param parent The parent container
* @param buttonText The button's text
* @param booleanHolder The holder of the selection state
* @param helpId The topic help ID to be registered for the new check box
* @return The newly created <code>Button</code>
*
* @category Layout
*/
protected final Button buildCheckBox(Composite parent,
String buttonText,
WritablePropertyValueModel<Boolean> booleanHolder,
String helpId) {
return this.buildToggleButton(
parent,
buttonText,
booleanHolder,
helpId,
SWT.CHECK
);
}
/**
* Creates a new non-editable <code>Combo</code>.
*
* @param container The parent container
* @return The newly created <code>Combo</code>
*
* @category Layout
*/
protected final Combo buildCombo(Composite container) {
Combo combo = this.widgetFactory.createCombo(container);
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
combo.setLayoutData(gridData);
return combo;
}
/**
* Creates a new <code>ComboViewer</code> using a <code>Combo</code>.
*
* @param container The parent container
* @param labelProvider The provider responsible to convert the combo's items
* into human readable strings
* @return The newly created <code>ComboViewer</code>
*
* @category Layout
*/
protected final ComboViewer buildComboViewer(Composite container,
IBaseLabelProvider labelProvider) {
Combo combo = this.buildCombo(container);
ComboViewer viewer = new ComboViewer(combo);
viewer.setLabelProvider(labelProvider);
return viewer;
}
/**
* Creates the main container of this pane. The layout and layout data are
* automatically set.
*
* @param parent The parent container
* @return The newly created <code>Composite</code> that will holds all the
* widgets created by this pane through {@link #initializeLayout(Composite)}
*/
protected Composite buildContainer(Composite parent) {
return this.buildSubPane(parent);
}
/**
* Creates a new editable <code>CCombo</code>.
*
* @param container The parent container
* @return The newly created <code>CCombo</code>
*
* @category Layout
*/
protected final CCombo buildEditableCCombo(Composite container) {
CCombo combo = this.widgetFactory.createEditableCCombo(container);
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
combo.setLayoutData(gridData);
return combo;
}
/**
* Creates a new editable <code>ComboViewer</code> using a <code>CCombo</code>.
*
* @param container The parent container
* @param labelProvider The provider responsible to convert the combo's items
* into human readable strings
* @return The newly created <code>ComboViewer</code>
*
* @category Layout
*/
protected final ComboViewer buildEditableCComboViewer(Composite container,
IBaseLabelProvider labelProvider) {
CCombo combo = this.buildEditableCCombo(container);
ComboViewer viewer = new ComboViewer(combo);
viewer.setLabelProvider(labelProvider);
return viewer;
}
/**
* Creates a new editable <code>Combo</code>.
*
* @param container The parent container
* @return The newly created <code>Combo</code>
*
* @category Layout
*/
protected final Combo buildEditableCombo(Composite container) {
Combo combo = this.widgetFactory.createEditableCombo(container);
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
combo.setLayoutData(gridData);
return combo;
}
/**
* Creates a new editable <code>ComboViewer</code> using a <code>Combo</code>.
*
* @param container The parent container
* @param labelProvider The provider responsible to convert the combo's items
* into human readable strings
* @return The newly created <code>ComboViewer</code>
*
* @category Layout
*/
protected final ComboViewer buildEditableComboViewer(Composite container,
IBaseLabelProvider labelProvider) {
Combo combo = this.buildEditableCombo(container);
ComboViewer viewer = new ComboViewer(combo);
viewer.setLabelProvider(labelProvider);
return viewer;
}
private PropertyChangeListener buildExpandedStateChangeListener(final Section section) {
return new SWTPropertyChangeListenerWrapper(buildExpandedStateChangeListener_(section));
}
private PropertyChangeListener buildExpandedStateChangeListener_(final Section section) {
return new PropertyChangeListener() {
public void propertyChanged(final PropertyChangeEvent e) {
Boolean value = (Boolean) e.newValue();
if (value == null) {
value = Boolean.TRUE;
}
section.setExpanded(value);
}
};
}
/**
* Creates a new <code>Hyperlink</code> that will invoked the given
* <code>Runnable</code> when selected. The given action is always invoked
* from the UI thread.
*
* @param parent The parent container
* @param text The hyperlink's text
* @param hyperLinkAction The action to be invoked when the link was selected
* return The newly created <code>Hyperlink</code>
*/
protected final Hyperlink buildHyperLink(Composite parent,
String text,
final Runnable hyperLinkAction) {
Hyperlink link = this.widgetFactory.createHyperlink(parent, text);
link.addMouseListener(new MouseAdapter() {
@Override
public void mouseUp(MouseEvent e) {
SWTUtil.asyncExec(hyperLinkAction);
}
});
return link;
}
/**
* Creates a new lable using the given information.
*
* @param parent The parent container
* @param labelText The label's text
*
* @category Layout
*/
protected final Label buildLabel(Composite container,
String labelText) {
return this.widgetFactory.createLabel(container, labelText);
}
/**
* Creates a new container that will have a non-editable combo labeled with
* the given text.
*
* @param container The parent container
* @param labelText The text of the label
* @param comboListener The listener that will be notified when the selection
* changes
* @param rightControl The control shown to the right of the main widget
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The newly created <code>CCombo</code>
*
* @category Layout
*/
protected final CCombo buildLabeledCCombo(Composite container,
String labelText,
ModifyListener comboListener,
Control rightControl,
String helpId) {
CCombo combo = this.buildCCombo(container);
combo.addModifyListener(comboListener);
this.buildLabeledComposite(
container,
labelText,
(combo.getParent() != container) ? combo.getParent() : combo,
rightControl,
helpId
);
return combo;
}
/**
* Creates a new container that will have the given center control labeled
* with the given label.
*
* @param container The parent container
* @param leftControl The widget shown to the left of the main widget
* @param centerControl The main widget
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The newly created <code>CCombo</code>
*
* @category Layout
*/
protected final CCombo buildLabeledCCombo(Composite container,
String labelText,
ModifyListener comboListener,
String helpId) {
return this.buildLabeledCCombo(
container,
labelText,
comboListener,
null,
helpId
);
}
/**
* Creates a new container that will have a non-editable combo labeled with
* the given text.
*
* @param container The parent container
* @param labelText The text of the label
* @param comboListener The listener that will be notified when the selection
* changes
* @param rightControl The control shown to the right of the main widget
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Combo buildLabeledCombo(Composite container,
String labelText,
ModifyListener comboListener,
Control rightControl,
String helpId) {
Combo combo = this.buildCombo(container);
combo.addModifyListener(comboListener);
this.buildLabeledComposite(
container,
labelText,
(combo.getParent() != container) ? combo.getParent() : combo,
rightControl,
helpId
);
return combo;
}
/**
* Creates a new container that will have the given center control labeled
* with the given label.
*
* @param container The parent container
* @param leftControl The widget shown to the left of the main widget
* @param centerControl The main widget
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Combo buildLabeledCombo(Composite container,
String labelText,
ModifyListener comboListener,
String helpId) {
return this.buildLabeledCombo(
container,
labelText,
comboListener,
null,
helpId
);
}
/**
* Creates a new container that will have the given center control labeled
* with the given label.
*
* @param container The parent container
* @param leftControl The widget shown to the left of the main widget
* @param centerControl The main widget
* @param rightControl The control shown to the right of the main widget
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Composite buildLabeledComposite(Composite container,
Control leftControl,
Control centerControl,
Control rightControl,
String helpId) {
// Container for the label and main composite
container = this.buildSubPane(container, 3, 5, 0, 0, 0);
// Left control
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.BEGINNING;
gridData.grabExcessHorizontalSpace = false;
leftControl.setLayoutData(gridData);
// Re-parent the left control to the new sub pane
leftControl.setParent(container);
this.leftControlAligner.add(leftControl);
// Center control
gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
centerControl.setLayoutData(gridData);
// Re-parent the center control to the new sub pane
centerControl.setParent(container);
// Register the help id for the center control
if (helpId != null) {
helpSystem().setHelp(centerControl, helpId);
}
// Right control
if (rightControl == null) {
Composite spacer = this.buildPane(container);
spacer.setLayout(buildSpacerLayout());
this.rightControlAligner.add(spacer);
}
else {
gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL_HORIZONTAL;
gridData.grabExcessHorizontalSpace = false;
rightControl.setLayoutData(gridData);
rightControl.setParent(container);
this.rightControlAligner.add(rightControl);
// Register the help id for the right control
if (helpId != null) {
helpSystem().setHelp(rightControl, helpId);
}
}
return container;
}
/**
* Creates a new container that will have the given center control labeled
* with the given label.
*
* @param container The parent container
* @param label The label used to describe the center control
* @param centerControl The main widget
* @param helpId The topic help ID to be registered for the given center
* control
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Composite buildLabeledComposite(Composite container,
Control label,
Control centerControl,
String helpId) {
return this.buildLabeledComposite(
container,
label,
centerControl,
null,
helpId
);
}
/**
* Creates a new container that will have the given center control labeled
* with the given label.
*
* @param container The parent container
* @param label The label used to describe the center control
* @param centerControl The main widget
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Composite buildLabeledComposite(Composite container,
Label label,
Control centerControl) {
return this.buildLabeledComposite(
container,
label,
centerControl,
null
);
}
/**
* Creates a new container that will have the given center composite labeled
* with the given label text.
*
* @param container The parent container
* @param labelText The text to label the main composite
* @param centerPane The main widget
* @param rightControl The control shown to the right of the main widget
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Composite buildLabeledComposite(Composite container,
String labelText,
AbstractPane<?> centerPane,
Control rightCentrol,
String helpId) {
return this.buildLabeledComposite(
container,
labelText,
centerPane.getControl(),
rightCentrol,
helpId
);
}
/**
* Creates a new container that will have the given center composite labeled
* with the given label text.
*
* @param container The parent container
* @param labelText The text to label the main composite
* @param centerPane The main widget
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Composite buildLabeledComposite(Composite container,
String labelText,
AbstractPane<?> centerPane,
String helpId) {
return this.buildLabeledComposite(
container,
labelText,
centerPane.getControl(),
helpId
);
}
/**
* Creates a new container that will have the given center composite labeled
* with the given label text.
*
* @param container The parent container
* @param labelText The text to label the main composite
* @param centerControl The main widget
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Composite buildLabeledComposite(Composite container,
String labelText,
Control centerControl) {
return this.buildLabeledComposite(
container,
labelText,
centerControl,
null,
null
);
}
/**
* Creates a new container that will have the given center composite labeled
* with the given label text.
*
* @param container The parent container
* @param labelText The text to label the main composite
* @param centerControl The main widget
* @param rightControl The control shown to the right of the main widget
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Composite buildLabeledComposite(Composite container,
String labelText,
Control centerControl,
Control rightControl) {
return this.buildLabeledComposite(
container,
labelText,
centerControl,
rightControl,
null
);
}
/**
* Creates a new container that will have the given center composite labeled
* with the given label text.
*
* @param container The parent container
* @param labelText The text to label the main composite
* @param centerControl The main widget
* @param rightControl The control shown to the right of the main widget
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Composite buildLabeledComposite(Composite container,
String labelText,
Control centerControl,
Control rightCentrol,
String helpId) {
Label label = this.buildLabel(container, labelText);
return this.buildLabeledComposite(
container,
label,
centerControl,
rightCentrol,
helpId
);
}
/**
* Creates a new container that will have the given center composite labeled
* with the given label text.
*
* @param container The parent container
* @param labelText The text to label the main composite
* @param centerControl The main widget
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Composite buildLabeledComposite(Composite container,
String labelText,
Control centerControl,
String helpId) {
Label label = this.buildLabel(container, labelText);
return this.buildLabeledComposite(
container,
label,
centerControl,
helpId
);
}
/**
* Creates a new container that will have the given center control labeled
* with the given label.
*
* @param container The parent container
* @param leftControl The widget shown to the left of the main widget
* @param centerControl The main widget
* @param rightControl The control shown to the right of the main widget
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The newly created <code>CCombo</code>
*
* @category Layout
*/
protected final CCombo buildLabeledEditableCCombo(Composite container,
String labelText,
ModifyListener comboListener,
Control rightControl,
String helpId) {
CCombo combo = this.buildEditableCCombo(container);
combo.addModifyListener(comboListener);
this.buildLabeledComposite(
container,
labelText,
(combo.getParent() != container) ? combo.getParent() : combo,
rightControl,
helpId
);
return combo;
}
/**
* Creates a new container that will have an editable combo labeled with the
* given text.
*
* @param container The parent container
* @param labelText The text of the label
* @param comboListener The listener that will be notified when the selection
* changes
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The newly created <code>CCombo</code>
*
* @category Layout
*/
protected final CCombo buildLabeledEditableCCombo(Composite container,
String labelText,
ModifyListener comboListener,
String helpId) {
return this.buildLabeledEditableCCombo(
container,
labelText,
comboListener,
null,
helpId
);
}
/**
* Creates a new container that will have the given center control labeled
* with the given label.
*
* @param container The parent container
* @param leftControl The widget shown to the left of the main widget
* @param centerControl The main widget
* @param labelProvider The provider responsible to convert the combo's items
* into human readable strings
* @param rightControl The control shown to the right of the main widget
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The newly created <code>CCombo</code>
*
* @category Layout
*/
protected final CCombo buildLabeledEditableCComboViewer(Composite container,
String labelText,
ModifyListener comboListener,
ILabelProvider labelProvider,
Control rightControl,
String helpId) {
ComboViewer comboViewer = this.buildEditableCComboViewer(
container,
labelProvider
);
CCombo combo = comboViewer.getCCombo();
combo.addModifyListener(comboListener);
this.buildLabeledComposite(
container,
labelText,
(combo.getParent() != container) ? combo.getParent() : combo,
rightControl,
helpId
);
return combo;
}
/**
* Creates a new container that will have an editable combo labeled with the
* given text.
*
* @param container The parent container
* @param labelText The text of the label
* @param comboListener The listener that will be notified when the selection
* changes
* @param labelProvider The provider responsible to convert the combo's items
* into human readable strings
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The newly created <code>CCombo</code>
*
* @category Layout
*/
protected final CCombo buildLabeledEditableCComboViewer(Composite container,
String labelText,
ModifyListener comboListener,
ILabelProvider labelProvider,
String helpId) {
return this.buildLabeledEditableCComboViewer(
container,
labelText,
comboListener,
null,
helpId
);
}
/**
* Creates a new container that will have the given center control labeled
* with the given label.
*
* @param container The parent container
* @param leftControl The widget shown to the left of the main widget
* @param centerControl The main widget
* @param rightControl The control shown to the right of the main widget
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Combo buildLabeledEditableCombo(Composite container,
String labelText,
ModifyListener comboListener,
Control rightControl,
String helpId) {
Combo combo = this.buildEditableCombo(container);
combo.addModifyListener(comboListener);
this.buildLabeledComposite(
container,
labelText,
(combo.getParent() != container) ? combo.getParent() : combo,
rightControl,
helpId
);
return combo;
}
/**
* Creates a new container that will have an editable combo labeled with the
* given text.
*
* @param container The parent container
* @param labelText The text of the label
* @param comboListener The listener that will be notified when the selection
* changes
* @param helpId The topic help ID to be registered for the given center
* compositer
* @return The container of the label and the given center control
*
* @category Layout
*/
protected final Combo buildLabeledEditableCombo(Composite container,
String labelText,
ModifyListener comboListener,
String helpId) {
return this.buildLabeledEditableCombo(
container,
labelText,
comboListener,
null,
helpId
);
}
/**
* Creates a new container that will have a text field as the center control
* labeled with the given label.
*
* @param container The parent container
* @param labelText The text area's label
* @param textHolder The holder of the text field's input
* @return The newly created <code>Text</code>
*
* @category Layout
*/
protected final Text buildLabeledMultiLineText(Composite container,
String labelText,
WritablePropertyValueModel<String> textHolder) {
return this.buildLabeledMultiLineText(
container,
labelText,
textHolder,
null
);
}
/**
* Creates a new container that will have a text field as the center control
* labeled with the given label.
*
* @param container The parent container
* @param labelText The text area's label
* @param textHolder The holder of the text field's input
* @param rightControl The widget to be placed to the right of the text area
* @return The newly created <code>Text</code>
*
* @category Layout
*/
protected final Text buildLabeledMultiLineText(Composite container,
String labelText,
WritablePropertyValueModel<String> textHolder,
Control rightControl,
String helpId) {
Text text = this.buildMultiLineText(container, textHolder);
this.buildLabeledComposite(
container,
labelText,
text,
rightControl,
helpId
);
return text;
}
/**
* Creates a new container that will have a text field as the center control
* labeled with the given label.
*
* @param container The parent container
* @param labelText The text area's label
* @param textHolder The holder of the text field's input
* @param lineCount The number of lines the text area should display
* @return The newly created <code>Text</code>
*
* @category Layout
*/
protected final Text buildLabeledMultiLineText(Composite container,
String labelText,
WritablePropertyValueModel<String> textHolder,
int lineCount) {
return this.buildLabeledMultiLineText(
container,
labelText,
textHolder,
lineCount,
null
);
}
/**
* Creates a new container that will have a text field as the center control
* labeled with the given label.
*
* @param container The parent container
* @param labelText The text area's label
* @param textHolder The holder of the text field's input
* @param lineCount The number of lines the text area should display
* @return The newly created <code>Text</code>
*
* @category Layout
*/
protected final Text buildLabeledMultiLineText(Composite container,
String labelText,
WritablePropertyValueModel<String> textHolder,
int lineCount,
String helpId) {
Text text = this.buildMultiLineText(container, textHolder, lineCount);
container = this.buildLabeledComposite(
container,
labelText,
text,
helpId
);
int textHeight = text.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
// Specify the number of lines the text area should display
GridData gridData = (GridData) text.getLayoutData();
gridData.heightHint = text.getLineHeight() * lineCount;
// Move the label to the top of its cell
Control label = container.getChildren()[0];
int labelHeight = label.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
gridData = (GridData) label.getLayoutData();
gridData.verticalAlignment = SWT.TOP;
gridData.verticalIndent += (Math.abs(textHeight - labelHeight) / 2);
return text;
}
/**
* Creates a new container that will have a text field as the center control
* labeled with the given label.
*
* @param container The parent container
* @param labelText The text area's label
* @param textHolder The holder of the text field's input
* @return The newly created <code>Text</code>
*
* @category Layout
*/
protected final Text buildLabeledMultiLineText(Composite container,
String labelText,
WritablePropertyValueModel<String> textHolder,
String helpId) {
return this.buildLabeledMultiLineText(
container,
labelText,
textHolder,
3,
helpId
);
}
/**
* Creates a new container that will have a text field as the center control
* labeled with the given label.
*
* @param container The parent container
* @param labelText The text field's label
* @param textListener The listener that is notified when the text field's
* input changes
* @return The newly created <code>Text</code>
*
* @category Layout
*/
protected final Text buildLabeledText(Composite container,
String labelText,
ModifyListener textListener) {
Text text = this.buildText(container);
text.addModifyListener(textListener);
this.buildLabeledComposite(
container,
labelText,
text
);
return text;
}
/**
* Creates a new container that will have a text field as the center control
* labeled with the given label.
*
* @param container The parent container
* @param textListener The listener that is notified when the text field's
* input changes
* @param rightControl The control shown to the right of the main widget
* @return The newly created <code>Text</code>
*
* @category Layout
*/
protected final Text buildLabeledText(Composite container,
String labelText,
ModifyListener textListener,
Control rightControl) {
Text text = this.buildText(container);
text.addModifyListener(textListener);
this.buildLabeledComposite(
container,
labelText,
text,
rightControl
);
return text;
}
/**
* Creates a new container that will have a text field as the center control
* labeled with the given label.
*
* @param container The parent container
* @param textListener The listener that is notified when the text field's
* input changes
* @param rightControl The control shown to the right of the main widget
* @param helpId The topic help ID to be registered for the text field
* @return The newly created <code>Text</code>
*
* @category Layout
*/
protected final Text buildLabeledText(Composite container,
String labelText,
ModifyListener textListener,
Control rightControl,
String helpId) {
Text text = this.buildText(container);
text.addModifyListener(textListener);
this.buildLabeledComposite(
container,
labelText,
text,
rightControl,
helpId
);
return text;
}
/**
* Creates a new container that will have a text field as the center control
* labeled with the given label.
*
* @param container The parent container
* @param textListener The listener that is notified when the text field's
* input changes
* @param helpId The topic help ID to be registered for the text field
* @return The newly created <code>Text</code>
*
* @category Layout
*/
protected final Text buildLabeledText(Composite container,
String labelText,
ModifyListener textListener,
String helpId) {
Text text = this.buildText(container);
text.addModifyListener(textListener);
this.buildLabeledComposite(
container,
labelText,
text,
helpId
);
return text;
}
/**
* Creates a new container that will have a text field as the center control
* labeled with the given label.
*
* @param container The parent container
* @param textHolder The holder of the text field's input
* @return The newly created <code>Text</code>
*
* @category Layout
*/
protected final Text buildLabeledText(Composite container,
String labelText,
WritablePropertyValueModel<String> textHolder) {
return this.buildLabeledText(container, labelText, textHolder, null);
}
/**
* Creates a new container that will have a text field as the center control
* labeled with the given label.
*
* @param container The parent container
* @param labelText The text field's label
* @param rightComponent The component to be placed to the right of the text
* field
* @param textHolder The holder of the text field's input
* @param helpId The topic help ID to be registered for the text field
* @return The newly created <code>Text</code>
*
* @category Layout
*/
protected final Text buildLabeledText(Composite container,
String labelText,
WritablePropertyValueModel<String> textHolder,
Control rightComponent,
String helpId) {
Text text = this.buildText(container);
TextFieldModelAdapter.adapt(textHolder, text);
this.buildLabeledComposite(
container,
labelText,
text,
rightComponent,
helpId
);
return text;
}
/**
* Creates a new container that will have a text field as the center control
* labeled with the given label.
*
* @param container The parent container
* @param textHolder The holder of the text field's input
* @param helpId The topic help ID to be registered for the text field
* @return The newly created <code>Text</code>
*
* @category Layout
*/
protected final Text buildLabeledText(Composite container,
String labelText,
WritablePropertyValueModel<String> textHolder,
String helpId) {
return this.buildLabeledText(
container,
labelText,
textHolder,
null,
helpId
);
}
/**
* Creates a new list and notify the given selection holder when the
* selection changes. If the selection count is different than one than the
* holder will receive <code>null</code>.
*
* @param container The parent container
* @return The newly created <code>List</code>
*
* @category Layout
*/
protected final List buildList(Composite container) {
return this.buildList(container, (String) null);
}
/**
* Creates a new list and notify the given selection holder when the
* selection changes. If the selection count is different than one than the
* holder will receive <code>null</code>.
*
* @param container The parent container
* @param helpId The topic help ID to be registered for the new radio button
* @return The newly created <code>List</code>
*
* @category Layout
*/
protected final List buildList(Composite container, String helpId) {
return this.buildList(
container,
new SimplePropertyValueModel<String>(),
helpId
);
}
/**
* Creates a new list and notify the given selection holder when the
* selection changes. If the selection count is different than one than the
* holder will receive <code>null</code>.
*
* @param container The parent container
* @param selectionHolder The holder of the unique selected item
* @return The newly created <code>List</code>
*
* @category Layout
*/
protected final List buildList(Composite container,
WritablePropertyValueModel<String> selectionHolder) {
return this.buildList(container, selectionHolder, null);
}
/**
* Creates a new list and notify the given selection holder when the
* selection changes. If the selection count is different than one than the
* holder will receive <code>null</code>.
*
* @param container The parent container
* @param selectionHolder The holder of the unique selected item
* @param helpId The topic help ID to be registered for the new radio button
* @return The newly created <code>List</code>
*
* @category Layout
*/
protected final List buildList(Composite container,
WritablePropertyValueModel<String> selectionHolder,
String helpId) {
List list = this.widgetFactory.createList(
container,
SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI
);
list.addSelectionListener(buildSelectionListener(selectionHolder));
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.verticalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
gridData.grabExcessVerticalSpace = true;
list.setLayoutData(gridData);
if (helpId != null) {
helpSystem().setHelp(list, helpId);
}
return list;
}
/**
* Creates a new <code>Text</code> widget that has multiple lines.
*
* @param container The parent container
* @return The newly created <code>Text</code> widget
*
* @category Layout
*/
protected final Text buildMultiLineText(Composite container) {
return this.buildMultiLineText(container, 3, null);
}
/**
* Creates a new <code>Text</code> widget that has multiple lines.
*
* @param container The parent container
* @param lineCount The number of lines the text area should display
* @param helpId The topic help ID to be registered for the new text
* @return The newly created <code>Text</code> widget
*
* @category Layout
*/
protected final Text buildMultiLineText(Composite container,
int lineCount,
String helpId) {
Text text = this.widgetFactory.createMultiLineText(container);
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
gridData.heightHint = text.getLineHeight() * lineCount;
text.setLayoutData(gridData);
if (helpId != null) {
helpSystem().setHelp(text, helpId);
}
return text;
}
/**
* Creates a new <code>Text</code> widget that has multiple lines.
*
* @param container The parent container
* @param helpId The topic help ID to be registered for the new text
* @return The newly created <code>Text</code> widget
*
* @category Layout
*/
protected final Text buildMultiLineText(Composite container, String helpId) {
return this.buildMultiLineText(container, 3, helpId);
}
/**
* Creates a new <code>Text</code> widget that has multiple lines.
*
* @param container The parent container
* @param textHolder The holder of the text field's input
* @return The newly created <code>Text</code> widget
*
* @category Layout
*/
protected final Text buildMultiLineText(Composite container,
WritablePropertyValueModel<String> textHolder) {
return this.buildMultiLineText(container, textHolder, null);
}
/**
* Creates a new <code>Text</code> widget that has multiple lines.
*
* @param container The parent container
* @param textHolder The holder of the text field's input
* @param lineCount The number of lines the text area should display
* @return The newly created <code>Text</code> widget
*
* @category Layout
*/
protected final Text buildMultiLineText(Composite container,
WritablePropertyValueModel<String> textHolder,
int lineCount) {
return this.buildMultiLineText(container, textHolder, lineCount, null);
}
/**
* Creates a new <code>Text</code> widget that has multiple lines.
*
* @param container The parent container
* @param textHolder The holder of the text field's input
* @param helpId The topic help ID to be registered for the new text
* @return The newly created <code>Text</code> widget
*
* @category Layout
*/
protected final Text buildMultiLineText(Composite container,
WritablePropertyValueModel<String> textHolder,
int lineCount,
String helpId) {
Text text = this.buildMultiLineText(container, lineCount, helpId);
TextFieldModelAdapter.adapt(textHolder, text);
return text;
}
/**
* Creates a new <code>Text</code> widget that has multiple lines.
*
* @param container The parent container
* @param textHolder The holder of the text field's input
* @param helpId The topic help ID to be registered for the new text
* @return The newly created <code>Text</code> widget
*
* @category Layout
*/
protected final Text buildMultiLineText(Composite container,
WritablePropertyValueModel<String> textHolder,
String helpId) {
return this.buildMultiLineText(container, textHolder, 3, helpId);
}
/**
* Creates a new <code>PageBook</code> and set the proper layout and layout
* data.
*
* @param container The parent container
* @return The newly created <code>PageBook</code>
*/
protected final PageBook buildPageBook(Composite container) {
PageBook pageBook = new PageBook(container, SWT.NULL);
pageBook.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
return pageBook;
}
/**
* Creates a new container without specifying any layout manager.
*
* @param container The parent of the new container
* @return The newly created <code>Composite</code>
*
* @category Layout
*/
protected final Composite buildPane(Composite parent) {
return this.widgetFactory.createComposite(parent);
}
/**
* Creates a new container using the given layout manager.
*
* @param parent The parent of the new container
* @param layout The layout manager of the new container
* @return The newly created container
*
* @category Layout
*/
protected final Composite buildPane(Composite container, Layout layout) {
container = this.widgetFactory.createComposite(container);
container.setLayout(layout);
GridData gridData = new GridData();
gridData.grabExcessHorizontalSpace = true;
gridData.horizontalAlignment = SWT.FILL;
container.setLayoutData(gridData);
return container;
}
/**
* Creates a new push button using the given information.
*
* @param parent The parent container
* @param buttonText The button's text
* @param buttonAction The action to be invoked when the button is pressed
* @return The newly created <code>Button</code>
*
* @category Layout
*/
protected final Button buildPushButton(Composite parent,
String buttonText,
final Runnable buttonAction) {
return this.buildPushButton(parent, buttonText, null, buttonAction);
}
/**
* Creates a new push button using the given information.
*
* @param parent The parent container
* @param buttonText The button's text
* @param buttonAction The action to be invoked when the button is pressed
* @param helpId The topic help ID to be registered for the new radio button
* @return The newly created <code>Button</code>
*
* @category Layout
*/
protected final Button buildPushButton(Composite parent,
String buttonText,
String helpId,
final Runnable buttonAction) {
Button button = this.widgetFactory.createPushButton(parent, buttonText);
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
SWTUtil.asyncExec(buttonAction);
}
});
button.setLayoutData(new GridData());
if (helpId != null) {
helpSystem().setHelp(button, helpId);
}
return button;
}
/**
* Creates a new radio button using the given information.
*
* @param parent The parent container
* @param buttonText The button's text
* @param booleanHolder The holder of the selection state
* @return The newly created <code>Button</code>
*
* @category Layout
*/
protected final Button buildRadioButton(Composite parent,
String buttonText,
WritablePropertyValueModel<Boolean> booleanHolder) {
return this.buildRadioButton(parent, buttonText, booleanHolder, null);
}
/**
* Creates a new check box using the given information.
*
* @param parent The parent container
* @param buttonText The button's text
* @param booleanHolder The holder of the selection state
* @param helpId The topic help ID to be registered for the new radio button
* @return The newly created <code>Button</code>
*
* @category Layout
*/
protected final Button buildRadioButton(Composite parent,
String buttonText,
WritablePropertyValueModel<Boolean> booleanHolder,
String helpId) {
return this.buildToggleButton(
parent,
buttonText,
booleanHolder,
helpId,
SWT.RADIO
);
}
/**
* Creates a new <code>Section</code>. A sub-pane is automatically added as
* its client and is the returned <code>Composite</code>.
*
* @param container The container of the new widget
* @param sectionText The text of the new section
* @return The <code>Section</code>'s sub-pane
*
* @category Layout
*/
protected final Composite buildSection(Composite container,
String sectionText) {
return this.buildSection(
container,
sectionText,
ExpandableComposite.TITLE_BAR
);
}
/**
* Creates a new <code>Section</code>. A sub-pane is automatically added as
* its client and is the returned <code>Composite</code>.
*
* @param container The container of the new widget
* @param sectionText The text of the new section
* @param type The type of section to create
* @return The <code>Section</code>'s sub-pane
*
* @category Layout
*/
private Composite buildSection(Composite container,
String sectionText,
int type) {
return this.buildSection(
container,
sectionText,
type,
new SimplePropertyValueModel<Boolean>(Boolean.FALSE)
);
}
/**
* Creates a new <code>Section</code>. A sub-pane is automatically added as
* its client and is the returned <code>Composite</code>.
*
* @param container The container of the new widget
* @param sectionText The text of the new section
* @param type The type of section to create
* @param expandedStateHolder The holder of the boolean that will dictate
* when to expand or collapse the section
* @return The <code>Section</code>'s sub-pane
*
* @category Layout
*/
private Composite buildSection(Composite container,
String sectionText,
int type,
PropertyValueModel<Boolean> expandedStateHolder) {
Section section = this.widgetFactory.createSection(
container,
ExpandableComposite.TWISTIE | type
);
section.setText(sectionText);
section.marginWidth = 0;
section.marginHeight = 0;
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
section.setLayoutData(gridData);
Composite subPane = this.buildSubPane(section);
section.setClient(subPane);
expandedStateHolder.addPropertyChangeListener(
PropertyValueModel.VALUE,
buildExpandedStateChangeListener(section)
);
section.setExpanded(
expandedStateHolder.value() != null ? expandedStateHolder.value() : true
);
return subPane;
}
/**
* Creates a new <code>Section</code>. A sub-pane is automatically added as
* its client and is the returned <code>Composite</code>.
*
* @param container The container of the new widget
* @param sectionText The text of the new section
* @param expandedStateHolder The holder of the boolean that will dictate
* when to expand or collapse the section
* @return The <code>Section</code>'s sub-pane
*
* @category Layout
*/
protected final Composite buildSection(Composite container,
String sectionText,
PropertyValueModel<Boolean> expandedStateHolder) {
return this.buildSection(
container,
sectionText,
ExpandableComposite.TITLE_BAR,
expandedStateHolder
);
}
private SelectionListener buildSelectionListener(final WritablePropertyValueModel<String> selectionHolder) {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
List list = (List) e.widget;
String[] selectedItems = list.getSelection();
if ((selectedItems == null) || (selectedItems.length != 1)) {
selectionHolder.setValue(null);
}
else {
selectionHolder.setValue(selectedItems[0]);
}
}
};
}
private Layout buildSpacerLayout() {
return new Layout() {
@Override
protected Point computeSize(Composite composite,
int widthHint,
int heightHint,
boolean flushCache) {
return new Point(widthHint, heightHint);
}
@Override
protected void layout(Composite composite, boolean flushCache) {
GridData data = (GridData) composite.getLayoutData();
composite.setBounds(0, 0, data.widthHint, data.heightHint);
}
};
}
private PropertyChangeListener buildSubjectChangeListener() {
return new SWTPropertyChangeListenerWrapper(this.buildSubjectChangeListener_());
}
private PropertyChangeListener buildSubjectChangeListener_() {
return new PropertyChangeListener() {
@SuppressWarnings("unchecked")
public void propertyChanged(PropertyChangeEvent e) {
AbstractPane.this.subjectChanged((T) e.oldValue(), (T) e.newValue());
}
};
}
/**
* Creates a new <code>Composite</code> used as a sub-pane.
*
* @param container The parent container
* @return The newly created <code>Composite</code> used as a sub-pane
*
* @category Layout
*/
protected final Composite buildSubPane(Composite container) {
return this.buildSubPane(container, 0);
}
/**
* Creates a new <code>Composite</code> used as a sub-pane.
*
* @param container The parent container
* @param topMargin The extra spacing to add at the top of the pane
* @return The newly created <code>Composite</code> used as a sub-pane
*
* @category Layout
*/
protected final Composite buildSubPane(Composite container, int topMargin) {
return this.buildSubPane(container, topMargin, 0);
}
/**
* Creates a new <code>Composite</code> used as a sub-pane.
*
* @param container The parent container
* @param topMargin The extra spacing to add at the top of the pane
* @param leftMargin The extra spacing to add to the left of the pane
* @return The newly created <code>Composite</code> used as a sub-pane
*
* @category Layout
*/
protected final Composite buildSubPane(Composite container,
int topMargin,
int leftMargin) {
return this.buildSubPane(container, topMargin, leftMargin, 0, 0);
}
/**
* Creates a new <code>Composite</code> used as a sub-pane, the new widget
* will have its layout and layout data already initialized, the layout will
* be a <code>GridLayout</code> with 1 column.
*
* @param container The parent container
* @param topMargin The extra spacing to add at the top of the pane
* @param leftMargin The extra spacing to add to the left of the pane
* @param bottomMargin The extra spacing to add at the bottom of the pane
* @param rightMargin The extra spacing to add to the right of the pane
* @return The newly created <code>Composite</code> used as a sub-pane
*
* @category Layout
*/
protected final Composite buildSubPane(Composite container,
int topMargin,
int leftMargin,
int bottomMargin,
int rightMargin) {
return this.buildSubPane(
container,
1,
topMargin,
leftMargin,
bottomMargin,
rightMargin);
}
/**
* Creates a new <code>Composite</code> used as a sub-pane, the new widget
* will have its layout and layout data already initialized, the layout will
* be a <code>GridLayout</code> with 1 column.
*
* @param container The parent container
* @param topMargin The extra spacing to add at the top of the pane
* @param leftMargin The extra spacing to add to the left of the pane
* @param bottomMargin The extra spacing to add at the bottom of the pane
* @param rightMargin The extra spacing to add to the right of the pane
* @return The newly created <code>Composite</code> used as a sub-pane
*
* @category Layout
*/
protected final Composite buildSubPane(Composite container,
int columnCount,
int topMargin,
int leftMargin,
int bottomMargin,
int rightMargin) {
GridLayout layout = new GridLayout(columnCount, false);
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.marginTop = topMargin;
layout.marginLeft = leftMargin;
layout.marginBottom = bottomMargin;
layout.marginRight = rightMargin;
container = this.buildPane(container, layout);
return container;
}
/**
* Creates a new <code>Section</code>. A sub-pane is automatically added as
* its client which can be typed cast directly as a <code>Composite</code>.
*
* @param container The container of the new widget
* @param sectionText The text of the new section
* @return The <code>Section</code>'s sub-pane
*
* @category Layout
*/
protected final Composite buildSubSection(Composite container,
String sectionText) {
return this.buildSubSection(
container,
sectionText,
new SimplePropertyValueModel<Boolean>(Boolean.TRUE)
);
}
/**
* Creates a new <code>Section</code>. A sub-pane is automatically added as
* its client which can be typed cast directly as a <code>Composite</code>.
*
* @param container The container of the new widget
* @param sectionText The text of the new section
* @param expandedStateHolder The holder of the boolean that will dictate
* when to expand or collapse the section
* @return The <code>Section</code>'s sub-pane
*
* @category Layout
*/
protected final Composite buildSubSection(Composite container,
String sectionText,
PropertyValueModel<Boolean> expandedStateHolder) {
return this.buildSection(
container,
sectionText,
SWT.NULL,
expandedStateHolder
);
}
/**
* Creates a new list and notify the given selection holder when the
* selection changes. If the selection count is different than one than the
* holder will receive <code>null</code>.
*
* @param container The parent container
* @return The newly created <code>Table</code>
*
* @category Layout
*/
protected final Table buildTable(Composite container) {
return this.buildTable(container, null);
}
/**
* Creates a new list and notify the given selection holder when the
* selection changes. If the selection count is different than one than the
* holder will receive <code>null</code>.
*
* @param container The parent container
* @param helpId The topic help ID to be registered for the new radio button
* @return The newly created <code>Table</code>
*
* @category Layout
*/
protected final Table buildTable(Composite container, String helpId) {
Table table = new Table(
container,
SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION | SWT.MULTI
);
table.setHeaderVisible(true);
table.setLinesVisible(true);
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.verticalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
gridData.grabExcessVerticalSpace = true;
gridData.heightHint = table.getItemHeight() * 4;
table.setLayoutData(gridData);
if (helpId != null) {
helpSystem().setHelp(table, helpId);
}
return table;
}
/**
* Creates a new <code>Text</code> widget.
*
* @param container The parent container
* @return The newly created <code>Text</code> widget
*
* @category Layout
*/
protected final Text buildText(Composite container) {
return this.widgetFactory.createText(container);
}
/**
* Creates a new <code>Text</code> widget.
*
* @param container The parent container
* @param helpId The topic help ID to be registered for the new text
* @return The newly created <code>Text</code> widget
*
* @category Layout
*/
protected final Text buildText(Composite container, String helpId) {
Text text = this.widgetFactory.createText(container);
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
text.setLayoutData(gridData);
if (helpId != null) {
helpSystem().setHelp(text, helpId);
}
return text;
}
/**
* Creates a new <code>Text</code> widget.
*
* @param container The parent container
* @param textHolder The holder of the text field's input
* @return The newly created <code>Text</code> widget
*
* @category Layout
*/
protected final Text buildText(Composite container,
WritablePropertyValueModel<String> textHolder) {
return this.buildText(container, textHolder, null);
}
/**
* Creates a new <code>Text</code> widget.
*
* @param container The parent container
* @param textHolder The holder of the text field's input
* @param helpId The topic help ID to be registered for the new text
* @return The newly created <code>Text</code> widget
*
* @category Layout
*/
protected final Text buildText(Composite container,
WritablePropertyValueModel<String> textHolder,
String helpId) {
Text text = this.widgetFactory.createText(container);
TextFieldModelAdapter.adapt(textHolder, text);
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
text.setLayoutData(gridData);
if (helpId != null) {
helpSystem().setHelp(text, helpId);
}
return text;
}
/**
* Creates a new container with a titled border.
*
* @param title The text of the titled border
* @param container The parent container
* @return The newly created <code>Composite</code> with a titled border
*
* @category Layout
*/
protected final Group buildTitledPane(Composite container, String title) {
return this.buildTitledPane(container, title, null);
}
/**
* Creates a new container with a titled border.
*
* @param title The text of the titled border
* @param container The parent container
* @param helpId The topic help ID to be registered for the new group
* @return The newly created <code>Composite</code> with a titled border
*
* @category Layout
*/
protected final Group buildTitledPane(Composite container,
String title,
String helpId) {
Group group = this.widgetFactory.createGroup(container, title);
GridLayout layout = new GridLayout(1, false);
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.marginTop = 5;
layout.marginLeft = 5;
layout.marginBottom = 5;
layout.marginRight = 5;
group.setLayout(layout);
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.grabExcessHorizontalSpace = true;
gridData.verticalIndent = 0;
group.setLayoutData(gridData);
if (helpId != null) {
helpSystem().setHelp(group, helpId);
}
return group;
}
/**
* Creates a new toggle button (radio button or check box) using the given
* information.
*
* @param parent The parent container
* @param buttonText The button's text
* @param booleanHolder The holder of the selection state
* @param helpId The topic help ID to be registered for the new button
* @return The newly created <code>Button</code>
*
* @category Layout
*/
private Button buildToggleButton(Composite parent,
String buttonText,
WritablePropertyValueModel<Boolean> booleanHolder,
String helpId,
int toggleButtonType) {
Button button;
if (toggleButtonType == SWT.PUSH) {
button = this.widgetFactory.createPushButton(parent, buttonText);
}
else if (toggleButtonType == SWT.RADIO) {
button = this.widgetFactory.createRadioButton(parent, buttonText);
}
else if (toggleButtonType == SWT.CHECK) {
button = this.widgetFactory.createCheckBox(parent, buttonText);
}
else {
button = this.widgetFactory.createButton(parent, buttonText);
}
button.setLayoutData(new GridData());
BooleanButtonModelAdapter.adapt(booleanHolder, button);
if (helpId != null) {
helpSystem().setHelp(button, helpId);
}
return button;
}
/**
* Creates a new check box that can have 3 selection states (selected,
* unselected and partially selected.
*
* @param parent The parent container
* @param text The button's text
* @param booleanHolder The holder of the boolean value where <code>null</code>
* means partially selected
* @return The newly created <code>TriStateCheckBox</code>
*/
protected final TriStateCheckBox buildTriStateCheckBox(Composite parent,
String text,
WritablePropertyValueModel<Boolean> booleanHolder) {
return this.buildTriStateCheckBox(parent, text, booleanHolder, null);
}
/**
* Creates a new check box that can have 3 selection states (selected,
* unselected and partially selected.
*
* @param parent The parent container
* @param text The button's text
* @param booleanHolder The holder of the boolean value where <code>null</code>
* means partially selected
* @param helpId The topic help ID to be registered for the new check box
* @return The newly created <code>TriStateCheckBox</code>
*/
protected final TriStateCheckBox buildTriStateCheckBox(Composite parent,
String text,
WritablePropertyValueModel<Boolean> booleanHolder,
String helpId) {
TriStateCheckBox checkBox = new TriStateCheckBox(
parent,
text,
this.getWidgetFactory()
);
TriStateCheckBoxModelAdapter.adapt(
booleanHolder,
checkBox
);
if (helpId != null) {
helpSystem().setHelp(checkBox.getCheckBox(), helpId);
}
return checkBox;
}
/**
* Creates a new check box that can have 3 selection states (selected,
* unselected and partially selected.
*
* @param parent The parent container
* @param text The button's text
* @param booleanHolder The holder of the boolean value where <code>null</code>
* means partially selected
* @param stringHolder The holder of the string to put in parenthesis after
* the check box's text when it is partially selected
* @return The newly created <code>TriStateCheckBox</code>
*/
protected final TriStateCheckBox buildTriStateCheckBoxWithDefault(Composite parent,
String text,
WritablePropertyValueModel<Boolean> booleanHolder,
PropertyValueModel<String> stringHolder) {
return this.buildTriStateCheckBoxWithDefault(
parent,
text,
booleanHolder,
stringHolder,
null
);
}
/**
* Creates a new check box that can have 3 selection states (selected,
* unselected and partially selected.
*
* @param parent The parent container
* @param text The button's text
* @param booleanHolder The holder of the boolean value where <code>null</code>
* means partially selected
* @param stringHolder The holder of the string to put in parenthesis after
* the check box's text when it is partially selected
* @param helpId The topic help ID to be registered for the new check box
* @return The newly created <code>TriStateCheckBox</code>
*/
protected final TriStateCheckBox buildTriStateCheckBoxWithDefault(Composite parent,
String text,
WritablePropertyValueModel<Boolean> booleanHolder,
PropertyValueModel<String> stringHolder,
String helpId) {
TriStateCheckBox checkBox = this.buildTriStateCheckBox(
parent,
text,
booleanHolder,
helpId
);
new LabeledControlUpdater(
new LabeledButton(checkBox.getCheckBox()),
stringHolder
);
return checkBox;
}
/**
* Uninstalls any listeners from the subject in order to stop being notified
* for changes made outside of this panes.
*
* @category Populate
*/
protected void disengageListeners() {
this.log(Tracing.UI_LAYOUT, " ->disengageListeners()");
this.subjectHolder.removePropertyChangeListener(
PropertyValueModel.VALUE,
this.subjectChangeListener
);
this.disengageListeners(this.subject());
for (AbstractPane<?> subPane : this.subPanes) {
subPane.disengageListeners();
}
}
/**
* Removes any property change listeners from the given subject.
*
* @param subject The old subject
*
* @category Populate
*/
protected void disengageListeners(T subject) {
if (subject != null) {
// this.log(" ->disengageListeners() from " + subject);
for (String propertyName : this.propertyNames()) {
subject.removePropertyChangeListener(propertyName, this.aspectChangeListener);
}
}
}
/**
* Notifies this pane is should dispose itself.
*
* @category Populate
*/
public final void dispose() {
if (!this.container.isDisposed()) {
this.log(Tracing.UI_LAYOUT, "dispose()");
this.performDispose();
this.disengageListeners();
}
}
/**
* Requests this pane to dispose itself.
*
* @category Populate
*/
protected void doDispose() {
this.log(Tracing.UI_LAYOUT, " ->doDispose()");
}
/**
* Requests this pane to populate its widgets with the subject's values.
*
* @category Populate
*/
protected void doPopulate() {
this.log(Tracing.UI_LAYOUT, " ->doPopulate()");
}
/**
* Changes the enablement state of the widgets of this pane.
*
* @param enabled <code>true</code> to enable the widgets or <code>false</code>
* to disable them
*
* @category Layout
*/
public void enableWidgets(boolean enabled) {
if (!container.isDisposed()) {
container.setEnabled(enabled);
for (AbstractPane<?> subPane : this.subPanes) {
subPane.enableWidgets(enabled);
}
}
}
/**
* Installs the listeners on the subject in order to be notified from changes
* made outside of this panes and notifies the sub-panes to do the same.
*
* @category Populate
*/
protected void engageListeners() {
this.log(Tracing.UI_LAYOUT, " ->engageListeners()");
this.engageSubjectHolder();
this.engageListeners(this.subject());
for (AbstractPane<?> subPane : this.subPanes) {
subPane.engageListeners();
}
}
/**
* TODO
*
* @param subject
*
* @category Populate
*/
protected void engageListeners(T subject) {
if (subject != null) {
// this.log(" ->engageListeners() on " + subject);
for (String propertyName : this.propertyNames()) {
subject.addPropertyChangeListener(propertyName, this.aspectChangeListener);
}
}
}
private void engageSubjectHolder() {
this.subjectHolder.addPropertyChangeListener(
PropertyValueModel.VALUE,
this.subjectChangeListener
);
}
/**
* Returns the main <code>Control</code> of this pane.
*
* @return The main container
*
* @category Layout
*/
public Composite getControl() {
return this.container;
}
/**
* Returns the subject holder used by this pane.
*
* @return The holder of the subject
*
* @category Populate
*/
protected final PropertyValueModel<T> getSubjectHolder() {
return this.subjectHolder;
}
/**
* Returns
*
* @return
*
* @category Layout
*/
protected final WidgetFactory getWidgetFactory() {
return this.widgetFactory;
}
/**
* Returns the margin taken by a group box, which is the number of pixel the
* group box border and its margin takes before displaying its widgets plus
* 5 pixels since the widgets inside of the group box and the border should
* have that extra 5 pixels.
*
* @return The width taken by the group box border with its margin
*
* @category Layout
*/
protected final int groupBoxMargin() {
Group group = this.widgetFactory.createGroup(Display.getCurrent().getActiveShell(), "");
Rectangle clientArea = group.getClientArea();
group.dispose();
return clientArea.x + 5;
}
/**
* Returns the helps system.
*
* @return The platform's help system
*
* @category Helper
*/
protected final IWorkbenchHelpSystem helpSystem() {
return PlatformUI.getWorkbench().getHelpSystem();
}
/**
* Initializes this <code>AbstractSubjectPane</code>.
*
* @category Initialization
*/
protected void initialize() {
}
/**
* Registers this pane with the parent pane.
*
* @param parentPane The parent pane
* @param automaticallyAlignWidgets <code>true</code> to make the widgets
* this pane aligned with the widgets of the given parent pane;
* <code>false</code> to not align them
*
* @category Initialization
*/
private void initialize(AbstractPane<?> parentPane,
boolean automaticallyAlignWidgets) {
// Register this pane with the parent pane, it will call the methods
// automatically (engageListeners(), disengageListeners(), populate(),
// dispose(), etc)
parentPane.registerSubPane(this);
// Align the left and right controls with the controls from the parent
// pane
if (automaticallyAlignWidgets) {
parentPane.leftControlAligner .add(this.leftControlAligner);
parentPane.rightControlAligner.add(this.rightControlAligner);
}
}
/**
* Initializes this <code>AbstractSubjectPane</code>.
*
* @param subjectHolder The holder of this pane's subject
* @param widgetFactory The factory used to create various widgets
*
* @category Initialization
*/
@SuppressWarnings("unchecked")
private void initialize(PropertyValueModel<? extends T> subjectHolder,
WidgetFactory widgetFactory)
{
Assert.isNotNull(subjectHolder, "The subject holder cannot be null");
this.subjectHolder = (PropertyValueModel<T>) subjectHolder;
this.widgetFactory = widgetFactory;
this.subPanes = new ArrayList<AbstractPane<?>>();
this.leftControlAligner = new ControlAligner();
this.rightControlAligner = new ControlAligner();
this.subjectChangeListener = this.buildSubjectChangeListener();
this.aspectChangeListener = this.buildAspectChangeListener();
this.initialize();
}
/**
* Initializes the layout of this pane.
*
* @param container The parent container
*
* @category Layout
*/
protected abstract void initializeLayout(Composite container);
/**
* Determines whether
*
* @return
*
* @category Populate
*/
protected final boolean isPopulating() {
return this.populating;
}
/**
* Logs the given message if the <code>Tracing.DEBUG_LAYOUT</code> is enabled.
*
* @param flag
* @param message The logging message
*/
protected void log(String flag, String message) {
if (Tracing.UI_LAYOUT.equals(flag) &&
Tracing.booleanDebugOption(Tracing.UI_LAYOUT)) {
Class<?> thisClass = getClass();
String className = ClassTools.shortNameFor(thisClass);
if (thisClass.isAnonymousClass()) {
className = className.substring(0, className.indexOf('$'));
className += "->" + ClassTools.shortNameFor(thisClass.getSuperclass());
}
Tracing.log(className + ": " + message);
}
}
/**
* Notifies this pane is should dispose itself.
*
* @category Populate
*/
protected void performDispose() {
this.log(Tracing.UI_LAYOUT, " ->performDispose()");
// Dispose this pane
doDispose();
// Ask the sub-panes to perform the dispose themselves
for (AbstractPane<?> subPane : this.subPanes) {
subPane.performDispose();
}
}
/**
* Notifies this pane to populate itself using the subject's information.
*
* @category Populate
*/
public final void populate() {
if (!this.container.isDisposed()) {
this.log(Tracing.UI_LAYOUT, "populate()");
this.engageListeners();
this.repopulate();
}
}
/**
* Notifies the subject's property associated with the given property name
* has changed.
*
* @param propertyName The property name associated with the property change
*
* @category Populate
*/
protected void propertyChanged(String propertyName) {
}
/**
* Returns the list of names to listen for properties changing from the
* subject.
*
* @return A non-<code>null</code> list of property names
*
* @category Populate
*/
protected Collection<String> propertyNames() {
ArrayList<String> propertyNames = new ArrayList<String>();
addPropertyNames(propertyNames);
return propertyNames;
}
/**
* Registers another <code>AbstractSubjectPane</code> with this one so it can
* be automatically notified about certain events such as engaging or
* disengaging the listeners, etc.
*
* @param subPane The sub-pane to register
*
* @category Controller
*/
protected final void registerSubPane(AbstractPane<?> subPane) {
this.subPanes.add(subPane);
}
/**
* Removes the given pane's widgets (those that were registered with
* its left <code>ControlAligner</code>) from this pane's left
* <code>ControlAligner</code> so that their width will no longer be adjusted
* with the width of the widest widget.
*
* @param pane The pane containing the widgets to remove
*
* @category Layout
*/
protected final void removeAlignLeft(AbstractPane<?> pane) {
this.leftControlAligner.remove(pane.leftControlAligner);
}
/**
* Removes the given control from the collection of widgets that are aligned
* to have the same width when they are shown to the left side of the 3
* widget colums.
*
* @param pane The pane to remove, its width will no longer be
* ajusted to be the width of the longest widget
*
* @category Layout
*/
protected final void removeAlignLeft(Control control) {
this.leftControlAligner.remove(control);
}
/**
* Removes the given pane's widgets (those that were registered with
* its right <code>ControlAligner</code>) from this pane's right
* <code>ControlAligner</code> so that their width will no longer be adjusted
* with the width of the widest widget.
*
* @param pane The pane containing the widgets to remove
*
* @category Layout
*/
protected final void removeAlignRight(AbstractPane<?> pane) {
this.rightControlAligner.remove(pane.rightControlAligner);
}
/**
* Removes the given control from the collection of widgets that are aligned
* to have the same width when they are shown to the right side of the 3
* widget colums.
*
* @param pane The pane to remove, its width will no longer be
* ajusted to be the width of the longest widget
*
* @category Layout
*/
protected final void removeAlignRight(Control control) {
this.rightControlAligner.remove(control);
}
/**
* Removes the given pane's controls (those that were registered for
* alignment) from this pane.
*
* @param pane The pane containing the widgets that no longer
* requires their width adjusted with the width of the longest widget
*
* @category Layout
*/
protected final void removePaneForAlignment(AbstractPane<?> pane) {
removeAlignLeft(pane);
removeAlignRight(pane);
}
/**
* This method is called (perhaps internally) when this needs to repopulate
* but the object of interest has not changed.
*
* @category Populate
*/
protected final void repopulate() {
this.log(Tracing.UI_LAYOUT, " ->repopulate()");
// Populate this pane
try {
setPopulating(true);
doPopulate();
}
finally {
setPopulating(false);
}
// Ask the sub-panes to repopulate themselves
for (AbstractPane<?> subPane : this.subPanes) {
subPane.repopulate();
}
}
/**
* Determines whether this pane should be repopulate even if the subject if
* <code>null</code>.
*
* @return <code>true</code> is returned by default
*/
protected boolean repopulateWithNullSubject() {
return true;
}
/**
* Sets (TODO)
*
* @param populating
*
* @category Populate
*/
protected final void setPopulating(boolean populating) {
this.populating = populating;
}
/**
* Either show or hides this pane.
*
* @param visible The new visibility state
*/
public void setVisible(boolean visible) {
if (!this.container.isDisposed()) {
this.container.setVisible(visible);
}
}
/**
* Returns the nearest <code>Shell</code> displaying the main widget of this
* pane.
*
* @return The nearest window displaying this pane
*/
protected final Shell shell() {
return this.container.getShell();
}
/**
* Returns the subject of this pane.
*
* @return The subject if this pane was not disposed; <code>null</code>
* if it was
*
* @category Populate
*/
protected T subject() {
return this.subjectHolder.value();
}
/**
* The subject has changed, disconnects any listeners from the old subject
* and connects those listeners onto the new subject.
*
* @param oldsubject The old subject or <code>null</code> if none was set
* @param newSubject The new subject or <code>null</code> if none needs to be
* set
*
* @category Populate
*/
protected final void subjectChanged(T oldSubject, T newSubject) {
if (!this.container.isDisposed()) {
this.log(Tracing.UI_LAYOUT, "subjectChanged()");
this.disengageListeners(oldSubject);
// Only repopulate if it is allowed when the subject is null
if (newSubject != null ||
(newSubject == null && repopulateWithNullSubject()))
{
this.repopulate();
}
this.engageListeners(newSubject);
}
}
/**
* Unregisters the given <code>AbstractSubjectPane</code> from this one so it
* can no longer be automatically notified about certain events such as
* engaging or disengaging the listeners, etc.
*
* @param subPane The sub-pane to unregister
*
* @category Controller
*/
protected final void unregisterSubPane(AbstractPane<?> subPane) {
this.subPanes.remove(subPane);
}
private void updatePane(String propertyName) {
if (!isPopulating() && !container.isDisposed()) {
populating = true;
try {
propertyChanged(propertyName);
}
finally {
populating = false;
}
}
}
}