]> gerrit.simantics Code Review - simantics/sysdyn.git/blob
39e7381d955862507c718a1d50b108ea41cdffcc
[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     ExpressionFieldConfiguration expressionFieldConfiguration;\r
73     \r
74     /**\r
75      * Create a new expression field\r
76      * @param parent\r
77      * @param style\r
78      */\r
79     public ExpressionField(Composite parent, int style, Table allowedVariables, boolean allowFunctions) {\r
80         super(parent, style);\r
81         \r
82         GridLayoutFactory.fillDefaults().applyTo(this);\r
83         \r
84         int styles = SWT.V_SCROLL\r
85         | SWT.MULTI\r
86         | SWT.FULL_SELECTION\r
87         | SWT.WRAP;\r
88 \r
89         _document = new Document();\r
90         _document.set("");\r
91 \r
92         _annotationModel = new AnnotationModel();\r
93         _annotationModel.connect(_document);\r
94 \r
95         _sourceViewer = new SourceViewer(this,\r
96 \r
97                 null,\r
98                 null,\r
99                 true,\r
100                 styles);\r
101         \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
107 \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
115         \r
116         _sourceViewer.setDocument(_document, _annotationModel);\r
117 \r
118         GridDataFactory.fillDefaults().grab(true, true).applyTo(_sourceViewer.getControl());\r
119 \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
126         \r
127         \r
128         // Listener for canceling editing. ESC -> revert back to original text\r
129         _sourceViewer.getTextWidget().addKeyListener(new KeyListener() {\r
130 \r
131             @Override\r
132             public void keyReleased(KeyEvent e) {\r
133             }\r
134 \r
135             @Override\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
142                                         }\r
143                 }   \r
144             }\r
145         });\r
146        \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
150          */\r
151         _sourceViewer.getTextWidget().addFocusListener(new FocusListener() {\r
152             \r
153             Point selection = null;\r
154             @Override\r
155             public void focusLost(FocusEvent e) {\r
156                 selection = ((StyledText)e.widget).getSelection();\r
157                 ((StyledText)e.widget).setSelection(0);\r
158             }\r
159             \r
160             @Override\r
161             public void focusGained(FocusEvent e) {\r
162                 if(selection != null)\r
163                     ((StyledText)e.widget).setSelection(selection);\r
164             }\r
165         });\r
166         \r
167         _sourceViewer.appendVerifyKeyListener(new VerifyKeyListener() {\r
168                         @Override\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
176                                         }\r
177                                         // Veto this key press to avoid further processing\r
178                                         event.doit = false;\r
179                                 }\r
180                         }\r
181 \r
182                 });\r
183         \r
184     }\r
185 \r
186     /**\r
187      * Returns the {@link SourceViewer} of this ExpressionField\r
188      * @return Returns the {@link SourceViewer} of this ExpressionField\r
189      */\r
190     public SourceViewer getSourceViewer() {\r
191         return this._sourceViewer;\r
192     }\r
193     \r
194         public boolean isAssistSessionActive() {\r
195                 return expressionFieldConfiguration.isAssistSessionActive();\r
196         }\r
197 \r
198     /**\r
199      * Sets missing link annotations to given positions\r
200      * @param positions Positions for missing link annotations\r
201      */\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
208         }\r
209     }\r
210     \r
211     /**\r
212      * Sets no such variable annotations to given positions\r
213      * @param positions Positions for no such variable annotations\r
214      */\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
221         }\r
222     }\r
223 \r
224     /**\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
228      */\r
229     public void setSyntaxError(Token token, String message){\r
230         setSyntaxError(token.image, message, token.beginLine, token.beginColumn, token.endLine, token.endColumn);\r
231     }\r
232     \r
233     /**\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
237      */\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
240     }\r
241     \r
242     /**\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
250      */\r
251     public void setSyntaxError(String image, String message, int beginLine, int beginColumn, int endLine, int endColumn) {\r
252         int start = 0;\r
253         int offset = this._document.getLength();\r
254         if(image != null && this._document.getLength() > 0) {\r
255             try {\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
260             }\r
261         }\r
262         setSyntaxError(start, offset, SYNTAX_ERROR, message == null ? "Syntax Error" : message);\r
263     }\r
264     \r
265     /**\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
271      */\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
278     }\r
279 \r
280     /**\r
281      * Resets all annotations\r
282      */\r
283     public void resetAnnotations() {\r
284         _annotationModel.removeAllAnnotations();\r
285     }\r
286     \r
287     /**\r
288      * Sets an expression to this expression field\r
289      * @param expression\r
290      */\r
291     public void setExpression(String expression) {\r
292         _document.set(expression);\r
293         this.oldExpression = expression;\r
294     }\r
295 \r
296     /**\r
297      * Returns the expression of this expression field\r
298      * @return\r
299      */\r
300     public String getExpression() {\r
301         return this._document.get();\r
302     }\r
303 \r
304     /**\r
305      * Returns the current selection\r
306      * @return current selection\r
307      */\r
308     public Point getSelection() {\r
309         return _sourceViewer.getSelectedRange();\r
310     }\r
311 \r
312     /**\r
313      * Set selection for this expression field. The length of the selection is 0\r
314      * @param selection Selection location\r
315      */\r
316     public void setSelection(int selection) {\r
317         this._sourceViewer.setSelectedRange(selection, 0);\r
318     }\r
319 \r
320     public IDocument getDocument() {\r
321         return _document;\r
322     }\r
323 \r
324     /**\r
325      * Focus to this expression field\r
326      */\r
327     public void focus() {\r
328         this._sourceViewer.getTextWidget().forceFocus();\r
329     }\r
330 \r
331 }\r