/* * Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton, * Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY. * * THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS. * WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE * IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR * CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. * NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. * DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, * OR MODIFICATIONS. * THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION, * USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS * PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY * AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM */ package org.csstudio.sds.components.ui.internal.editparts; import org.csstudio.data.values.INumericMetaData; import org.csstudio.data.values.ISeverity; import org.csstudio.data.values.ITimestamp; import org.csstudio.data.values.IValue; import org.csstudio.data.values.IValue.Quality; import org.csstudio.data.values.TimestampFactory; import org.csstudio.data.values.ValueFactory; import org.csstudio.sds.components.model.TextInputModel; import org.csstudio.sds.components.ui.internal.figures.RefreshableLabelFigure; import org.csstudio.sds.model.AbstractWidgetModel; import org.csstudio.sds.model.TextTypeEnum; import org.csstudio.sds.ui.editparts.ExecutionMode; import org.csstudio.sds.ui.editparts.IWidgetPropertyChangeHandler; import org.csstudio.ui.util.CustomMediaFactory; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.EditPolicy; import org.eclipse.gef.Request; import org.eclipse.gef.RequestConstants; import org.eclipse.gef.commands.Command; import org.eclipse.gef.editpolicies.DirectEditPolicy; import org.eclipse.gef.requests.DirectEditRequest; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.events.VerifyListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * EditPart controller for <code>TextInputModel</code> elements with support for * direct editing. * * EditPart controller for the TextInput widget. The controller mediates between * {@link TextInputModel} and {@link RefreshableLabelFigure}. * * @author Alexander Will * */ public final class TextInputEditPart extends AbstractTextTypeWidgetEditPart { private static final Logger LOG = LoggerFactory.getLogger(TextInputEditPart.class); /** * {@inheritDoc} */ @Override protected IFigure doCreateFigure() { TextInputModel model = (TextInputModel) getWidgetModel(); RefreshableLabelFigure label = new RefreshableLabelFigure(); label.setTextValue(determineLabel(null)); label.setFont(getModelFont(TextInputModel.PROP_FONT)); label.setTextAlignment(model.getTextAlignment()); label.setTransparent(model.getTransparent()); label.setEnabled(model.isAccesible() && getExecutionMode().equals(ExecutionMode.RUN_MODE)); return label; } /** * {@inheritDoc} */ @Override protected void createEditPoliciesHook() { installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE, new LabelDirectEditPolicy()); } /** * {@inheritDoc} */ @Override public void performRequest(final Request req) { Object type = req.getType(); // entering a value is only allowed in run mode and when the widget is // enabled if ((type != null) && (type.equals(RequestConstants.REQ_OPEN) || type.equals(RequestConstants.REQ_DIRECT_EDIT))) { performDirectEdit(); } } /** * Open the cell editor for direct editing. */ private void performDirectEdit() { CellEditor cellEditor = createCellEditor(); locateCellEditor(cellEditor); cellEditor.activate(); cellEditor.setFocus(); } /** * Create the cell editor for direct editing. * * @return The cell editor for direct editing. */ private CellEditor createCellEditor() { final CellEditor result = new TextCellEditor((Composite) getViewer().getControl()); // init cell editor... String currentValue = "N/A"; //$NON-NLS-1$ currentValue = getWidgetModel().getStringProperty(TextInputModel.PROP_INPUT_TEXT); result.setValue(currentValue); final Text text = (Text) result.getControl(); text.addKeyListener(new KeyAdapter() { @Override public void keyPressed(final KeyEvent e) { if ((e.keyCode == SWT.CR) || (e.keyCode == SWT.KEYPAD_CR)) { int option = getWidgetModel().getArrayOptionProperty(TextInputModel.PROP_TEXT_TYPE); TextTypeEnum propertyValue = TextTypeEnum.values()[option]; if (!propertyValue.isValidFormat(text.getText())) { InvalidFormatDialog dialog = new InvalidFormatDialog(Display.getCurrent().getActiveShell()); dialog.setText(text.getText()); dialog.open(); LOG.warn("Invalid value format: " + text.getText()); return; } DirectEditCommand cmd = new DirectEditCommand(text.getText(), getExecutionMode()); // In EDIT mode use the CommandStack provided by the DisplayEditor to execute the command. if (getExecutionMode() == ExecutionMode.EDIT_MODE) { getViewer().getEditDomain().getCommandStack().execute(cmd); } else { cmd.execute(); } } else if (e.keyCode == SWT.ESC) { result.deactivate(); } } }); text.addVerifyListener(new VerifyListener() { @Override public void verifyText(final VerifyEvent e) { e.doit = true; int option = getWidgetModel().getArrayOptionProperty(TextInputModel.PROP_TEXT_TYPE); TextTypeEnum propertyValue = TextTypeEnum.values()[option]; e.doit = propertyValue.isValidChars(e.character, e.text, e.start); } }); text.setForeground(getModelColor(AbstractWidgetModel.PROP_COLOR_FOREGROUND)); text.setFont(getModelFont(TextInputModel.PROP_FONT)); // calculate background color RGB backgroundRgb = getModelColor(AbstractWidgetModel.PROP_COLOR_BACKGROUND).getRGB(); int red = Math.min(backgroundRgb.red + INPUT_FIELD_BRIGHTNESS, 255); int green = Math.min(backgroundRgb.green + INPUT_FIELD_BRIGHTNESS, 255); int blue = Math.min(backgroundRgb.blue + INPUT_FIELD_BRIGHTNESS, 255); Color backgroundColor = CustomMediaFactory.getInstance().getColor(new RGB(red, green, blue)); text.setBackground(backgroundColor); text.selectAll(); return result; } /** * Locate the given cell editor . * * @param cellEditor * A cell editor. */ private void locateCellEditor(final CellEditor cellEditor) { Rectangle rect = TextInputEditPart.this.figure.getBounds().getCopy(); rect.x = rect.x + FRAME_WIDTH; rect.y = rect.y + FRAME_WIDTH; rect.height = rect.height - (FRAME_WIDTH * 1); rect.width = rect.width - (FRAME_WIDTH * 1); getFigure().translateToAbsolute(rect); cellEditor.getControl().setBounds(rect.x, rect.y, rect.width, rect.height); cellEditor.getControl().setLayoutData(new GridData(SWT.CENTER)); cellEditor.getControl().setVisible(true); } private final class InvalidFormatDialog extends Dialog { private String _text; private InvalidFormatDialog(final Shell parentShell) { super(parentShell); setShellStyle(SWT.MODELESS | SWT.CLOSE | SWT.MAX | SWT.TITLE | SWT.BORDER | SWT.RESIZE); this.setText("Titel"); } /** * {@inheritDoc} */ @Override protected void configureShell(final Shell shell) { super.configureShell(shell); shell.setText("Invalid format"); } @Override protected Control createDialogArea(final Composite parent) { Composite composite = (Composite) super.createDialogArea(parent); composite.setLayout(new GridLayout(2, false)); Label label = new Label(composite, SWT.NONE); label.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false)); label.setText("Invalid format of new value: "); Text text = new Text(composite, SWT.NONE); text.setText(_text); text.setEditable(false); text.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); return composite; } @Override protected Control createButtonBar(final Composite parent) { Control createButtonBar = super.createButtonBar(parent); getButton(IDialogConstants.OK_ID).setText("Copy"); return createButtonBar; } @Override protected void okPressed() { Clipboard clipboard = new Clipboard(Display.getCurrent()); TextTransfer textTransfer = TextTransfer.getInstance(); Transfer[] transfers = new Transfer[] { textTransfer }; Object[] data = new Object[] { _text }; clipboard.setContents(data, transfers); clipboard.dispose(); super.okPressed(); } public void setText(final String text) { _text = text; } } /** * The direct edit command that changes the input text. */ private class DirectEditCommand extends Command { /** * The entered input text. */ private final String _text; private final ExecutionMode _executionMode; private String _oldValue; /** * Standard constructor. * * @param text * The entered input text. */ public DirectEditCommand(final String text, final ExecutionMode executionMode) { _text = text; _executionMode = executionMode; } /** * {@inheritDoc} */ @Override public void execute() { execute(_text); } private void execute(final String newText) { _oldValue = getWidgetModel().getStringProperty(TextInputModel.PROP_INPUT_TEXT); if (_executionMode == ExecutionMode.RUN_MODE) { // In RUN mode set the manual value, because the connected // channel sets the // property value. getCastedModel().setPropertyManualValue(TextInputModel.PROP_INPUT_TEXT, newText); } else { // In EDIT mode we can set the property value directly. getCastedModel().setPropertyValue(TextInputModel.PROP_INPUT_TEXT, newText); } } /** * {@inheritDoc} */ @Override public boolean canUndo() { // Only in EDIT mode the command can be undone. return _executionMode == ExecutionMode.EDIT_MODE; } @Override public void undo() { execute(_oldValue); } } /** * The direct edit policy. */ private class LabelDirectEditPolicy extends DirectEditPolicy { /** * {@inheritDoc} */ @Override protected Command getDirectEditCommand(final DirectEditRequest request) { DirectEditCommand command = new DirectEditCommand((String) request.getCellEditor().getValue(), getExecutionMode()); return command; } /** * {@inheritDoc} */ @Override protected void showCurrentEditValue(final DirectEditRequest request) { // do nothing } } /** * {@inheritDoc} */ @Override protected void registerPropertyChangeHandlers() { super.registerPropertyChangeHandlers(); // input text IWidgetPropertyChangeHandler textHandler = new IWidgetPropertyChangeHandler() { @Override public boolean handleChange(final Object oldValue, final Object newValue, final IFigure refreshableFigure) { RefreshableLabelFigure label = (RefreshableLabelFigure) refreshableFigure; label.setTextValue(determineLabel(TextInputModel.PROP_INPUT_TEXT)); return true; } }; setPropertyChangeHandler(TextInputModel.PROP_INPUT_TEXT, textHandler); // font setPropertyChangeHandler(TextInputModel.PROP_FONT, new FontChangeHandler<RefreshableLabelFigure>() { @Override protected void doHandle(final RefreshableLabelFigure refreshLableFigure, final Font font) { refreshLableFigure.setFont(font); } }); // text alignment IWidgetPropertyChangeHandler alignmentHandler = new IWidgetPropertyChangeHandler() { @Override public boolean handleChange(final Object oldValue, final Object newValue, final IFigure refreshableFigure) { RefreshableLabelFigure label = (RefreshableLabelFigure) refreshableFigure; label.setTextAlignment((Integer) newValue); return true; } }; setPropertyChangeHandler(TextInputModel.PROP_TEXT_ALIGNMENT, alignmentHandler); // transparent background IWidgetPropertyChangeHandler transparentHandler = new IWidgetPropertyChangeHandler() { @Override public boolean handleChange(final Object oldValue, final Object newValue, final IFigure refreshableFigure) { RefreshableLabelFigure label = (RefreshableLabelFigure) refreshableFigure; label.setTransparent((Boolean) newValue); return true; } }; setPropertyChangeHandler(TextInputModel.PROP_TRANSPARENT, transparentHandler); } /** * {@inheritDoc} */ public IValue getSample(final int index) { if (index != 0) { throw new IndexOutOfBoundsException(index + " is not a valid sample index"); } TextInputModel model = (TextInputModel) getWidgetModel(); ITimestamp timestamp = TimestampFactory.now(); // Note: the IValue implementations require a Severity, otherwise the // format() method will throw a NullPointerException. We don't really // have a severity here, so we fake one. This may cause problems for // clients who rely on getting a meaningful severity from the IValue. ISeverity severity = ValueFactory.createOKSeverity(); IValue result; switch (model.getValueType()) { case DOUBLE: // try to convert the input text to a double double value = 0.0; try { value = Double.parseDouble(model.getInputText()); } catch (NumberFormatException e) { // ProcessVariableWithSamples doesn't define // what to do in case of error and there aren't any declared // checked exceptions for this method. So, the best we can // do is to rethrow an unchecked exception and hope that the // caller will handle it. throw new IllegalStateException("Text input type is Double," + " but text is not a floating point value.", e); } // Have to create a meta data object because otherwise DoubleValue's // format() method might throw a NullPointerException :( int precision = model.getPrecision(); INumericMetaData md = ValueFactory.createNumericMetaData(0, 0, 0, 0, 0, 0, precision, ""); result = ValueFactory.createDoubleValue(timestamp, severity, null, md, Quality.Original, new double[] { value }); break; case TEXT: case HEX: // hex and alias are undocumented, so treating them case ALIAS: // like text for now result = ValueFactory.createStringValue(timestamp, severity, null, Quality.Original, new String[] { model.getInputText() }); break; default: throw new AssertionError("Never get here"); } return result; } /** * {@inheritDoc} */ public int size() { // we always have one sample return 1; } }