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    }