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 	/*
151 
152 	private void removeUndoMananger()
153 	{
154 		if (undoManager == null)
155 			return;
156 		undoManager.end();
157 		undoManager = null;
158 	}
159 	
160 	*/
161 
162 	public void focusGained(FocusEvent fe)
163 	{
164 		if (isEditable() && undoManager == null )
165 			createUndoMananger();
166 	}
167 
168 	public void setEditable(boolean enabled)
169 	{
170 		super.setEditable(enabled);
171 		getPainter().setBackground( enabled ? Color.WHITE : new Color(220, 220, 220) );
172 		repaint();
173 	}
174 
175 	public void focusLost(FocusEvent fe)
176 	{
177 		//removeUndoMananger();
178 	}
179 
180 	public void undoableEditHappened(UndoableEditEvent e)
181 	{
182 		if (undoManager != null)
183 			undoManager.addEdit(e.getEdit());
184 	}
185 
186 	private class UndoAction extends AbstractAction
187 	{
188 		public UndoAction()
189 		{
190 			super( "Undo");
191 			putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_Z, KeyEvent.CTRL_MASK ));
192 		}
193 		
194 		public void actionPerformed(ActionEvent e)
195 		{
196 			if( !isEditable()  )
197 			{
198 				getToolkit().beep();
199 				return;
200 			}
201 			
202 			try
203 			{
204 				if( undoManager != null )
205 					undoManager.undo();
206 			}
207 			catch (CannotUndoException cue)
208 			{
209 				Toolkit.getDefaultToolkit().beep();
210 			}
211 		}
212 	}
213 
214 	private class RedoAction extends AbstractAction
215 	{
216 		public RedoAction()
217 		{
218 			super( "Redo");
219 			putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_Y, KeyEvent.CTRL_MASK ));
220 		}
221 
222 		public void actionPerformed(ActionEvent e)
223 		{
224 			if( !isEditable()  )
225 			{
226 				getToolkit().beep();
227 				return;
228 			}
229 			
230 			try
231 			{
232 				if( undoManager != null )
233 					undoManager.redo();
234 			}
235 			catch (CannotRedoException cue)
236 			{
237 				Toolkit.getDefaultToolkit().beep();
238 			}
239 		}
240 	}
241 	
242 	public class NextElementValueAction implements ActionListener
243 	{
244 		public void actionPerformed(ActionEvent e)
245 		{
246 			int pos = getCaretPosition();
247 			String text = getText();
248 			
249 			while( pos < text.length() )
250 			{
251 				// find ending >
252 				if( text.charAt( pos ) == '>' && pos < text.length()-1 && 
253 						(pos > 2 && !text.substring(pos-2,pos).equals( "--" )) &&
254 						(pos > 1 && text.charAt(pos-1) != '/' ) &&
255 						text.indexOf( '/', pos ) == text.indexOf( '<', pos) +1 &&
256 						text.lastIndexOf( '/', pos ) != text.lastIndexOf( '<', pos) +1	)
257 				{
258 					setCaretPosition( pos+1 );
259 					return;
260 				}		
261 				
262 				pos++;
263 			}			
264 			
265 			getToolkit().beep();
266 		}
267 	}
268 	
269 	public class PreviousElementValueAction implements ActionListener
270 	{
271 		public void actionPerformed(ActionEvent e)
272 		{
273 			int pos = getCaretPosition()-2;
274 			String text = getText();
275 			
276 			while( pos > 0 )
277 			{
278 				// find ending >
279 				if( text.charAt( pos ) == '>' && pos < text.length()-1 && 
280 						(pos > 2 && !text.substring(pos-2,pos).equals( "--" )) &&
281 						(pos > 1 && text.charAt(pos-1) != '/' ) &&
282 						text.indexOf( '/', pos ) == text.indexOf( '<', pos) +1 &&
283 						text.lastIndexOf( '/', pos ) != text.lastIndexOf( '<', pos) +1	)
284 				{
285 					setCaretPosition( pos+1 );
286 					return;
287 				}		
288 				
289 				pos--;
290 			}				
291 
292 			getToolkit().beep();
293 		}
294 	}
295 
296 	
297 	public class DeleteLineAction implements ActionListener
298 	{
299 		public void actionPerformed(ActionEvent e)
300 		{
301 			if( !isEditable() )
302 			{
303 				getToolkit().beep();
304 				return;
305 			}
306 			
307 			int caretLine = getCaretLine();
308 			if( caretLine == -1 ) return;
309 			int lineStartOffset = getLineStartOffset( caretLine);
310 			int lineEndOffset = getLineEndOffset( caretLine);
311 			
312 			try
313 			{
314 				int len = lineEndOffset-lineStartOffset;
315 				if( lineStartOffset+len >= getDocumentLength() ) len = getDocumentLength()-lineStartOffset;
316 				
317 				getDocument().remove( lineStartOffset, len );
318 			}
319 			catch (BadLocationException e1)
320 			{
321 				e1.printStackTrace();
322 			}
323 		}
324 	}
325 
326 	public Action getCopyAction()
327 	{
328 		return DefaultInputHandler.CLIP_COPY;
329 	}
330 	
331 	public Action getCutAction()
332 	{
333 		return DefaultInputHandler.CLIP_CUT;
334 	}
335 	
336 	public Action getPasteAction()
337 	{
338 		return DefaultInputHandler.CLIP_PASTE;
339 	}
340 }