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 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
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
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
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 }