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
72 ExpressionFieldConfiguration expressionFieldConfiguration;
\r
75 * Create a new expression field
\r
79 public ExpressionField(Composite parent, int style, Table allowedVariables, boolean allowFunctions) {
\r
80 super(parent, style);
\r
82 GridLayoutFactory.fillDefaults().applyTo(this);
\r
84 int styles = SWT.V_SCROLL
\r
86 | SWT.FULL_SELECTION
\r
89 _document = new Document();
\r
92 _annotationModel = new AnnotationModel();
\r
93 _annotationModel.connect(_document);
\r
95 _sourceViewer = new SourceViewer(this,
\r
102 // Configuration for color management
\r
103 expressionFieldConfiguration = new ExpressionFieldConfiguration(cManager, allowedVariables, allowFunctions);
\r
104 _sourceViewer.configure(expressionFieldConfiguration);
\r
105 AnnotationPainter painter = new AnnotationPainter(_sourceViewer, annotationAccess);
\r
106 _sourceViewer.addPainter(painter);
\r
108 // Annotation types
\r
109 painter.addAnnotationType(MISSING_LINK);
\r
110 painter.setAnnotationTypeColor(MISSING_LINK, new Color(this.getDisplay(), 255,215,0));
\r
111 painter.addAnnotationType(NO_SUCH_VARIABLE);
\r
112 painter.setAnnotationTypeColor(NO_SUCH_VARIABLE, new Color(this.getDisplay(), 255,0,0));
\r
113 painter.addAnnotationType(SYNTAX_ERROR);
\r
114 painter.setAnnotationTypeColor(SYNTAX_ERROR, new Color(this.getDisplay(), 255,0,0));
\r
116 _sourceViewer.setDocument(_document, _annotationModel);
\r
118 GridDataFactory.fillDefaults().grab(true, true).applyTo(_sourceViewer.getControl());
\r
120 // Parenthesis matching
\r
121 PaintManager paintManager = new PaintManager(_sourceViewer);
\r
122 MatchingCharacterPainter matchingCharacterPainter = new MatchingCharacterPainter(_sourceViewer,
\r
123 new DefaultCharacterPairMatcher( new char[] {'(', ')', '{', '}', '[', ']'} ));
\r
124 matchingCharacterPainter.setColor(new Color(Display.getCurrent(), new RGB(160, 160, 160)));
\r
125 paintManager.addPainter(matchingCharacterPainter);
\r
128 // Listener for canceling editing. ESC -> revert back to original text
\r
129 _sourceViewer.getTextWidget().addKeyListener(new KeyListener() {
\r
132 public void keyReleased(KeyEvent e) {
\r
136 public void keyPressed(KeyEvent e) {
\r
137 // Check if the expression field has an active completion assistant
\r
138 if (!isAssistSessionActive()) {
\r
139 if(e.keyCode == SWT.ESC && getExpression() != null) {
\r
140 ((StyledText)e.widget).setText(oldExpression);
\r
141 ((StyledText)e.widget).setSelection(getExpression().length());
\r
147 /* Focus listener saving and restoring selections
\r
148 * When focus is lost, current selection is saved, but the selection is removed.
\r
149 * When focus is gained back, the selection is restored
\r
151 _sourceViewer.getTextWidget().addFocusListener(new FocusListener() {
\r
153 Point selection = null;
\r
155 public void focusLost(FocusEvent e) {
\r
156 selection = ((StyledText)e.widget).getSelection();
\r
157 ((StyledText)e.widget).setSelection(0);
\r
161 public void focusGained(FocusEvent e) {
\r
162 if(selection != null)
\r
163 ((StyledText)e.widget).setSelection(selection);
\r
167 _sourceViewer.appendVerifyKeyListener(new VerifyKeyListener() {
\r
169 public void verifyKey(VerifyEvent event) {
\r
170 // Check for Ctrl+Spacebar
\r
171 if (event.stateMask == SWT.CTRL && event.character == ' ') {
\r
172 // Check if source viewer is able to perform operation
\r
173 if (_sourceViewer.canDoOperation(SourceViewer.CONTENTASSIST_PROPOSALS)) {
\r
174 // Perform operation
\r
175 _sourceViewer.doOperation(SourceViewer.CONTENTASSIST_PROPOSALS);
\r
177 // Veto this key press to avoid further processing
\r
178 event.doit = false;
\r
187 * Returns the {@link SourceViewer} of this ExpressionField
\r
188 * @return Returns the {@link SourceViewer} of this ExpressionField
\r
190 public SourceViewer getSourceViewer() {
\r
191 return this._sourceViewer;
\r
194 public boolean isAssistSessionActive() {
\r
195 return expressionFieldConfiguration.isAssistSessionActive();
\r
199 * Sets missing link annotations to given positions
\r
200 * @param positions Positions for missing link annotations
\r
202 public void setMissingLinkAnnotations(List<Position> positions){
\r
203 for(Position p : positions) {
\r
204 Annotation annotation = new Annotation(false);
\r
205 annotation.setType(MISSING_LINK);
\r
206 annotation.setText("No link to this variable");
\r
207 _annotationModel.addAnnotation(annotation, p);
\r
212 * Sets no such variable annotations to given positions
\r
213 * @param positions Positions for no such variable annotations
\r
215 public void setNoSuchVariableAnnotations(List<Position> positions){
\r
216 for(Position p : positions) {
\r
217 Annotation annotation = new Annotation(false);
\r
218 annotation.setType(NO_SUCH_VARIABLE);
\r
219 annotation.setText("No such variable in model");
\r
220 _annotationModel.addAnnotation(annotation, p);
\r
225 * Sets syntax error for the given token
\r
226 * @param token Token with syntax error
\r
227 * @param message Message to be displayed in tool tips
\r
229 public void setSyntaxError(Token token, String message){
\r
230 setSyntaxError(token.image, message, token.beginLine, token.beginColumn, token.endLine, token.endColumn);
\r
234 * Sets syntax error for the given token
\r
235 * @param token Token with syntax error
\r
236 * @param message Message to be displayed in tool tips
\r
238 public void setSyntaxError(org.simantics.sysdyn.tableParser.Token token, String message){
\r
239 setSyntaxError(token.image, message, token.beginLine, token.beginColumn, token.endLine, token.endColumn);
\r
243 * Sets syntax error to given location
\r
244 * @param image Token image
\r
245 * @param message Message to be displayed in tool tips
\r
246 * @param beginLine Begin line
\r
247 * @param beginColumn Begin column
\r
248 * @param endLine End line
\r
249 * @param endColumn End column
\r
251 public void setSyntaxError(String image, String message, int beginLine, int beginColumn, int endLine, int endColumn) {
\r
253 int offset = this._document.getLength();
\r
254 if(image != null && this._document.getLength() > 0) {
\r
256 start = this._document.getLineOffset(beginLine - 1) + beginColumn - 1;
\r
257 offset = this._document.getLineOffset(endLine - 1) + endColumn - start;
\r
258 } catch (BadLocationException e) {
\r
259 e.printStackTrace();
\r
262 setSyntaxError(start, offset, SYNTAX_ERROR, message == null ? "Syntax Error" : message);
\r
266 * Sets syntax error to given start and offset
\r
267 * @param start Start location
\r
268 * @param offset Offset
\r
269 * @param type Error type (SYNTAX_ERROR, MISSING_LINK, NO_SUCH_VARIABLE)
\r
270 * @param text Message to be displayedin tool tips
\r
272 public void setSyntaxError(int start, int offset, String type, String text) {
\r
273 Annotation annotation = new Annotation(false);
\r
274 annotation.setType(type);
\r
275 annotation.setText(text);
\r
276 Position p = new Position(start, offset);
\r
277 _annotationModel.addAnnotation(annotation, p);
\r
281 * Resets all annotations
\r
283 public void resetAnnotations() {
\r
284 _annotationModel.removeAllAnnotations();
\r
288 * Sets an expression to this expression field
\r
289 * @param expression
\r
291 public void setExpression(String expression) {
\r
292 _document.set(expression);
\r
293 this.oldExpression = expression;
\r
297 * Returns the expression of this expression field
\r
300 public String getExpression() {
\r
301 return this._document.get();
\r
305 * Returns the current selection
\r
306 * @return current selection
\r
308 public Point getSelection() {
\r
309 return _sourceViewer.getSelectedRange();
\r
313 * Set selection for this expression field. The length of the selection is 0
\r
314 * @param selection Selection location
\r
316 public void setSelection(int selection) {
\r
317 this._sourceViewer.setSelectedRange(selection, 0);
\r
320 public IDocument getDocument() {
\r
325 * Focus to this expression field
\r
327 public void focus() {
\r
328 this._sourceViewer.getTextWidget().forceFocus();
\r