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 }