1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.impl.wsdl.panels.teststeps.support;
14
15 import java.awt.BorderLayout;
16 import java.awt.Color;
17 import java.awt.Font;
18 import java.awt.event.ActionEvent;
19 import java.awt.event.ActionListener;
20 import java.awt.event.FocusAdapter;
21 import java.awt.event.FocusEvent;
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24
25 import javax.swing.AbstractAction;
26 import javax.swing.Action;
27 import javax.swing.BorderFactory;
28 import javax.swing.JCheckBoxMenuItem;
29 import javax.swing.JPanel;
30 import javax.swing.KeyStroke;
31 import javax.swing.event.CaretListener;
32 import javax.swing.text.Document;
33
34 import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
35 import org.fife.ui.rsyntaxtextarea.SyntaxScheme;
36 import org.fife.ui.rtextarea.LineNumberBorder;
37 import org.fife.ui.rtextarea.RTextScrollPane;
38
39 import com.eviware.soapui.StandaloneSoapUICore;
40 import com.eviware.soapui.model.settings.Settings;
41 import com.eviware.soapui.model.settings.SettingsListener;
42 import com.eviware.soapui.settings.UISettings;
43 import com.eviware.soapui.support.DocumentListenerAdapter;
44 import com.eviware.soapui.support.UISupport;
45 import com.eviware.soapui.support.actions.FindAndReplaceDialog;
46 import com.eviware.soapui.support.actions.FindAndReplaceable;
47 import com.eviware.soapui.support.components.JEditorStatusBar.JEditorStatusBarTarget;
48 import com.eviware.soapui.support.swing.RSyntaxAreaPopupMenu;
49
50 /***
51 * Groovy editor wrapper
52 *
53 * @author ole.matzura
54 */
55
56 public class GroovyEditor extends JPanel implements JEditorStatusBarTarget, PropertyChangeListener
57 {
58 private RSyntaxTextArea editArea;
59 private GroovyEditorModel model;
60 private InternalSettingsListener settingsListener;
61 private GroovyDocumentListener groovyDocumentListener;
62 private RTextScrollPane scrollPane;
63 private JCheckBoxMenuItem toggleLineNumbersMenuItem;
64 private boolean updating;
65
66
67
68 public GroovyEditor( GroovyEditorModel model )
69 {
70 super( new BorderLayout() );
71 this.model = model;
72
73 model.addPropertyChangeListener( this );
74
75 Settings settings = model.getSettings();
76 Font editorFont = UISupport.getEditorFont( settings );
77
78 editArea = new RSyntaxTextArea();
79 editArea.restoreDefaultSyntaxHighlightingColorScheme();
80
81 setEditorFont( editorFont );
82
83 editArea.setSyntaxEditingStyle( RSyntaxTextArea.GROOVY_SYNTAX_STYLE );
84 editArea.setBorder( BorderFactory.createMatteBorder( 0, 2, 0, 0, Color.WHITE ) );
85
86 editArea.setText( model.getScript() );
87 editArea.setCaretPosition( 0 );
88 editArea.setCurrentLineHighlightEnabled( false );
89 Action runAction = model.getRunAction();
90 if( runAction != null )
91 {
92 editArea.getInputMap().put( KeyStroke.getKeyStroke( "alt ENTER" ), "run-action" );
93 editArea.getActionMap().put( "run-action", runAction );
94 }
95
96 editArea.getInputMap().put( KeyStroke.getKeyStroke( "F3" ), "find-action" );
97 editArea.getInputMap().put( KeyStroke.getKeyStroke( "ctrl F" ), "find-action" );
98 RSyntaxTextAreaFindAndReplaceable findAndReplaceable = new RSyntaxTextAreaFindAndReplaceable();
99 editArea.getActionMap().put( "find-action", new FindAndReplaceDialog( findAndReplaceable ) );
100
101 groovyDocumentListener = new GroovyDocumentListener();
102 editArea.getDocument().addDocumentListener( groovyDocumentListener );
103
104 settingsListener = new InternalSettingsListener();
105 settings.addSettingsListener( settingsListener );
106
107 scrollPane = new RTextScrollPane( 500, 300, editArea, true );
108 add( scrollPane );
109
110 UISupport.addPreviewCorner( scrollPane, true );
111
112 addFocusListener( new FocusAdapter()
113 {
114 public void focusGained( FocusEvent e )
115 {
116 editArea.requestFocusInWindow();
117 }
118 } );
119
120 RSyntaxAreaPopupMenu popup = RSyntaxAreaPopupMenu.add( editArea );
121 popup.add( new FindAndReplaceDialog( findAndReplaceable ) );
122 popup.addSeparator();
123 popup.add( new GoToLineAction() );
124
125 toggleLineNumbersMenuItem = new JCheckBoxMenuItem( "Show Line Numbers", scrollPane.areLineNumbersEnabled() );
126 toggleLineNumbersMenuItem.setAccelerator( UISupport.getKeyStroke( "alt L" ) );
127 toggleLineNumbersMenuItem.addActionListener( new ActionListener()
128 {
129 public void actionPerformed( ActionEvent e )
130 {
131 enableLineNumbers( toggleLineNumbersMenuItem.isSelected() );
132 }
133 } );
134
135 editArea.getInputMap().put( KeyStroke.getKeyStroke( "alt L" ), new AbstractAction()
136 {
137 public void actionPerformed( ActionEvent e )
138 {
139 enableLineNumbers( !scrollPane.areLineNumbersEnabled() );
140 }
141 } );
142
143 popup.add( toggleLineNumbersMenuItem );
144 editArea.setComponentPopupMenu( popup );
145
146 enableLineNumbers( settings.getBoolean( UISettings.SHOW_GROOVY_LINE_NUMBERS ) );
147 }
148
149 @Override
150 public void setEnabled( boolean enabled )
151 {
152 super.setEnabled( enabled );
153
154 editArea.setEnabled( enabled );
155 }
156
157 public void enableLineNumbers( boolean enable )
158 {
159 scrollPane.setLineNumbersEnabled( enable );
160 try
161 {
162 if( scrollPane.areLineNumbersEnabled() )
163 ( ( LineNumberBorder )scrollPane.getViewportBorder() )
164 .setBackground( StandaloneSoapUICore.SoapUITheme.BACKGROUND_COLOR );
165 }
166 catch( Exception e )
167 {
168 e.printStackTrace();
169 }
170
171 toggleLineNumbersMenuItem.setSelected( enable );
172 }
173
174 public RSyntaxTextArea getEditArea()
175 {
176 return editArea;
177 }
178
179 public void release()
180 {
181 if( model != null )
182 {
183 model.getSettings().removeSettingsListener( settingsListener );
184 model.removePropertyChangeListener( this );
185 }
186
187 model = null;
188 editArea.getDocument().removeDocumentListener( groovyDocumentListener );
189 }
190
191 public void selectError( String message )
192 {
193 int ix = message == null ? -1 : message.indexOf( "@ line " );
194 if( ix >= 0 )
195 {
196 try
197 {
198 int ix2 = message.indexOf( ',', ix );
199 int line = ix2 == -1 ? Integer.parseInt( message.substring( ix + 6 ).trim() ) : Integer.parseInt( message
200 .substring( ix + 6, ix2 ).trim() );
201 int column = 0;
202 if( ix2 != -1 )
203 {
204 ix = message.indexOf( "column ", ix2 );
205 if( ix >= 0 )
206 {
207 ix2 = message.indexOf( '.', ix );
208 column = ix2 == -1 ? Integer.parseInt( message.substring( ix + 7 ).trim() ) : Integer
209 .parseInt( message.substring( ix + 7, ix2 ).trim() );
210 }
211 }
212
213 editArea.setCaretPosition( editArea.getLineStartOffset( line - 1 ) + column - 1 );
214 }
215 catch( Exception ex )
216 {
217 }
218
219 editArea.requestFocus();
220 }
221 }
222
223 private final class GroovyDocumentListener extends DocumentListenerAdapter
224 {
225 public void update( Document document )
226 {
227 if( !updating )
228 {
229 GroovyEditor.this.model.setScript( editArea.getText() );
230 }
231 }
232 }
233
234 private final class InternalSettingsListener implements SettingsListener
235 {
236 public void settingChanged( String name, String newValue, String oldValue )
237 {
238 if( name.equals( UISettings.EDITOR_FONT ) )
239 {
240 Font newFont = Font.decode( newValue );
241 setEditorFont( newFont );
242 invalidate();
243 }
244 }
245 }
246
247 public void setEditorFont( Font newFont )
248 {
249 editArea.setFont( newFont );
250
251 for( SyntaxScheme scheme : editArea.getSyntaxHighlightingColorScheme().syntaxSchemes )
252 {
253 if( scheme != null )
254 {
255 scheme.font = newFont;
256 }
257 }
258 }
259
260 public void addCaretListener( CaretListener listener )
261 {
262 editArea.addCaretListener( listener );
263 }
264
265 public int getCaretPosition()
266 {
267 return editArea.getCaretPosition();
268 }
269
270 public int getLineOfOffset( int offset ) throws Exception
271 {
272 return editArea.getLineOfOffset( offset );
273 }
274
275 public int getLineStartOffset( int line ) throws Exception
276 {
277 return editArea.getLineStartOffset( line );
278 }
279
280 public void removeCaretListener( CaretListener listener )
281 {
282 editArea.removeCaretListener( listener );
283 }
284
285 private final class GoToLineAction extends AbstractAction
286 {
287 public GoToLineAction()
288 {
289 super( "Go To Line" );
290 putValue( Action.SHORT_DESCRIPTION, "Moves the caret to the specified line" );
291 putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "menu alt L" ) );
292 }
293
294 public void actionPerformed( ActionEvent e )
295 {
296 String line = UISupport.prompt( "Enter line-number to (1.." + ( editArea.getLineCount() ) + ")", "Go To Line",
297 String.valueOf( editArea.getCaretLineNumber() + 1 ) );
298
299 if( line != null )
300 {
301 try
302 {
303 int ln = Integer.parseInt( line ) - 1;
304 if( ln >= 0 && ln < editArea.getLineCount() )
305 {
306 editArea.scrollRectToVisible( editArea.modelToView( editArea.getLineStartOffset( ln ) ) );
307 editArea.setCaretPosition( getLineStartOffset( ln ) );
308 }
309 }
310 catch( Exception e1 )
311 {
312 }
313 }
314 }
315 }
316
317 private class RSyntaxTextAreaFindAndReplaceable implements FindAndReplaceable
318 {
319 public boolean isEditable()
320 {
321 return editArea.isEditable();
322 }
323
324 public int getCaretPosition()
325 {
326 return editArea.getCaretPosition();
327 }
328
329 public String getText()
330 {
331 return editArea.getText();
332 }
333
334 public void select( int start, int end )
335 {
336 editArea.select( start, end );
337 }
338
339 public int getSelectionStart()
340 {
341 return editArea.getSelectionStart();
342 }
343
344 public int getSelectionEnd()
345 {
346 return editArea.getSelectionEnd();
347 }
348
349 public void setSelectedText( String txt )
350 {
351 editArea.replaceSelection( txt );
352 }
353
354 public String getSelectedText()
355 {
356 return editArea.getSelectedText();
357 }
358 }
359
360 public void propertyChange( PropertyChangeEvent evt )
361 {
362 if( evt.getPropertyName().equals( "script" ))
363 {
364 updating = true;
365 editArea.setText( String.valueOf( evt.getNewValue() ));
366 updating = false;
367 }
368 }
369 }