KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > bluej > editor > moe > MoeActions


1 // Copyright (c) 2000, 2005 BlueJ Group, Deakin University
2
//
3
// This software is made available under the terms of the "MIT License"
4
// A copy of this license is included with this source distribution
5
// in "license.txt" and is also available at:
6
// http://www.opensource.org/licenses/mit-license.html
7
// Any queries should be directed to Michael Kolling mik@bluej.org
8

9 package bluej.editor.moe;
10
11 import java.awt.Container JavaDoc;
12 import java.awt.Event JavaDoc;
13 import java.awt.Toolkit JavaDoc;
14 import java.awt.datatransfer.*;
15 import java.awt.event.ActionEvent JavaDoc;
16 import java.awt.event.KeyAdapter JavaDoc;
17 import java.awt.event.KeyEvent JavaDoc;
18 import java.io.*;
19 import java.util.ArrayList JavaDoc;
20 import java.util.Hashtable JavaDoc;
21 import java.util.Iterator JavaDoc;
22
23 import javax.swing.*;
24 import javax.swing.event.DocumentEvent JavaDoc;
25 import javax.swing.text.*;
26 import javax.swing.undo.CannotRedoException JavaDoc;
27 import javax.swing.undo.CannotUndoException JavaDoc;
28
29 import bluej.Config;
30 import bluej.prefmgr.PrefMgr;
31 import bluej.prefmgr.PrefMgrDialog;
32 import bluej.utility.Debug;
33 import bluej.utility.DialogManager;
34
35 /**
36  * A set of actions supported by the Moe editor. This is a singleton: the
37  * actions are shared between all editor instances.
38  *
39  * Actions are stored both in a hashtable and in an array. The hashtable is used
40  * for fast lookup by name, whereas the array is needed to support complete,
41  * ordered access.
42  *
43  * @author Michael Kolling
44  * @author Bruce Quig
45  */

46
47 public final class MoeActions
48 {
49     // -------- CONSTANTS --------
50

51     private static final String JavaDoc KEYS_FILE = "editor.keys";
52
53     private static int SHORTCUT_MASK;
54     private static int ALT_SHORTCUT_MASK;
55     private static int SHIFT_SHORTCUT_MASK;
56     private static int SHIFT_ALT_SHORTCUT_MASK;
57     private static int DOUBLE_SHORTCUT_MASK; // two masks (ie. CTRL + META)
58

59     private static final int tabSize = Config.getPropInteger("bluej.editor.tabsize", 4);
60     private static final String JavaDoc spaces = " ";
61     private static final char TAB_CHAR = '\t';
62
63     // -------- INSTANCE VARIABLES --------
64

65     private Action[] actionTable; // table of all known actions
66
private Hashtable JavaDoc actions; // the same actions in a hashtable
67
private String JavaDoc[] categories;
68     private int[] categoryIndex;
69
70     private Keymap keymap; // the editor's keymap
71
private KeyCatcher keyCatcher;
72
73     private boolean lastActionWasCut; // true if last action was a cut action
74
// undo helpers
75
public UndoAction undoAction;
76     public RedoAction redoAction;
77
78     // frequently needed actions
79
public Action compileAction;
80
81     // for bug workaround:
82
private InputMap componentInputMap;
83
84     // =========================== STATIC METHODS ===========================
85

86     private static MoeActions moeActions;
87
88     /**
89      * Get the actions object (a singleton) and, at the same time, install the
90      * action keymap as the main keymap for the given textComponent..
91      */

92     public static MoeActions getActions(JTextComponent textComponent)
93     {
94         if (moeActions == null)
95             moeActions = new MoeActions(textComponent);
96
97         if (textComponent != null)
98             textComponent.setKeymap(moeActions.keymap);
99         return moeActions;
100     }
101
102     // ========================== INSTANCE METHODS ==========================
103

104     /**
105      * Constructor. Singleton, thus private.
106      */

107     private MoeActions(JTextComponent textComponent)
108     {
109         // sort out modifier keys...
110
SHORTCUT_MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
111
112         if (SHORTCUT_MASK == Event.CTRL_MASK)
113             ALT_SHORTCUT_MASK = Event.META_MASK; // alternate (second) modifier
114
else
115             ALT_SHORTCUT_MASK = Event.CTRL_MASK;
116
117         SHIFT_SHORTCUT_MASK = SHORTCUT_MASK + Event.SHIFT_MASK;
118         SHIFT_ALT_SHORTCUT_MASK = Event.SHIFT_MASK + ALT_SHORTCUT_MASK;
119         DOUBLE_SHORTCUT_MASK = SHORTCUT_MASK + ALT_SHORTCUT_MASK;
120
121         // install our own keymap, with the existing one as parent
122
keymap = JTextComponent.addKeymap("BlueJ map", textComponent.getKeymap());
123
124         createActionTable(textComponent);
125         keyCatcher = new KeyCatcher();
126         if (!load())
127             setDefaultKeyBindings();
128         lastActionWasCut = false;
129
130         // for bug workaround (below)
131
componentInputMap = textComponent.getInputMap();
132     }
133
134     /**
135      * Return an action with a given name.
136      */

137     public Action getActionByName(String JavaDoc name)
138     {
139         return (Action) (actions.get(name));
140     }
141
142     /**
143      * Get a keystroke for an action. Return null is there is none.
144      */

145     public KeyStroke[] getKeyStrokesForAction(Action action)
146     {
147         KeyStroke[] keys = keymap.getKeyStrokesForAction(action);
148         keys = addComponentKeyStrokes(action, keys); // BUG workaround
149
if (keys != null && keys.length > 0)
150             return keys;
151         else
152             return null;
153     }
154
155     /**
156      * BUG WORKAROUND: currently, keymap.getKeyStrokesForAction() misses
157      * keystrokes that come from JComponents inputMap. Here, we add those
158      * ourselves...
159      */

160     public KeyStroke[] addComponentKeyStrokes(Action action, KeyStroke[] keys)
161     {
162         ArrayList JavaDoc keyStrokes = null;
163         KeyStroke[] componentKeys = componentInputMap.allKeys();
164
165         // find all component keys that bind to this action
166
for (int i = 0; i < componentKeys.length; i++) {
167             if (componentInputMap.get(componentKeys[i]).equals(action.getValue(Action.NAME))) {
168                 if (keyStrokes == null)
169                     keyStrokes = new ArrayList JavaDoc();
170                 keyStrokes.add(componentKeys[i]);
171             }
172         }
173
174         // test whether this keyStroke was redefined in keymap
175
if (keyStrokes != null) {
176             for (Iterator JavaDoc i = keyStrokes.iterator(); i.hasNext();) {
177                 if (keymap.getAction((KeyStroke) i.next()) != null) {
178                     i.remove();
179                 }
180             }
181         }
182
183         // merge found keystrokes into key array
184
if ((keyStrokes == null) || (keyStrokes.size() == 0))
185             return keys;
186
187         KeyStroke[] allKeys;
188         if (keys == null) {
189             allKeys = new KeyStroke[keyStrokes.size()];
190             keyStrokes.toArray(allKeys);
191         }
192         else { // merge new keystrokes into keys
193
allKeys = new KeyStroke[keyStrokes.size() + keys.length];
194             keyStrokes.toArray(allKeys);
195             System.arraycopy(allKeys, 0, allKeys, keys.length, keyStrokes.size());
196             System.arraycopy(keys, 0, allKeys, 0, keys.length);
197         }
198         return allKeys;
199     }
200
201     /**
202      * Add a new key binding into the action table.
203      */

204     public void addActionForKeyStroke(KeyStroke key, Action a)
205     {
206         keymap.addActionForKeyStroke(key, a);
207     }
208
209     /**
210      * Remove a key binding from the action table.
211      */

212     public void removeKeyStrokeBinding(KeyStroke key)
213     {
214         keymap.removeKeyStrokeBinding(key);
215     }
216
217     /**
218      * Save the key bindings. Return true if successful.
219      */

220     public boolean save()
221     {
222         try {
223             File file = Config.getUserConfigFile(KEYS_FILE);
224             FileOutputStream ostream = new FileOutputStream(file);
225             ObjectOutputStream stream = new ObjectOutputStream(ostream);
226             KeyStroke[] keys = keymap.getBoundKeyStrokes();
227             stream.writeInt(MoeEditor.version);
228             stream.writeInt(keys.length);
229             for (int i = 0; i < keys.length; i++) {
230                 stream.writeObject(keys[i]);
231                 stream.writeObject(keymap.getAction(keys[i]).getValue(Action.NAME));
232             }
233             stream.flush();
234             ostream.close();
235             return true;
236         }
237         catch (Exception JavaDoc exc) {
238             Debug.message("Cannot save key bindings: " + exc);
239             return false;
240         }
241     }
242
243     /**
244      * Load the key bindings. Return true if successful.
245      */

246     public boolean load()
247     {
248         try {
249             File file = Config.getUserConfigFile(KEYS_FILE);
250             FileInputStream istream = new FileInputStream(file);
251             ObjectInputStream stream = new ObjectInputStream(istream);
252             //KeyStroke[] keys = keymap.getBoundKeyStrokes();
253
int version = 0;
254             int count = stream.readInt();
255             if (count > 100) { // it was new format: version number stored first
256
version = count;
257                 count = stream.readInt();
258             }
259             if (Config.isMacOS() && (version < 140)) {
260                 // do not attempt to load old bindings on MacOS when switching
261
// to jdk 1.4.1
262
return false;
263             }
264
265             for (int i = 0; i < count; i++) {
266                 KeyStroke key = (KeyStroke) stream.readObject();
267                 String JavaDoc actionName = (String JavaDoc) stream.readObject();
268                 Action action = (Action) (actions.get(actionName));
269                 if (action != null) {
270                     keymap.addActionForKeyStroke(key, action);
271                 }
272             }
273             istream.close();
274
275             // set up bindings for new actions in recent releases
276

277             if (version < 130) {
278                 keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0),
279                         (Action) (actions.get("indent")));
280                 keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, Event.SHIFT_MASK),
281                         (Action) (actions.get("insert-tab")));
282                 keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
283                         (Action) (actions.get("new-line")));
284                 keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, Event.SHIFT_MASK),
285                         (Action) (actions.get("insert-break")));
286             }
287             if (version < 200) {
288                 keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, Event.SHIFT_MASK),
289                         (Action) (actions.get("de-indent")));
290                 keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_I, SHORTCUT_MASK),
291                         (Action) (actions.get("insert-tab")));
292             }
293             return true;
294         }
295         catch (Exception JavaDoc exc) {
296             // ignore - file probably didn't exist (yet)
297
return false;
298         }
299     }
300
301     /**
302      * Called to inform that any one of the user actions (text edit or caret
303      * move) was executed.
304      */

305     public void userAction()
306     {
307         lastActionWasCut = false;
308     }
309
310     /**
311      * Called at every insertion of text into the document.
312      */

313     public void textInsertAction(DocumentEvent JavaDoc evt, JTextComponent textPane)
314     {
315         try {
316             if (evt.getLength() == 1) { // single character inserted
317
Document doc = evt.getDocument();
318                 int offset = evt.getOffset();
319                 char ch = doc.getText(offset, 1).charAt(0);
320
321                 // 'ch' is the character that was just typed
322
// currently, the only character upon which we act is the
323
// closing brace ('}')
324

325                 if (ch == '}') {
326                     closingBrace(textPane, doc, offset);
327                 }
328             }
329         }
330         catch (BadLocationException e) {}
331     }
332
333     /**
334      * We just typed a closing brace character - indent appropriately.
335      */

336     private void closingBrace(JTextComponent textPane, Document doc, int offset)
337         throws BadLocationException
338     {
339         int lineIndex = getCurrentLineIndex(textPane);
340         Element line = getLine(textPane, lineIndex);
341         int lineStart = line.getStartOffset();
342         String JavaDoc prefix = doc.getText(lineStart, offset - lineStart);
343         
344         if(prefix.trim().length() == 0) { // only if there is no other text before '}'
345
textPane.setCaretPosition(lineStart);
346             doIndent(textPane, true);
347             textPane.setCaretPosition(textPane.getCaretPosition() + 1);
348         }
349     }
350
351     /**
352      * Get a keystroke for an action by action name. Return null is there is
353      * none.
354      */

355     // public KeyStroke[] getKeyStrokesForName(String actionName)
356
// {
357
// Action action = getActionByName(actionName);
358
// KeyStroke[] keys = keymap.getKeyStrokesForAction(action);
359
// if (keys != null && keys.length > 0)
360
// return keys;
361
// else
362
// return null;
363
// }
364
// ============================ USER ACTIONS =============================
365

366     abstract class MoeAbstractAction extends TextAction
367     {
368
369         public MoeAbstractAction(String JavaDoc name)
370         {
371             super(name);
372         }
373
374         /* side effect: clears message in editor! */
375         protected final MoeEditor getEditor(ActionEvent JavaDoc e)
376         {
377             MoeEditor ed = null;
378             
379             // the source of the event is the first place to look
380
Object JavaDoc source = e.getSource();
381             if (source instanceof JComponent) {
382                 Container JavaDoc c = ((JComponent) source).getTopLevelAncestor();
383                 if (c instanceof MoeEditor)
384                     ed = (MoeEditor) c;
385             }
386             
387             // otherwise use 'getTextComponent'
388
if (ed == null) {
389                 JTextComponent textComponent = getTextComponent(e);
390                 if (textComponent != null) {
391                     Container JavaDoc c = textComponent.getTopLevelAncestor();
392                     if (c instanceof MoeEditor)
393                         ed = (MoeEditor) c;
394                 }
395             }
396             
397             if (ed != null)
398                 ed.clearMessage();
399             return ed;
400         }
401     }
402
403     // === File: ===
404
// --------------------------------------------------------------------
405

406     class SaveAction extends MoeAbstractAction
407     {
408
409         public SaveAction()
410         {
411             super("save");
412         }
413
414         public void actionPerformed(ActionEvent JavaDoc e)
415         {
416             getEditor(e).userSave();
417         }
418     }
419
420     // --------------------------------------------------------------------
421

422     /**
423      * Reload has been chosen. Ask "Really?" and call "doReload" if the answer
424      * is yes.
425      */

426     class ReloadAction extends MoeAbstractAction
427     {
428
429         public ReloadAction()
430         {
431             super("reload");
432         }
433
434         public void actionPerformed(ActionEvent JavaDoc e)
435         {
436             getEditor(e).reload();
437         }
438     }
439
440     // --------------------------------------------------------------------
441

442     class PrintAction extends MoeAbstractAction
443     {
444
445         public PrintAction()
446         {
447             super("print");
448         }
449
450         public void actionPerformed(ActionEvent JavaDoc e)
451         {
452             getEditor(e).print();
453         }
454     }
455
456     // --------------------------------------------------------------------
457

458     class PageSetupAction extends MoeAbstractAction
459     {
460
461         public PageSetupAction()
462         {
463             super("page-setup");
464         }
465
466         public void actionPerformed(ActionEvent JavaDoc e)
467         {
468             getEditor(e).pageSetup();
469         }
470     }
471
472     // --------------------------------------------------------------------
473

474     class CloseAction extends MoeAbstractAction
475     {
476
477         public CloseAction()
478         {
479             super("close");
480         }
481
482         public void actionPerformed(ActionEvent JavaDoc e)
483         {
484             getEditor(e).close();
485         }
486     }
487
488     // === Edit: ===
489
// --------------------------------------------------------------------
490

491     class UndoAction extends MoeAbstractAction
492     {
493
494         public UndoAction()
495         {
496             super("undo");
497             this.setEnabled(false);
498         }
499
500         public void actionPerformed(ActionEvent JavaDoc e)
501         {
502             MoeEditor editor = getEditor(e);
503             try {
504                 editor.undoManager.undo();
505             }
506             catch (CannotUndoException JavaDoc ex) {
507                 Debug.message("moe: cannot undo...");
508             }
509             editor.updateUndoControls();
510             editor.updateRedoControls();
511         }
512     }
513
514     // --------------------------------------------------------------------
515

516     class RedoAction extends MoeAbstractAction
517     {
518
519         public RedoAction()
520         {
521             super("redo");
522             this.setEnabled(false);
523         }
524
525         public void actionPerformed(ActionEvent JavaDoc e)
526         {
527             MoeEditor editor = getEditor(e);
528             try {
529                 editor.undoManager.redo();
530             }
531             catch (CannotRedoException JavaDoc ex) {
532                 Debug.message("moe: cannot redo...");
533             }
534             editor.updateUndoControls();
535             editor.updateRedoControls();
536         }
537     }
538
539     // --------------------------------------------------------------------
540

541     class CommentBlockAction extends MoeAbstractAction
542     {
543
544         public CommentBlockAction()
545         {
546             super("comment-block");
547         }
548
549         public void actionPerformed(ActionEvent JavaDoc e)
550         {
551             getEditor(e);
552             blockAction(getTextComponent(e), new CommentLineAction());
553         }
554     }
555
556     // --------------------------------------------------------------------
557

558     class UncommentBlockAction extends MoeAbstractAction
559     {
560
561         public UncommentBlockAction()
562         {
563             super("uncomment-block");
564         }
565
566         public void actionPerformed(ActionEvent JavaDoc e)
567         {
568             getEditor(e);
569             blockAction(getTextComponent(e), new UncommentLineAction());
570         }
571     }
572
573     // --------------------------------------------------------------------
574

575     class IndentBlockAction extends MoeAbstractAction
576     {
577
578         public IndentBlockAction()
579         {
580             super("indent-block");
581         }
582
583         public void actionPerformed(ActionEvent JavaDoc e)
584         {
585             getEditor(e);
586             blockAction(getTextComponent(e), new IndentLineAction());
587         }
588     }
589
590     // --------------------------------------------------------------------
591

592     class DeindentBlockAction extends MoeAbstractAction
593     {
594
595         public DeindentBlockAction()
596         {
597             super("deindent-block");
598         }
599
600         public void actionPerformed(ActionEvent JavaDoc e)
601         {
602             getEditor(e);
603             blockAction(getTextComponent(e), new DeindentLineAction());
604         }
605     }
606
607     // --------------------------------------------------------------------
608

609     class InsertMethodAction extends MoeAbstractAction
610     {
611
612         public InsertMethodAction()
613         {
614             super("insert-method");
615         }
616
617         public void actionPerformed(ActionEvent JavaDoc e)
618         {
619             getEditor(e);
620             insertTemplate(getTextComponent(e), "method");
621         }
622     }
623
624     // --------------------------------------------------------------------
625

626     class IndentAction extends MoeAbstractAction
627     {
628
629         public IndentAction()
630         {
631             super("indent");
632         }
633
634         public void actionPerformed(ActionEvent JavaDoc e)
635         {
636             JTextComponent textPane = getTextComponent(e);
637             MoeEditor ed = getEditor(e);
638
639             // if necessary, convert all TABs in the current editor to spaces
640
int converted = 0;
641             if (ed.checkExpandTabs()) // do TABs need expanding?
642
converted = convertTabsToSpaces(textPane);
643
644             if (PrefMgr.getFlag(PrefMgr.AUTO_INDENT))
645                 doIndent(textPane, false);
646             else
647                 insertSpacedTab(textPane);
648
649             if (converted > 0)
650                 ed.writeMessage(Config.getString("editor.info.tabsExpanded"));
651         }
652     }
653
654     // --------------------------------------------------------------------
655

656     class DeIndentAction extends MoeAbstractAction
657     {
658
659         public DeIndentAction()
660         {
661             super("de-indent");
662         }
663
664         public void actionPerformed(ActionEvent JavaDoc e)
665         {
666             JTextComponent textPane = getTextComponent(e);
667             MoeEditor ed = getEditor(e);
668
669             // if necessary, convert all TABs in the current editor to spaces
670
if (ed.checkExpandTabs()) { // do TABs need expanding?
671
int converted = convertTabsToSpaces(textPane);
672
673                 if (converted > 0)
674                     ed.writeMessage(Config.getString("editor.info.tabsExpanded"));
675             }
676             doDeIndent(textPane);
677         }
678     }
679
680     // --------------------------------------------------------------------
681

682     class NewLineAction extends MoeAbstractAction
683     {
684
685         public NewLineAction()
686         {
687             super("new-line");
688         }
689
690         public void actionPerformed(ActionEvent JavaDoc e)
691         {
692
693             Action action = (Action) (actions.get(DefaultEditorKit.insertBreakAction));
694             action.actionPerformed(e);
695
696             if (PrefMgr.getFlag(PrefMgr.AUTO_INDENT)) {
697                 JTextComponent textPane = getTextComponent(e);
698                 doIndent(textPane, true);
699             }
700         }
701     }
702
703     // --------------------------------------------------------------------
704

705     class CopyLineAction extends MoeAbstractAction
706     {
707
708         public CopyLineAction()
709         {
710             super("copy-line");
711         }
712
713         public void actionPerformed(ActionEvent JavaDoc e)
714         {
715             boolean addToClipboard = lastActionWasCut;
716             getActionByName("caret-begin-line").actionPerformed(e);
717             getActionByName("selection-down").actionPerformed(e);
718             if (addToClipboard)
719                 addSelectionToClipboard(getTextComponent(e));
720             else
721                 getActionByName("copy-to-clipboard").actionPerformed(e);
722             lastActionWasCut = true;
723         }
724     }
725
726     // --------------------------------------------------------------------
727

728     class CutLineAction extends MoeAbstractAction
729     {
730
731         public CutLineAction()
732         {
733             super("cut-line");
734         }
735
736         public void actionPerformed(ActionEvent JavaDoc e)
737         {
738             boolean addToClipboard = lastActionWasCut;
739             getActionByName("caret-begin-line").actionPerformed(e);
740             getActionByName("selection-down").actionPerformed(e);
741             if (addToClipboard) {
742                 addSelectionToClipboard(getTextComponent(e));
743                 getActionByName("delete-previous").actionPerformed(e);
744             }
745             else
746                 getActionByName("cut-to-clipboard").actionPerformed(e);
747             lastActionWasCut = true;
748         }
749     }
750
751     // --------------------------------------------------------------------
752

753     class CutEndOfLineAction extends MoeAbstractAction
754     {
755
756         public CutEndOfLineAction()
757         {
758             super("cut-end-of-line");
759         }
760
761         public void actionPerformed(ActionEvent JavaDoc e)
762         {
763             boolean addToClipboard = lastActionWasCut;
764
765             getActionByName("selection-end-line").actionPerformed(e);
766             JTextComponent textComponent = getTextComponent(e);
767             String JavaDoc selection = textComponent.getSelectedText();
768             if (selection == null)
769                 getActionByName("selection-forward").actionPerformed(e);
770
771             if (addToClipboard) {
772                 addSelectionToClipboard(textComponent);
773                 getActionByName("delete-previous").actionPerformed(e);
774             }
775             else
776                 getActionByName("cut-to-clipboard").actionPerformed(e);
777             lastActionWasCut = true;
778         }
779     }
780
781     // --------------------------------------------------------------------
782

783     class CutWordAction extends MoeAbstractAction
784     {
785
786         public CutWordAction()
787         {
788             super("cut-word");
789         }
790
791         public void actionPerformed(ActionEvent JavaDoc e)
792         {
793             boolean addToClipboard = lastActionWasCut;
794             getActionByName("caret-previous-word").actionPerformed(e);
795             getActionByName("selection-next-word").actionPerformed(e);
796             if (addToClipboard) {
797                 addSelectionToClipboard(getTextComponent(e));
798                 getActionByName("delete-previous").actionPerformed(e);
799             }
800             else
801                 getActionByName("cut-to-clipboard").actionPerformed(e);
802             lastActionWasCut = true;
803         }
804     }
805
806     // --------------------------------------------------------------------
807

808     class CutEndOfWordAction extends MoeAbstractAction
809     {
810
811         public CutEndOfWordAction()
812         {
813             super("cut-end-of-word");
814         }
815
816         public void actionPerformed(ActionEvent JavaDoc e)
817         {
818             boolean addToClipboard = lastActionWasCut;
819             getActionByName("selection-next-word").actionPerformed(e);
820             if (addToClipboard) {
821                 addSelectionToClipboard(getTextComponent(e));
822                 getActionByName("delete-previous").actionPerformed(e);
823             }
824             else
825                 getActionByName("cut-to-clipboard").actionPerformed(e);
826             lastActionWasCut = true;
827         }
828     }
829
830     // === Tools: ===
831

832     // --------------------------------------------------------------------
833

834     class FindAction extends MoeAbstractAction
835     {
836
837         public FindAction()
838         {
839             super("find");
840         }
841
842         public void actionPerformed(ActionEvent JavaDoc e)
843         {
844             getEditor(e).find();
845         }
846     }
847
848     // --------------------------------------------------------------------
849

850     class FindNextAction extends MoeAbstractAction
851     {
852
853         public FindNextAction()
854         {
855             super("find-next");
856         }
857
858         public void actionPerformed(ActionEvent JavaDoc e)
859         {
860             getEditor(e).findNext();
861         }
862     }
863
864     // --------------------------------------------------------------------
865

866     class FindNextBackwardAction extends MoeAbstractAction
867     {
868
869         public FindNextBackwardAction()
870         {
871             super("find-next-backward");
872         }
873
874         public void actionPerformed(ActionEvent JavaDoc e)
875         {
876             getEditor(e).findNextBackward();
877         }
878     }
879
880     // --------------------------------------------------------------------
881

882     class ReplaceAction extends MoeAbstractAction
883     {
884
885         public ReplaceAction()
886         {
887             super("replace");
888         }
889
890         public void actionPerformed(ActionEvent JavaDoc e)
891         {
892             getEditor(e).replace();
893         }
894     }
895
896     // --------------------------------------------------------------------
897

898     class CompileAction extends MoeAbstractAction
899     {
900
901         public CompileAction()
902         {
903             super("compile");
904         }
905
906         public void actionPerformed(ActionEvent JavaDoc e)
907         {
908             getEditor(e).compile();
909         }
910     }
911
912     // --------------------------------------------------------------------
913

914     class ToggleInterfaceAction extends MoeAbstractAction
915     {
916
917         public ToggleInterfaceAction()
918         {
919             super("toggle-interface-view");
920         }
921
922         public void actionPerformed(ActionEvent JavaDoc e)
923         {
924             Object JavaDoc source = e.getSource();
925             if (source instanceof JComboBox)
926                 getEditor(e).toggleInterface();
927             else
928                 getEditor(e).toggleInterfaceMenu();
929         }
930     }
931
932     // === Debug: ===
933
// --------------------------------------------------------------------
934

935     class ToggleBreakPointAction extends MoeAbstractAction
936     {
937
938         public ToggleBreakPointAction()
939         {
940             super("toggle-breakpoint");
941         }
942
943         public void actionPerformed(ActionEvent JavaDoc e)
944         {
945             getEditor(e).toggleBreakpoint();
946         }
947     }
948
949     // === Options: ===
950
// --------------------------------------------------------------------
951

952     class KeyBindingsAction extends MoeAbstractAction
953     {
954
955         public KeyBindingsAction()
956         {
957             super("key-bindings");
958         }
959
960         public void actionPerformed(ActionEvent JavaDoc e)
961         {
962             FunctionDialog dlg = new FunctionDialog(getEditor(e), actionTable, categories, categoryIndex);
963
964             dlg.setVisible(true);
965         }
966     }
967
968     // --------------------------------------------------------------------
969

970     class PreferencesAction extends MoeAbstractAction
971     {
972
973         public PreferencesAction()
974         {
975             super("preferences");
976         }
977
978         public void actionPerformed(ActionEvent JavaDoc e)
979         {
980             PrefMgrDialog.showDialog(0); // 0 is the index of the editor pane in
981
// the pref dialog
982
}
983     }
984
985     // === Help: ===
986
// --------------------------------------------------------------------
987

988     class AboutAction extends MoeAbstractAction
989     {
990
991         public AboutAction()
992         {
993             super("about-editor");
994         }
995
996         public void actionPerformed(ActionEvent JavaDoc e)
997         {
998             JOptionPane.showMessageDialog(getEditor(e), new String JavaDoc[]{"Moe", "Version " + MoeEditor.versionString, " ",
999                     "Moe is the editor of the BlueJ programming environment.",
1000                    "Written by Michael K\u00F6lling (mik@bluej.org)."}, "About Moe", JOptionPane.INFORMATION_MESSAGE);
1001        }
1002    }
1003
1004    // --------------------------------------------------------------------
1005

1006    class DescribeKeyAction extends MoeAbstractAction
1007    {
1008
1009        public DescribeKeyAction()
1010        {
1011            super("describe-key");
1012        }
1013
1014        public void actionPerformed(ActionEvent JavaDoc e)
1015        {
1016            JTextComponent textComponent = getTextComponent(e);
1017            textComponent.addKeyListener(keyCatcher);
1018            MoeEditor ed = getEditor(e);
1019            keyCatcher.setEditor(ed);
1020            ed.writeMessage("Describe key: ");
1021        }
1022    }
1023
1024    // --------------------------------------------------------------------
1025

1026    class HelpMouseAction extends MoeAbstractAction
1027    {
1028
1029        public HelpMouseAction()
1030        {
1031            super("help-mouse");
1032        }
1033
1034        public void actionPerformed(ActionEvent JavaDoc e)
1035        {
1036            JOptionPane.showMessageDialog(getEditor(e), new String JavaDoc[]{"Moe Mouse Buttons:", " ", "left button:",
1037                    " click: place cursor", " double-click: select word", " triple-click: select line",
1038                    " drag: make selection", " ", "right button:", " (currently unused)",}, "Moe Mouse Buttons",
1039                    JOptionPane.INFORMATION_MESSAGE);
1040        }
1041    }
1042
1043    // --------------------------------------------------------------------
1044

1045    class ShowManualAction extends MoeAbstractAction
1046    {
1047
1048        public ShowManualAction()
1049        {
1050            super("show-manual");
1051        }
1052
1053        public void actionPerformed(ActionEvent JavaDoc e)
1054        {
1055            DialogManager.NYI(getEditor(e));
1056        }
1057    }
1058
1059    // --------------------------------------------------------------------
1060

1061    class GoToLineAction extends MoeAbstractAction
1062    {
1063
1064        public GoToLineAction()
1065        {
1066            super("go-to-line");
1067        }
1068
1069        public void actionPerformed(ActionEvent JavaDoc e)
1070        {
1071            getEditor(e).goToLine();
1072        }
1073    }
1074
1075    // --------------------------------------------------------------------
1076
// class Action extends MoeAbstractAction {
1077
//
1078
// public Action() {
1079
// super("");
1080
// }
1081
//
1082
// public void actionPerformed(ActionEvent e) {
1083
// DialogManager.NYI(editor);
1084
// }
1085
// }
1086

1087    // ========================= SUPPORT ROUTINES ==========================
1088

1089    /**
1090     * Add the current selection of the text component to the clipboard.
1091     */

1092    public void addSelectionToClipboard(JTextComponent textComponent)
1093    {
1094        Clipboard clipboard = textComponent.getToolkit().getSystemClipboard();
1095
1096        // get text from clipboard
1097
Transferable content = clipboard.getContents(this);
1098        String JavaDoc clipContent = "";
1099        if (content != null) {
1100            try {
1101                clipContent = (String JavaDoc) (content.getTransferData(DataFlavor.stringFlavor));
1102            }
1103            catch (Exception JavaDoc exc) {} // content was not string
1104
}
1105
1106        // add current selection and store back in clipboard
1107
StringSelection contents = new StringSelection(clipContent + textComponent.getSelectedText());
1108        clipboard.setContents(contents, contents);
1109    }
1110
1111    // --------------------------------------------------------------------
1112
/**
1113     * Return the current line.
1114     */

1115    // private Element getCurrentLine(JTextComponent text)
1116
// {
1117
// MoeSyntaxDocument document = (MoeSyntaxDocument)text.getDocument();
1118
// return document.getParagraphElement(text.getCaretPosition());
1119
// }
1120
// --------------------------------------------------------------------
1121
/**
1122     * Return the current column number.
1123     */

1124    private int getCurrentColumn(JTextComponent textPane)
1125    {
1126        Caret caret = textPane.getCaret();
1127        int pos = Math.min(caret.getMark(), caret.getDot());
1128        AbstractDocument doc = (AbstractDocument) textPane.getDocument();
1129        int lineStart = doc.getParagraphElement(pos).getStartOffset();
1130        return (pos - lineStart);
1131    }
1132
1133    // --------------------------------------------------------------------
1134
/**
1135     * Find and return a line by line number
1136     */

1137    private Element getLine(JTextComponent text, int lineNo)
1138    {
1139        return text.getDocument().getDefaultRootElement().getElement(lineNo);
1140    }
1141
1142    // -------------------------------------------------------------------
1143
/**
1144     * Find and return a line by text position
1145     */

1146    private Element getLineAt(JTextComponent text, int pos)
1147    {
1148        MoeSyntaxDocument document = (MoeSyntaxDocument) text.getDocument();
1149        return document.getParagraphElement(pos);
1150    }
1151
1152    // -------------------------------------------------------------------
1153
/**
1154     * Return the number of the current line.
1155     */

1156    private int getCurrentLineIndex(JTextComponent text)
1157    {
1158        MoeSyntaxDocument document = (MoeSyntaxDocument) text.getDocument();
1159        return document.getDefaultRootElement().getElementIndex(text.getCaretPosition());
1160    }
1161
1162    // ===================== ACTION IMPLEMENTATION ======================
1163

1164    /**
1165     * Do some semi-intelligent indentation. That is: indent the current line to
1166     * the same depth, using the same characters (TABs or spaces) as the line
1167     * immediately above.
1168     */

1169    private void doIndent(JTextComponent textPane, boolean isNewLine)
1170    {
1171        int lineIndex = getCurrentLineIndex(textPane);
1172        if (lineIndex == 0) { // first line
1173
if(!isNewLine)
1174                insertSpacedTab(textPane);
1175            return;
1176        }
1177
1178        MoeSyntaxDocument doc = (MoeSyntaxDocument) textPane.getDocument();
1179
1180        Element line = getLine(textPane, lineIndex);
1181        int lineStart = line.getStartOffset();
1182        int pos = textPane.getCaretPosition();
1183
1184        try {
1185            boolean isOpenBrace = false;
1186            boolean isCommentEnd = false, isCommentEndOnly = false;
1187
1188            // if there is any text before the cursor, just insert a tab
1189

1190            String JavaDoc prefix = doc.getText(lineStart, pos - lineStart);
1191            if (prefix.trim().length() > 0) {
1192                insertSpacedTab(textPane);
1193                return;
1194            }
1195
1196            // get indentation string from previous line
1197

1198            boolean foundLine = false;
1199            int lineOffset = 1;
1200            String JavaDoc prevLineText = null;
1201            while ((lineIndex - lineOffset >= 0) && !foundLine) {
1202                Element prevline = getLine(textPane, lineIndex - lineOffset);
1203                int prevLineStart = prevline.getStartOffset();
1204                int prevLineEnd = prevline.getEndOffset();
1205                prevLineText = doc.getText(prevLineStart, prevLineEnd - prevLineStart);
1206                if(!isWhiteSpaceOnly(prevLineText)) {
1207                    foundLine = true;
1208                }
1209                else {
1210                   lineOffset++;
1211                }
1212            }
1213            if(!foundLine) {
1214                if(!isNewLine)
1215                    insertSpacedTab(textPane);
1216                return;
1217            }
1218            
1219            if (isOpenBrace(prevLineText))
1220                isOpenBrace = true;
1221            else {
1222                isCommentEnd = prevLineText.trim().endsWith("*/");
1223                isCommentEndOnly = prevLineText.trim().equals("*/");
1224            }
1225
1226            int indentPos = findFirstNonIndentChar(prevLineText, isCommentEnd);
1227
1228            // if the cursor is already past the indentation point, insert tab
1229
// (unless we just did a line break, then we just stop)
1230

1231            int caretColumn = getCurrentColumn(textPane);
1232            if (caretColumn >= indentPos) {
1233                if (!isNewLine)
1234                    insertSpacedTab(textPane);
1235                return;
1236            }
1237
1238            String JavaDoc indent = prevLineText.substring(0, indentPos);
1239
1240            if (isNewLine && isCommentStart(indent)) {
1241                completeNewCommentBlock(textPane, indent);
1242                return;
1243            }
1244
1245            // find and replace indentation of current line
1246

1247            int lineEnd = line.getEndOffset();
1248            String JavaDoc lineText = doc.getText(lineStart, lineEnd - lineStart);
1249            indentPos = findFirstNonIndentChar(lineText, true);
1250            char firstChar = lineText.charAt(indentPos);
1251            doc.remove(lineStart, indentPos);
1252            doc.insertString(lineStart, nextIndent(indent, isOpenBrace, isCommentEndOnly), null);
1253            if(firstChar == '}')
1254                removeTab(textPane, doc);
1255        }
1256        catch (BadLocationException exc) {}
1257    }
1258
1259    /**
1260     * Return true if s contains only whitespace (or nothing).
1261     */

1262    private boolean isWhiteSpaceOnly(String JavaDoc s)
1263    {
1264        return s.trim().length() == 0;
1265    }
1266    
1267    /**
1268     * Do some semi-intelligent de-indentation. That is: indent the current line
1269     * one indentation level less that the line above, or less than it currently
1270     * is.
1271     */

1272    private void doDeIndent(JTextComponent textPane)
1273    {
1274        // set cursor to first non-blank character (or eol if none)
1275
// if indentation is more than line above: indent as line above
1276
// if indentation is same or less than line above: indent one level back
1277

1278        int lineIndex = getCurrentLineIndex(textPane);
1279        MoeSyntaxDocument doc = (MoeSyntaxDocument) textPane.getDocument();
1280
1281        try {
1282            Element line = getLine(textPane, lineIndex);
1283            int lineStart = line.getStartOffset();
1284            int lineEnd = line.getEndOffset();
1285            String JavaDoc lineText = doc.getText(lineStart, lineEnd - lineStart);
1286
1287            int currentIndentPos = findFirstNonIndentChar(lineText, true);
1288            char firstChar = lineText.charAt(currentIndentPos);
1289
1290            textPane.setCaretPosition(lineStart + currentIndentPos);
1291            
1292            if (lineIndex == 0) { // first line
1293
removeTab(textPane, doc);
1294                return;
1295            }
1296
1297            // get indentation details from previous line
1298

1299            Element prevline = getLine(textPane, lineIndex - 1);
1300            int prevLineStart = prevline.getStartOffset();
1301            int prevLineEnd = prevline.getEndOffset();
1302            String JavaDoc prevLineText = doc.getText(prevLineStart, prevLineEnd - prevLineStart);
1303
1304            int targetIndentPos = findFirstNonIndentChar(prevLineText, true);
1305
1306            if (currentIndentPos > targetIndentPos) {
1307                // indent same as line above
1308
String JavaDoc indent = prevLineText.substring(0, targetIndentPos);
1309                doc.remove(lineStart, currentIndentPos);
1310                doc.insertString(lineStart, indent, null);
1311                if(firstChar == '}')
1312                    removeTab(textPane, doc);
1313            }
1314            else {
1315                // we are at same level as line above or less - go one indentation
1316
// level back
1317
removeTab(textPane, doc);
1318            }
1319        }
1320        catch (BadLocationException exc) {}
1321    }
1322
1323    /**
1324     * Check whether the indentation s opens a new multi-line comment
1325     */

1326    private boolean isCommentStart(String JavaDoc s)
1327    {
1328        s = s.trim();
1329        return s.endsWith("/**") || s.endsWith("/*");
1330    }
1331
1332    /**
1333     * Insert text to complete a new, started block comment and place the cursor
1334     * appropriately.
1335     *
1336     * The indentString passed in always ends with "/*".
1337     */

1338    private void completeNewCommentBlock(JTextComponent textPane, String JavaDoc indentString)
1339    {
1340        String JavaDoc nextIndent = indentString.substring(0, indentString.length() - 2);
1341        textPane.replaceSelection(nextIndent + " * ");
1342        int pos = textPane.getCaretPosition();
1343        textPane.replaceSelection("\n");
1344        textPane.replaceSelection(nextIndent + " */");
1345        textPane.setCaretPosition(pos);
1346    }
1347
1348    /**
1349     * Check whether the given line ends with an opening brace.
1350     */

1351    private boolean isOpenBrace(String JavaDoc s)
1352    {
1353        int index = s.lastIndexOf('{');
1354        if (index == -1)
1355            return false;
1356
1357        return s.indexOf('}', index + 1) == -1;
1358    }
1359
1360    /**
1361     * Find the position of the first non-indentation character in a string.
1362     * Indentation characters are <whitespace>, //, *, /*, /**.
1363     */

1364    private int findFirstNonIndentChar(String JavaDoc s, boolean whitespaceOnly)
1365    {
1366        int cnt = 0;
1367        char ch = s.charAt(0);
1368
1369        // if this line ends a comment, indent whitepace only;
1370
// otherwise indent across whitespace, asterisks and comment starts
1371

1372        if (whitespaceOnly) {
1373            while (ch == ' ' || ch == '\t') { // SPACE or TAB
1374
cnt++;
1375                ch = s.charAt(cnt);
1376            }
1377        }
1378        else {
1379            while (ch == ' ' || ch == '\t' || ch == '*') { // SPACE, TAB or *
1380
cnt++;
1381                ch = s.charAt(cnt);
1382            }
1383            if ((s.charAt(cnt) == '/') && (s.charAt(cnt + 1) == '*'))
1384                cnt += 2;
1385        }
1386        return cnt;
1387    }
1388
1389    /**
1390     * Transform indentation string to ensure: after " / *" follows " *" after " / * *"
1391     * follows " *" after " * /" follows ""
1392     */

1393    private String JavaDoc nextIndent(String JavaDoc s, boolean openBrace, boolean commentEndOnly)
1394    {
1395        // after an opening brace, add some spaces to the indentation
1396
if (openBrace)
1397            return s + spaces.substring(0, tabSize);
1398
1399        if (commentEndOnly)
1400            return s.substring(0, s.length() - 1);
1401
1402        if (s.endsWith("/*"))
1403            return s.substring(0, s.length() - 2) + " * ";
1404
1405        return s;
1406    }
1407
1408    /**
1409     * Insert a spaced tab at the current caret position in to the textPane.
1410     */

1411    private void insertSpacedTab(JTextComponent textPane)
1412    {
1413        int numSpaces = tabSize - (getCurrentColumn(textPane) % tabSize);
1414        textPane.replaceSelection(spaces.substring(0, numSpaces));
1415    }
1416
1417    /**
1418     * Remove characters before the current caret position to take the
1419     * caret back to the previous TAB position. No check is made what kind
1420     * of characters those are - the caller should make sure they can be
1421     * removed (usually they should be whitespace).
1422     */

1423    private void removeTab(JTextComponent textPane, Document doc)
1424        throws BadLocationException
1425    {
1426        int col = getCurrentColumn(textPane);
1427        if(col > 0) {
1428            int remove = col % tabSize;
1429            if(remove == 0)
1430                remove = tabSize;
1431            int pos = textPane.getCaretPosition();
1432            doc.remove(pos-remove, remove);
1433        }
1434    }
1435
1436    /**
1437     * Convert all tabs in this text to spaces, maintaining the current
1438     * indentation.
1439     *
1440     * @param textPane The text pane to convert
1441     * @return The number of tab characters converted
1442     */

1443    private int convertTabsToSpaces(JTextComponent textPane)
1444    {
1445        int count = 0;
1446        int lineNo = 0;
1447        AbstractDocument doc = (AbstractDocument) textPane.getDocument();
1448        Element root = doc.getDefaultRootElement();
1449        Element line = root.getElement(lineNo);
1450        try {
1451            while (line != null) {
1452                int start = line.getStartOffset();
1453                int length = line.getEndOffset() - start;
1454                String JavaDoc text = doc.getText(start, length);
1455                int startCount = count;
1456                int tabIndex = text.indexOf('\t');
1457                while (tabIndex != -1) {
1458                    text = expandTab(text, tabIndex);
1459                    count++;
1460                    tabIndex = text.indexOf('\t');
1461                }
1462                if (count != startCount) { // there was a TAB in this line...
1463
doc.remove(start, length);
1464                    doc.insertString(start, text, null);
1465                }
1466                lineNo++;
1467                line = root.getElement(lineNo);
1468            }
1469        }
1470        catch (BadLocationException exc) {
1471            Debug.reportError("stuffed up in 'convertTabsToSpaces'");
1472        }
1473        return count;
1474    }
1475
1476    private String JavaDoc expandTab(String JavaDoc s, int idx)
1477    {
1478        int numSpaces = tabSize - (idx % tabSize);
1479        return s.substring(0, idx) + spaces.substring(0, numSpaces) + s.substring(idx + 1);
1480    }
1481
1482    /**
1483     * Insert text from a named template into the editor at the current cursor
1484     * position. Every line in the template will be indented to the current
1485     * cursor position (in addition to possible indentation in the template
1486     * itself), and TAB characters at beginnings of lines in the template will
1487     * be converted to a spaced tab according to the current tabsize.
1488     *
1489     * @param textPane
1490     * The editor pane to enter the text into
1491     * @param templateName
1492     * The name of the template (without path or suffix)
1493     */

1494    private void insertTemplate(JTextComponent textPane, String JavaDoc templateName)
1495    {
1496        try {
1497            File template = Config.getTemplateFile(templateName);
1498            BufferedReader in = new BufferedReader(new FileReader(template));
1499            int pos = textPane.getCaretPosition();
1500            int column = getCurrentColumn(textPane);
1501            if (column > 40)
1502                column = 40;
1503            String JavaDoc line = in.readLine();
1504            while (line != null) {
1505                while ((line.length() > 0) && (line.charAt(0) == '\t')) {
1506                    insertSpacedTab(textPane);
1507                    line = line.substring(1);
1508                }
1509                textPane.replaceSelection(line);
1510                textPane.replaceSelection("\n");
1511                textPane.replaceSelection(spaces.substring(0, column)); // indent
1512
line = in.readLine();
1513            }
1514            textPane.setCaretPosition(pos);
1515        }
1516        catch (IOException exc) {
1517            Debug.reportError("Could not read method template.");
1518            Debug.reportError("Exception: " + exc);
1519        }
1520    }
1521
1522    /**
1523     *
1524     */

1525    private void blockAction(JTextComponent textPane, LineAction lineAction)
1526    {
1527        Caret caret = textPane.getCaret();
1528        int selectionStart = caret.getMark();
1529        int selectionEnd = caret.getDot();
1530        if (selectionStart > selectionEnd) {
1531            int tmp = selectionStart;
1532            selectionStart = selectionEnd;
1533            selectionEnd = tmp;
1534        }
1535        if (selectionStart != selectionEnd)
1536            selectionEnd = selectionEnd - 1; // skip last position
1537

1538        MoeSyntaxDocument doc = (MoeSyntaxDocument) textPane.getDocument();
1539        Element text = doc.getDefaultRootElement();
1540
1541        int firstLineIndex = text.getElementIndex(selectionStart);
1542        int lastLineIndex = text.getElementIndex(selectionEnd);
1543        for (int i = firstLineIndex; i <= lastLineIndex; i++) {
1544            Element line = text.getElement(i);
1545            lineAction.apply(line, doc);
1546        }
1547
1548        textPane.setCaretPosition(text.getElement(firstLineIndex).getStartOffset());
1549        textPane.moveCaretPosition(text.getElement(lastLineIndex).getEndOffset());
1550    }
1551
1552    // --------------------------------------------------------------------
1553

1554    /**
1555     * Create the table of action supported by this editor
1556     */

1557    private void createActionTable(JTextComponent textComponent)
1558    {
1559        undoAction = new UndoAction();
1560        redoAction = new RedoAction();
1561        compileAction = new CompileAction();
1562
1563        // get all actions into arrays
1564

1565        Action[] textActions = textComponent.getActions();
1566        Action[] myActions = {
1567                new SaveAction(),
1568                new ReloadAction(),
1569                new PageSetupAction(),
1570                new PrintAction(),
1571                new CloseAction(),
1572
1573                undoAction,
1574                redoAction,
1575                new CommentBlockAction(),
1576                new UncommentBlockAction(),
1577                new IndentBlockAction(),
1578                new DeindentBlockAction(),
1579                new InsertMethodAction(),
1580                new IndentAction(),
1581                new DeIndentAction(),
1582                new NewLineAction(),
1583                new CopyLineAction(),
1584                new CutLineAction(),
1585                new CutEndOfLineAction(),
1586                new CutWordAction(),
1587                new CutEndOfWordAction(),
1588
1589                new FindAction(),
1590                new FindNextAction(),
1591                new FindNextBackwardAction(),
1592                new ReplaceAction(),
1593                compileAction,
1594                new GoToLineAction(),
1595                new ToggleInterfaceAction(),
1596                new ToggleBreakPointAction(),
1597
1598                new KeyBindingsAction(),
1599                new PreferencesAction(),
1600
1601                new AboutAction(),
1602                new DescribeKeyAction(),
1603                new HelpMouseAction(),
1604                new ShowManualAction(),
1605            };
1606
1607        // insert all actions into a hashtable
1608

1609        actions = new Hashtable JavaDoc();
1610
1611        Action action;
1612        for (int i = 0; i < textActions.length; i++) {
1613            action = textActions[i];
1614            //Debug.message("a: " + action.getValue(Action.NAME));
1615
actions.put(action.getValue(Action.NAME), action);
1616        }
1617        for (int i = 0; i < myActions.length; i++) {
1618            action = myActions[i];
1619            actions.put(action.getValue(Action.NAME), action);
1620        }
1621
1622        // sort all actions into a big, ordered table
1623

1624        actionTable = new Action[] {
1625
1626        // edit functions
1627

1628                (Action) (actions.get(DefaultEditorKit.deletePrevCharAction)), // 0
1629
(Action) (actions.get(DefaultEditorKit.deleteNextCharAction)),
1630                (Action) (actions.get(DefaultEditorKit.copyAction)),
1631                (Action) (actions.get(DefaultEditorKit.cutAction)),
1632                (Action) (actions.get("copy-line")),
1633                (Action) (actions.get("cut-line")),
1634                (Action) (actions.get("cut-end-of-line")),
1635                (Action) (actions.get("cut-word")),
1636                (Action) (actions.get("cut-end-of-word")),
1637                (Action) (actions.get(DefaultEditorKit.pasteAction)),
1638                (Action) (actions.get("indent")),
1639                (Action) (actions.get("de-indent")),
1640                (Action) (actions.get(DefaultEditorKit.insertTabAction)),
1641                (Action) (actions.get("new-line")),
1642                (Action) (actions.get(DefaultEditorKit.insertBreakAction)),
1643                (Action) (actions.get("insert-method")),
1644                (Action) (actions.get("comment-block")),
1645                (Action) (actions.get("uncomment-block")),
1646                (Action) (actions.get("indent-block")),
1647                (Action) (actions.get("deindent-block")),
1648
1649                (Action) (actions.get(DefaultEditorKit.selectWordAction)), // 20
1650
(Action) (actions.get(DefaultEditorKit.selectLineAction)),
1651                (Action) (actions.get(DefaultEditorKit.selectParagraphAction)),
1652                (Action) (actions.get(DefaultEditorKit.selectAllAction)),
1653                (Action) (actions.get(DefaultEditorKit.selectionBackwardAction)),
1654                (Action) (actions.get(DefaultEditorKit.selectionForwardAction)),
1655                (Action) (actions.get(DefaultEditorKit.selectionUpAction)),
1656                (Action) (actions.get(DefaultEditorKit.selectionDownAction)),
1657                (Action) (actions.get(DefaultEditorKit.selectionBeginWordAction)),
1658                (Action) (actions.get(DefaultEditorKit.selectionEndWordAction)),
1659                (Action) (actions.get(DefaultEditorKit.selectionPreviousWordAction)), // 30
1660
(Action) (actions.get(DefaultEditorKit.selectionNextWordAction)),
1661                (Action) (actions.get(DefaultEditorKit.selectionBeginLineAction)),
1662                (Action) (actions.get(DefaultEditorKit.selectionEndLineAction)),
1663                (Action) (actions.get(DefaultEditorKit.selectionBeginParagraphAction)),
1664                (Action) (actions.get(DefaultEditorKit.selectionEndParagraphAction)),
1665                (Action) (actions.get("selection-page-up")),
1666                (Action) (actions.get("selection-page-down")),
1667                (Action) (actions.get(DefaultEditorKit.selectionBeginAction)),
1668                (Action) (actions.get(DefaultEditorKit.selectionEndAction)),
1669                (Action) (actions.get("unselect")),
1670
1671                // move and scroll functions
1672

1673                (Action) (actions.get(DefaultEditorKit.backwardAction)), // 41
1674
(Action) (actions.get(DefaultEditorKit.forwardAction)),
1675                (Action) (actions.get(DefaultEditorKit.upAction)),
1676                (Action) (actions.get(DefaultEditorKit.downAction)),
1677                (Action) (actions.get(DefaultEditorKit.beginWordAction)),
1678                (Action) (actions.get(DefaultEditorKit.endWordAction)),
1679                (Action) (actions.get(DefaultEditorKit.previousWordAction)),
1680                (Action) (actions.get(DefaultEditorKit.nextWordAction)),
1681                (Action) (actions.get(DefaultEditorKit.beginLineAction)),
1682                (Action) (actions.get(DefaultEditorKit.endLineAction)), // 50
1683
(Action) (actions.get(DefaultEditorKit.beginParagraphAction)),
1684                (Action) (actions.get(DefaultEditorKit.endParagraphAction)),
1685                (Action) (actions.get(DefaultEditorKit.pageUpAction)),
1686                (Action) (actions.get(DefaultEditorKit.pageDownAction)),
1687                (Action) (actions.get(DefaultEditorKit.beginAction)),
1688                (Action) (actions.get(DefaultEditorKit.endAction)),
1689
1690                // class functions
1691
(Action) (actions.get("save")), // 57
1692
(Action) (actions.get("reload")),
1693                (Action) (actions.get("close")),
1694                (Action) (actions.get("print")),
1695                (Action) (actions.get("page-setup")),
1696
1697                // customisation functions
1698
(Action) (actions.get("key-bindings")), // 62
1699
(Action) (actions.get("preferences")),
1700
1701                // help functions
1702
(Action) (actions.get("describe-key")), // 64
1703
(Action) (actions.get("help-mouse")),
1704                (Action) (actions.get("show-manual")),
1705                (Action) (actions.get("about-editor")),
1706
1707                // misc functions
1708
undoAction, // 68
1709
redoAction,
1710                (Action) (actions.get("find")),
1711                (Action) (actions.get("find-next")),
1712                (Action) (actions.get("find-next-backward")),
1713                (Action) (actions.get("replace")),
1714                (Action) (actions.get("compile")),
1715                (Action) (actions.get("toggle-interface-view")),
1716                (Action) (actions.get("toggle-breakpoint")),
1717                (Action) (actions.get("go-to-line")),
1718        }; // 78
1719

1720        categories = new String JavaDoc[] {
1721                Config.getString("editor.functions.editFunctions"),
1722                Config.getString("editor.functions.moveScroll"), Config.getString("editor.functions.classFunctions"),
1723                Config.getString("editor.functions.customisation"), Config.getString("editor.functions.help"),
1724                Config.getString("editor.functions.misc")
1725        };
1726
1727        categoryIndex = new int[] { 0, 41, 57, 62, 64, 68, 78 };
1728    }
1729
1730    /**
1731     * Set up the default key bindings. Used for initial setup, or restoring the
1732     * default later on.
1733     */

1734    public void setDefaultKeyBindings()
1735    {
1736        keymap.removeBindings();
1737
1738        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_MASK),
1739                (Action) (actions.get("save")));
1740        // "reload" not bound
1741
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_P, SHORTCUT_MASK),
1742                (Action) (actions.get("print")));
1743        // "page-setup" not bound
1744
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_W, SHORTCUT_MASK), (Action) (actions
1745                .get("close")));
1746        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_Z, SHORTCUT_MASK), (Action) (actions
1747                .get("undo")));
1748        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_Y, SHORTCUT_MASK), (Action) (actions
1749                .get("redo")));
1750        keymap
1751                .addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0), (Action) (actions
1752                        .get("comment-block")));
1753        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0), (Action) (actions
1754                .get("uncomment-block")));
1755        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0), (Action) (actions.get("indent-block")));
1756        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0),
1757                (Action) (actions.get("deindent-block")));
1758        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_M, SHORTCUT_MASK),
1759                (Action) (actions.get("insert-method")));
1760        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0),
1761                (Action) (actions.get("indent")));
1762        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, Event.SHIFT_MASK),
1763                (Action) (actions.get("de-indent")));
1764        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_I, SHORTCUT_MASK),
1765                (Action) (actions.get("insert-tab")));
1766        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
1767                (Action) (actions.get("new-line")));
1768        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, Event.SHIFT_MASK),
1769                (Action) (actions.get("insert-break")));
1770        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_F, SHORTCUT_MASK),
1771                (Action) (actions.get("find")));
1772        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_G, SHORTCUT_MASK),
1773                (Action) (actions.get("find-next")));
1774        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_G, SHIFT_SHORTCUT_MASK), (Action) (actions
1775                .get("find-next-backward")));
1776        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_R, SHORTCUT_MASK), (Action) (actions
1777                .get("replace")));
1778        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_L, SHORTCUT_MASK), (Action) (actions
1779                .get("go-to-line")));
1780        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_K, SHORTCUT_MASK), (Action) (actions
1781                .get("compile")));
1782        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_J, SHORTCUT_MASK), (Action) (actions
1783                .get("toggle-interface-view")));
1784        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_B, SHORTCUT_MASK), (Action) (actions
1785                .get("toggle-breakpoint")));
1786        // "key-bindings" not bound
1787
// "preferences" not bound
1788
// "about-editor" not bound
1789
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_D, SHORTCUT_MASK), (Action) (actions
1790                .get("describe-key")));
1791        // "help-mouse" not bound
1792
// "show-manual" not bound
1793

1794        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_C, SHORTCUT_MASK), (Action) (actions
1795                .get(DefaultEditorKit.copyAction)));
1796        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_X, SHORTCUT_MASK), (Action) (actions
1797                .get(DefaultEditorKit.cutAction)));
1798        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_V, SHORTCUT_MASK), (Action) (actions
1799                .get(DefaultEditorKit.pasteAction)));
1800
1801        // F2, F3, F4
1802
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), (Action) (actions.get("copy-line")));
1803        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0), (Action) (actions
1804                .get(DefaultEditorKit.pasteAction)));
1805        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0), (Action) (actions.get("cut-line")));
1806
1807        // cursor block
1808
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_UP, ALT_SHORTCUT_MASK), (Action) (actions
1809                .get(DefaultEditorKit.pasteAction)));
1810        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, ALT_SHORTCUT_MASK), (Action) (actions
1811                .get(DefaultEditorKit.deletePrevCharAction)));
1812        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, ALT_SHORTCUT_MASK), (Action) (actions
1813                .get(DefaultEditorKit.deleteNextCharAction)));
1814        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, SHIFT_ALT_SHORTCUT_MASK),
1815                (Action) (actions.get("cut-line")));
1816        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, SHIFT_ALT_SHORTCUT_MASK),
1817                (Action) (actions.get("cut-end-of-line")));
1818        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, DOUBLE_SHORTCUT_MASK), (Action) (actions
1819                .get("cut-word")));
1820        keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, DOUBLE_SHORTCUT_MASK), (Action) (actions
1821                .get("cut-end-of-word")));
1822    }
1823
1824    /**
1825     * Interface LineAction - a superclass for all line actions. Line actions
1826     * manipulate a single line of text and are used by the blockAction method.
1827     * The blockAction applies a LineAction to each line in a block of text.
1828     */

1829    interface LineAction
1830    {
1831        /**
1832         * Apply some action to a line in the document.
1833         */

1834        public void apply(Element line, MoeSyntaxDocument doc);
1835    }
1836    
1837    
1838
1839    /**
1840     * Class CommentLineAction - add a comment symbol to the given line.
1841     */

1842    class CommentLineAction
1843        implements LineAction
1844    {
1845        /**
1846         * Comment the given line
1847         */

1848        public void apply(Element line, MoeSyntaxDocument doc)
1849        {
1850            int lineStart = line.getStartOffset();
1851            try {
1852                doc.insertString(lineStart, "// ", null);
1853            }
1854            catch (Exception JavaDoc exc) {}
1855        }
1856    }
1857
1858    
1859    /**
1860     * Class UncommentLineAction - remove the comment symbol (if any) from the
1861     * given line.
1862     */

1863    class UncommentLineAction
1864        implements LineAction
1865    {
1866        public void apply(Element line, MoeSyntaxDocument doc)
1867        {
1868            int lineStart = line.getStartOffset();
1869            int lineEnd = line.getEndOffset();
1870            try {
1871                String JavaDoc lineText = doc.getText(lineStart, lineEnd - lineStart);
1872                if (lineText.trim().startsWith("//")) {
1873                    int cnt = 0;
1874                    while (lineText.charAt(cnt) != '/')
1875                        // whitespace chars
1876
cnt++;
1877                    if (lineText.charAt(cnt + 2) == ' ')
1878                        doc.remove(lineStart, cnt + 3);
1879                    else
1880                        doc.remove(lineStart, cnt + 2);
1881                }
1882            }
1883            catch (Exception JavaDoc exc) {}
1884        }
1885    }
1886
1887    /**
1888     * Class IndentLineAction - add one level of indentation to the given line.
1889     */

1890    class IndentLineAction
1891        implements LineAction
1892    {
1893        public void apply(Element line, MoeSyntaxDocument doc)
1894        {
1895            int lineStart = line.getStartOffset();
1896            try {
1897                doc.insertString(lineStart, spaces.substring(0, tabSize), null);
1898            }
1899            catch (Exception JavaDoc exc) {}
1900        }
1901    }
1902
1903    /**
1904     * Class DeindentLineAction - remove one indentation level from the given
1905     * line.
1906     */

1907    class DeindentLineAction
1908        implements LineAction
1909    {
1910        public void apply(Element line, MoeSyntaxDocument doc)
1911        {
1912            int lineStart = line.getStartOffset();
1913            int lineEnd = line.getEndOffset();
1914            try {
1915                String JavaDoc lineText = doc.getText(lineStart, lineEnd - lineStart);
1916                String JavaDoc spacedTab = spaces.substring(0, tabSize);
1917                if (lineText.startsWith(spacedTab))
1918                    doc.remove(lineStart, tabSize); // remove spaced tab
1919
else if (lineText.charAt(0) == TAB_CHAR)
1920                    doc.remove(lineStart, 1); // remove hard tab
1921
else {
1922                    int cnt = 0;
1923                    while (lineText.charAt(cnt) == ' ')
1924                        // remove spaces
1925
cnt++;
1926                    doc.remove(lineStart, cnt);
1927                }
1928            }
1929            catch (Exception JavaDoc exc) {}
1930        }
1931    }
1932
1933    /**
1934     * Class KeyCatcher - used for implementation of "describe-key" command to
1935     * catch the next key press so that we can see what it does.
1936     */

1937    class KeyCatcher extends KeyAdapter JavaDoc
1938    {
1939        MoeEditor editor;
1940
1941        public void keyPressed(KeyEvent JavaDoc e)
1942        {
1943            int keyCode = e.getKeyCode();
1944
1945            if (keyCode == KeyEvent.VK_CAPS_LOCK || // the keys we want to
1946
// ignore...
1947
keyCode == KeyEvent.VK_SHIFT || keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_META
1948                    || keyCode == KeyEvent.VK_ALT || keyCode == KeyEvent.VK_ALT_GRAPH || keyCode == KeyEvent.VK_COMPOSE
1949                    || keyCode == KeyEvent.VK_NUM_LOCK || keyCode == KeyEvent.VK_SCROLL_LOCK
1950                    || keyCode == KeyEvent.VK_UNDEFINED)
1951                return;
1952
1953            KeyStroke key = KeyStroke.getKeyStrokeForEvent(e);
1954            String JavaDoc modifierName = KeyEvent.getKeyModifiersText(key.getModifiers());
1955            String JavaDoc keyName = KeyEvent.getKeyText(keyCode);
1956            if (modifierName.length() > 0)
1957                keyName = modifierName + "+" + keyName;
1958
1959            Keymap map = keymap;
1960            Action action = null;
1961
1962            while (map != null && action == null) {
1963                action = map.getAction(key);
1964                map = map.getResolveParent();
1965            }
1966
1967            if (action == null) {
1968                // BUG workaround: bindings inhertited from component are not
1969
// found
1970
// through the keymap. we search for them explicitly here...
1971
Object JavaDoc binding = componentInputMap.get(key);
1972                if (binding == null){
1973                    //editor.writeMessage(keyName + " is not bound to a function.");
1974
editor.writeMessage(keyName + Config.getString("editor.keypressed.keyIsNotBound"));
1975                }
1976                else {
1977                    editor.writeMessage(keyName + Config.getString("editor.keypressed.callsTheFunction") + binding + "\"");
1978                }
1979            }
1980            else {
1981                String JavaDoc name = (String JavaDoc) action.getValue(Action.NAME);
1982                editor.writeMessage(keyName + Config.getString("editor.keypressed.callsTheFunction") + name + "\"");
1983            }
1984            e.getComponent().removeKeyListener(keyCatcher);
1985            e.consume();
1986        }
1987
1988        public void setEditor(MoeEditor ed)
1989        {
1990            editor = ed;
1991        }
1992
1993    }
1994
1995} // end class MoeActions
1996
Popular Tags