View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2009 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
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  	// private JPanel lineNumbersPanel;
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 }