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.text.BadLocationException;
\r
19 import org.eclipse.jface.text.Document;
\r
20 import org.eclipse.jface.text.IDocument;
\r
21 import org.eclipse.jface.text.PaintManager;
\r
22 import org.eclipse.jface.text.Position;
\r
23 import org.eclipse.jface.text.source.Annotation;
\r
24 import org.eclipse.jface.text.source.AnnotationModel;
\r
25 import org.eclipse.jface.text.source.AnnotationPainter;
\r
26 import org.eclipse.jface.text.source.DefaultCharacterPairMatcher;
\r
27 import org.eclipse.jface.text.source.IAnnotationAccess;
\r
28 import org.eclipse.jface.text.source.MatchingCharacterPainter;
\r
29 import org.eclipse.jface.text.source.SourceViewer;
\r
30 import org.eclipse.swt.SWT;
\r
31 import org.eclipse.swt.custom.StyledText;
\r
32 import org.eclipse.swt.custom.VerifyKeyListener;
\r
33 import org.eclipse.swt.events.FocusEvent;
\r
34 import org.eclipse.swt.events.FocusListener;
\r
35 import org.eclipse.swt.events.KeyEvent;
\r
36 import org.eclipse.swt.events.KeyListener;
\r
37 import org.eclipse.swt.events.VerifyEvent;
\r
38 import org.eclipse.swt.graphics.Color;
\r
39 import org.eclipse.swt.graphics.RGB;
\r
40 import org.eclipse.swt.widgets.Composite;
\r
41 import org.eclipse.swt.widgets.Display;
\r
42 import org.eclipse.swt.widgets.Table;
\r
43 import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess;
\r
44 import org.eclipse.swt.graphics.Point;
\r
45 import org.simantics.sysdyn.expressionParser.Token;
\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
66 String oldExpression;
\r
68 ColorManager cManager = new ColorManager();
\r
70 IAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess();
\r
73 * Create a new expression field
\r
77 public ExpressionField(Composite parent, int style, Table allowedVariables, boolean allowFunctions) {
\r
78 super(parent, style);
\r
80 GridLayoutFactory.fillDefaults().applyTo(this);
\r
82 int styles = SWT.V_SCROLL
\r
84 | SWT.FULL_SELECTION
\r
87 _document = new Document();
\r
90 _annotationModel = new AnnotationModel();
\r
91 _annotationModel.connect(_document);
\r
93 _sourceViewer = new SourceViewer(this,
\r
100 // Configuration for color management
\r
101 _sourceViewer.configure(new ExpressionFieldConfiguration(cManager, allowedVariables, allowFunctions));
\r
102 AnnotationPainter painter = new AnnotationPainter(_sourceViewer, annotationAccess);
\r
103 _sourceViewer.addPainter(painter);
\r
105 // Annotation types
\r
106 painter.addAnnotationType(MISSING_LINK);
\r
107 painter.setAnnotationTypeColor(MISSING_LINK, new Color(this.getDisplay(), 255,215,0));
\r
108 painter.addAnnotationType(NO_SUCH_VARIABLE);
\r
109 painter.setAnnotationTypeColor(NO_SUCH_VARIABLE, new Color(this.getDisplay(), 255,0,0));
\r
110 painter.addAnnotationType(SYNTAX_ERROR);
\r
111 painter.setAnnotationTypeColor(SYNTAX_ERROR, new Color(this.getDisplay(), 255,0,0));
\r
113 _sourceViewer.setDocument(_document, _annotationModel);
\r
115 GridDataFactory.fillDefaults().grab(true, true).applyTo(_sourceViewer.getControl());
\r
117 // Parenthesis matching
\r
118 PaintManager paintManager = new PaintManager(_sourceViewer);
\r
119 MatchingCharacterPainter matchingCharacterPainter = new MatchingCharacterPainter(_sourceViewer,
\r
120 new DefaultCharacterPairMatcher( new char[] {'(', ')', '{', '}', '[', ']'} ));
\r
121 matchingCharacterPainter.setColor(new Color(Display.getCurrent(), new RGB(160, 160, 160)));
\r
122 paintManager.addPainter(matchingCharacterPainter);
\r
125 // Listener for canceling editing. ESC -> revert back to original text
\r
126 _sourceViewer.getTextWidget().addKeyListener(new KeyListener() {
\r
129 public void keyReleased(KeyEvent e) {
\r
133 public void keyPressed(KeyEvent e) {
\r
134 if(e.keyCode == SWT.ESC && getExpression() != null) {
\r
135 ((StyledText)e.widget).setText(oldExpression);
\r
136 ((StyledText)e.widget).setSelection(getExpression().length());
\r
141 /* Focus listener saving and restoring selections
\r
142 * When focus is lost, current selection is saved, but the selection is removed.
\r
143 * When focus is gained back, the selection is restored
\r
145 _sourceViewer.getTextWidget().addFocusListener(new FocusListener() {
\r
147 Point selection = null;
\r
149 public void focusLost(FocusEvent e) {
\r
150 selection = ((StyledText)e.widget).getSelection();
\r
151 ((StyledText)e.widget).setSelection(0);
\r
155 public void focusGained(FocusEvent e) {
\r
156 if(selection != null)
\r
157 ((StyledText)e.widget).setSelection(selection);
\r
161 _sourceViewer.appendVerifyKeyListener(new VerifyKeyListener() {
\r
163 public void verifyKey(VerifyEvent event) {
\r
164 // Check for Ctrl+Spacebar
\r
165 if (event.stateMask == SWT.CTRL && event.character == ' ') {
\r
166 // Check if source viewer is able to perform operation
\r
167 if (_sourceViewer.canDoOperation(SourceViewer.CONTENTASSIST_PROPOSALS)) {
\r
168 // Perform operation
\r
169 _sourceViewer.doOperation(SourceViewer.CONTENTASSIST_PROPOSALS);
\r
171 // Veto this key press to avoid further processing
\r
172 event.doit = false;
\r
180 * Returns the {@link SourceViewer} of this ExpressionField
\r
181 * @return Returns the {@link SourceViewer} of this ExpressionField
\r
183 public SourceViewer getSourceViewer() {
\r
184 return this._sourceViewer;
\r
188 * Sets missing link annotations to given positions
\r
189 * @param positions Positions for missing link annotations
\r
191 public void setMissingLinkAnnotations(List<Position> positions){
\r
192 for(Position p : positions) {
\r
193 Annotation annotation = new Annotation(false);
\r
194 annotation.setType(MISSING_LINK);
\r
195 annotation.setText("No link to this variable");
\r
196 _annotationModel.addAnnotation(annotation, p);
\r
201 * Sets no such variable annotations to given positions
\r
202 * @param positions Positions for no such variable annotations
\r
204 public void setNoSuchVariableAnnotations(List<Position> positions){
\r
205 for(Position p : positions) {
\r
206 Annotation annotation = new Annotation(false);
\r
207 annotation.setType(NO_SUCH_VARIABLE);
\r
208 annotation.setText("No such variable in model");
\r
209 _annotationModel.addAnnotation(annotation, p);
\r
214 * Sets syntax error for the given token
\r
215 * @param token Token with syntax error
\r
216 * @param message Message to be displayed in tool tips
\r
218 public void setSyntaxError(Token token, String message){
\r
219 setSyntaxError(token.image, message, token.beginLine, token.beginColumn, token.endLine, token.endColumn);
\r
223 * Sets syntax error for the given token
\r
224 * @param token Token with syntax error
\r
225 * @param message Message to be displayed in tool tips
\r
227 public void setSyntaxError(org.simantics.sysdyn.tableParser.Token token, String message){
\r
228 setSyntaxError(token.image, message, token.beginLine, token.beginColumn, token.endLine, token.endColumn);
\r
232 * Sets syntax error to given location
\r
233 * @param image Token image
\r
234 * @param message Message to be displayed in tool tips
\r
235 * @param beginLine Begin line
\r
236 * @param beginColumn Begin column
\r
237 * @param endLine End line
\r
238 * @param endColumn End column
\r
240 public void setSyntaxError(String image, String message, int beginLine, int beginColumn, int endLine, int endColumn) {
\r
242 int offset = this._document.getLength();
\r
243 if(image != null && this._document.getLength() > 0) {
\r
245 start = this._document.getLineOffset(beginLine - 1) + beginColumn - 1;
\r
246 offset = this._document.getLineOffset(endLine - 1) + endColumn - start;
\r
247 } catch (BadLocationException e) {
\r
248 e.printStackTrace();
\r
251 setSyntaxError(start, offset, SYNTAX_ERROR, message == null ? "Syntax Error" : message);
\r
255 * Sets syntax error to given start and offset
\r
256 * @param start Start location
\r
257 * @param offset Offset
\r
258 * @param type Error type (SYNTAX_ERROR, MISSING_LINK, NO_SUCH_VARIABLE)
\r
259 * @param text Message to be displayedin tool tips
\r
261 public void setSyntaxError(int start, int offset, String type, String text) {
\r
262 Annotation annotation = new Annotation(false);
\r
263 annotation.setType(type);
\r
264 annotation.setText(text);
\r
265 Position p = new Position(start, offset);
\r
266 _annotationModel.addAnnotation(annotation, p);
\r
270 * Resets all annotations
\r
272 public void resetAnnotations() {
\r
273 _annotationModel.removeAllAnnotations();
\r
277 * Sets an expression to this expression field
\r
278 * @param expression
\r
280 public void setExpression(String expression) {
\r
281 _document.set(expression);
\r
282 this.oldExpression = expression;
\r
286 * Returns the expression of this expression field
\r
289 public String getExpression() {
\r
290 return this._document.get();
\r
294 * Returns the current selection
\r
295 * @return current selection
\r
297 public Point getSelection() {
\r
298 return _sourceViewer.getSelectedRange();
\r
302 * Set selection for this expression field. The length of the selection is 0
\r
303 * @param selection Selection location
\r
305 public void setSelection(int selection) {
\r
306 this._sourceViewer.setSelectedRange(selection, 0);
\r
309 public IDocument getDocument() {
\r
314 * Focus to this expression field
\r
316 public void focus() {
\r
317 this._sourceViewer.getTextWidget().forceFocus();
\r