001 package nl.cwi.sen1.gui.plugin.editor;
002
003 import java.awt.event.ActionEvent;
004
005 import javax.swing.AbstractAction;
006 import javax.swing.Action;
007 import javax.swing.JEditorPane;
008 import javax.swing.event.UndoableEditEvent;
009 import javax.swing.event.UndoableEditListener;
010 import javax.swing.text.BadLocationException;
011 import javax.swing.text.StyledEditorKit;
012 import javax.swing.text.TextAction;
013 import javax.swing.text.Highlighter.Highlight;
014 import javax.swing.undo.CannotRedoException;
015 import javax.swing.undo.CannotUndoException;
016 import javax.swing.undo.CompoundEdit;
017 import javax.swing.undo.UndoManager;
018
019 import nl.cwi.sen1.gui.StudioImpl;
020
021 public class EditorKit extends StyledEditorKit {
022 private final int UNDO_LIMIT = 1000;
023
024 private UndoManager undoManager = new EditorUndoManager();
025
026 private UndoAction undo;
027
028 private RedoAction redo;
029
030 private DeleteLineAction deleteLine;
031
032 private FindAction find;
033
034 private GotoLineAction gotoLine;
035
036 private GotoMatchingBracketAction gotoMatchingBracket;
037
038 private SelectFocusAction selectFocus;
039
040 private DeletePreviousWordAction deletePreviousWord;
041
042 private DeleteNextWordAction deleteNextWord;
043
044 public static final String undoAction = "undo";
045
046 public static final String redoAction = "redo";
047
048 public static final String findAction = "find";
049
050 public static final String gotoLineAction = "goto-line";
051
052 public static final String deleteLineAction = "delete-line";
053
054 public static final String gotoMatchingBracketAction = "goto-matching-bracket";
055
056 public static final String selectFocusAction = "select-focus";
057
058 public static final String deletePreviousWordAction = "delete-previous-word";
059
060 public static final String deleteNextWordAction = "delete-next-word";
061
062 private UndoableEditListener undoListener;
063
064 private CompoundEdit compoundEdit = null;
065
066 private boolean undoableSequence = false;
067
068 public EditorKit() {
069 undoListener = new UndoableEditListener() {
070 public void undoableEditHappened(UndoableEditEvent e) {
071 if (undoableSequence == true) {
072 if (compoundEdit == null) {
073 compoundEdit = new CompoundEdit();
074 undoManager.addEdit(compoundEdit);
075 }
076 compoundEdit.addEdit(e.getEdit());
077 } else {
078 undoManager.addEdit(e.getEdit());
079 }
080 undo.updateUndoState();
081 redo.updateRedoState();
082 }
083 };
084 undoManager.setLimit(UNDO_LIMIT);
085
086 createActions();
087 }
088
089 public UndoManager getUndoManager() {
090 return undoManager;
091 }
092
093 public UndoableEditListener getUndoListener() {
094 return undoListener;
095 }
096
097 protected void startUndoableSequence() {
098 endUndoableSequence();
099 undoableSequence = true;
100 }
101
102 protected void endUndoableSequence() {
103 if (compoundEdit != null && compoundEdit.isInProgress()) {
104 compoundEdit.end();
105 }
106 compoundEdit = null;
107 undoableSequence = false;
108 undo.updateUndoState();
109 redo.updateRedoState();
110 }
111
112 private void createActions() {
113 undo = new UndoAction();
114 redo = new RedoAction();
115 find = new FindAction();
116 gotoLine = new GotoLineAction();
117 gotoMatchingBracket = new GotoMatchingBracketAction();
118 deleteLine = new DeleteLineAction();
119 selectFocus = new SelectFocusAction();
120 deletePreviousWord = new DeletePreviousWordAction();
121 deleteNextWord = new DeleteNextWordAction();
122 }
123
124 public Action[] getActions() {
125 return TextAction.augmentList(super.getActions(), new Action[] { undo,
126 redo, find, gotoLine, deleteLine, gotoMatchingBracket,
127 selectFocus, deletePreviousWord, deleteNextWord });
128 }
129
130 public Action getAction(String name) {
131 Action[] actions = getActions();
132
133 for (int i = 0; i < actions.length; i++) {
134 if (actions[i].getValue(Action.NAME).equals(name)) {
135 return actions[i];
136 }
137 }
138
139 return null;
140 }
141
142 public abstract static class EditorTextAction extends StyledTextAction {
143 public EditorTextAction(String nm) {
144 super(nm);
145 }
146
147 protected final EditorPane getEditorPane(ActionEvent e) {
148 JEditorPane editorPane = getEditor(e);
149 if (editorPane instanceof EditorPane) {
150 return (EditorPane) editorPane;
151 }
152 return null;
153 }
154 }
155
156 public class UndoAction extends AbstractAction {
157 public UndoAction() {
158 super(undoAction);
159 setEnabled(false);
160 }
161
162 public void actionPerformed(ActionEvent e) {
163 try {
164 undoManager.undo();
165 } catch (CannotUndoException ex) {
166 System.out.println("Unable to undo: " + ex);
167 ex.printStackTrace();
168 }
169 updateUndoState();
170 redo.updateRedoState();
171 }
172
173 protected void updateUndoState() {
174 if (undoManager.canUndo()) {
175 setEnabled(true);
176 putValue(Action.NAME, undoAction);
177 } else {
178 setEnabled(false);
179 putValue(Action.NAME, undoAction);
180 }
181 }
182 }
183
184 public class RedoAction extends AbstractAction {
185
186 public RedoAction() {
187 super(redoAction);
188 setEnabled(false);
189 }
190
191 public void actionPerformed(ActionEvent e) {
192 try {
193 undoManager.redo();
194 } catch (CannotRedoException ex) {
195 System.out.println("Unable to redo: " + ex);
196 ex.printStackTrace();
197 }
198 updateRedoState();
199 undo.updateUndoState();
200 }
201
202 protected void updateRedoState() {
203 if (undoManager.canRedo()) {
204 setEnabled(true);
205 putValue(Action.NAME, redoAction);
206 } else {
207 setEnabled(false);
208 putValue(Action.NAME, redoAction);
209 }
210 }
211 }
212
213 public class FindAction extends EditorTextAction {
214 private SearchReplaceDialog searchReplaceDialog;
215
216 public FindAction() {
217 super(findAction);
218 }
219
220 public void actionPerformed(ActionEvent e) {
221 EditorPane editor = getEditorPane(e);
222 if (editor != null) {
223 if (searchReplaceDialog == null) {
224 searchReplaceDialog = new SearchReplaceDialog(editor,
225 StudioImpl.getFrame());
226 }
227 searchReplaceDialog.setVisible(true);
228 }
229 }
230 }
231
232 public class GotoLineAction extends EditorTextAction {
233 public GotoLineAction() {
234 super(gotoLineAction);
235 }
236
237 public void actionPerformed(ActionEvent e) {
238 GotoLineDialog d = new GotoLineDialog();
239 d.setVisible(true);
240 EditorPane editor = getEditorPane(e);
241 if (editor != null) {
242 editor.gotoLine(d.getLineNumber());
243 }
244 }
245 }
246
247 public class GotoMatchingBracketAction extends EditorTextAction {
248 public GotoMatchingBracketAction() {
249 super(gotoMatchingBracketAction);
250 }
251
252 public void actionPerformed(ActionEvent e) {
253 EditorPane editor = getEditorPane(e);
254 if (editor != null) {
255 try {
256 int offset = TextUtilities.findMatchingBracket(editor
257 .getDocument(), editor.getCaretPosition() - 1);
258 if (offset != -1) {
259 editor.setCaretPosition(offset + 1);
260 }
261 } catch (BadLocationException e1) {
262 }
263 }
264 }
265 }
266
267 public class DeleteLineAction extends EditorTextAction {
268 public DeleteLineAction() {
269 super(deleteLineAction);
270 }
271
272 public void actionPerformed(ActionEvent e) {
273 EditorPane editor = getEditorPane(e);
274 if (editor != null && editor.isEditable()) {
275 getAction(selectLineAction).actionPerformed(e);
276 int selectionStart = editor.getSelectionStart();
277 int selectionEnd = editor.getSelectionEnd();
278 int offset = selectionStart;
279 int length = selectionEnd - selectionStart;
280
281 if ((selectionEnd + 1) < editor.getDocument().getLength()) {
282 length++;
283 } else {
284 if (selectionStart - 1 > 0) {
285 offset = selectionStart - 1;
286 length++;
287 }
288 }
289
290 try {
291 editor.getDocument().remove(offset, length);
292 } catch (BadLocationException e1) {
293 e1.printStackTrace();
294 }
295 }
296 }
297 }
298
299 public class SelectFocusAction extends EditorTextAction {
300 public SelectFocusAction() {
301 super(selectFocusAction);
302 }
303
304 public void actionPerformed(ActionEvent e) {
305 EditorPane editor = getEditorPane(e);
306 if (editor != null) {
307 Highlight highlight = (Highlight) editor.getFocusTag();
308
309 editor.clearFocus();
310 editor.setSelectionStart(highlight.getStartOffset());
311 editor.setSelectionEnd(highlight.getEndOffset());
312 }
313 }
314 }
315
316 public class DeletePreviousWordAction extends EditorTextAction {
317 public DeletePreviousWordAction() {
318 super(deletePreviousWordAction);
319 }
320
321 public void actionPerformed(ActionEvent e) {
322 EditorPane editor = getEditorPane(e);
323 if (editor != null && editor.isEditable()) {
324 getAction(selectionPreviousWordAction).actionPerformed(e);
325 int selectionStart = editor.getSelectionStart();
326 int selectionEnd = editor.getSelectionEnd();
327 int offset = selectionStart;
328 int length = selectionEnd - selectionStart;
329
330 try {
331 editor.getDocument().remove(offset, length);
332 } catch (BadLocationException e1) {
333 e1.printStackTrace();
334 }
335 }
336 }
337 }
338
339 public class DeleteNextWordAction extends EditorTextAction {
340 public DeleteNextWordAction() {
341 super(deleteNextWordAction);
342 }
343
344 public void actionPerformed(ActionEvent e) {
345 EditorPane editor = getEditorPane(e);
346 if (editor != null && editor.isEditable()) {
347 getAction(selectionNextWordAction).actionPerformed(e);
348 int selectionStart = editor.getSelectionStart();
349 int selectionEnd = editor.getSelectionEnd();
350 int offset = selectionStart;
351 int length = selectionEnd - selectionStart;
352
353 try {
354 editor.getDocument().remove(offset, length);
355 } catch (BadLocationException e1) {
356 e1.printStackTrace();
357 }
358 }
359 }
360 }
361 }