blob: cb6e767db5f8d0f77aafcfa5abbc2ee153b229d4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2016 Wind River Systems, Inc. and others. 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.te.ui.controls;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.IDialogPage;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Scrollable;
import org.eclipse.swt.widgets.Text;
import org.eclipse.tcf.te.ui.controls.nls.Messages;
import org.eclipse.tcf.te.ui.controls.validator.Validator;
import org.eclipse.tcf.te.ui.jface.interfaces.IValidatingContainer;
import org.eclipse.tcf.te.ui.swt.SWTControlUtil;
import org.eclipse.tcf.te.ui.utils.DialogSettingsUtil;
import org.eclipse.ui.forms.widgets.FormToolkit;
/**
* Base implementation of a common UI control providing an
* editable field or combo box to the user with the additional capability
* of browsing for the field value.
*/
public class BaseEditBrowseTextControl extends AbstractDecoratedDialogPageControl implements SelectionListener, ModifyListener {
private boolean isGroup = true;
private boolean hasHistroy = true;
private boolean isReadOnly = false;
private boolean labelIsButton = false;
private int labelButtonStyle = SWT.RADIO;
private boolean parentControlIsInnerPanel = false;
private boolean hideBrowseButton = false;
private boolean hideEditFieldControl = false;
private boolean hideEditFieldControlDecoration = false;
private boolean hideLabelControl = false;
private boolean adjustBackgroundColor = false;
private boolean restoreHistoryChangesEditFieldControl = false;
boolean isInitializing = true;
private String groupLabel = ""; //$NON-NLS-1$
private String editFieldLabelTooltip = null;
private String editFieldLabel = ""; //$NON-NLS-1$
private String buttonLabel = Messages.BaseEditBrowseTextControl_button_label;
private Composite innerPanel;
private Control labelControl;
private Control editFieldControl;
private Button buttonControl;
private String dialogSettingsSlotId;
private Validator editFieldValidator;
/**
* Constructor.
*
* @param parentPage The parent dialog page this control is embedded in.
* Might be <code>null</code> if the control is not associated with a page.
*/
public BaseEditBrowseTextControl(IDialogPage parentPage) {
super(parentPage);
setAdjustBackgroundColor(parentPage != null);
}
/**
* Set if or if not the control should be enclosed in an group control.
*
* @param isGroup Specify <code>true</code> to enclose the control into a group control, <code>false</code> otherwise.
*/
public final void setIsGroup(boolean isGroup) {
this.isGroup = isGroup;
}
/**
* Returns if or if not the control is enclosed in an group control.
*
* @return <code>true</code> if the control is enclosed into a group control, <code>false</code> otherwise.
*/
public final boolean isGroup() {
return isGroup;
}
/**
* Set if or if not this control should have a history or not.
*
* @param hasHistory Specify <code>true</code> if the control should have an history, <code>false</code> otherwise.
*/
public void setHasHistory(boolean hasHistory) {
this.hasHistroy = hasHistory;
}
/**
* Returns if or if not this control has a history or not.
*
* @return <code>true</code> if the control should has an history, <code>false</code> otherwise.
*/
public final boolean hasHistory() {
return hasHistroy;
}
/**
* Set if or if not this control can be edited by the user.
*
* @param readOnly Specify <code>true</code> if the control should be not editable by the user, <code>false</code> otherwise.
*/
public final void setReadOnly(boolean readOnly) {
this.isReadOnly = readOnly;
}
/**
* Returns if or if not this control can be edited by the user.
*
* @return <code>true</code> if the control is editable by the user, <code>false</code> otherwise.
*/
public final boolean isReadOnly() {
return isReadOnly;
}
/**
* Sets if of if not the label control should be hidden.
*
* @param hide <code>True</code> if to hide the label control, <code>false</code> otherwise.
*/
public final void setHideLabelControl(boolean hide) {
this.hideLabelControl = hide;
}
/**
* Returns if or if not the label control is hidden.
*
* @return <code>True</code> if the label control is hidden, <code>false</code> otherwise.
*/
public final boolean isHideLabelControl() {
return hideLabelControl;
}
/**
* Sets if of if not the edit field control should be hidden.
* <p>
* If set to <code>true</code>, the button control and the edit
* field control decoration are set to hidden automatically.
*
* @param hide <code>True</code> if to hide the edit field control, <code>false</code> otherwise.
*/
public final void setHideEditFieldControl(boolean hide) {
this.hideEditFieldControl = hide;
if (hide) {
setHideEditFieldControlDecoration(hide);
}
}
/**
* Returns if or if not the edit field control is hidden.
*
* @return <code>True</code> if the edit field control is hidden, <code>false</code> otherwise.
*/
public final boolean isHideEditFieldControl() {
return hideEditFieldControl;
}
/**
* Sets if of if not the edit field control decoration should be hidden.
*
* @param hide <code>True</code> if to hide the edit field control, <code>false</code> otherwise.
*/
public final void setHideEditFieldControlDecoration(boolean hide) {
this.hideEditFieldControlDecoration = hide;
}
/**
* Returns if or if not the edit field control decoration is hidden.
*
* @return <code>True</code> if the edit field control is hidden, <code>false</code> otherwise.
*/
public final boolean isHideEditFieldControlDecoration() {
return hideEditFieldControlDecoration;
}
/**
* Set if or if not the button behind the edit field control should be hidden.
*
* @param hide <code>true</code> to hide the button behind the edit field control, <code>false</code> otherwise.
*/
public final void setHideBrowseButton(boolean hide) {
this.hideBrowseButton = hide;
}
/**
* Returns if or if not the button behind the edit field control is hidden or not.
*
* @return <code>true</code> if the button behind the edit field control is hidden, <code>false</code> otherwise.
*/
public final boolean isHideBrowseButton() {
return hideBrowseButton;
}
/**
* Sets if to adjusts the background color of the created UI elements.
*
* @param adjust <code>True</code> to adjust the background color, <code>false</code> otherwise.
*/
public final void setAdjustBackgroundColor(boolean adjust) {
this.adjustBackgroundColor = adjust;
}
/**
* Returns if to adjust the background color of the created UI elements.
*
* @return <code>True</code> to adjust the background color, <code>false</code> otherwise.
*/
public final boolean isAdjustBackgroundColor() {
return adjustBackgroundColor;
}
/**
* Sets if restore history changes the edit field control content.
*
* @param change <code>True</code> if restore history changes the content, <code>false</code> otherwise.
*/
public final void setRestoreHistoryChangesEditFieldControl(boolean change) {
this.restoreHistoryChangesEditFieldControl = change;
}
/**
* Returns if restore history changes the edit field control content.
*
* @return <code>True</code> if restore history changes the content, <code>false</code> otherwise.
*/
public final boolean isRestoreHistoryChangesEditFieldControl() {
return restoreHistoryChangesEditFieldControl;
}
/**
* Enables or disables all UI elements belonging to this control.
*
* @param enabled <code>true</code> to enable the UI elements, <code>false</code> otherwise.
*/
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
SWTControlUtil.setEnabled(getLabelControl(), enabled);
SWTControlUtil.setEnabled(getEditFieldControl(), enabled && (isLabelIsButton() ? isLabelControlSelected() : true));
setButtonControlEnabled(enabled);
// Hide or show the control decoration if one is available
if (getControlDecoration() != null) {
if (enabled && getControlDecoration().getDescriptionText() != null) {
getControlDecoration().show();
} else {
getControlDecoration().hide();
}
}
}
/**
* Enables or disables the button control.
* <p>
* <b>Note:</b> The button control might need special considerations to calculate the
* enablement different from the global enabled state of the controls UI element.
* <p>
* Method is called from {@link #setEnabled(boolean)}.
*
* @param enabled <code>True</code> to enable the button control, <code>false</code> otherwise.
*/
protected void setButtonControlEnabled(boolean enabled) {
SWTControlUtil.setEnabled(getButtonControl(), enabled && (isLabelIsButton() ? isLabelControlSelected() : true));
}
/**
* Sets all UI elements belonging to this control visible or not.
*
* @param visible <code>True</code> to set all UI controls visible, <code>false</code> otherwise.
*/
public void setVisible(boolean visible) {
SWTControlUtil.setEnabled(getLabelControl(), visible);
SWTControlUtil.setEnabled(getEditFieldControl(), visible);
SWTControlUtil.setEnabled(getButtonControl(), visible);
}
/**
* Sets if or if not the parent control, passed by <code>setupPanel(parentControl)</code> should
* be used directly as the controls inner panel. The associated parent controls layout <b>must</b>
* be a <code>org.eclipse.swt.layout.GridLayout</code>. The assumed number of columns is <code>3</code>.
* If the panel contains more than <code>3</code> columns, the edit field controls horizontal span value
* should be adjusted using <code>setEditFieldControlLayoutHorizontalSpan</code>. In all other cases,
* a new independent inner panel will create even if <code>isParentControlIsInnerPanel()</code> returns <code>true</code>!
*
* @param parentIsInnerPanel <code>true</code> if the passed parent control is used directly as the inner panel, <code>false</code> otherwise.
*/
public final void setParentControlIsInnerPanel(boolean parentIsInnerPanel) {
this.parentControlIsInnerPanel = parentIsInnerPanel;
}
/**
* Returns if or if not the parent control, passed by <code>setupPanel(parentControl)</code> is
* used directly as the controls inner panel.
*
* @param parentIsInnerPanel <code>true</code> if the passed parent control is used directly as the inner panel, <code>false</code> otherwise.
*/
public final boolean isParentControlIsInnerPanel() {
return parentControlIsInnerPanel;
}
/**
* Sets the label to use for the enclosing group. If <code>null</code>, the
* group label is set to an empty string.
*
* @param groupLabel The group label to use for the enclosing group or <code>null</code>.
*/
public final void setGroupLabel(String groupLabel) {
if (groupLabel != null) {
this.groupLabel = groupLabel;
} else {
this.groupLabel = ""; //$NON-NLS-1$
}
}
/**
* Returns the label used for the enclosing group. The method is called
* only if <code>isGroup()</code> returns <code>true</code>.
*
* @return The label used for the enclosing group. If not set, a empty string will be returned.
*/
public final String getGroupLabel() {
return groupLabel;
}
/**
* Sets the tool tip to appear if the user hovers the mouse over the label control.
*
* @param tooltip The tool tip or <code>null</code> if none.
*/
public final void setEditFieldLabelTooltip(String tooltip) {
this.editFieldLabelTooltip = tooltip;
// Apply directly to the label control if created already.
SWTControlUtil.setToolTipText(getLabelControl(), editFieldLabelTooltip);
}
/**
* Returns the tool tip to appear if the user hovers the mouse over the label control.
*
* @return The tool tip or <code>null</code> if none.
*/
public final String getEditFieldLabelTooltip() {
return editFieldLabelTooltip;
}
/**
* Sets the label to use for the edit field. If <code>null</code>, the
* edit field label is set to an empty string.
*
* @param label The edit field label to use or <code>null</code>.
*/
public final void setEditFieldLabel(String label) {
if (label != null) {
this.editFieldLabel = label;
} else {
this.editFieldLabel = ""; //$NON-NLS-1$
}
// Update the control as well if already created.
setLabelControlText(label);
}
/**
* Returns the label used for the edit field.
*
* @return The label used for the edit field. If not set, a empty string will be returned.
*/
public final String getEditFieldLabel() {
return editFieldLabel;
}
/**
* Sets the label to use for the button. If <code>null</code>, the
* button label is set to an empty string.
*
* @param label The button label to use or <code>null</code>.
*/
public final void setButtonLabel(String label) {
if (label != null) {
this.buttonLabel = label;
} else {
this.buttonLabel = ""; //$NON-NLS-1$
}
}
/**
* Returns the label used for the button.
*
* @return The label used for the button. If not set, a empty string will be returned.
*/
public final String getButtonLabel() {
return buttonLabel;
}
/**
* Sets if or if not the label control should be an radio button control or not.
* If <code>true</code>, <code>configureLabelControl()</code> will automatically register
* a selection listener to the radio button control to enable/disable the edit field and
* button controls depending on the selection state of the radio button control.
*
* @param isRadioButton <code>true</code> if the label should be an radio button control, <code>false</code> otherwise.
*/
public final void setLabelIsButton(boolean isRadioButton) {
this.labelIsButton = isRadioButton;
}
/**
* Returns if or if not the label control is an radio button control.
*
* @return <code>true</code> if the label control is an radio button control, <code>false</code> otherwise.
*/
public final boolean isLabelIsButton() {
return labelIsButton;
}
/**
* Sets the button style to be used for the button in front of the label in case
* <code>isLabelIsButton()</code> returns <code>true</code>. The style to set is
* typically either <code>SWT.RADIO</code> or <code>SWT.CHECK</code>. The default
* is set to <code>SWT.RADIO</code>.
*
* @param style The button style to use. @see the <code>SWT</code> constants for details.
*/
public final void setLabelButtonStyle(int style) {
this.labelButtonStyle = style;
}
/**
* Returns the button style used for the button in front of the label in case <code>
* isLabelIsButton()</code> returns <code>true</code>.
*
* @return The button style used. @see the <code>SWT</code> constants for details.
*/
public int getLabelButtonStyle() {
return labelButtonStyle;
}
/**
* Returns the controls inner panel composite.
*
* @return The controls inner panel composite or <code>null</code> if the composite has not been created yet.
*/
public final Composite getInnerPanelComposite() {
return innerPanel;
}
/**
* The method is called to create the controls inner panel composite during setup of
* the controls UI elements. Subclasses may override this method to create their own
* inner panel composite.
*
* @param parent The parent control for the inner panel composite to create. Must not be <code>null</code>!
* @return The created inner panel composite.
*/
protected Composite doCreateInnerPanelComposite(Composite parent) {
Assert.isNotNull(parent);
Composite innerPanel = null;
if (isGroup()) {
innerPanel = new Group(parent, SWT.NONE);
((Group)innerPanel).setText(getGroupLabel());
} else {
innerPanel = new Composite(parent, SWT.NONE);
}
return innerPanel;
}
/**
* Configure the given controls inner panel composite before the control is set visible.
* Subclasses may use this hook to configure the controls inner panel composite for their
* specific needs.
*
* @param innerPanel The inner panel composite to configure. Must not be <code>null</code>!
*/
protected void configureInnerPanelComposite(Composite innerPanel) {
Assert.isNotNull(innerPanel);
if (isAdjustBackgroundColor()) {
SWTControlUtil.setBackground(innerPanel, innerPanel.getParent().getBackground());
}
// Calculate the number of columns within the grid
int numColumns = 3;
if (isHideLabelControl()) {
numColumns--;
}
if (isHideEditFieldControl()) {
numColumns--;
}
if (isHideBrowseButton()) {
numColumns--;
}
GridLayout layout = new GridLayout(numColumns, false);
// if the inner panel is not a group (group is a composite as well, so we cannot test for
// the composite directly), set the layouts margins to 0.
if (!(innerPanel instanceof Group)) {
// We assume a plain composite here and set back the layout margins to 0
layout.marginHeight = 0; layout.marginWidth = 0;
}
innerPanel.setLayout(layout);
innerPanel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
}
/**
* Adjust the inner panel layout if necessary. The method is called
* before the sub controls are added to the inner panel and only if the
* inner panel is using a {@link GridLayout}.
*
* @param layout The grid layout. Must not be <code>null</code>
*/
protected void doAdjustInnerPanelLayout(GridLayout layout) {
Assert.isNotNull(layout);
}
/**
* Returns the label control. This control might be a label, a simple text, a radio button
* or any other SWT control.
*
* @return The label control or <code>null</code> if the control has not been created yet.
*/
public Control getLabelControl() {
return labelControl;
}
/**
* The method is called to create the label control during setup of the
* controls UI elements. Subclasses may override this method to create their
* own SWT control to be used as label.
*
* @param parent The parent control for the label control to create. Must not be <code>null</code>!
* @return The created label control.
*/
protected Control doCreateLabelControl(Composite parent) {
Assert.isNotNull(parent);
Control labelControl = null;
FormToolkit toolkit = getFormToolkit();
if (!isLabelIsButton()) {
labelControl = new Label(parent, SWT.NONE);
} else {
labelControl = toolkit != null ? toolkit.createButton(parent, null, getLabelButtonStyle()) : new Button(parent, getLabelButtonStyle());
SWTControlUtil.setSelection((Button)labelControl, false);
}
SWTControlUtil.setText(labelControl, getEditFieldLabel());
return labelControl;
}
/**
* Configure the given label control before the control is set visible. Subclasses may use
* this hook to configure the label control for their specific needs and to register any
* required listener to the control.
*
* @param labelControl The label control to configure. Must not be <code>null</code>!
*/
protected void configureLabelControl(final Control labelControl) {
Assert.isNotNull(labelControl);
if (isAdjustBackgroundColor()) {
SWTControlUtil.setBackground(labelControl, labelControl.getParent().getBackground());
}
if (isLabelIsButton() && labelControl instanceof Button) {
((Button)labelControl).addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onLabelControlSelectedChanged();
}
});
}
SWTControlUtil.setToolTipText(labelControl, getEditFieldLabelTooltip());
}
/**
* This method is called from {@link #configureEditFieldControl(Control)} after the
* edit field has been configured. Reconfigure the label control layout data if
* necessary based on the just created edit field control settings.
* <p>
* The default implementation is aligning the label control on top of the cell in
* case the edit field control has the {@link SWT#MULTI} attribute set.
*/
protected void doAdjustLabelControlLayoutData() {
// Get the edit field control style bits
int style = getEditFieldControl().getStyle();
// If SWT.MULTI is set, we have to align the control label on top of the cell.
if ((style & SWT.MULTI) != 0) {
Object data = getLabelControl().getLayoutData();
if (data == null || data instanceof GridData) {
GridData layoutData = data != null ? (GridData)data : new GridData();
layoutData.verticalAlignment = SWT.TOP;
getLabelControl().setLayoutData(layoutData);
}
}
}
/**
* This method is called either from <code>setLabelControlSelection(...)</code> or
* from the registered controls selection listener. The method enables/disables the
* edit field and button control depending on the selection state if <code>isLabelIsRadioButton()</code>
* returns <code>true</code> and the label control is an button. In all other cases,
* the method will do nothing.
*/
protected void onLabelControlSelectedChanged() {
if (isLabelIsButton() && labelControl instanceof Button) {
if (((Button)labelControl).getSelection()) {
SWTControlUtil.setEnabled(getEditFieldControl(), true);
SWTControlUtil.setEnabled(getButtonControl(), true);
} else {
SWTControlUtil.setEnabled(getEditFieldControl(), false);
SWTControlUtil.setEnabled(getButtonControl(), false);
if (getControlDecoration() != null) {
getControlDecoration().hide();
}
}
// validate the page
IValidatingContainer validatingContainer = getValidatingContainer();
if (validatingContainer != null) {
validatingContainer.validate();
}
}
}
/**
* In case the label control is an button and <code>isLabelIsRadioButton()</code>
* returns <code>true</code>, the selection state of the radio button is changed
* to the given state. In all other cases, the method will do nothing.
*
* @param selected The selection state of the radio button to set.
*/
public void setLabelControlSelection(boolean selected) {
if (isLabelIsButton() && labelControl instanceof Button) {
SWTControlUtil.setSelection((Button)labelControl, selected);
onLabelControlSelectedChanged();
}
}
/**
* Returns <code>true</code> if <code>isLabelIsRadioButton()</code> returns
* <code>true</code> and the label control is an button and the control is
* selected. In all other cases, the method will return <code>true</code>,
* because a label always indicates a "selected" edit field.
*
* @return <code>true</code> if the label control is a label or a selected radio button, <code>false</code> otherwise.
*/
public boolean isLabelControlSelected() {
if (isLabelIsButton() && labelControl instanceof Button) {
return SWTControlUtil.getSelection((Button)labelControl);
}
return true;
}
/**
* Returns the current set text from the label control.<br>
* Override if using custom label controls.
*
* @return The label controls text or an empty string.
*/
public String getLabelControlText() {
String value = SWTControlUtil.getText(labelControl);
if (value == null)
{
value = ""; //$NON-NLS-1$
}
return value;
}
/**
* Sets the text to show within the label control. This method can handle
* <code>Label</code>, <code>Text</code>, <code>Button</code> and <code>Combo</code>
* SWT controls only by default. If subclasses use different edit field controls than
* these, the subclass must override this method in order to apply the given text
* correctly to the label control.
*
* @param text The text to set within the label control. Must not be <code>null</code>.
*/
public void setLabelControlText(String text) {
SWTControlUtil.setText(labelControl, text);
}
/**
* Returns the edit field control. This control might be an text or combobox
* or any other SWT control.
*
* @return The edit field control or <code>null</code> if the control has not been created yet.
*/
public Control getEditFieldControl() {
return editFieldControl;
}
/**
* The method is called from default implementation of <code>doCreateEditFieldControl</code>
* in order to allow overrides to adjust the edit field control creation style bits finally.
* Whatever this method will return is used to create the edit field control. The default
* implementation returns the passed in style bits completely unmodified.
*
* @param style The default style bits to apply to create the edit field control.
* @return The possibly modified style bits to apply to create the edit field control.
*/
protected int doAdjustEditFieldControlStyles(int style) {
return style;
}
/**
* The method is called from default implementation of {@link #doCreateEditFieldControl(Composite)}
* in order to allow overrider to adjust the edit field control layout data bits finally.
* Whatever this method sets to the passed in layout data, will be associated with the
* the edit field control.
* <p>
* If {@link #isAdjustEditFieldControlWidthHint()} returns <code>true</code>, the default implementation
* calculates a width hint for the edit field control as following:
* <ul>
* <li>Set default width hint to the width of approximately 50 characters in the current dialog font.</li>
* <li>If a parent control is associated, recalculate the width hint to be 85% of parent controls horizontal size.</li>
* </ul>
*
* @param layoutData The layout data to apply to the edit field control. Must not be <code>null</code>.
*/
protected void doAdjustEditFieldControlLayoutData(GridData layoutData) {
Assert.isNotNull(layoutData);
// adjust the control indentation
if (getControlDecoration() != null) {
layoutData.horizontalIndent = FieldDecorationRegistry.getDefault().getMaximumDecorationWidth();
}
// adjust the horizontal span.
layoutData.horizontalSpan = calculateEditFieldControlHorizontalSpan();
// adjust the controls width hint within the given layout data
if (isAdjustEditFieldControlWidthHint()) {
layoutData.widthHint = SWTControlUtil.convertWidthInCharsToPixels(getEditFieldControl(), 50);
Composite parent = getParentControl();
if (parent != null) {
// Calculate the size of the parent. We are interested to get the width of the parent control.
int wHint = parent.getLayoutData() instanceof GridData ? ((GridData)parent.getLayoutData()).widthHint : SWT.DEFAULT;
Point parentSize = parent.computeSize(wHint, SWT.DEFAULT, false);
if (parentSize != null) {
// Calculate the child widthHint to be 85% of the parent
layoutData.widthHint = (85 * parentSize.x) / 100;
// Update the parent layout width hint if calculated once
if (parent.getLayoutData() instanceof GridData) {
((GridData)parent.getLayoutData()).widthHint = parentSize.x;
}
}
}
}
}
/**
* Controls whether {@link #doAdjustEditFieldControlLayoutData(GridData)} is adjusting
* the layout data width hint for the edit field control or not.
* <p>
* The default implementation returns <code>false</code>.
*
* @return <code>True</code> to adjust the edit field controls layout data width hint attributed, <code>false</code> for not to adjust.
*/
protected boolean isAdjustEditFieldControlWidthHint() {
return false;
}
/**
* The method is called to create the edit field control during setup of the
* controls UI elements. Subclasses may override this method to create their
* own SWT control to be used as edit field.
*
* @param parent The parent control for the edit field control to create. Must not be <code>null</code>!
* @return The created edit field control.
*/
protected Control doCreateEditFieldControl(Composite parent) {
Assert.isNotNull(parent);
final Scrollable editField;
FormToolkit toolkit = getFormToolkit();
if (hasHistory()) {
// if the control should have an history, the edit field control is an combobox
int style = SWT.DROP_DOWN;
if (isReadOnly()) {
style |= SWT.READ_ONLY;
}
editField = new Combo(parent, doAdjustEditFieldControlStyles(style));
((Combo)editField).addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
if (!isInitializing) { // do not call this unless the boundaries of the control are calculated yet
SWTControlUtil.setValueToolTip(editField);
}
}
});
((Combo)editField).addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (!isInitializing) { // do not call this unless the boundaries of the control are calculated yet
SWTControlUtil.setValueToolTip(editField);
}
}
});
// make sure that after resizing a control, the necessity of showing the tool tip is recalculated
((Combo)editField).addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
if (!isInitializing) { // do not call this unless the boundaries of the control are calculated yet
SWTControlUtil.setValueToolTip(editField);
}
}});
} else {
int style = SWT.SINGLE;
if (isReadOnly()) {
style |= SWT.READ_ONLY | SWT.NO_FOCUS;
}
editField = toolkit != null ? toolkit.createText(parent, null, doAdjustEditFieldControlStyles(SWT.BORDER | style)) : new Text(parent, doAdjustEditFieldControlStyles(SWT.BORDER | style));
((Text)editField).addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
if (!isInitializing) { // do not call this unless the boundaries of the control are calculated yet
SWTControlUtil.setValueToolTip(editField);
}
}
});
// make sure that after resizing a control, the necessity of showing the tool tip is recalculated
((Text)editField).addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
if (!isInitializing) { // do not call this unless the boundaries of the control are calculated yet
SWTControlUtil.setValueToolTip(editField);
}
}});
}
return editField;
}
/**
* Configure the given edit field control before the control is set visible. Subclasses may use
* this hook to configure the edit field control for their specific needs and to register any
* required listener to the control.
*
* @param control The edit field control to configure. Must not be <code>null</code>!
*/
protected void configureEditFieldControl(Control control) {
Assert.isNotNull(control);
// the edit field control expands within the inner composite
GridData layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false);
doAdjustEditFieldControlLayoutData(layoutData);
control.setLayoutData(layoutData);
// The edit field can influence the layout data of the label control (SWT.MULTI).
// Give the label control the chance to reconfigure after the edit field control
// got created and the control style bits can be queried.
doAdjustLabelControlLayoutData();
// register the modification and selection listener if they are available.
ModifyListener modifyListener = doGetEditFieldControlModifyListener();
VerifyListener verifyListener = doGetEditFieldControlVerifyListener();
SelectionListener selectionListener = doGetEditFieldControlSelectionListener();
if (control instanceof Text) {
if (modifyListener != null) {
((Text)control).addModifyListener(modifyListener);
}
if (verifyListener != null) {
((Text)control).addVerifyListener(verifyListener);
}
}
if (control instanceof Combo) {
if (modifyListener != null) {
((Combo)control).addModifyListener(modifyListener);
}
if (verifyListener != null) {
((Combo)control).addVerifyListener(verifyListener);
}
if (selectionListener != null) {
((Combo)control).addSelectionListener(selectionListener);
}
}
// if the label control is an button control, trigger an initial onLabelControlSelectedChanged to
// enable/disable the edit field control correctly initially.
if (isLabelIsButton()) {
onLabelControlSelectedChanged();
}
}
/**
* Returns the horizontal span value which is set to the edit field controls grid
* layout data.
*/
public int calculateEditFieldControlHorizontalSpan() {
// Default horizontal span is always 1.
int span = 1;
if (getEditFieldControl() != null && getEditFieldControl().getParent() != null) {
// Get the parent control of the edit field
Composite parent = getEditFieldControl().getParent();
// Determine the number of columns within the parent
int numColumns = parent.getLayout() instanceof GridLayout ? ((GridLayout)parent.getLayout()).numColumns : 1;
// Calculate the number of columns consumed
int consumed = 0;
if (!isHideLabelControl()) {
consumed++; // The label
}
if (!isHideEditFieldControl()) {
consumed++; // The edit field control
}
if (!isHideBrowseButton()) {
consumed++; // The browse button
}
// In case there are more columns available than consumed,
// make the edit field control spanning over all the remaining columns.
if (numColumns > consumed) {
span = numColumns - consumed + 1;
}
}
return span;
}
/**
* Returns the modification listener instance to be registered for the edit field
* control if not <code>null</code>. The default implementation returns always <code>
* null</code>. Subclasses may override this method to provide a suitable modification
* listener for the edit field control.
*
* @return The modification listener to register to the edit field control or <code>null</code>.
*/
protected ModifyListener doGetEditFieldControlModifyListener() {
return this;
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
*/
@Override
public void modifyText(ModifyEvent e) {
// validate the page
IValidatingContainer validatingContainer = getValidatingContainer();
if (validatingContainer != null) {
validatingContainer.validate();
}
}
/**
* Returns the verify listener instance to be registered for the edit field
* control if not <code>null</code>. The default implementation returns always <code>
* null</code>. Subclasses may override this method to provide a suitable verify
* listener for the edit field control.
*
* @return The verify listener to register to the edit field control or <code>null</code>.
*/
protected VerifyListener doGetEditFieldControlVerifyListener() {
return null;
}
/**
* Returns the selection listener instance to be registered for the edit field
* control if not <code>null</code>. The default implementation returns always <code>
* null</code>. Subclasses may override this method to provide a suitable selection
* listener for the edit field control.
*
* @return The modification listener to register to the edit field control or <code>null</code>.
*/
protected SelectionListener doGetEditFieldControlSelectionListener() {
if (getEditFieldControl() instanceof Combo) {
return this;
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e) {
// validate the page
IValidatingContainer validatingContainer = getValidatingContainer();
if (validatingContainer != null) {
validatingContainer.validate();
}
}
/**
* Returns the current set text from the edit field control.
*
* @return The edit field controls text or an empty string, but never <code>null</code>.
*/
public String getEditFieldControlText() {
String value = SWTControlUtil.getText(editFieldControl);
return value != null ? value : ""; //$NON-NLS-1$
}
/**
* The content of the edit field control might require preparation before it
* can be validated at all. By default, this method returns the same value as
* a call to <code>getEditFieldControlText()</code>.
*
* @return The edit field control text to validate.
*/
public String getEditFieldControlTextForValidation() {
return getEditFieldControlText();
}
/**
* Sets the text to show within the edit field control. This method can handle
* <code>Text</code> and <code>Combo</code> SWT controls only by default. If subclasses
* use different edit field controls than these two, the subclass must override
* this method in order to apply the given text correctly to the edit field control.
*
* @param text The text to set within the edit field control. Must not be <code>null</code>.
*/
public void setEditFieldControlText(String text) {
if (text == null) {
text = ""; //$NON-NLS-1$
}
String oldText = SWTControlUtil.getText(editFieldControl);
if (!text.equals(oldText)) {
SWTControlUtil.setText(editFieldControl, text);
}
}
/**
* Set the edit field control history to the given history entries if <code>
* hasHistory()</code> returns <code>true</code> and the edit field control
* is an SWT <code>Combo</code> control. If subclasses use different edit field
* controls, the subclass must override this method if the used control supports
* history lists. Duplicated history entries, empty history entries or <code>null</code>
* values are not applied to the history.
*
* @param historyEntries The history entries to set. Must not be <code>null</code>!
*/
public void setEditFieldControlHistory(String[] historyEntries) {
Assert.isNotNull(historyEntries);
if (hasHistory() && getEditFieldControl() instanceof Combo) {
Combo combo = (Combo)getEditFieldControl();
List<String> oldItems = new ArrayList<String>(Arrays.asList(SWTControlUtil.getItems(combo)));
List<String> newItems = new ArrayList<String>();
String oldSelectedItem = getEditFieldControlText();
// we add the entries one by one to filter out duplicates, empty strings and null values.
for (String entry : historyEntries) {
if (entry == null || entry.trim().length() == 0 || newItems.contains(entry)) {
continue;
}
newItems.add(entry);
}
// Create the array of new items to apply before sorting the
// new items list. Otherwise we will loose the order of the
// items in which the clients wants to set them to the control
final String[] newItemsArray = newItems.toArray(new String[newItems.size()]);
// The two lists must be in the same order to compare them with equals
Collections.sort(oldItems);
Collections.sort(newItems);
if (!newItems.equals(oldItems)) {
SWTControlUtil.setItems(combo, newItemsArray);
}
// Restore the previously selected item if still available
if (newItems.contains(oldSelectedItem) || !oldItems.contains(oldSelectedItem)) {
setEditFieldControlText(oldSelectedItem);
}
}
}
/**
* Adds the given string to the edit field control history if <code>
* hasHistory()</code> returns <code>true</code> and the edit field control
* is an SWT <code>Combo</code> control. If subclasses use different edit field
* controls, the subclass must override this method if the used control supports
* history lists. Duplicated history entries, empty history entries or <code>null</code>
* values are not applied to the history.
*
* @param entry
*/
public void addToEditFieldControlHistory(String entry) {
if (hasHistory() && getEditFieldControl() instanceof Combo) {
Combo combo = (Combo)getEditFieldControl();
if (entry != null && entry.trim().length() > 0 && combo.indexOf(entry) == -1) {
combo.add(entry);
}
}
}
public void addToEditFieldControlHistory(String entry, int index) {
if (hasHistory() && getEditFieldControl() instanceof Combo) {
Combo combo = (Combo)getEditFieldControl();
if (entry != null && entry.trim().length() > 0 && combo.indexOf(entry) == -1) {
combo.add(entry, index >= 0 && index <= combo.getItemCount() ? index : combo.getItemCount());
}
}
}
/**
* The method is called to create an edit field validator during setup.
* Subclasses have to override this method to create the right validator.
* The default validator is <code>null</code> and so it isn't used.
*
* @return The new created edit field validator.
*/
protected Validator doCreateEditFieldValidator() {
return null;
}
/**
* Configure the edit field validator.
* Subclasses should override this method to configure the validator.
*
* @param validator The validator to be configured.
*/
protected void configureEditFieldValidator(Validator validator) {
// do nothing
}
/**
* Returns the button control.
*
* @return The button control or <code>null</code> if the control has not been created yet.
*/
public Button getButtonControl() {
return buttonControl;
}
/**
* The method is called to create the button control during setup of the controls
* UI elements. Subclasses may override this method to create their own button control.
*
* @param parent The parent control for the button control to create. Must not be <code>null</code>!
* @return The created button control.
*/
protected Button doCreateButtonControl(Composite parent) {
Assert.isNotNull(parent);
Button button = new Button(parent, SWT.PUSH);
button.setText(getButtonLabel());
return button;
}
/**
* Configure the given button control before the control is set visible. Subclasses may use
* this hook to configure the button control for their specific needs and to register any
* required listener to the control.
*
* @param button The button control to configure. Must not be <code>null</code>!
*/
protected void configureButtonControl(Button button) {
Assert.isNotNull(button);
if (isAdjustBackgroundColor()) {
SWTControlUtil.setBackground(button, button.getParent().getBackground());
}
// add the selection listener to open the file dialog if the user pressed the button
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onButtonControlSelected();
}
});
// If not yet set, assure that the buttons fill in the available space
if (button.getLayoutData() == null) {
GridData layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false);
layoutData.widthHint = SWTControlUtil.convertWidthInCharsToPixels(button, getButtonLabel().length() + 2);
button.setLayoutData(layoutData);
}
}
/**
* Called if the user pressed the button control. Subclasses may override
* this method to plugin the desired functionality.
*/
protected void onButtonControlSelected() {
}
/**
* Returns the layout data to be used for the top most controls composite. Because this
* top most composite is directly embedded into the parent control, it cannot be predicted
* which layout data object class must be associated to the top most controls composite. Subclasses
* must override this method if the layout of the parent object is not a <code>org.eclipse.swt.layout.GridLayout</code>!
*
* @param parentLayout The associated layout of the parent composite of the top most controls composite. Might be <code>null</code>!
* @return The layout data object to be associated to the top most controls composite. Must be never <code>null</code>!
*/
protected Object getTopMostCompositeLayoutData(Layout parentLayout) {
GridData layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false);
if (parentLayout instanceof GridLayout) {
layoutData.horizontalSpan = ((GridLayout)parentLayout).numColumns;
}
return layoutData;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.controls.BaseControl#setupPanel(org.eclipse.swt.widgets.Composite)
*/
@Override
public void setupPanel(Composite parent) {
isInitializing = true;
super.setupPanel(parent);
// do we need a group or a plain composite
if (!isParentControlIsInnerPanel() || !(parent.getLayout() instanceof GridLayout)) {
// create the control most enclosing composite
Composite composite = new Composite(parent, SWT.NONE);
if (isAdjustBackgroundColor()) {
SWTControlUtil.setBackground(composite, parent.getBackground());
}
composite.setLayoutData(getTopMostCompositeLayoutData(parent.getLayout()));
// within the top most controls composite, the layout management is
// in our own hands again.
GridLayout layout = new GridLayout();
layout.marginHeight = 0; layout.marginWidth = 0;
composite.setLayout(layout);
innerPanel = doCreateInnerPanelComposite(composite);
// give the subclasses the chance to reconfigure the inner panel composite for specific needs.
configureInnerPanelComposite(innerPanel);
} else {
innerPanel = parent;
}
// Adjust the inner panel layout data. This is the final point in time
// to influence the inner panel layout.
if (innerPanel.getLayout() instanceof GridLayout) {
doAdjustInnerPanelLayout((GridLayout)innerPanel.getLayout());
}
// now, the label control for the edit field control comes first
if (!isHideLabelControl()) {
labelControl = doCreateLabelControl(innerPanel);
// give the subclasses the chance to reconfigure the label control for specific needs
configureLabelControl(labelControl);
}
// In case, the button is not hidden and the inner panel to use
// has only 1 or 2 columns, we need an additional inner inner panel to
// squeeze the edit field control and the button into such panel
Composite innerInnerPanel = innerPanel;
int numColumns = ((GridLayout)innerInnerPanel.getLayout()).numColumns;
if ((numColumns == 1 || numColumns == 2) && !isHideBrowseButton() && !isHideEditFieldControl()) {
innerInnerPanel = new Composite(innerPanel, SWT.NONE);
if (isAdjustBackgroundColor()) {
SWTControlUtil.setBackground(innerInnerPanel, innerPanel.getBackground());
}
GridLayout layout = new GridLayout(2, false);
layout.marginHeight = 0; layout.marginWidth = 0;
innerInnerPanel.setLayout(layout);
innerInnerPanel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
}
if (!isHideEditFieldControl()) {
// Create the edit field control itself. The result of
// doCreateEditFieldControl(...) must be always not null!
editFieldControl = doCreateEditFieldControl(innerInnerPanel);
Assert.isNotNull(editFieldControl);
// Once the edit field got created, the control decoration must
// be created and configured _before_ the edit field itself is
// configured. Otherwise, the layout data for the edit field may
// not be configured correctly.
if (!isHideEditFieldControlDecoration()) {
ControlDecoration controlDecoration = doCreateControlDecoration(editFieldControl, parent);
Assert.isNotNull(controlDecoration);
configureControlDecoration(controlDecoration);
}
// Configure the edit field control (including layout data)
configureEditFieldControl(editFieldControl);
// before validation, create the edit field validator
setEditFieldValidator(doCreateEditFieldValidator());
// now configure the edit field validator
configureEditFieldValidator(getEditFieldValidator());
}
else if (!isHideBrowseButton()) {
@SuppressWarnings("unused")
Label spacer = new Label(innerInnerPanel, SWT.NONE);
}
if (!isHideBrowseButton()) {
// finally, the button most right end.
buttonControl = doCreateButtonControl(innerInnerPanel);
// give the subclasses the chance to reconfigure the button control for specific needs
configureButtonControl(buttonControl);
}
// validate the control before setting the control visible
isValid();
isInitializing = false;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.controls.BaseControl#dispose()
*/
@Override
public void dispose() {
super.dispose();
labelControl = null;
editFieldControl = null;
buttonControl = null;
}
/**
* Set the dialog settings slot id to use for saving and restoring the controls history
* to and from a given dialog settings instance. If the slot id is set to <code>null</code>,
* the edit field control label is used as slot id!
*
* @param settingsSlotId The dialog settings slot id to use or <code>null</code> to use the edit field control label as slot id.
*/
public void setDialogSettingsSlotId(String settingsSlotId) {
dialogSettingsSlotId = settingsSlotId;
}
/**
* Returns the dialog settings slot id to use for saving and restoring the controls history
* to and from a given dialog settings instance. The returned dialog settings slot id is
* automatically prefixed if the given prefix is not <code>null</code> or empty.
*
* @param prefix The dialog settings slot id prefix or <code>null</code>.
* @return The dialog settings slot id to use. Must be never <code>null</code>!
*/
public String getDialogSettingsSlotId(String prefix) {
String settingsSlotId = dialogSettingsSlotId;
if (settingsSlotId == null) {
settingsSlotId = getEditFieldLabel().replace(':', ' ').trim().replace(' ', '_');
}
return prefixDialogSettingsSlotId(settingsSlotId, prefix);
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.controls.BaseControl#getDialogSettingsSectionName()
*/
@Override
protected String getDialogSettingsSectionName() {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.controls.BaseControl#doRestoreWidgetValues(org.eclipse.jface.dialogs.IDialogSettings, java.lang.String)
*/
@Override
public void doRestoreWidgetValues(IDialogSettings settings, String idPrefix) {
String[] historyEntries = getHistory(settings, idPrefix);
if (historyEntries.length > 0) {
setEditFieldControlHistory(historyEntries);
if ("".equals(getEditFieldControlText()) && restoreHistoryChangesEditFieldControl) { //$NON-NLS-1$
setEditFieldControlText(historyEntries[0]);
}
}
}
/**
* Get the history entries from the dialog setting.
* @param settings The dialog setting.
* @param idPrefix The prefix for the dialog setting slot id
* @return The history entries or an empty array. Will never return <code>null</code>!
*/
protected String[] getHistory(IDialogSettings settings, String idPrefix) {
Assert.isNotNull(settings);
if (getDialogSettingsSlotId(idPrefix) != null) {
return DialogSettingsUtil.getSettingsArraySafe(settings, getDialogSettingsSlotId(idPrefix));
}
return new String[0];
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.controls.BaseControl#doSaveWidgetValues(org.eclipse.jface.dialogs.IDialogSettings, java.lang.String)
*/
@Override
public void doSaveWidgetValues(IDialogSettings settings, String idPrefix) {
Assert.isNotNull(settings);
if (getDialogSettingsSlotId(idPrefix) != null) {
String[] historyEntries = DialogSettingsUtil.getSettingsArraySafe(settings, getDialogSettingsSlotId(idPrefix));
historyEntries = DialogSettingsUtil.addToHistory(historyEntries, getEditFieldControlText());
settings.put(getDialogSettingsSlotId(idPrefix), historyEntries);
}
}
/**
* Returns the validator for the edit field.
*
* @return The edit field validator.
*/
public final Validator getEditFieldValidator() {
return editFieldValidator;
}
/**
* Set the validator for the edit field.
* This method should be overwritten to check whether the validator type is
* valid for the edit field.
*
* @param editFieldValidator The validator for the edit field.
*/
public void setEditFieldValidator(Validator editFieldValidator) {
this.editFieldValidator = editFieldValidator;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.controls.BaseControl#isValid()
*/
@Override
public boolean isValid() {
if (isInitializing) {
return true;
}
boolean valid = internalIsValid();
if (getControlDecoration() != null) {
// Setup and show the control decoration if necessary
if (isEnabled() && (!valid || (getMessage() != null && getMessageType() != IMessageProvider.NONE))) {
// Update the control decorator
updateControlDecoration(getMessage(), getMessageType());
} else {
updateControlDecoration(null, IMessageProvider.NONE);
}
}
return valid;
}
protected boolean internalIsValid() {
boolean valid = super.isValid();
if (getEditFieldValidator() != null &&
getEditFieldControl() != null &&
!getEditFieldControl().isDisposed() &&
SWTControlUtil.isEnabled(getEditFieldControl()) &&
!isReadOnly() &&
isLabelControlSelected()) {
valid = getEditFieldValidator().isValid(getEditFieldControlTextForValidation());
setMessage(getEditFieldValidator().getMessage(), getEditFieldValidator().getMessageType());
}
return valid;
}
}