1539ee221260aec920b493831763b88c95485101
[public/netxms.git] / src / java / netxms-eclipse / NXSL / src / org / netxms / ui / eclipse / nxsl / widgets / ScriptEditor.java
1 /**
2 * NetXMS - open source network management system
3 * Copyright (C) 2003-2015 Victor Kirhenshtein
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 package org.netxms.ui.eclipse.nxsl.widgets;
20
21 import java.util.Collection;
22 import java.util.HashSet;
23 import java.util.Set;
24 import org.eclipse.jface.resource.JFaceResources;
25 import org.eclipse.jface.text.IFindReplaceTarget;
26 import org.eclipse.jface.text.ITextOperationTarget;
27 import org.eclipse.jface.text.TextViewerUndoManager;
28 import org.eclipse.jface.text.source.CompositeRuler;
29 import org.eclipse.jface.text.source.ISourceViewer;
30 import org.eclipse.jface.text.source.LineNumberRulerColumn;
31 import org.eclipse.jface.text.source.SourceViewer;
32 import org.eclipse.swt.SWT;
33 import org.eclipse.swt.custom.CLabel;
34 import org.eclipse.swt.custom.StyledText;
35 import org.eclipse.swt.custom.VerifyKeyListener;
36 import org.eclipse.swt.events.MouseAdapter;
37 import org.eclipse.swt.events.MouseEvent;
38 import org.eclipse.swt.events.MouseListener;
39 import org.eclipse.swt.events.VerifyEvent;
40 import org.eclipse.swt.graphics.Image;
41 import org.eclipse.swt.graphics.Point;
42 import org.eclipse.swt.layout.GridData;
43 import org.eclipse.swt.layout.GridLayout;
44 import org.eclipse.swt.widgets.Composite;
45 import org.eclipse.swt.widgets.Label;
46 import org.eclipse.swt.widgets.Text;
47 import org.netxms.ui.eclipse.console.resources.SharedColors;
48 import org.netxms.ui.eclipse.console.resources.SharedIcons;
49 import org.netxms.ui.eclipse.nxsl.Activator;
50 import org.netxms.ui.eclipse.nxsl.widgets.internal.NXSLDocument;
51 import org.netxms.ui.eclipse.nxsl.widgets.internal.NXSLSourceViewerConfiguration;
52
53 /**
54 * NXSL script editor
55 *
56 */
57 public class ScriptEditor extends Composite
58 {
59 private SourceViewer editor;
60 private CompositeRuler ruler;
61 private Set<String> functions = new HashSet<String>(0);
62 private Set<String> variables = new HashSet<String>(0);
63 private String[] functionsCache = new String[0];
64 private String[] variablesCache = new String[0];
65 private Image[] proposalIcons = new Image[4];
66 private String hintText;
67 private Composite hintArea;
68 private Text hintTextControl = null;
69 private Label hintsExpandButton = null;
70
71 /**
72 * @param parent
73 * @param style
74 * @param editorStyle
75 */
76 public ScriptEditor(Composite parent, int style, int editorStyle)
77 {
78 this(parent, style, editorStyle, true, null);
79 }
80
81 /**
82 * @param parent
83 * @param style
84 * @param editorStyle
85 * @param showLineNumbers
86 */
87 public ScriptEditor(Composite parent, int style, int editorStyle, boolean showLineNumbers)
88 {
89 this(parent, style, editorStyle, showLineNumbers, null);
90 }
91
92 /**
93 * @param parent
94 * @param style
95 * @param editorStyle
96 * @param showLineNumbers
97 * @param hints
98 */
99 public ScriptEditor(Composite parent, int style, int editorStyle, boolean showLineNumbers, String hints)
100 {
101 super(parent, style);
102
103 hintText = hints;
104
105 GridLayout layout = new GridLayout();
106 layout.marginWidth = 0;
107 layout.marginHeight = 0;
108 layout.verticalSpacing = 0;
109 setLayout(layout);
110
111 if (hints != null)
112 {
113 createHintsArea();
114 }
115
116 proposalIcons[0] = Activator.getImageDescriptor("icons/function.gif").createImage(); //$NON-NLS-1$
117 proposalIcons[1] = Activator.getImageDescriptor("icons/var_global.gif").createImage(); //$NON-NLS-1$
118 proposalIcons[2] = Activator.getImageDescriptor("icons/var_local.gif").createImage(); //$NON-NLS-1$
119 proposalIcons[3] = Activator.getImageDescriptor("icons/constant.gif").createImage(); //$NON-NLS-1$
120
121 ruler = new CompositeRuler();
122 editor = new SourceViewer(this, ruler, editorStyle);
123 editor.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
124 editor.configure(new NXSLSourceViewerConfiguration(this));
125 if (showLineNumbers)
126 ruler.addDecorator(0, new LineNumberRulerColumn());
127
128 final TextViewerUndoManager undoManager = new TextViewerUndoManager(50);
129 editor.setUndoManager(undoManager);
130 undoManager.connect(editor);
131
132 editor.getFindReplaceTarget();
133
134 editor.prependVerifyKeyListener(new VerifyKeyListener() {
135 @Override
136 public void verifyKey(VerifyEvent event)
137 {
138 if (!event.doit)
139 return;
140
141 if (event.stateMask == SWT.MOD1)
142 {
143 switch(event.character)
144 {
145 case ' ':
146 editor.doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS);
147 event.doit = false;
148 break;
149 case 0x19: // Ctrl+Y
150 undoManager.redo();
151 event.doit = false;
152 break;
153 case 0x1A: // Ctrl+Z
154 undoManager.undo();
155 event.doit = false;
156 break;
157 }
158 }
159 else if (event.stateMask == SWT.NONE)
160 {
161 if (event.character == 0x09)
162 {
163 if (editor.getSelectedRange().y > 0)
164 {
165 editor.doOperation(ITextOperationTarget.SHIFT_RIGHT);
166 event.doit = false;
167 }
168 }
169 }
170 else if (event.stateMask == SWT.SHIFT)
171 {
172 if (event.character == 0x09)
173 {
174 if (editor.getSelectedRange().y > 0)
175 {
176 editor.doOperation(ITextOperationTarget.SHIFT_LEFT);
177 event.doit = false;
178 }
179 }
180 }
181 }
182 });
183
184 StyledText control = editor.getTextWidget();
185 control.setFont(JFaceResources.getTextFont());
186 control.setWordWrap(false);
187
188 editor.setDocument(new NXSLDocument(""));
189 }
190
191 /**
192 * Create hints area
193 */
194 private void createHintsArea()
195 {
196 hintArea = new Composite(this, SWT.NONE);
197 GridLayout layout = new GridLayout();
198 layout.marginWidth = 0;
199 layout.marginHeight = 0;
200 layout.verticalSpacing = 0;
201 layout.numColumns = 2;
202 hintArea.setLayout(layout);
203 hintArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
204 hintArea.setBackground(SharedColors.getColor(SharedColors.MESSAGE_BAR_BACKGROUND, getDisplay()));
205
206 CLabel hintsTitle = new CLabel(hintArea, SWT.NONE);
207 hintsTitle.setBackground(SharedColors.getColor(SharedColors.MESSAGE_BAR_BACKGROUND, getDisplay()));
208 hintsTitle.setForeground(SharedColors.getColor(SharedColors.MESSAGE_BAR_TEXT, getDisplay()));
209 hintsTitle.setImage(SharedIcons.IMG_INFORMATION);
210 hintsTitle.setText("Hints");
211 hintsTitle.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
212 hintsTitle.addMouseListener(new MouseAdapter() {
213 @Override
214 public void mouseDoubleClick(MouseEvent e)
215 {
216 if (e.button == 1)
217 toggleHints();
218 }
219 });
220
221 hintsExpandButton = new Label(hintArea, SWT.NONE);
222 hintsExpandButton.setBackground(hintArea.getBackground());
223 hintsExpandButton.setCursor(getDisplay().getSystemCursor(SWT.CURSOR_HAND));
224 hintsExpandButton.setImage(SharedIcons.IMG_EXPAND);
225 hintsExpandButton.setToolTipText("Hide message");
226 GridData gd = new GridData();
227 gd.verticalAlignment = SWT.CENTER;
228 hintsExpandButton.setLayoutData(gd);
229 hintsExpandButton.addMouseListener(new MouseListener() {
230 private boolean doAction = false;
231
232 @Override
233 public void mouseDoubleClick(MouseEvent e)
234 {
235 if (e.button == 1)
236 doAction = false;
237 }
238
239 @Override
240 public void mouseDown(MouseEvent e)
241 {
242 if (e.button == 1)
243 doAction = true;
244 }
245
246 @Override
247 public void mouseUp(MouseEvent e)
248 {
249 if ((e.button == 1) && doAction)
250 toggleHints();
251 }
252 });
253
254 Label separator = new Label(this, SWT.SEPARATOR | SWT.HORIZONTAL);
255 separator.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
256 }
257
258 /* (non-Javadoc)
259 * @see org.eclipse.swt.widgets.Widget#dispose()
260 */
261 @Override
262 public void dispose()
263 {
264 for(int i = 0; i < proposalIcons.length; i++)
265 proposalIcons[i].dispose();
266 super.dispose();
267 }
268
269 /**
270 * Get underlying text widget
271 * @return text widget
272 */
273 public StyledText getTextWidget()
274 {
275 return editor.getTextWidget();
276 }
277
278 /**
279 * Set text for editing
280 * @param text
281 */
282 public void setText(String text)
283 {
284 editor.setDocument(new NXSLDocument(text));
285 }
286
287 /**
288 * Get editor's content
289 * @return
290 */
291 public String getText()
292 {
293 return editor.getDocument().get();
294 }
295
296 /**
297 * @param functions the functions to set
298 */
299 public void setFunctions(Set<String> functions)
300 {
301 this.functions = functions;
302 functionsCache = functions.toArray(new String[functions.size()]);
303 }
304
305 /**
306 * Add functions
307 *
308 * @param fc
309 */
310 public void addFunctions(Collection<String> fc)
311 {
312 functions.addAll(fc);
313 functionsCache = functions.toArray(new String[functions.size()]);
314 }
315
316 /**
317 * @param variables the variables to set
318 */
319 public void setVariables(Set<String> variables)
320 {
321 this.variables = variables;
322 variablesCache = variables.toArray(new String[variables.size()]);
323 }
324
325 /**
326 * Add variables
327 *
328 * @param vc
329 */
330 public void addVariables(Collection<String> vc)
331 {
332 variables.addAll(vc);
333 variablesCache = variables.toArray(new String[variables.size()]);
334 }
335
336 /**
337 * @return the functionsCache
338 */
339 public String[] getFunctions()
340 {
341 return functionsCache;
342 }
343
344 /**
345 * @return the variablesCache
346 */
347 public String[] getVariables()
348 {
349 return variablesCache;
350 }
351
352 /**
353 * Get icon for given autocompletion proposal type. Proposal types defined in NXSLProposalProcessor.
354 *
355 * @param type proposal type
356 * @return image for given proposal type or null
357 */
358 public Image getProposalIcon(int type)
359 {
360 try
361 {
362 return proposalIcons[type];
363 }
364 catch(ArrayIndexOutOfBoundsException e)
365 {
366 return null;
367 }
368 }
369
370 /**
371 * Returns the find/replace target of underlying source viewer
372 * @return the find/replace target of underlying source viewer
373 */
374 public IFindReplaceTarget getFindReplaceTarget()
375 {
376 return editor.getFindReplaceTarget();
377 }
378
379 /* (non-Javadoc)
380 * @see org.eclipse.swt.widgets.Composite#computeSize(int, int, boolean)
381 */
382 @Override
383 public Point computeSize(int wHint, int hHint, boolean changed)
384 {
385 Point p = editor.getTextWidget().computeSize(wHint, hHint, changed);
386 p.y += 4;
387 return p;
388 }
389
390 /**
391 * Show/hide line numbers in editor
392 *
393 * @param show
394 */
395 public void showLineNumbers(boolean show)
396 {
397 if (show)
398 {
399 if (!ruler.getDecoratorIterator().hasNext())
400 ruler.addDecorator(0, new LineNumberRulerColumn());
401 }
402 else
403 {
404 if (ruler.getDecoratorIterator().hasNext())
405 ruler.removeDecorator(0);
406 }
407 }
408
409 /**
410 * Toggle hints area
411 */
412 private void toggleHints()
413 {
414 if (hintTextControl != null)
415 {
416 hintTextControl.dispose();
417 hintTextControl = null;
418 hintsExpandButton.setImage(SharedIcons.IMG_EXPAND);
419 }
420 else
421 {
422 hintTextControl = new Text(hintArea, SWT.MULTI | SWT.WRAP);
423 hintTextControl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
424 hintTextControl.setEditable(false);
425 hintTextControl.setText(hintText);
426 hintTextControl.setBackground(SharedColors.getColor(SharedColors.MESSAGE_BAR_BACKGROUND, getDisplay()));
427 hintTextControl.setForeground(SharedColors.getColor(SharedColors.MESSAGE_BAR_TEXT, getDisplay()));
428 hintsExpandButton.setImage(SharedIcons.IMG_COLLAPSE);
429 }
430 layout(true, true);
431 }
432 }