1 /*******************************************************************************
\r
2 * Copyright (c) 2010 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.sysdyn.ui.properties.widgets.expressions;
\r
14 import java.util.List;
\r
16 import org.eclipse.jface.layout.GridDataFactory;
\r
17 import org.eclipse.jface.layout.GridLayoutFactory;
\r
18 import org.eclipse.jface.resource.JFaceResources;
\r
19 import org.eclipse.jface.resource.LocalResourceManager;
\r
20 import org.eclipse.jface.text.Document;
\r
21 import org.eclipse.jface.text.IDocument;
\r
22 import org.eclipse.jface.text.PaintManager;
\r
23 import org.eclipse.jface.text.Position;
\r
24 import org.eclipse.jface.text.source.Annotation;
\r
25 import org.eclipse.jface.text.source.AnnotationModel;
\r
26 import org.eclipse.jface.text.source.AnnotationPainter;
\r
27 import org.eclipse.jface.text.source.DefaultCharacterPairMatcher;
\r
28 import org.eclipse.jface.text.source.IAnnotationAccess;
\r
29 import org.eclipse.jface.text.source.MatchingCharacterPainter;
\r
30 import org.eclipse.jface.text.source.SourceViewer;
\r
31 import org.eclipse.swt.SWT;
\r
32 import org.eclipse.swt.custom.StyledText;
\r
33 import org.eclipse.swt.custom.VerifyKeyListener;
\r
34 import org.eclipse.swt.events.FocusEvent;
\r
35 import org.eclipse.swt.events.FocusListener;
\r
36 import org.eclipse.swt.events.KeyEvent;
\r
37 import org.eclipse.swt.events.KeyListener;
\r
38 import org.eclipse.swt.events.VerifyEvent;
\r
39 import org.eclipse.swt.graphics.Color;
\r
40 import org.eclipse.swt.graphics.Point;
\r
41 import org.eclipse.swt.graphics.RGB;
\r
42 import org.eclipse.swt.widgets.Composite;
\r
43 import org.eclipse.swt.widgets.Table;
\r
44 import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess;
\r
45 import org.simantics.sysdyn.ui.utils.SyntaxError;
\r
48 * Field for displaying a part of an expression. Expression field uses SourceViewer
\r
49 * to display annotations and other visual elements just like any other
\r
50 * source viewer in eclipse.
\r
52 * @author Teemu Lempinen
\r
53 * @author Tuomas Miettinen
\r
56 public class ExpressionField extends Composite {
\r
58 protected SourceViewer _sourceViewer;
\r
59 protected IDocument _document;
\r
60 protected AnnotationModel _annotationModel;
\r
62 public static final String MISSING_LINK = "MissingLink";
\r
63 public static final String NO_SUCH_VARIABLE = "NoSuchVariable";
\r
64 public static final String SYNTAX_ERROR = "SyntaxError";
\r
65 public static final String SYNTAX_WARNING = "SyntaxWarning";
\r
67 String oldExpression;
\r
69 IAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess();
\r
71 ExpressionFieldConfiguration expressionFieldConfiguration;
\r
73 private final LocalResourceManager resourceManager;
\r
74 private final RGB warningRGB = new RGB(255,215,0);
\r
75 private final RGB errorRGB = new RGB(255,0,0);
\r
78 * Create a new expression field
\r
82 public ExpressionField(Composite parent, int style, Table allowedVariables, boolean allowFunctions, ExpressionWidgetInput input) {
\r
83 super(parent, style);
\r
85 // Create a ResourceManager to dispose images when the widget is disposed.
\r
86 this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), this);
\r
88 GridLayoutFactory.fillDefaults().applyTo(this);
\r
90 int styles = SWT.V_SCROLL
\r
92 | SWT.FULL_SELECTION
\r
95 _document = new Document();
\r
98 _annotationModel = new AnnotationModel();
\r
99 _annotationModel.connect(_document);
\r
101 _sourceViewer = new SourceViewer(this,
\r
108 // Configuration for color management
\r
109 expressionFieldConfiguration = new ExpressionFieldConfiguration(
\r
110 new LocalResourceManager(JFaceResources.getResources(), _sourceViewer.getControl()),
\r
111 allowedVariables, allowFunctions, input);
\r
112 _sourceViewer.configure(expressionFieldConfiguration);
\r
113 AnnotationPainter painter = new AnnotationPainter(_sourceViewer, annotationAccess);
\r
114 _sourceViewer.addPainter(painter);
\r
116 // Annotation types
\r
117 Color warningColor = resourceManager.createColor(warningRGB);
\r
118 Color errorColor = resourceManager.createColor(errorRGB);
\r
119 painter.addAnnotationType(MISSING_LINK);
\r
120 painter.setAnnotationTypeColor(MISSING_LINK, warningColor);
\r
121 painter.addAnnotationType(NO_SUCH_VARIABLE);
\r
122 painter.setAnnotationTypeColor(NO_SUCH_VARIABLE, errorColor);
\r
123 painter.addAnnotationType(SYNTAX_ERROR);
\r
124 painter.setAnnotationTypeColor(SYNTAX_ERROR, errorColor);
\r
125 painter.addAnnotationType(SYNTAX_WARNING);
\r
126 painter.setAnnotationTypeColor(SYNTAX_WARNING, warningColor);
\r
128 _sourceViewer.setDocument(_document, _annotationModel);
\r
130 GridDataFactory.fillDefaults().grab(true, true).applyTo(_sourceViewer.getControl());
\r
132 // Parenthesis matching
\r
133 PaintManager paintManager = new PaintManager(_sourceViewer);
\r
134 MatchingCharacterPainter matchingCharacterPainter = new MatchingCharacterPainter(_sourceViewer,
\r
135 new DefaultCharacterPairMatcher( new char[] {'(', ')', '{', '}', '[', ']'} ));
\r
136 matchingCharacterPainter.setColor(resourceManager.createColor(new RGB(160, 160, 160)));
\r
137 paintManager.addPainter(matchingCharacterPainter);
\r
140 // Listener for canceling editing. ESC -> revert back to original text
\r
141 _sourceViewer.getTextWidget().addKeyListener(new KeyListener() {
\r
144 public void keyReleased(KeyEvent e) {
\r
148 public void keyPressed(KeyEvent e) {
\r
149 // Check if the expression field has an active completion assistant
\r
150 if (!isAssistSessionActive()) {
\r
151 if(e.keyCode == SWT.ESC && getExpression() != null) {
\r
152 ((StyledText)e.widget).setText(oldExpression);
\r
153 ((StyledText)e.widget).setSelection(getExpression().length());
\r
159 /* Focus listener saving and restoring selections
\r
160 * When focus is lost, current selection is saved, but the selection is removed.
\r
161 * When focus is gained back, the selection is restored
\r
163 _sourceViewer.getTextWidget().addFocusListener(new FocusListener() {
\r
165 Point selection = null;
\r
167 public void focusLost(FocusEvent e) {
\r
168 selection = ((StyledText)e.widget).getSelection();
\r
169 ((StyledText)e.widget).setSelection(0);
\r
173 public void focusGained(FocusEvent e) {
\r
174 if(selection != null)
\r
175 ((StyledText)e.widget).setSelection(selection);
\r
179 _sourceViewer.appendVerifyKeyListener(new VerifyKeyListener() {
\r
181 public void verifyKey(VerifyEvent event) {
\r
182 // Check for Ctrl+Spacebar
\r
183 if (event.stateMask == SWT.CTRL && event.character == ' ') {
\r
184 // Check if source viewer is able to perform operation
\r
185 if (_sourceViewer.canDoOperation(SourceViewer.CONTENTASSIST_PROPOSALS)) {
\r
186 // Perform operation
\r
187 _sourceViewer.doOperation(SourceViewer.CONTENTASSIST_PROPOSALS);
\r
189 // Veto this key press to avoid further processing
\r
190 event.doit = false;
\r
199 * Returns the {@link SourceViewer} of this ExpressionField
\r
200 * @return Returns the {@link SourceViewer} of this ExpressionField
\r
202 public SourceViewer getSourceViewer() {
\r
203 return this._sourceViewer;
\r
206 public boolean isAssistSessionActive() {
\r
207 return expressionFieldConfiguration.isAssistSessionActive();
\r
211 * Sets missing link annotations to given positions
\r
212 * @param positions Positions for missing link annotations
\r
214 public void setMissingLinkAnnotations(List<Position> positions){
\r
215 for(Position p : positions) {
\r
216 Annotation annotation = new Annotation(false);
\r
217 annotation.setType(MISSING_LINK);
\r
218 annotation.setText("No link to this variable");
\r
219 _annotationModel.addAnnotation(annotation, p);
\r
224 * Sets no such variable annotations to given positions
\r
225 * @param positions Positions for no such variable annotations
\r
227 public void setNoSuchVariableAnnotations(List<Position> positions){
\r
228 for(Position p : positions) {
\r
229 Annotation annotation = new Annotation(false);
\r
230 annotation.setType(NO_SUCH_VARIABLE);
\r
231 annotation.setText("No such variable in model");
\r
232 _annotationModel.addAnnotation(annotation, p);
\r
238 * Sets a syntax error annoattion to the expression field
\r
239 * @param syntaxError
\r
241 public void setSyntaxError(SyntaxError syntaxError) {
\r
242 Annotation annotation = new Annotation(false);
\r
243 annotation.setType(syntaxError.getType());
\r
244 annotation.setText(syntaxError.getMessage());
\r
245 Position p = new Position(syntaxError.getStart(_document), syntaxError.getOffset(_document));
\r
246 _annotationModel.addAnnotation(annotation, p);
\r
250 * Resets all annotations
\r
252 public void resetAnnotations() {
\r
253 _annotationModel.removeAllAnnotations();
\r
257 * Sets an expression to this expression field
\r
258 * @param expression
\r
260 public void setExpression(String expression) {
\r
261 _document.set(expression);
\r
262 this.oldExpression = expression;
\r
266 * Returns the expression of this expression field
\r
269 public String getExpression() {
\r
270 return this._document.get();
\r
274 * Returns the current selection
\r
275 * @return current selection
\r
277 public Point getSelection() {
\r
278 return _sourceViewer.getSelectedRange();
\r
282 * Set selection for this expression field. The length of the selection is 0
\r
283 * @param selection Selection location
\r
285 public void setSelection(int selection) {
\r
286 this._sourceViewer.setSelectedRange(selection, 0);
\r
289 public IDocument getDocument() {
\r
294 * Focus to this expression field
\r
296 public void focus() {
\r
297 this._sourceViewer.getTextWidget().forceFocus();
\r