/******************************************************************************* * 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; } }