1
2
3
4
5
6
7
8
9
10
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
153
154
155
156
157
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
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
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
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 }