View Javadoc

1   /*
2    *  soapui, copyright (C) 2005 Ole Matzura / eviware.com 
3    *
4    *  SoapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of the GNU Lesser General Public License as published by the Free Software Foundation; 
6    *  either version 2.1 of the License, or (at your option) any later version.
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.support;
14  
15  import java.awt.Color;
16  import java.awt.Font;
17  import java.awt.Toolkit;
18  import java.awt.event.ActionEvent;
19  import java.awt.event.ActionListener;
20  import java.awt.event.FocusEvent;
21  import java.awt.event.FocusListener;
22  import java.awt.event.KeyEvent;
23  
24  import javax.swing.AbstractAction;
25  import javax.swing.Action;
26  import javax.swing.BorderFactory;
27  import javax.swing.KeyStroke;
28  import javax.swing.event.UndoableEditEvent;
29  import javax.swing.event.UndoableEditListener;
30  import javax.swing.text.BadLocationException;
31  import javax.swing.undo.CannotRedoException;
32  import javax.swing.undo.CannotUndoException;
33  import javax.swing.undo.UndoManager;
34  
35  import org.syntax.jedit.DefaultInputHandler;
36  import org.syntax.jedit.JEditTextArea;
37  import org.syntax.jedit.SyntaxStyle;
38  import org.syntax.jedit.tokenmarker.Token;
39  import org.syntax.jedit.tokenmarker.XMLTokenMarker;
40  
41  import com.eviware.soapui.support.actions.FindAndReplaceAction;
42  
43  /***
44   * JEditTextArea extension targeted specifically at XML-editing.
45   * 
46   * @author Ole.Matzura
47   */
48  
49  public class JXmlTextArea extends JEditTextArea implements
50  		UndoableEditListener, FocusListener
51  {
52  	
53  	public static final int UNDO_LIMIT = 1500;
54  
55  	private UndoManager undoManager;
56  
57  	private UndoAction undoAction;
58  
59  	private RedoAction redoAction;
60  
61  	private FindAndReplaceAction findAndReplaceAction;
62  	
63  	private boolean discardEditsOnSet = true;
64  
65  	public JXmlTextArea()
66  	{
67  		getPainter().setFont(new Font("Courier", Font.PLAIN, 11));
68  		getPainter().setStyles(createXmlStyles());
69  		setTokenMarker(new XMLTokenMarker());
70  		setBorder(BorderFactory.createEtchedBorder());
71  
72  		getDocument().addUndoableEditListener(this);
73  		addFocusListener(this);
74  
75  		undoAction = new UndoAction();
76  		getInputHandler().addKeyBinding("C+Z", undoAction);
77  		redoAction = new RedoAction();
78  		getInputHandler().addKeyBinding("C+Y", redoAction);
79  		findAndReplaceAction = new FindAndReplaceAction( this );
80  		getInputHandler().addKeyBinding( "C+F", findAndReplaceAction );
81  		getInputHandler().addKeyBinding("A+RIGHT", new NextElementValueAction() );
82  		getInputHandler().addKeyBinding("A+LEFT", new PreviousElementValueAction() );
83  		getInputHandler().addKeyBinding("C+D", new DeleteLineAction() );
84  	}
85  
86  	public Action getFindAndReplaceAction()
87  	{
88  		return findAndReplaceAction;
89  	}
90  
91  	public Action getRedoAction()
92  	{
93  		return redoAction;
94  	}
95  
96  	public Action getUndoAction()
97  	{
98  		return undoAction;
99  	}
100 
101 	public void setText(String text)
102 	{
103 		super.setText(text);
104 		
105 		if( discardEditsOnSet && undoManager != null )
106 			undoManager.discardAllEdits();
107 	}
108 	
109 	public boolean isDiscardEditsOnSet()
110 	{
111 		return discardEditsOnSet;
112 	}
113 
114 	public void setDiscardEditsOnSet(boolean discardEditsOnSet)
115 	{
116 		this.discardEditsOnSet = discardEditsOnSet;
117 	}
118 
119 	public UndoManager getUndoManager()
120 	{
121 		return undoManager;
122 	}
123 
124 	public SyntaxStyle[] createXmlStyles()
125 	{
126 		SyntaxStyle[] styles = new SyntaxStyle[Token.ID_COUNT];
127 
128 		styles[Token.COMMENT1] = new SyntaxStyle(Color.black, true, false);
129 		styles[Token.COMMENT2] = new SyntaxStyle(new Color(0x990033), true, false);
130 		styles[Token.KEYWORD1] = new SyntaxStyle(Color.blue, false, false);
131 		styles[Token.KEYWORD2] = new SyntaxStyle(Color.magenta, false, false);
132 		styles[Token.KEYWORD3] = new SyntaxStyle(new Color(0x009600), false,
133 				false);
134 		styles[Token.LITERAL1] = new SyntaxStyle(new Color(0x650099), false,
135 				false);
136 		styles[Token.LITERAL2] = new SyntaxStyle(new Color(0x650099), false, true);
137 		styles[Token.LABEL] = new SyntaxStyle(new Color(0x990033), false, true);
138 		styles[Token.OPERATOR] = new SyntaxStyle(Color.black, false, true);
139 		styles[Token.INVALID] = new SyntaxStyle(Color.red, false, true);
140 
141 		return styles;
142 	}
143 
144 	private void createUndoMananger()
145 	{
146 		undoManager = new UndoManager();
147 		undoManager.setLimit(UNDO_LIMIT);
148 	}
149 
150 	private void removeUndoMananger()
151 	{
152 		if (undoManager == null)
153 			return;
154 		undoManager.end();
155 		undoManager = null;
156 	}
157 
158 	public void focusGained(FocusEvent fe)
159 	{
160 		if (isEditable() && undoManager == null )
161 			createUndoMananger();
162 	}
163 
164 	public void setEditable(boolean enabled)
165 	{
166 		super.setEditable(enabled);
167 		getPainter().setBackground( enabled ? Color.WHITE : new Color(220, 220, 220) );
168 		repaint();
169 	}
170 
171 	public void focusLost(FocusEvent fe)
172 	{
173 		//removeUndoMananger();
174 	}
175 
176 	public void undoableEditHappened(UndoableEditEvent e)
177 	{
178 		if (undoManager != null)
179 			undoManager.addEdit(e.getEdit());
180 	}
181 
182 	private class UndoAction extends AbstractAction
183 	{
184 		public UndoAction()
185 		{
186 			super( "Undo");
187 			putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_Z, KeyEvent.CTRL_MASK ));
188 		}
189 		
190 		public void actionPerformed(ActionEvent e)
191 		{
192 			if( !isEditable()  )
193 			{
194 				getToolkit().beep();
195 				return;
196 			}
197 			
198 			try
199 			{
200 				if( undoManager != null )
201 					undoManager.undo();
202 			}
203 			catch (CannotUndoException cue)
204 			{
205 				Toolkit.getDefaultToolkit().beep();
206 			}
207 		}
208 	}
209 
210 	private class RedoAction extends AbstractAction
211 	{
212 		public RedoAction()
213 		{
214 			super( "Redo");
215 			putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_Y, KeyEvent.CTRL_MASK ));
216 		}
217 
218 		public void actionPerformed(ActionEvent e)
219 		{
220 			if( !isEditable()  )
221 			{
222 				getToolkit().beep();
223 				return;
224 			}
225 			
226 			try
227 			{
228 				if( undoManager != null )
229 					undoManager.redo();
230 			}
231 			catch (CannotRedoException cue)
232 			{
233 				Toolkit.getDefaultToolkit().beep();
234 			}
235 		}
236 	}
237 	
238 	public class NextElementValueAction implements ActionListener
239 	{
240 		public void actionPerformed(ActionEvent e)
241 		{
242 			int pos = getCaretPosition();
243 			String text = getText();
244 			
245 			while( pos < text.length() )
246 			{
247 				// find ending >
248 				if( text.charAt( pos ) == '>' && pos < text.length()-1 && 
249 						(pos > 2 && !text.substring(pos-2,pos).equals( "--" )) &&
250 						(pos > 1 && text.charAt(pos-1) != '/' ) &&
251 						text.indexOf( '/', pos ) == text.indexOf( '<', pos) +1 &&
252 						text.lastIndexOf( '/', pos ) != text.lastIndexOf( '<', pos) +1	)
253 				{
254 					setCaretPosition( pos+1 );
255 					return;
256 				}		
257 				
258 				pos++;
259 			}			
260 			
261 			getToolkit().beep();
262 		}
263 	}
264 	
265 	public class PreviousElementValueAction implements ActionListener
266 	{
267 		public void actionPerformed(ActionEvent e)
268 		{
269 			int pos = getCaretPosition()-2;
270 			String text = getText();
271 			
272 			while( pos > 0 )
273 			{
274 				// find ending >
275 				if( text.charAt( pos ) == '>' && pos < text.length()-1 && 
276 						(pos > 2 && !text.substring(pos-2,pos).equals( "--" )) &&
277 						(pos > 1 && text.charAt(pos-1) != '/' ) &&
278 						text.indexOf( '/', pos ) == text.indexOf( '<', pos) +1 &&
279 						text.lastIndexOf( '/', pos ) != text.lastIndexOf( '<', pos) +1	)
280 				{
281 					setCaretPosition( pos+1 );
282 					return;
283 				}		
284 				
285 				pos--;
286 			}				
287 
288 			getToolkit().beep();
289 		}
290 	}
291 
292 	
293 	public class DeleteLineAction implements ActionListener
294 	{
295 		public void actionPerformed(ActionEvent e)
296 		{
297 			if( !isEditable() )
298 			{
299 				getToolkit().beep();
300 				return;
301 			}
302 			
303 			int caretLine = getCaretLine();
304 			if( caretLine == -1 ) return;
305 			int lineStartOffset = getLineStartOffset( caretLine);
306 			int lineEndOffset = getLineEndOffset( caretLine);
307 			
308 			try
309 			{
310 				int len = lineEndOffset-lineStartOffset;
311 				if( lineStartOffset+len >= getDocumentLength() ) len = getDocumentLength()-lineStartOffset;
312 				
313 				getDocument().remove( lineStartOffset, len );
314 			}
315 			catch (BadLocationException e1)
316 			{
317 				e1.printStackTrace();
318 			}
319 		}
320 	}
321 
322 
323 	public Action getCopyAction()
324 	{
325 		return DefaultInputHandler.CLIP_COPY;
326 	}
327 	
328 	public Action getCutAction()
329 	{
330 		return DefaultInputHandler.CLIP_CUT;
331 	}
332 	
333 	public Action getPasteAction()
334 	{
335 		return DefaultInputHandler.CLIP_PASTE;
336 	}
337 
338 }