]> gerrit.simantics Code Review - simantics/sysdyn.git/blob
696c960a182c2592a2689eb75b29050a3f934314
[simantics/sysdyn.git] /
1 /*******************************************************************************\r
2  * Copyright (c) 2010 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\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
8  *\r
9  * Contributors:\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
13 \r
14 import java.util.List;\r
15 \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
46 \r
47 /**\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
51  * \r
52  * @author Teemu Lempinen\r
53  * @author Tuomas Miettinen\r
54  *\r
55  */\r
56 public class ExpressionField extends Composite {\r
57 \r
58     protected SourceViewer                  _sourceViewer;\r
59     protected IDocument                     _document;\r
60     protected AnnotationModel               _annotationModel;\r
61     \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     \r
66     String oldExpression;\r
67 \r
68     ColorManager cManager = new ColorManager();\r
69 \r
70     IAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess();\r
71 \r
72     /**\r
73      * Create a new expression field\r
74      * @param parent\r
75      * @param style\r
76      */\r
77     public ExpressionField(Composite parent, int style, Table allowedVariables, boolean allowFunctions) {\r
78         super(parent, style);\r
79         \r
80         GridLayoutFactory.fillDefaults().applyTo(this);\r
81         \r
82         int styles = SWT.V_SCROLL\r
83         | SWT.MULTI\r
84         | SWT.FULL_SELECTION\r
85         | SWT.WRAP;\r
86 \r
87         _document = new Document();\r
88         _document.set("");\r
89 \r
90         _annotationModel = new AnnotationModel();\r
91         _annotationModel.connect(_document);\r
92 \r
93         _sourceViewer = new SourceViewer(this,\r
94 \r
95                 null,\r
96                 null,\r
97                 true,\r
98                 styles);\r
99         \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
104 \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
112         \r
113         _sourceViewer.setDocument(_document, _annotationModel);\r
114 \r
115         GridDataFactory.fillDefaults().grab(true, true).applyTo(_sourceViewer.getControl());\r
116 \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
123         \r
124         \r
125         // Listener for canceling editing. ESC -> revert back to original text\r
126         _sourceViewer.getTextWidget().addKeyListener(new KeyListener() {\r
127 \r
128             @Override\r
129             public void keyReleased(KeyEvent e) {\r
130             }\r
131 \r
132             @Override\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
137                 }   \r
138             }\r
139         });\r
140        \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
144          */\r
145         _sourceViewer.getTextWidget().addFocusListener(new FocusListener() {\r
146             \r
147             Point selection = null;\r
148             @Override\r
149             public void focusLost(FocusEvent e) {\r
150                 selection = ((StyledText)e.widget).getSelection();\r
151                 ((StyledText)e.widget).setSelection(0);\r
152             }\r
153             \r
154             @Override\r
155             public void focusGained(FocusEvent e) {\r
156                 if(selection != null)\r
157                     ((StyledText)e.widget).setSelection(selection);\r
158             }\r
159         });\r
160         \r
161         _sourceViewer.appendVerifyKeyListener(new VerifyKeyListener() {\r
162                         @Override\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
170                                         }\r
171                                         // Veto this key press to avoid further processing\r
172                                         event.doit = false;\r
173                                 }\r
174                         }\r
175 \r
176                 });\r
177     }\r
178 \r
179     /**\r
180      * Returns the {@link SourceViewer} of this ExpressionField\r
181      * @return Returns the {@link SourceViewer} of this ExpressionField\r
182      */\r
183     public SourceViewer getSourceViewer() {\r
184         return this._sourceViewer;\r
185     }\r
186 \r
187     /**\r
188      * Sets missing link annotations to given positions\r
189      * @param positions Positions for missing link annotations\r
190      */\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
197         }\r
198     }\r
199     \r
200     /**\r
201      * Sets no such variable annotations to given positions\r
202      * @param positions Positions for no such variable annotations\r
203      */\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
210         }\r
211     }\r
212 \r
213     /**\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
217      */\r
218     public void setSyntaxError(Token token, String message){\r
219         setSyntaxError(token.image, message, token.beginLine, token.beginColumn, token.endLine, token.endColumn);\r
220     }\r
221     \r
222     /**\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
226      */\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
229     }\r
230     \r
231     /**\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
239      */\r
240     public void setSyntaxError(String image, String message, int beginLine, int beginColumn, int endLine, int endColumn) {\r
241         int start = 0;\r
242         int offset = this._document.getLength();\r
243         if(image != null && this._document.getLength() > 0) {\r
244             try {\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
249             }\r
250         }\r
251         setSyntaxError(start, offset, SYNTAX_ERROR, message == null ? "Syntax Error" : message);\r
252     }\r
253     \r
254     /**\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
260      */\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
267     }\r
268 \r
269     /**\r
270      * Resets all annotations\r
271      */\r
272     public void resetAnnotations() {\r
273         _annotationModel.removeAllAnnotations();\r
274     }\r
275     \r
276     /**\r
277      * Sets an expression to this expression field\r
278      * @param expression\r
279      */\r
280     public void setExpression(String expression) {\r
281         _document.set(expression);\r
282         this.oldExpression = expression;\r
283     }\r
284 \r
285     /**\r
286      * Returns the expression of this expression field\r
287      * @return\r
288      */\r
289     public String getExpression() {\r
290         return this._document.get();\r
291     }\r
292 \r
293     /**\r
294      * Returns the current selection\r
295      * @return current selection\r
296      */\r
297     public Point getSelection() {\r
298         return _sourceViewer.getSelectedRange();\r
299     }\r
300 \r
301     /**\r
302      * Set selection for this expression field. The length of the selection is 0\r
303      * @param selection Selection location\r
304      */\r
305     public void setSelection(int selection) {\r
306         this._sourceViewer.setSelectedRange(selection, 0);\r
307     }\r
308 \r
309     public IDocument getDocument() {\r
310         return _document;\r
311     }\r
312 \r
313     /**\r
314      * Focus to this expression field\r
315      */\r
316     public void focus() {\r
317         this._sourceViewer.getTextWidget().forceFocus();\r
318     }\r
319 \r
320 }\r