KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > armedbear > j > Editor


1 /*
2  * Editor.java
3  *
4  * Copyright (C) 1998-2004 Peter Graves
5  * $Id: Editor.java,v 1.130 2004/09/19 18:27:45 piso Exp $
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */

21
22 package org.armedbear.j;
23
24 import gnu.regexp.RE;
25 import gnu.regexp.REException;
26 import gnu.regexp.REMatch;
27 import gnu.regexp.UncheckedRE;
28 import java.awt.AWTEvent JavaDoc;
29 import java.awt.BorderLayout JavaDoc;
30 import java.awt.Cursor JavaDoc;
31 import java.awt.Dimension JavaDoc;
32 import java.awt.Point JavaDoc;
33 import java.awt.Rectangle JavaDoc;
34 import java.awt.Toolkit JavaDoc;
35 import java.awt.datatransfer.DataFlavor JavaDoc;
36 import java.awt.datatransfer.Transferable JavaDoc;
37 import java.awt.dnd.DropTarget JavaDoc;
38 import java.awt.event.ComponentEvent JavaDoc;
39 import java.awt.event.ComponentListener JavaDoc;
40 import java.awt.event.KeyEvent JavaDoc;
41 import java.awt.event.MouseEvent JavaDoc;
42 import java.awt.event.MouseWheelEvent JavaDoc;
43 import java.awt.event.MouseWheelListener JavaDoc;
44 import java.awt.event.WindowEvent JavaDoc;
45 import java.io.BufferedReader JavaDoc;
46 import java.io.BufferedWriter JavaDoc;
47 import java.io.IOException JavaDoc;
48 import java.io.InputStreamReader JavaDoc;
49 import java.io.OutputStream JavaDoc;
50 import java.io.OutputStreamWriter JavaDoc;
51 import java.io.StringReader JavaDoc;
52 import java.io.UnsupportedEncodingException JavaDoc;
53 import java.lang.reflect.Method JavaDoc;
54 import java.net.ConnectException JavaDoc;
55 import java.net.MalformedURLException JavaDoc;
56 import java.net.Socket JavaDoc;
57 import java.text.SimpleDateFormat JavaDoc;
58 import java.util.ArrayList JavaDoc;
59 import java.util.Date JavaDoc;
60 import java.util.Enumeration JavaDoc;
61 import java.util.Hashtable JavaDoc;
62 import java.util.List JavaDoc;
63 import java.util.Properties JavaDoc;
64 import java.util.Stack JavaDoc;
65 import java.util.StringTokenizer JavaDoc;
66 import java.util.Vector JavaDoc;
67 import javax.swing.Box JavaDoc;
68 import javax.swing.BoxLayout JavaDoc;
69 import javax.swing.FocusManager JavaDoc;
70 import javax.swing.JComponent JavaDoc;
71 import javax.swing.JDialog JavaDoc;
72 import javax.swing.JLabel JavaDoc;
73 import javax.swing.JMenuBar JavaDoc;
74 import javax.swing.JPanel JavaDoc;
75 import javax.swing.JPopupMenu JavaDoc;
76 import javax.swing.SwingUtilities JavaDoc;
77 import javax.swing.undo.CompoundEdit JavaDoc;
78 import org.armedbear.j.mail.MailCommands;
79 import org.armedbear.j.mail.MailboxURL;
80 import org.armedbear.lisp.Condition;
81 import org.armedbear.lisp.ConditionThrowable;
82 import org.armedbear.lisp.Interpreter;
83 import org.armedbear.lisp.Lisp;
84 import org.armedbear.lisp.LispObject;
85 import org.armedbear.lisp.LispThread;
86
87 public final class Editor extends JPanel JavaDoc implements Constants,
88     ComponentListener JavaDoc, MouseWheelListener JavaDoc
89 {
90     private static final long startTimeMillis = System.currentTimeMillis();
91
92     private static boolean debug = false;
93     private static boolean saveSession = true;
94
95     static File portfile;
96
97     private static EditorList editorList = new EditorList();
98
99     private static PendingOperations pendingOperations = new PendingOperations();
100
101     private static Editor currentEditor;
102
103     private static KillRing killRing = new KillRing();
104
105     public static final KillRing getKillRing()
106     {
107         return killRing;
108     }
109
110     private static String JavaDoc killedColumn;
111
112     private static SessionProperties sessionProperties;
113
114     public static SessionProperties getSessionProperties()
115     {
116         return sessionProperties;
117     }
118
119     private static final Preferences prefs = new Preferences();
120
121     public static final Preferences preferences()
122     {
123         return prefs;
124     }
125
126     private static boolean isRecordingMacro;
127
128     public static synchronized boolean isRecordingMacro()
129     {
130         return isRecordingMacro;
131     }
132
133     public static synchronized void setRecordingMacro(boolean b)
134     {
135         isRecordingMacro = b;
136     }
137
138     static String JavaDoc lookAndFeel;
139
140     private Buffer buffer;
141
142     private final Display display;
143     private final Dispatcher dispatcher;
144     private final Frame frame;
145
146     private Search lastSearch;
147
148     public final Search getLastSearch()
149     {
150         return lastSearch;
151     }
152
153     public final void setLastSearch(Search search)
154     {
155         lastSearch = search;
156     }
157
158     // The current position in the buffer (that is, in the actual text).
159
private Position dot;
160
161     // The position of the other end of the selection, if any,
162
private Position mark;
163
164     private Selection selection;
165     private boolean isColumnSelection;
166
167     Hashtable JavaDoc views = new Hashtable JavaDoc();
168
169     // BUG! This stuff should be factored somehow...
170
private int currentCommand = COMMAND_NOTHING;
171     private int lastCommand = COMMAND_NOTHING;
172
173     public final int getCurrentCommand()
174     {
175         return currentCommand;
176     }
177
178     public final void setCurrentCommand(int command)
179     {
180         currentCommand = command;
181     }
182
183     public final int getLastCommand()
184     {
185         return lastCommand;
186     }
187
188     public final void setLastCommand(int command)
189     {
190         lastCommand = command;
191     }
192
193     private static Marker[] bookmarks = new Marker[11];
194
195     private static TagFileManager tagFileManager;
196
197     private static boolean tabsAreVisible = false;
198
199     public static final boolean tabsAreVisible()
200     {
201         return tabsAreVisible;
202     }
203
204     static boolean isMenuSelected = false;
205
206     DirectoryTree localDirectoryTree;
207
208     private static ModeList modeList;
209
210     public static final ModeList getModeList()
211     {
212         if (modeList == null)
213             modeList = ModeList.getInstance();
214         return modeList;
215     }
216
217     private static final BufferList bufferList = new BufferList();
218
219     public static final BufferList getBufferList()
220     {
221         return bufferList;
222     }
223
224     public static long getStartTimeMillis()
225     {
226         return startTimeMillis;
227     }
228
229     private static String JavaDoc when()
230     {
231         return String.valueOf(System.currentTimeMillis() - startTimeMillis) + " ms";
232     }
233
234     public static void main(String JavaDoc[] args)
235     {
236         final File currentDir = File.getInstance(System.getProperty("user.dir"));
237         boolean forceNewInstance = false;
238         boolean restoreSession = true;
239         boolean startServer = true;
240         int quick = 0;
241         File userHomeDir = null;
242         List JavaDoc files = null;
243
244         // Process command line.
245
for (int i = 0; i < args.length; i++) {
246             final String JavaDoc arg = args[i];
247             if (arg.startsWith("-")) {
248                 if (arg.equals("-h") || arg.equals("-help") || arg.equals("--help")) {
249                     usage();
250                     System.exit(0);
251                 }
252                 if (arg.equals("-version")) {
253                     version();
254                     System.exit(0);
255                 }
256                 if (arg.equals("-d") || arg.equals("--debug")) {
257                     debug = true;
258                     continue;
259                 }
260                 if (arg.equals("-q")) {
261                     if (quick < 1)
262                         quick = 1;
263                     continue;
264                 }
265                 if (arg.equals("-n") || arg.equals("--no-restore")) {
266                     restoreSession = false;
267                     continue;
268                 }
269                 if (arg.equals("-session")) {
270                     if (i < args.length-1)
271                         sessionName = args[++i];
272                     continue;
273                 }
274                 if (arg.equals("--force-new-instance")) {
275                     forceNewInstance = true;
276                     continue;
277                 }
278                 if (arg.equals("--no-session")) {
279                     restoreSession = false;
280                     saveSession = false;
281                     continue;
282                 }
283                 if (arg.equals("--no-server")) {
284                     startServer = false;
285                     continue;
286                 }
287                 if (arg.startsWith("--home")) {
288                     String JavaDoc home = null;
289                     if (arg.equals("--home")) {
290                         if (i < args.length-1)
291                             home = args[++i];
292                     } else if (arg.startsWith("--home="))
293                         home = arg.substring(7);
294                     else
295                         unknown(arg);
296
297                     if (home == null || home.length() == 0)
298                         fatal("Option \"--home\" requires an argument.");
299
300                     userHomeDir = File.getInstance(currentDir, home);
301
302                     if (userHomeDir == null || !userHomeDir.isDirectory()) {
303                         fatal("Specified home directory \"" +
304                             userHomeDir.canonicalPath() +
305                             "\" does not exist.");
306                     }
307
308                     if (!userHomeDir.canWrite()) {
309                         fatal("Specified home directory \"" +
310                             userHomeDir.canonicalPath() +
311                             "\" is not writable.");
312                     }
313
314                     // Specified directory is OK.
315
Utilities.setUserHome(userHomeDir.canonicalPath());
316
317                     continue;
318                 }
319                 // If we get here, it's an unknown option.
320
unknown(arg);
321             } else {
322                 // It's a file to be opened.
323
if (files == null)
324                     files = new ArrayList JavaDoc();
325                 files.add(arg);
326             }
327         }
328
329         // At this point the user has had a chance to tell us where his home
330
// directory is.
331
Directories.initialize(userHomeDir);
332
333         boolean alreadyRunning = false;
334         portfile = File.getInstance(Directories.getEditorDirectory(), "port");
335         if (portfile.exists()) {
336             try {
337                 BufferedReader JavaDoc in = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(portfile.getInputStream()));
338                 String JavaDoc s = in.readLine();
339                 in.close();
340
341                 int port = Integer.parseInt(s);
342
343                 Socket JavaDoc socket = new Socket JavaDoc("localhost", port);
344
345                 // No ConnectException. We found a running instance.
346
alreadyRunning = true;
347
348                 if (!forceNewInstance) {
349                     BufferedWriter JavaDoc out =
350                         new BufferedWriter JavaDoc(new OutputStreamWriter JavaDoc(socket.getOutputStream()));
351                     File dir = File.getInstance(System.getProperty("user.dir"));
352                     out.write(dir.canonicalPath());
353                     out.newLine();
354                     if (files != null) {
355                         for (int i = 0; i < files.size(); i++) {
356                             out.write((String JavaDoc)files.get(i));
357                             out.newLine();
358                         }
359                     }
360                     out.flush();
361                     out.close();
362                     socket.close();
363                     System.exit(0);
364                 }
365             }
366             catch (ConnectException JavaDoc e) {
367                 portfile.delete();
368             }
369             catch (IOException JavaDoc e) {
370                 Log.error(e);
371             }
372         }
373
374         loadPreferences();
375         Log.initialize();
376         Directories.moveUnsentMessagesToDraftsFolder();
377         loadExtensions();
378         if (quick == 0) {
379             runStartupScript();
380         }
381         DefaultLookAndFeel.setLookAndFeel();
382
383         sessionProperties = new SessionProperties();
384
385         if (!alreadyRunning)
386             Autosave.recover();
387
388         tagFileManager = new TagFileManager();
389
390         setCurrentEditor(new Editor(null));
391
392         currentEditor.getFrame().updateControls();
393
394         // With Java 1.4, we only need to do this to support the key-pressed
395
// hook.
396
FocusManager.setCurrentManager(new CustomFocusManager());
397
398         currentEditor.getFrame().placeWindow();
399
400         Buffer toBeActivated = null;
401
402         if (restoreSession) {
403             Session session = null;
404             if (sessionName != null)
405                 session = Session.getSession(sessionName);
406             if (session == null)
407                 session = Session.getDefaultSession();
408             toBeActivated = session.restore();
409         }
410
411         if (files != null) {
412             ArrayList JavaDoc list = new ArrayList JavaDoc();
413             list.add(currentDir.canonicalPath());
414             for (int i = 0; i < files.size(); i++)
415                 list.add(files.get(i));
416             Buffer buf = currentEditor.openFiles(list);
417             if (buf != null) {
418                 Debug.assertTrue(bufferList.contains(buf));
419                 toBeActivated = buf;
420             }
421         }
422
423         if (toBeActivated == null)
424             toBeActivated = new Directory(currentDir);
425
426         currentEditor.activate(toBeActivated);
427
428         if (startServer)
429             Server.startServer();
430
431         Runnable JavaDoc r = new Runnable JavaDoc() {
432             public void run()
433             {
434                 currentEditor.getFrame().setVisible(true);
435                 Sidebar sidebar = currentEditor.getSidebar();
436                 if (sidebar != null)
437                     sidebar.setUpdateFlag(SIDEBAR_ALL);
438             }
439         };
440         SwingUtilities.invokeLater(r);
441
442         Log.debug("leaving main " + when());
443     }
444
445     private static final void usage()
446     {
447         version();
448         System.out.println("Usage: j [options] [+linenum] file");
449         System.out.println("Options:");
450         System.out.println(" -h, -help");
451         System.out.println(" -d, --debug");
452         System.out.println(" -n, --no-restore");
453         System.out.println(" -version");
454         System.out.println(" --force-new-instance");
455         System.out.println(" --no-session");
456         System.out.println(" --no-server");
457         System.out.println(" --home=directory");
458     }
459
460     private static final void version()
461     {
462         String JavaDoc longVersionString = Version.getLongVersionString();
463         if (longVersionString != null)
464             System.out.println(longVersionString);
465         String JavaDoc snapshotInformation = Version.getSnapshotInformation();
466         if (snapshotInformation != null)
467             System.out.println(snapshotInformation);
468     }
469
470     public static final void fatal(String JavaDoc message)
471     {
472         System.err.println(message);
473         System.exit(1);
474     }
475
476     private static final void unknown(String JavaDoc arg)
477     {
478         usage();
479         fatal("Unknown option \"" + arg + "\"");
480     }
481
482     public Editor(Frame f)
483     {
484         display = new Display(this);
485         dispatcher = new Dispatcher(this);
486         init();
487         frame = f != null ? f : new Frame(this);
488     }
489
490     private void init()
491     {
492         // Add this editor to the global editor list.
493
editorList.add(this);
494
495         setLayout(new BorderLayout JavaDoc());
496         display.setDoubleBuffered(true);
497         add(display, BorderLayout.CENTER);
498
499         new DropTarget JavaDoc(display, dispatcher);
500
501         addLocationBar();
502         addVerticalScrollBar();
503         addHorizontalScrollBar();
504
505         display.addKeyListener(dispatcher);
506         display.addMouseListener(dispatcher);
507         display.addMouseMotionListener(dispatcher);
508
509         addMouseWheelListener(this);
510         addComponentListener(this);
511     }
512
513     public static final boolean isDebugEnabled()
514     {
515         return debug;
516     }
517
518     public static final boolean isMailEnabled()
519     {
520         if (!prefs.getBooleanProperty(Property.ENABLE_EXPERIMENTAL_FEATURES))
521             return false;
522         if (!prefs.getBooleanProperty(Property.ENABLE_MAIL))
523             return false;
524         // Mail address must be configured!
525
if (prefs.getStringProperty(Property.USER_MAIL_ADDRESS) == null)
526             return false;
527         return true;
528     }
529
530     private LocationBar locationBar;
531
532     public final LocationBar getLocationBar()
533     {
534         return locationBar;
535     }
536
537     public final HistoryTextField getLocationBarTextField()
538     {
539         return locationBar == null ? null : locationBar.getTextField();
540     }
541
542     public void addLocationBar()
543     {
544         if (locationBar == null) {
545             locationBar = new LocationBar(this);
546             add(locationBar, BorderLayout.NORTH);
547         }
548     }
549
550     public void removeLocationBar()
551     {
552         if (locationBar != null) {
553             remove(locationBar);
554             locationBar = null;
555         }
556     }
557
558     public void updateLocation()
559     {
560         if (locationBar != null) {
561             HistoryTextField textField = locationBar.getTextField();
562             if (textField == null || textField != frame.getFocusedComponent())
563                 locationBar.update();
564         } else
565             Debug.bug();
566     }
567
568     public boolean isPrimaryEditor()
569     {
570         return frame.isPrimaryEditor(this);
571     }
572
573     public final Editor getOtherEditor()
574     {
575         return isPrimaryEditor() ? frame.getSecondaryEditor() : frame.getPrimaryEditor();
576     }
577
578     public static int indexOf(Editor editor)
579     {
580         for (int i = getEditorCount() - 1; i >= 0; i--) {
581             if (editor == getEditor(i))
582                 return i;
583         }
584
585         return -1;
586     }
587
588     public static final EditorList getEditorList()
589     {
590         return editorList;
591     }
592
593     public static final int getEditorCount()
594     {
595         return editorList.size();
596     }
597
598     public static final Editor getEditor(int i)
599     {
600         return editorList.get(i);
601     }
602
603     public static final void removeEditor(Editor editor)
604     {
605         editorList.remove(editor);
606     }
607
608     public final Frame getFrame()
609     {
610         return frame;
611     }
612
613     static Vector JavaDoc frames = new Vector JavaDoc();
614
615     public static int indexOf(Frame frame)
616     {
617         for (int i = getFrameCount() - 1; i >= 0; i--) {
618             if (frame == getFrame(i))
619                 return i;
620         }
621
622         return -1;
623     }
624
625     public static final int getFrameCount()
626     {
627         return frames.size();
628     }
629
630     public static final Frame getFrame(int i)
631     {
632         if (i >= 0 && i < frames.size())
633             return (Frame) frames.get(i);
634         return null;
635     }
636
637     public final Buffer getBuffer()
638     {
639         return buffer;
640     }
641
642     public final Mode getMode()
643     {
644         return buffer.getMode();
645     }
646
647     public final int getModeId()
648     {
649         return buffer.getModeId();
650     }
651
652     public final Formatter getFormatter()
653     {
654         return buffer.getFormatter();
655     }
656
657     public final Display getDisplay()
658     {
659         return display;
660     }
661
662     public final Dispatcher getDispatcher()
663     {
664         return dispatcher;
665     }
666
667     public static final TagFileManager getTagFileManager()
668     {
669         return tagFileManager;
670     }
671
672     public final Sidebar getSidebar()
673     {
674         return frame.getSidebar();
675     }
676
677     public final StatusBar getStatusBar()
678     {
679         return frame.getStatusBar();
680     }
681
682     public static final PendingOperations getPendingOperations()
683     {
684         return pendingOperations;
685     }
686
687     private VerticalScrollBar verticalScrollBar;
688
689     private VerticalScrollBarListener verticalScrollBarListener;
690
691     public void addVerticalScrollBar()
692     {
693         if (verticalScrollBar == null) {
694             verticalScrollBar = new VerticalScrollBar(this);
695             verticalScrollBar.setMinimum(0);
696             add(verticalScrollBar, BorderLayout.EAST);
697             verticalScrollBarListener = new VerticalScrollBarListener(this, verticalScrollBar);
698             verticalScrollBar.addAdjustmentListener(verticalScrollBarListener);
699         }
700     }
701
702     public void removeVerticalScrollBar()
703     {
704         if (verticalScrollBar != null) {
705             if (verticalScrollBarListener == null) {
706                 verticalScrollBar.removeAdjustmentListener(verticalScrollBarListener);
707                 verticalScrollBarListener = null;
708             }
709             remove(verticalScrollBar);
710             verticalScrollBar = null;
711         }
712     }
713
714     private HorizontalScrollBar horizontalScrollBar;
715
716     private HorizontalScrollBarListener horizontalScrollBarListener;
717
718     public void addHorizontalScrollBar()
719     {
720         if (horizontalScrollBar == null) {
721             horizontalScrollBar = new HorizontalScrollBar(this);
722             horizontalScrollBar.setMinimum(0);
723             JPanel JavaDoc panel = new JPanel JavaDoc();
724             panel.setLayout(new BoxLayout JavaDoc(panel, BoxLayout.X_AXIS));
725             panel.add(horizontalScrollBar);
726             final int height = horizontalScrollBar.getPreferredSize().height;
727             panel.add(Box.createRigidArea(new Dimension JavaDoc(height, height)));
728             add(panel, BorderLayout.SOUTH);
729             horizontalScrollBarListener = new HorizontalScrollBarListener(this);
730             horizontalScrollBar.addAdjustmentListener(horizontalScrollBarListener);
731         }
732     }
733
734     public void removeHorizontalScrollBar()
735     {
736         if (horizontalScrollBar != null) {
737             if (horizontalScrollBarListener == null) {
738                 horizontalScrollBar.removeAdjustmentListener(horizontalScrollBarListener);
739                 horizontalScrollBarListener = null;
740             }
741             // Remove JPanel containing scroll bar.
742
remove(horizontalScrollBar.getParent());
743             horizontalScrollBar = null;
744         }
745     }
746
747     public static synchronized final Editor currentEditor()
748     {
749         return currentEditor;
750     }
751
752     public static synchronized final void setCurrentEditor(Editor editor)
753     {
754         Editor oldCurrentEditor = currentEditor;
755         currentEditor = editor;
756         editor.getFrame().setCurrentEditor(editor);
757         if (currentEditor != oldCurrentEditor) {
758             if (currentEditor != null)
759                 currentEditor.getLocationBar().repaint();
760             if (oldCurrentEditor != null)
761                 oldCurrentEditor.getLocationBar().repaint();
762             if (isLispInitialized())
763                 LispAPI.invokeBufferActivatedHook(currentEditor.getBuffer());
764         }
765     }
766
767     public static synchronized final Buffer currentBuffer()
768     {
769         return currentEditor.buffer;
770     }
771
772     public static synchronized final Frame getCurrentFrame()
773     {
774         return currentEditor.getFrame();
775     }
776
777     public static Editor createNewFrame()
778     {
779         Editor ed = new Editor(null);
780         ed.getFrame().updateControls();
781         ed.getFrame().placeWindow();
782         return ed;
783     }
784
785     public void newFrame()
786     {
787         saveView();
788         Editor ed = createNewFrame();
789         ed.activate(buffer);
790         ed.getFrame().setVisible(true);
791         setCurrentEditor(ed);
792         ed.updateDisplay();
793         display.repaint();
794         Runnable JavaDoc r = new Runnable JavaDoc() {
795             public void run()
796             {
797                 currentEditor.setFocusToDisplay();
798             }
799         };
800         SwingUtilities.invokeLater(r);
801     }
802
803     public View getCurrentView()
804     {
805         return (View) views.get(buffer);
806     }
807
808     // Might return null.
809
public final View getView(SystemBuffer buf)
810     {
811         return (View) views.get(buf);
812     }
813
814     public final void setView(SystemBuffer buf, View view)
815     {
816         views.put(buf, view);
817     }
818
819     public void removeView(SystemBuffer buf)
820     {
821         views.remove(buf);
822     }
823
824     // Find or create a view of buf.
825
private View findOrCreateView(Buffer buf)
826     {
827         View view = (View) views.get(buf);
828         if (view == null) {
829             view = buf.getLastView();
830             if (view == null)
831                 view = buf.getInitialView();
832             views.put(buf, view);
833         }
834         return view;
835     }
836
837     public final void saveView()
838     {
839         buffer.saveView(this);
840     }
841
842     private final void restoreView()
843     {
844         buffer.restoreView(this);
845     }
846
847     public final Position getDot()
848     {
849         return dot;
850     }
851
852     public final Position getDotCopy()
853     {
854         return dot != null ? new Position(dot) : null;
855     }
856
857     public final void setDot(Position pos)
858     {
859         dot = pos;
860     }
861
862     public final void setDot(Line line, int offset)
863     {
864         dot = new Position(line, offset);
865     }
866
867     public void setDot(int lineNumber, int offset)
868     {
869         Line line = buffer.getLine(lineNumber);
870         if (line != null)
871             setDot(line, offset);
872     }
873
874     public final Line getDotLine()
875     {
876         return dot.getLine();
877     }
878
879     public final int getDotLineNumber()
880     {
881         return dot.lineNumber();
882     }
883
884     public final int getDotOffset()
885     {
886         return dot.getOffset();
887     }
888
889     public final Selection getSelection()
890     {
891         return selection;
892     }
893
894     public final void setSelection(Selection selection)
895     {
896         this.selection = selection;
897     }
898
899     public final Position getMark()
900     {
901         return mark;
902     }
903
904     public void setMarkAtDot()
905     {
906         setMark(new Position(dot));
907         selection = new Selection();
908     }
909
910     public void setMark(Position pos)
911     {
912         mark = pos;
913         if (mark == null) {
914             selection = null;
915             setColumnSelection(false);
916         }
917     }
918
919     public void setMark(int lineNumber, int offset)
920     {
921         Line line = buffer.getLine(lineNumber);
922         if (line != null)
923             setMark(new Position(line, offset));
924     }
925
926     public final void setColumnSelection(boolean b)
927     {
928         isColumnSelection = b;
929     }
930
931     public final boolean isColumnSelection()
932     {
933         return isColumnSelection;
934     }
935
936     public final void notSupportedForColumnSelections()
937     {
938         MessageDialog.showMessageDialog(this,
939             "Operation not supported for column selections", "Error");
940     }
941
942     public final Line getMarkLine()
943     {
944         return mark.getLine();
945     }
946
947     public final int getMarkLineNumber()
948     {
949         return mark.lineNumber();
950     }
951
952     public final int getMarkOffset()
953     {
954         return mark.getOffset();
955     }
956
957     public File getCurrentDirectory()
958     {
959         return buffer.getCurrentDirectory();
960     }
961
962     public File getCompletionDirectory()
963     {
964         return buffer.getCompletionDirectory();
965     }
966
967     // Cycle through the most plausible possibilities for the tab width of the
968
// current buffer.
969
public void cycleTabWidth()
970     {
971         switch (buffer.getTabWidth()) {
972             case 2:
973                 buffer.setTabWidth(4);
974                 break;
975             case 4:
976                 buffer.setTabWidth(8);
977                 break;
978             case 8:
979             default:
980                 buffer.setTabWidth(2);
981                 break;
982         }
983         buffer.saveProperties();
984         status("Tab width set to " + buffer.getTabWidth());
985         buffer.repaint();
986     }
987
988     // Cycle through the most plausible possibilities for the indent size of
989
// the current buffer.
990
public void cycleIndentSize()
991     {
992         switch (buffer.getIndentSize()) {
993             case 2:
994                 buffer.setIndentSize(3);
995                 break;
996             case 3:
997                 buffer.setIndentSize(4);
998                 break;
999             case 4:
1000                buffer.setIndentSize(8);
1001                break;
1002            case 8:
1003            default:
1004                buffer.setIndentSize(2);
1005                break;
1006        }
1007        buffer.saveProperties();
1008        status("Indent size set to " + buffer.getIndentSize());
1009    }
1010
1011    public void adjustMarkers(Line line)
1012    {
1013        if (line == null)
1014            return;
1015        Position pos = null; // Where all displaced markers go.
1016
if (line.next() != null)
1017            pos = new Position(line.next(), 0);
1018        else if (line.previous() != null)
1019            pos = new Position(line.previous(), line.previous().length());
1020        if (pos == null)
1021            return;
1022        for (int i = 0; i < Editor.getEditorCount(); i++) {
1023            Editor ed = Editor.getEditor(i);
1024            if (ed.getBuffer() == buffer) {
1025                if (ed.getDotLine() == line) {
1026                    ed.getDot().moveTo(pos);
1027                    ed.moveCaretToDotCol();
1028                }
1029                if (ed.getMark() != null && ed.getMarkLine() == line)
1030                    ed.setMark(null); // Take no chances.
1031
if (ed.getTopLine() == line) {
1032                    ed.setTopLine(pos.getLine());
1033                    ed.setUpdateFlag(REPAINT);
1034                }
1035            } else {
1036                // Not presently displayed, but possibly in a stored view.
1037
View view = (View) ed.views.get(buffer);
1038                if (view != null) {
1039                    if (view.dot != null && view.dot.getLine() == line)
1040                        view.dot.moveTo(pos);
1041                    if (view.mark != null && view.mark.getLine() == line)
1042                        view.mark = null;
1043                    if (view.topLine == line)
1044                        view.topLine = pos.getLine();
1045                }
1046            }
1047        }
1048        for (int i = 0; i < bookmarks.length; i++) {
1049            Marker m = bookmarks[i];
1050            if (m != null && m.getLine() == line)
1051                m.setPosition(pos);
1052        }
1053    }
1054
1055    public static Marker[] getBookmarks()
1056    {
1057        return bookmarks;
1058    }
1059
1060    public void dropBookmark()
1061    {
1062        AWTEvent JavaDoc e = dispatcher.getLastEvent();
1063        if (e != null && e.getID() == KeyEvent.KEY_PRESSED) {
1064            int keyCode = ((KeyEvent JavaDoc)e).getKeyCode();
1065            int index = keyCode - KeyEvent.VK_0;
1066            if (index >= 0 && index < bookmarks.length) {
1067                if (bookmarks[index] == null ||
1068                    confirm("Drop Bookmark", "Overwrite existing bookmark?")) {
1069                    bookmarks[index] = new Marker(buffer, dot);
1070                    status("Bookmark dropped");
1071                }
1072            }
1073        }
1074    }
1075
1076    public void gotoBookmark()
1077    {
1078        AWTEvent JavaDoc e = dispatcher.getLastEvent();
1079        if (e != null && e.getID() == KeyEvent.KEY_PRESSED) {
1080            int keyCode = ((KeyEvent JavaDoc)e).getKeyCode();
1081            int index = keyCode - KeyEvent.VK_0;
1082            if (index >= 0 && index < bookmarks.length) {
1083                Marker m = bookmarks[index];
1084                if (m != null)
1085                    m.gotoMarker(this);
1086            }
1087        }
1088    }
1089
1090    // Drop a temporary bookmark, overwriting the existing temporary bookmark
1091
// if one exists.
1092
public void dropTemporaryMarker()
1093    {
1094        bookmarks[10] = new Marker(buffer, dot);
1095        status("Temporary marker dropped");
1096    }
1097
1098    public void gotoTemporaryMarker()
1099    {
1100        Marker m = bookmarks[10];
1101        if (m != null)
1102            m.gotoMarker(this);
1103    }
1104
1105    public void deleteLineSeparator()
1106    {
1107        final Line dotLine = getDotLine();
1108        final Line nextLine = dotLine.next();
1109        if (nextLine == null)
1110            return;
1111        try {
1112            buffer.lockWrite();
1113        }
1114        catch (InterruptedException JavaDoc e) {
1115            Log.error(e);
1116            return;
1117        }
1118        try {
1119            if (dotLine.length() == 0) {
1120                adjustMarkers(dotLine);
1121                // Save original text.
1122
FastStringBuffer sb = new FastStringBuffer();
1123                if (dotLine.getOriginalText() != null)
1124                    sb.append(dotLine.getOriginalText());
1125                sb.append('\n');
1126                if (nextLine.getOriginalText() != null)
1127                    sb.append(nextLine.getOriginalText());
1128                else
1129                    sb.append(nextLine.getText());
1130                nextLine.setOriginalText(sb.toString());
1131                // Unlink the current line.
1132
final Line prevLine = dotLine.previous();
1133                if (prevLine != null)
1134                    prevLine.setNext(nextLine);
1135                nextLine.setPrevious(prevLine);
1136                if (dotLine == buffer.getFirstLine()) {
1137                    Log.debug("deleteLineSeparator calling buffer.setFirstLine()");
1138                    buffer.setFirstLine(nextLine);
1139                    Log.debug("first line = |" + buffer.getFirstLine().getText() + "|");
1140                }
1141                if (dotLine == display.getTopLine())
1142                    display.setTopLine(nextLine);
1143                dot.moveTo(nextLine, 0);
1144            } else {
1145                // Append the next line's text to end of this line.
1146
dotLine.setText(dotLine.getText() + nextLine.getText());
1147                // Save original text.
1148
FastStringBuffer sb = new FastStringBuffer();
1149                if (dotLine.getOriginalText() != null)
1150                    sb.append(dotLine.getOriginalText());
1151                else
1152                    sb.append(dotLine.getText());
1153                if (!nextLine.isNew()) {
1154                    sb.append('\n');
1155                    if (nextLine.getOriginalText() != null)
1156                        sb.append(nextLine.getOriginalText());
1157                    else
1158                        sb.append(nextLine.getText());
1159                }
1160                dotLine.setOriginalText(sb.toString());
1161                // Move any markers that might be on the next line.
1162
adjustMarkers(nextLine);
1163                // Unlink the next line.
1164
if (nextLine.next() != null)
1165                    nextLine.next().setPrevious(dotLine);
1166                dotLine.setNext(nextLine.next());
1167            }
1168            buffer.repaint();
1169            setUpdateFlag(REFRAME);
1170            buffer.needsRenumbering = true;
1171            buffer.modified();
1172        }
1173        finally {
1174            buffer.unlockWrite();
1175        }
1176    }
1177
1178    private void deleteNormalChar()
1179    {
1180        addUndo(SimpleEdit.LINE_EDIT);
1181        final Line dotLine = getDotLine();
1182        final int dotOffset = getDotOffset();
1183        String JavaDoc head = dotLine.substring(0, dotOffset);
1184        String JavaDoc tail = "";
1185        if (dotOffset < dotLine.length() - 1)
1186            tail = dotLine.substring(dotOffset + 1);
1187        dotLine.setText(head.concat(tail));
1188        buffer.modified();
1189        updateInAllEditors(dotLine);
1190    }
1191
1192    // A deletion, not a kill!
1193
public void delete()
1194    {
1195        if (!checkReadOnly())
1196            return;
1197        try {
1198            buffer.lockWrite();
1199        }
1200        catch (InterruptedException JavaDoc e) {
1201            Log.error(e);
1202            return;
1203        }
1204        try {
1205            if (mark != null) {
1206                deleteRegion();
1207            } else {
1208                final Line dotLine = getDotLine();
1209                final int dotOffset = getDotOffset();
1210                final int length = dotLine.length();
1211                if (dotOffset < length) {
1212                    deleteNormalChar();
1213                } else if (dotOffset == length) {
1214                    if (dotLine.next() != null) {
1215                        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
1216                        fillToCaret();
1217                        addUndo(SimpleEdit.DELETE_LINE_SEP);
1218                        deleteLineSeparator();
1219                        endCompoundEdit(compoundEdit);
1220                    } else
1221                        status("End of buffer");
1222                } else {
1223                    // Shouldn't happen.
1224
Debug.bug();
1225                }
1226            }
1227        }
1228        finally {
1229            buffer.unlockWrite();
1230        }
1231    }
1232
1233    // A deletion, not a kill!
1234
public void backspace()
1235    {
1236        if (!checkReadOnly())
1237            return;
1238        try {
1239            buffer.lockWrite();
1240        }
1241        catch (InterruptedException JavaDoc e) {
1242            Log.error(e);
1243            return;
1244        }
1245        try {
1246            if (mark != null) {
1247                delete();
1248            } else if (display.getCaretCol() > buffer.getCol(getDotLine(), getDotLine().length())) {
1249                // The caret is beyond the end of the actual text on the current line.
1250
addUndo(SimpleEdit.MOVE);
1251                --display.caretCol;
1252                updateDotLine();
1253            } else if (dot.getOffset() > 0) {
1254                addUndo(SimpleEdit.LINE_EDIT);
1255                dot.moveLeft();
1256                deleteNormalChar();
1257                moveCaretToDotCol();
1258            } else if (getDotLine().previous() != null) {
1259                CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
1260                addUndo(SimpleEdit.MOVE);
1261                dot.moveTo(getDotLine().previous(), getDotLine().previous().length());
1262                addUndo(SimpleEdit.DELETE_LINE_SEP);
1263                deleteLineSeparator();
1264                endCompoundEdit(compoundEdit);
1265                moveCaretToDotCol();
1266            }
1267        }
1268        finally {
1269            buffer.unlockWrite();
1270        }
1271    }
1272
1273    private boolean nextChar()
1274    {
1275        if (getDotOffset() < getDotLine().length()) {
1276            dot.skip(1);
1277            return true;
1278        }
1279        if (getDotLine().next() != null) {
1280            dot.moveTo(getDotLine().next(), 0);
1281            return true;
1282        }
1283        return false;
1284    }
1285
1286    private boolean prevChar()
1287    {
1288        if (getDotOffset() > 0) {
1289            dot.skip(-1);
1290            return true;
1291        }
1292        final Line previous = getDotLine().previous();
1293        if (previous != null) {
1294            dot.moveTo(previous, previous.length());
1295            return true;
1296        }
1297        return false;
1298    }
1299
1300    public char getDotChar()
1301    {
1302        Debug.assertTrue(dot != null);
1303        return dot.getChar();
1304    }
1305
1306    public void cppFindMatch()
1307    {
1308        if (getDotLine().trim().startsWith("#")) {
1309            Line line = CMode.findMatchPreprocessor(getDotLine());
1310            if (line != null)
1311                moveDotTo(line, 0);
1312            else
1313                status("No match");
1314        } else
1315            findMatchingChar();
1316    }
1317
1318    // If numLines is non-zero, limit the search to that many lines either
1319
// forward or backward in the buffer.
1320
public Position findMatchInternal(Position start, int numLines)
1321    {
1322        if (start == null)
1323            return null;
1324        final String JavaDoc s1 = new String JavaDoc("{([})]");
1325        final char origChar = start.getChar();
1326        int index = s1.indexOf(origChar);
1327        if (index < 0)
1328            return null;
1329        final Mode mode = getMode();
1330        if (mode.isInComment(getBuffer(), start))
1331            return null;
1332        boolean inQuote = mode.isInQuote(buffer, start);
1333        if (inQuote)
1334            return null;
1335        final String JavaDoc s2 = new String JavaDoc("})]{([");
1336        final char match = s2.charAt(index);
1337        final boolean searchBackwards = index > 2;
1338        int stopLineNumber = searchBackwards ? 0 : buffer.getLineCount();
1339        if (numLines != 0)
1340            stopLineNumber = searchBackwards ? start.lineNumber() - numLines : start.lineNumber() + numLines;
1341        int count = 1;
1342        final SyntaxIterator it = mode.getSyntaxIterator(start);
1343        while (true) {
1344            char c;
1345            Position pos = it.getPosition();
1346            if (searchBackwards) {
1347                if (pos.lineNumber() < stopLineNumber)
1348                    return null;
1349                else
1350                    c = it.prevChar();
1351            } else {
1352                if (pos.lineNumber() > stopLineNumber)
1353                    return null;
1354                else
1355                    c = it.nextChar();
1356            }
1357            if (c == SyntaxIterator.DONE)
1358                return null;
1359            if (inQuote && c == '"')
1360                return null;
1361            if (c == origChar)
1362                ++count;
1363            else if (c == match)
1364                --count;
1365            if (count == 0) {
1366                // Found it!
1367
return it.getPosition();
1368            }
1369        }
1370    }
1371
1372    public void findMatchingChar()
1373    {
1374        setWaitCursor();
1375        Position pos = findDelimiterNearDot();
1376        if (pos != null) {
1377            Position match = findMatchInternal(pos, 0);
1378            if (match != null) {
1379                // If the match is a right delimiter, we want to put the caret
1380
// beyond it.
1381
if ("})]".indexOf(match.getChar()) >= 0)
1382                    match.next();
1383                addUndo(SimpleEdit.MOVE);
1384                unmark();
1385                updateDotLine();
1386                dot.moveTo(match);
1387                updateDotLine();
1388                moveCaretToDotCol();
1389            } else
1390                status("No match");
1391        }
1392        setDefaultCursor();
1393    }
1394
1395    public void selectSyntax()
1396    {
1397        setWaitCursor();
1398        Position pos = findDelimiterNearDot();
1399        if (pos != null) {
1400            Position match = findMatchInternal(pos, 0);
1401            if (match != null) {
1402                if ("})]".indexOf(pos.getChar()) >= 0)
1403                    pos.next();
1404                else if ("})]".indexOf(match.getChar()) >= 0)
1405                    match.next();
1406                if (pos.getLine() != match.getLine()) {
1407                    // Extend selection to full lines if possible.
1408
Region r = new Region(buffer, pos, match);
1409                    Position begin = r.getBegin();
1410                    String JavaDoc trim =
1411                        begin.getLine().substring(0, begin.getOffset()).trim();
1412                    if (trim.length() == 0) {
1413                        Position end = r.getEnd();
1414                        trim = end.getLine().substring(end.getOffset()).trim();
1415                        if (trim.length() == 0) {
1416                            // Extend selection to complete lines.
1417
begin.setOffset(0);
1418                            if (end.getNextLine() != null)
1419                                end.moveTo(end.getNextLine(), 0);
1420                            else
1421                                end.setOffset(end.getLineLength());
1422                            if (pos.isBefore(match)) {
1423                                pos = begin;
1424                                match = end;
1425                            } else {
1426                                match = begin;
1427                                pos = end;
1428                            }
1429                        }
1430                    }
1431                }
1432                addUndo(SimpleEdit.MOVE);
1433                unmark();
1434                dot.moveTo(pos);
1435                setMarkAtDot();
1436                updateDotLine();
1437                dot.moveTo(match);
1438                updateDotLine();
1439                moveCaretToDotCol();
1440                if (dot.getLine() != mark.getLine())
1441                    setUpdateFlag(REPAINT);
1442            } else
1443                status("No match");
1444        }
1445        setDefaultCursor();
1446    }
1447
1448    private Position findDelimiterNearDot()
1449    {
1450        Position pos = dot.copy();
1451        if ("{([".indexOf(pos.getChar()) >= 0) {
1452            // The character to the right of the caret is a left delimiter.
1453
return pos;
1454        }
1455        Position saved = dot.copy();
1456        if (pos.getOffset() > 0) {
1457            pos.prev();
1458            if ("})]".indexOf(pos.getChar()) >= 0) {
1459                // The character to the left of the caret is a right delimiter.
1460
return pos;
1461            }
1462        }
1463        // There's no delimiter at the exact location of the caret.
1464
final String JavaDoc delimiters = "{([})]";
1465        pos.moveTo(saved);
1466        while (pos.getOffset() > 0) {
1467            // Look at previous char.
1468
pos.prev();
1469            char c = pos.getChar();
1470            if (delimiters.indexOf(c) >= 0)
1471                return pos;
1472            if (!Character.isWhitespace(c) && c != ';')
1473                break;
1474        }
1475        pos.moveTo(saved);
1476        final int limit = pos.getLineLength();
1477        while (pos.getOffset() < limit) {
1478            char c = pos.getChar();
1479            if (delimiters.indexOf(c) >= 0)
1480                return pos;
1481            if (!Character.isWhitespace(c))
1482                return null;
1483            // Look at next char.
1484
pos.next();
1485        }
1486        return null;
1487    }
1488
1489    public void closeParen()
1490    {
1491        insertNormalChar(')');
1492        if (prefs.getBooleanProperty(Property.HIGHLIGHT_MATCHING_BRACKET) ||
1493            prefs.getBooleanProperty(Property.HIGHLIGHT_BRACKETS))
1494            return;
1495        // Limit search to 50 lines.
1496
Position match =
1497            findMatchInternal(new Position(getDotLine(), getDotOffset()-1), 50);
1498        if (match == null)
1499            return;
1500        // We don't want to reframe.
1501
if (match.lineNumber() < display.getTopLineNumber())
1502            return;
1503        if (buffer.getCol(match) < display.getShift())
1504            return;
1505        // Move caret to match momentarily and then return.
1506
Position saved = new Position(dot);
1507        updateDotLine();
1508        dot.moveTo(match);
1509        updateDotLine();
1510        moveCaretToDotCol();
1511        display.repaintChangedLines();
1512        try {
1513            Thread.sleep(300);
1514        }
1515        catch (InterruptedException JavaDoc e) {}
1516        updateDotLine();
1517        dot.moveTo(saved);
1518        moveCaretToDotCol();
1519        updateDotLine();
1520    }
1521
1522    // No undo.
1523
public void insertLineSeparator()
1524    {
1525        Debug.assertTrue(mark == null);
1526        try {
1527            buffer.lockWrite();
1528        }
1529        catch (InterruptedException JavaDoc e) {
1530            Log.error(e);
1531            return;
1532        }
1533        try {
1534            buffer.insertLineSeparator(dot);
1535        }
1536        finally {
1537            buffer.unlockWrite();
1538        }
1539        final Line dotLine = getDotLine();
1540        for (int i = 0; i < getEditorCount(); i++) {
1541            Editor ed = getEditor(i);
1542            if (ed.getTopLine() == dotLine)
1543                ed.setTopLine(dotLine.previous());
1544        }
1545    }
1546
1547    public void newline()
1548    {
1549        if (!checkReadOnly())
1550            return;
1551        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
1552        if (mark != null)
1553            deleteRegion();
1554        addUndo(SimpleEdit.INSERT_LINE_SEP);
1555        insertLineSeparator();
1556        moveCaretToDotCol();
1557        endCompoundEdit(compoundEdit);
1558    }
1559
1560    public void newlineAndIndent()
1561    {
1562        if (isColumnSelection()) {
1563            notSupportedForColumnSelections();
1564            return;
1565        }
1566        if (!checkReadOnly())
1567            return;
1568        try {
1569            buffer.lockWrite();
1570        }
1571        catch (InterruptedException JavaDoc e) {
1572            Log.error(e);
1573            return;
1574        }
1575        try {
1576            CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
1577            if (mark != null)
1578                deleteRegion();
1579            addUndo(SimpleEdit.INSERT_LINE_SEP);
1580            insertLineSeparator();
1581            final Mode mode = getMode();
1582            final Line dotLine = getDotLine();
1583            int indent;
1584            if (mode.canIndent()) {
1585                if (buffer.needsRenumbering())
1586                    buffer.renumber();
1587                getFormatter().parseBuffer();
1588                indent = mode.getCorrectIndentation(dotLine, buffer);
1589            } else {
1590                // Can't indent according to context. Match indentation of previous line.
1591
indent = buffer.getIndentation(dotLine.previous());
1592            }
1593            if (indent != buffer.getIndentation(dotLine)) {
1594                addUndo(SimpleEdit.LINE_EDIT);
1595                buffer.setIndentation(dotLine, indent);
1596            }
1597            if (dotLine.length() > 0) {
1598                moveDotToIndentation();
1599                moveCaretToDotCol();
1600            } else {
1601                display.setCaretCol(indent - display.getShift());
1602                if (buffer.getBooleanProperty(Property.RESTRICT_CARET))
1603                    fillToCaret();
1604            }
1605            endCompoundEdit(compoundEdit);
1606        }
1607        finally {
1608            buffer.unlockWrite();
1609        }
1610        setUpdateFlag(REFRAME);
1611    }
1612
1613    public void insertNormalChar(char c)
1614    {
1615        if (isColumnSelection()) {
1616            notSupportedForColumnSelections();
1617            return;
1618        }
1619        if (!checkReadOnly())
1620            return;
1621        try {
1622            buffer.lockWrite();
1623        }
1624        catch (InterruptedException JavaDoc e) {
1625            Log.error(e);
1626            return;
1627        }
1628        try {
1629            c = getMode().fixCase(this, c);
1630            if (mark != null) {
1631                CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
1632                deleteRegion();
1633                insertChar(c);
1634                endCompoundEdit(compoundEdit);
1635            } else {
1636                // No selection.
1637
if (buffer.getBooleanProperty(Property.WRAP) &&
1638                    getDotCol() >= buffer.getIntegerProperty(Property.WRAP_COL))
1639                {
1640                    CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
1641                    insertChar(c);
1642                    new WrapText(this).wrapLine();
1643                    endCompoundEdit(compoundEdit);
1644                } else
1645                    insertChar(c);
1646            }
1647        }
1648        finally {
1649            buffer.unlockWrite();
1650        }
1651        moveCaretToDotCol();
1652    }
1653
1654    public void tab()
1655    {
1656        if (isColumnSelection()) {
1657            notSupportedForColumnSelections();
1658            return;
1659        }
1660        if (buffer.getBooleanProperty(Property.TAB_ALWAYS_INDENT)) {
1661            indentLineOrRegion();
1662            return;
1663        }
1664        if (mark == null) {
1665            // No selection.
1666
if (dot.getOffset() <= dot.getLine().getIndentation())
1667                indentLine();
1668            else
1669                insertTab();
1670            return;
1671        }
1672        if (getMarkLine() != getDotLine()) {
1673            // Multi-line selection.
1674
indentRegion();
1675            return;
1676        }
1677        // Single-line selection.
1678
Region r = new Region(this);
1679        if (r.getBeginOffset() <= getDotLine().getIndentation())
1680            indentLine();
1681        else
1682            insertTab();
1683    }
1684
1685    public void insertTab()
1686    {
1687        if (isColumnSelection()) {
1688            notSupportedForColumnSelections();
1689            return;
1690        }
1691
1692        if (!checkReadOnly())
1693            return;
1694
1695        try {
1696            buffer.lockWrite();
1697        }
1698        catch (InterruptedException JavaDoc e) {
1699            Log.error(e);
1700            return;
1701        }
1702        try {
1703            CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
1704            if (mark != null)
1705                deleteRegion();
1706            if (buffer.getUseTabs())
1707                insertChar('\t');
1708            else {
1709                fillToCaret();
1710                int tabWidth = buffer.getTabWidth();
1711                addUndo(SimpleEdit.LINE_EDIT);
1712                buffer.insertChars(dot, Utilities.spaces(tabWidth - getDotCol() % tabWidth));
1713                updateInAllEditors(getDotLine());
1714            }
1715            moveCaretToDotCol();
1716            endCompoundEdit(compoundEdit);
1717        }
1718        finally {
1719            buffer.unlockWrite();
1720        }
1721    }
1722
1723    public void moveDotToIndentation()
1724    {
1725        final Line dotLine = getDotLine();
1726        final int limit = dotLine.length();
1727        int i;
1728        for (i = 0; i < limit; i++) {
1729            if (!Character.isWhitespace(dotLine.charAt(i)))
1730                break;
1731        }
1732        dot.setOffset(i);
1733    }
1734
1735    public void indentLineOrRegion()
1736    {
1737        if (isColumnSelection()) {
1738            notSupportedForColumnSelections();
1739            return;
1740        }
1741        if (getMode().canIndent()) {
1742            if (mark != null && getMarkLine() != getDotLine()) {
1743                indentRegion();
1744            } else {
1745                unmark();
1746                indentLine();
1747            }
1748        }
1749    }
1750
1751    public void indentRegion()
1752    {
1753        if (isColumnSelection()) {
1754            notSupportedForColumnSelections();
1755            return;
1756        }
1757        if (getMode().canIndent() && mark != null) {
1758            Region r = new Region(this);
1759            if (r.getBeginLine() == r.getEndLine()) {
1760                indentLine();
1761            } else {
1762                setWaitCursor();
1763                Position savedDot = new Position(dot);
1764                try {
1765                    buffer.lockWrite();
1766                }
1767                catch (InterruptedException JavaDoc e) {
1768                    Log.error(e);
1769                    return;
1770                }
1771                try {
1772                    if (buffer.needsParsing()) {
1773                        if (getFormatter().parseBuffer())
1774                            buffer.repaint();
1775                    }
1776                    CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
1777                    addUndo(SimpleEdit.MOVE);
1778                    dot.moveTo(r.getBeginLine(), 0);
1779                    do {
1780                        if (!dot.getLine().isBlank())
1781                            indentLineInternal();
1782                        dot.moveTo(dot.getNextLine(), 0);
1783                    } while (dot.getLine() != r.getEndLine());
1784                    addUndo(SimpleEdit.MOVE);
1785                    dot = savedDot;
1786                    if (dot.getOffset() > getDotLine().length())
1787                        dot.setOffset(getDotLine().length());
1788                    if (mark.getOffset() > getMarkLine().length())
1789                        mark.setOffset(getMarkLine().length());
1790                    moveCaretToDotCol();
1791                    endCompoundEdit(compoundEdit);
1792                }
1793                finally {
1794                    buffer.unlockWrite();
1795                }
1796                setUpdateFlag(REFRAME);
1797                setDefaultCursor();
1798            }
1799        }
1800    }
1801
1802    public void commentRegion()
1803    {
1804        if (isColumnSelection()) {
1805            notSupportedForColumnSelections();
1806            return;
1807        }
1808        commentRegion(true);
1809    }
1810
1811    public void uncommentRegion() {
1812        if (isColumnSelection()) {
1813            notSupportedForColumnSelections();
1814            return;
1815        }
1816        commentRegion(false);
1817    }
1818
1819    // If argument is false, uncomment the region.
1820
private void commentRegion(boolean comment)
1821    {
1822        if (!checkReadOnly())
1823            return;
1824        if (dot == null)
1825            return;
1826        try {
1827            buffer.lockWrite();
1828        }
1829        catch (InterruptedException JavaDoc e) {
1830            Log.error(e);
1831            return;
1832        }
1833        try {
1834            commentRegionInternal(comment);
1835        }
1836        finally {
1837            buffer.unlockWrite();
1838        }
1839    }
1840
1841    // If argument is false, uncomment the region.
1842
private void commentRegionInternal(boolean comment)
1843    {
1844        String JavaDoc commentStart = buffer.getCommentStart();
1845        if (commentStart == null)
1846            return;
1847        String JavaDoc commentEnd = buffer.getCommentEnd();
1848        Position savedDot = new Position(dot);
1849        Line beginLine, endLine;
1850        if (mark != null) {
1851            Region r = new Region(buffer, dot, mark);
1852            beginLine = r.getBeginLine();
1853            endLine = r.getEndLine();
1854        } else {
1855            beginLine = getDotLine();
1856            endLine = getDotLine().next();
1857        }
1858        if (endLine == beginLine)
1859            endLine = beginLine.next();
1860
1861        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
1862
1863        if (mark != null) {
1864            addUndo(SimpleEdit.MOVE);
1865            setMark(null);
1866            setUpdateFlag(REPAINT);
1867        }
1868
1869        if (getDotLine() != beginLine) {
1870            addUndo(SimpleEdit.MOVE);
1871            dot.moveTo(beginLine, 0);
1872        }
1873
1874        boolean modified = false;
1875
1876        while (true) {
1877            Line dotLine = getDotLine();
1878            String JavaDoc trim = dotLine.trim();
1879            if (comment) {
1880                if (trim.length() != 0) {
1881                    addUndo(SimpleEdit.LINE_EDIT);
1882                    if (commentEnd != null)
1883                        dotLine.setText(commentStart + dotLine.getText() + commentEnd);
1884                    else
1885                        dotLine.setText(commentStart + dotLine.getText());
1886                    modified = true;
1887                    if (dotLine == savedDot.getLine())
1888                        savedDot.setOffset(savedDot.getOffset() + commentStart.length());
1889                    updateInAllEditors(dotLine);
1890                }
1891            } else {
1892                // Uncomment.
1893
if (trim.startsWith(commentStart)) {
1894                    if (commentEnd == null || trim.endsWith(commentEnd)) {
1895                        addUndo(SimpleEdit.LINE_EDIT);
1896                        int index = dotLine.getText().indexOf(commentStart) + commentStart.length();
1897                        dotLine.setText(dotLine.substring(index));
1898                        if (commentEnd != null)
1899                            dotLine.setText(dotLine.substring(0, dotLine.length() - commentEnd.length()));
1900                        modified = true;
1901                        int dotCol = 0;
1902                        if (dotLine == savedDot.getLine()) {
1903                            // Adjust saved position.
1904
savedDot.setOffset(savedDot.getOffset() - index);
1905                            if (savedDot.getOffset() < 0)
1906                                savedDot.setOffset(0);
1907                            dotCol = buffer.getCol(dotLine, savedDot.getOffset());
1908                        }
1909
1910                        int oldIndent = buffer.getIndentation(dotLine);
1911                        int indent = getMode().getCorrectIndentation(dotLine, buffer);
1912                        if (indent != oldIndent) {
1913                            buffer.setIndentation(dotLine, indent);
1914                            if (dotLine == savedDot.getLine()) {
1915                                // Adjust saved position.
1916
if (dotCol >= oldIndent) {
1917                                    dotCol += indent - oldIndent;
1918                                    dot.moveToCol(dotCol, buffer.getTabWidth());
1919                                    savedDot.setOffset(dot.getOffset());
1920                                }
1921                            }
1922                        }
1923                        updateInAllEditors(dotLine);
1924                    }
1925                }
1926            }
1927
1928            if (dotLine.next() == endLine)
1929                break;
1930
1931            addUndo(SimpleEdit.MOVE);
1932            dot.moveTo(dotLine.next(), 0);
1933        }
1934
1935        addUndo(SimpleEdit.MOVE);
1936        setDot(savedDot);
1937        moveCaretToDotCol();
1938
1939        if (modified)
1940            buffer.modified();
1941
1942        endCompoundEdit(compoundEdit);
1943    }
1944
1945    public final void moveDotTo(Position pos)
1946    {
1947        if (pos != null)
1948            moveDotTo(pos.getLine(), pos.getOffset());
1949    }
1950
1951    public void moveDotTo(Line line, int offset)
1952    {
1953        if (dot == null)
1954            return;
1955        addUndo(SimpleEdit.MOVE);
1956        if (mark != null) {
1957            setMark(null);
1958            dot.moveTo(line, offset);
1959            setUpdateFlag(REPAINT);
1960        } else {
1961            updateDotLine();
1962            dot.moveTo(line, offset);
1963            updateDotLine();
1964        }
1965        moveCaretToDotCol();
1966    }
1967
1968    public void indentLine()
1969    {
1970        if (isColumnSelection()) {
1971            notSupportedForColumnSelections();
1972            return;
1973        }
1974        if (!checkReadOnly())
1975            return;
1976        if (!getMode().canIndent())
1977            return; // No change.
1978
try {
1979            buffer.lockWrite();
1980        }
1981        catch (InterruptedException JavaDoc e) {
1982            Log.error(e);
1983            return;
1984        }
1985        try {
1986            if (buffer.needsParsing()) {
1987                if (getFormatter().parseBuffer())
1988                    buffer.repaint();
1989            }
1990            indentLineInternal();
1991        }
1992        finally {
1993            buffer.unlockWrite();
1994        }
1995        setUpdateFlag(REFRAME);
1996    }
1997
1998    private void indentLineInternal()
1999    {
2000        final Line dotLine = getDotLine();
2001        final int indent = getMode().getCorrectIndentation(dotLine, buffer);
2002        final int shift = display.getShift();
2003
2004        if (dotLine.isBlank()) {
2005            // Put the caret where it needs to go...
2006
addUndo(SimpleEdit.LINE_EDIT);
2007            dotLine.setText("");
2008            dot.setOffset(0);
2009            display.setCaretCol(indent - shift);
2010            // Fill if necessary.
2011
if (buffer.getBooleanProperty(Property.RESTRICT_CARET))
2012                fillToCaret();
2013            updateInAllEditors(dotLine);
2014            return;
2015        }
2016
2017        // Line is not blank. Figure out current indentation.
2018
final int oldIndent = buffer.getIndentation(dotLine);
2019
2020        FastStringBuffer sb = null;
2021
2022        if (indent == oldIndent) {
2023            boolean ok = false;
2024            if (buffer.getBooleanProperty(Property.INDENT_LINE_FIX_WHITESPACE)) {
2025                sb = buffer.getCorrectIndentationString(indent);
2026                if (dotLine.getText().startsWith(sb.toString()))
2027                    ok = true;
2028            } else
2029                ok = true;
2030
2031            if (ok) {
2032                // Current indentation is correct. If the caret is in the
2033
// indentation area, move it to the start of the non-blank
2034
// text.
2035
if (display.getCaretCol() + shift < indent) {
2036                    addUndo(SimpleEdit.MOVE);
2037                    display.setCaretCol(indent - shift);
2038                    moveDotToCaretCol();
2039                }
2040                return;
2041            }
2042        }
2043
2044        // We need to fix the indentation. Figure out where we want to put the
2045
// caret when we're done. We want to maintain the existing offset from
2046
// the start of the non-blank text.
2047
final int existing = display.getCaretCol() + shift - oldIndent;
2048        final int goal = existing < 0 ? indent : existing + indent;
2049
2050        // Strip existing indentation.
2051
int i = 0;
2052        while (i < dotLine.length() && Character.isWhitespace(dotLine.charAt(i)))
2053            ++i;
2054        String JavaDoc nonBlank = dotLine.substring(i);
2055
2056        // Get the correct indentation string.
2057
if (sb == null)
2058            sb = buffer.getCorrectIndentationString(indent);
2059
2060        // Add the rest of the line.
2061
sb.append(nonBlank);
2062
2063        // Replace the existing text.
2064
addUndo(SimpleEdit.LINE_EDIT);
2065        dotLine.setText(sb.toString());
2066        buffer.modified();
2067        updateInAllEditors(dotLine);
2068
2069        // Put the caret where we want it.
2070
display.setCaretCol(goal - shift);
2071        moveDotToCaretCol();
2072    }
2073
2074    public void save()
2075    {
2076        save(buffer);
2077    }
2078
2079    public void save(Buffer toBeSaved)
2080    {
2081        if (toBeSaved.isLocked())
2082            return;
2083        if (toBeSaved.getType() == Buffer.TYPE_NORMAL) {
2084            if (toBeSaved.isModified()) {
2085                if (toBeSaved.isUntitled()) {
2086                    saveAs(toBeSaved);
2087                } else {
2088                    setWaitCursor();
2089                    status("Saving...");
2090                    if (toBeSaved.getBooleanProperty(Property.REMOVE_TRAILING_WHITESPACE))
2091                        toBeSaved.removeTrailingWhitespace();
2092                    if (toBeSaved.save())
2093                        status("Saving...done");
2094                    else
2095                        status("Save failed");
2096                    setDefaultCursor();
2097                }
2098            } else
2099                status("Not modified");
2100        }
2101    }
2102
2103    public void saveAs()
2104    {
2105        saveAs(buffer);
2106    }
2107
2108    private void saveAs(Buffer toBeSaved)
2109    {
2110        if (toBeSaved.isLocked())
2111            return;
2112        if (toBeSaved.getType() == Buffer.TYPE_NORMAL) {
2113            final String JavaDoc dialogTitle = "Save As";
2114            File destination =
2115                SaveFileDialog.getSaveFile(this, dialogTitle);
2116            if (destination == null)
2117                return;
2118
2119            // At this point, if the target file exists, the user has said
2120
// it's OK to overwrite it.
2121
repaintNow();
2122
2123            // Do we have the target file in a buffer?
2124
Buffer buf = bufferList.findBuffer(destination);
2125            if (buf != null) {
2126                // We do. Can we just get rid of it?
2127
if (!buf.isModified()) {
2128                    buf.deleteAutosaveFile();
2129                    bufferList.remove(buf);
2130                } else {
2131                    // Buffer is modified. Make user deal with it.
2132
setDefaultCursor();
2133                    String JavaDoc message = "Target file is in an active buffer. Please take care of that first.";
2134                    MessageDialog.showMessageDialog(this, message, dialogTitle);
2135                    return;
2136                }
2137            }
2138
2139            toBeSaved.saveAs(destination);
2140        }
2141    }
2142
2143    public void saveCopy()
2144    {
2145        if (buffer.isLocked())
2146            return;
2147        if (buffer.getType() == Buffer.TYPE_NORMAL) {
2148            final String JavaDoc dialogTitle = "Save Copy";
2149            final File destination =
2150                SaveFileDialog.getSaveFile(this, dialogTitle);
2151            if (destination == null)
2152                return;
2153
2154            repaintNow();
2155
2156            // Do we have the target file in a buffer?
2157
Buffer buf = bufferList.findBuffer(destination);
2158            if (buf != null) {
2159                // We do. Do we care?
2160
if (buf.isModified()) {
2161                    // Buffer is modified. Make user deal with it.
2162
setDefaultCursor();
2163                    String JavaDoc message = "Target file is in an active buffer. Please take care of that first.";
2164                    MessageDialog.showMessageDialog(this, message, "Save Copy");
2165                    return;
2166                }
2167            }
2168
2169            buffer.saveCopy(destination);
2170            if (buf != null && buf.isLoaded())
2171                reload(buf);
2172        }
2173    }
2174
2175    public void saveAll()
2176    {
2177        setWaitCursor();
2178        int numModified = 0;
2179        int numErrors = 0;
2180        for (BufferIterator it = new BufferIterator(); it.hasNext();) {
2181            Buffer buf = it.nextBuffer();
2182            if (buf.getModeId() == CHECKIN_MODE)
2183                continue;
2184            if (buf.isUntitled()) {
2185                setDefaultCursor();
2186                makeNext(buf);
2187                activate(buf);
2188                saveAs();
2189                setWaitCursor();
2190            } else if (buf.isModified()) {
2191                status("Saving modified buffers...");
2192                ++numModified;
2193                if (buffer.getFile() != null)
2194                    if (buffer.getBooleanProperty(Property.REMOVE_TRAILING_WHITESPACE))
2195                        buffer.removeTrailingWhitespace();
2196                if (!buf.save())
2197                    ++numErrors;
2198            }
2199        }
2200        if (numModified == 0)
2201            status("No modified buffers");
2202        else if (numErrors == 0)
2203            status("Saving modified buffers...done");
2204        else {
2205            // User will already have seen detailed error information from Buffer.save().
2206
status("Unable to save all modified buffers");
2207        }
2208        setDefaultCursor();
2209    }
2210
2211    public boolean okToClose(Buffer buf)
2212    {
2213        if (buf.getType() != Buffer.TYPE_NORMAL)
2214            return true;
2215        if (buf.isUntitled() || buf.isModified()) {
2216            makeNext(buf);
2217            activate(buf);
2218            repaintNow();
2219            if (!CloseBufferConfirmationDialog.confirmClose(this, buf))
2220                return false;
2221        }
2222        return true;
2223    }
2224
2225    public void closeAll()
2226    {
2227        repaintNow();
2228
2229        for (BufferIterator it = new BufferIterator(); it.hasNext();) {
2230            if (!okToClose(it.nextBuffer()))
2231                return;
2232        }
2233
2234        Marker.invalidateAllMarkers();
2235
2236        Buffer toBeActivated = null;
2237
2238        for (BufferIterator it = new BufferIterator(); it.hasNext();) {
2239            Buffer buf = it.nextBuffer();
2240            if (buf instanceof Directory && buf.getFile().equals(getCurrentDirectory())) {
2241                toBeActivated = buf;
2242                break;
2243            }
2244        }
2245
2246        if (toBeActivated == null)
2247            toBeActivated = new Directory(getCurrentDirectory());
2248
2249        setWaitCursor();
2250
2251        for (int i = 0; i < getEditorCount(); i++) {
2252            Editor ed = getEditor(i);
2253            ed.activate(toBeActivated);
2254        }
2255
2256        for (BufferIterator iter = new BufferIterator(); iter.hasNext();) {
2257            Buffer buf = iter.nextBuffer();
2258            if (buf != toBeActivated) {
2259                for (EditorIterator it = new EditorIterator(); it.hasNext();) {
2260                    Editor ed = it.nextEditor();
2261                    ed.views.remove(buf);
2262                }
2263                buf.deleteAutosaveFile();
2264                iter.remove();
2265                buf.dispose();
2266            }
2267        }
2268
2269        Sidebar.setUpdateFlagInAllFrames(SIDEBAR_BUFFER_LIST_ALL);
2270        Sidebar.refreshSidebarInAllFrames();
2271        setDefaultCursor();
2272    }
2273
2274    public void closeOthers()
2275    {
2276        repaintNow();
2277
2278        Buffer toBeActivated = buffer;
2279
2280        for (BufferIterator it = new BufferIterator(); it.hasNext();) {
2281            Buffer buf = it.nextBuffer();
2282            if (buf != buffer && !okToClose(buf))
2283                return;
2284        }
2285
2286        List JavaDoc markers = Marker.getAllMarkers();
2287        for (int i = 0; i < markers.size(); i++) {
2288            Marker m = (Marker) markers.get(i);
2289            if (m != null && m.getBuffer() != buffer)
2290                m.invalidate();
2291        }
2292
2293        setWaitCursor();
2294
2295        for (EditorIterator it = new EditorIterator(); it.hasNext();)
2296            it.nextEditor().activate(toBeActivated);
2297
2298        for (BufferIterator iter = new BufferIterator(); iter.hasNext();) {
2299            Buffer buf = iter.nextBuffer();
2300            if (buf != buffer) {
2301                for (EditorIterator it = new EditorIterator(); it.hasNext();) {
2302                    Editor ed = it.nextEditor();
2303                    ed.views.remove(buf);
2304                }
2305                buf.deleteAutosaveFile();
2306                iter.remove();
2307                buf.dispose();
2308            }
2309        }
2310
2311        Sidebar.setUpdateFlagInAllFrames(SIDEBAR_BUFFER_LIST_ALL);
2312        Sidebar.refreshSidebarInAllFrames();
2313        setDefaultCursor();
2314    }
2315
2316    public boolean execute(String JavaDoc command) throws NoSuchMethodException JavaDoc
2317    {
2318        String JavaDoc[] array = parseCommand(command);
2319        if (array == null)
2320            return false;
2321        String JavaDoc methodName = array[0];
2322        String JavaDoc parameters = array[1];
2323        return execute(methodName, parameters);
2324    }
2325
2326    public boolean execute(String JavaDoc commandName, String JavaDoc parameters) throws NoSuchMethodException JavaDoc
2327    {
2328        if (commandName == null)
2329            return false;
2330
2331        try {
2332            String JavaDoc className = null;
2333            String JavaDoc methodName = null;
2334            Method JavaDoc method = null;
2335
2336            Class JavaDoc[] parameterTypes;
2337            if (parameters == null)
2338                parameterTypes = new Class JavaDoc[0];
2339            else {
2340                parameterTypes = new Class JavaDoc[1];
2341                parameterTypes[0] = Class.forName("java.lang.String");
2342            }
2343
2344            Command command = CommandTable.getCommand(commandName);
2345            if (command != null) {
2346                method = command.getMethod();
2347                if (method != null) {
2348                    try {
2349                        invoke(method, parameters);
2350                        return true;
2351                    }
2352                    catch (IllegalArgumentException JavaDoc e) {
2353                        // The cached method requires different arguments.
2354
// Fall through.
2355
}
2356                }
2357                // Method is not cached yet.
2358
className = command.getClassName();
2359                methodName = command.getMethodName();
2360                if (className == null) {
2361                    // Special case. Command is implemented in org.armedbear.j.Editor.
2362
method = Editor.class.getMethod(methodName, parameterTypes);
2363                } else {
2364                    Class JavaDoc c = Class.forName("org.armedbear.j." + className);
2365                    if (c != null)
2366                        method = c.getMethod(methodName, parameterTypes);
2367                }
2368                if (method != null) {
2369                    // Cache the method for nest time.
2370
command.setMethod(method);
2371                    invoke(method, parameters);
2372                    return true;
2373                }
2374            } else {
2375                // This is the old code path.
2376
Debug.assertTrue(className == null);
2377                Debug.assertTrue(methodName == null);
2378                int index = commandName.indexOf('.');
2379                if (index < 0) {
2380                    // No class name. Must be a method in the Editor class.
2381
method = Editor.class.getMethod(commandName, parameterTypes);
2382                } else if (commandName.length() > index+1) {
2383                    // Class name was provided.
2384
className = commandName.substring(0, index);
2385                    methodName = commandName.substring(index+1);
2386                    Class JavaDoc c = null;
2387                    try {
2388                        c = Class.forName("org.armedbear.j." + className);
2389                    }
2390                    catch (ClassNotFoundException JavaDoc e) {}
2391                    if (c == null) {
2392                        // Check for extension class.
2393
c = loadExtensionClass(className);
2394                    }
2395                    if (c != null) {
2396                        // Might throw NoSuchMethodException.
2397
method = c.getMethod(methodName, parameterTypes);
2398                    }
2399                }
2400                if (method != null) {
2401                    invoke(method, parameters);
2402                    return true;
2403                }
2404            }
2405        }
2406        catch (NoSuchMethodException JavaDoc e) {
2407            throw e;
2408        }
2409        catch (Throwable JavaDoc t) {
2410            Log.error(t);
2411        }
2412        return false;
2413    }
2414
2415    private void invoke(Method JavaDoc method, String JavaDoc parameters) throws IllegalArgumentException JavaDoc
2416    {
2417        Object JavaDoc[] args;
2418        if (parameters == null)
2419            args = new Object JavaDoc[0]; // No arguments.
2420
else {
2421            args = new Object JavaDoc[1];
2422            args[0] = parameters;
2423        }
2424        try {
2425            method.invoke(this, args);
2426        }
2427        catch (IllegalArgumentException JavaDoc e) {
2428            throw e;
2429        }
2430        catch (Throwable JavaDoc t) {
2431            Log.error(t);
2432        }
2433    }
2434
2435    public boolean handleKeyEvent(char keyChar, int keyCode, int modifiers)
2436    {
2437        if (insertingKeyText) {
2438            insertKeyTextInternal(keyChar, keyCode, modifiers);
2439            return true;
2440        }
2441        KeyMapping mapping = getKeyMapping(keyChar, keyCode, modifiers);
2442        if (mapping != null) {
2443            Object JavaDoc command = mapping.getCommand();
2444            if (isRecordingMacro()) {
2445                if (command != "recordMacro" && command != "playbackMacro")
2446                    Macro.record(this, command);
2447            }
2448            if (command instanceof String JavaDoc) {
2449                String JavaDoc commandString = (String JavaDoc) command;
2450                if (commandString.length() > 0 && commandString.charAt(0) == '(') {
2451                    // Lisp form.
2452
executeCommand(commandString);
2453                    return true;
2454                }
2455                String JavaDoc[] array = parseCommand(commandString);
2456                if (array != null) {
2457                    String JavaDoc methodName = array[0];
2458                    String JavaDoc parameters = array[1];
2459                    try {
2460                        return execute(methodName, parameters);
2461                    }
2462                    catch (NoSuchMethodException JavaDoc e) {}
2463                }
2464            } else if (command instanceof LispObject) {
2465                try {
2466                    Lisp.funcall0(Lisp.coerceToFunction((LispObject)command),
2467                                  LispThread.currentThread());
2468                }
2469                catch (Throwable JavaDoc t) {
2470                    Log.error(t);
2471                }
2472                return true;
2473            }
2474        }
2475        return false;
2476    }
2477
2478    public KeyMapping getKeyMapping(char keyChar, int keyCode, int modifiers)
2479    {
2480        // Look in mode-specific key map.
2481
KeyMapping mapping =
2482            buffer.getMode().getKeyMap().lookup(keyChar, keyCode, modifiers);
2483        if (mapping != null)
2484            return mapping;
2485        // Look in global key map.
2486
return KeyMap.getGlobalKeyMap().lookup(keyChar, keyCode, modifiers);
2487    }
2488
2489    // Returns multiple values: mapping, mode.
2490
// mode == null means it's a global mapping.
2491
public Object JavaDoc[] getKeyMapping(String JavaDoc command)
2492    {
2493        Mode mode = null;
2494        // Look in buffer-local keymap first.
2495
KeyMapping mapping = buffer.getKeyMapForMode().getKeyMapping(command);
2496        // If not found there, try global keymap.
2497
if (mapping == null) {
2498            mapping = KeyMap.getGlobalKeyMap().getKeyMapping(command);
2499            // Don't let a global mapping hide a different mapping of the same
2500
// keystroke in the buffer-local keymap!
2501
if (mapping != null) {
2502                javax.swing.KeyStroke JavaDoc keyStroke =
2503                    javax.swing.KeyStroke.getKeyStroke(mapping.getKeyCode(),
2504                        mapping.getModifiers());
2505                if (buffer.getKeyMapForMode().lookup(keyStroke) != null)
2506                    mapping = null;
2507            }
2508        } else
2509            mode = getMode();
2510        Object JavaDoc[] values = new Object JavaDoc[2];
2511        values[0] = mapping;
2512        values[1] = mode;
2513        return values;
2514    }
2515
2516    public void pageDown()
2517    {
2518        if (dot == null)
2519            return;
2520        maybeResetGoalColumn();
2521        if (mark != null) {
2522            CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
2523            addUndo(SimpleEdit.MOVE);
2524            setMark(null);
2525            setUpdateFlag(REPAINT);
2526            pageDownInternal();
2527            endCompoundEdit(compoundEdit);
2528        } else
2529            pageDownInternal();
2530        setCurrentCommand(COMMAND_PAGE_DOWN);
2531    }
2532
2533    public void pageDownOtherWindow()
2534    {
2535        final Editor ed = frame.getOtherEditor();
2536        if (ed != null) {
2537            ed.pageDown();
2538            ed.updateDisplay();
2539        }
2540    }
2541
2542    public void selectPageDown()
2543    {
2544        if (dot == null)
2545            return;
2546        maybeResetGoalColumn();
2547        if (mark == null) {
2548            CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
2549            addUndo(SimpleEdit.MOVE);
2550            setMarkAtDot();
2551            pageDownInternal();
2552            endCompoundEdit(compoundEdit);
2553        } else
2554            pageDownInternal();
2555        setCurrentCommand(COMMAND_PAGE_DOWN);
2556    }
2557
2558    public void pageUp()
2559    {
2560        if (dot == null)
2561            return;
2562        maybeResetGoalColumn();
2563        if (mark != null) {
2564            CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
2565            addUndo(SimpleEdit.MOVE);
2566            setMark(null);
2567            setUpdateFlag(REPAINT);
2568            pageUpInternal();
2569            endCompoundEdit(compoundEdit);
2570        } else
2571            pageUpInternal();
2572        setCurrentCommand(COMMAND_PAGE_UP);
2573    }
2574
2575    public void pageUpOtherWindow()
2576    {
2577        final Editor ed = frame.getOtherEditor();
2578        if (ed != null) {
2579            ed.pageUp();
2580            ed.updateDisplay();
2581        }
2582    }
2583
2584    public void selectPageUp()
2585    {
2586        if (dot == null)
2587            return;
2588        maybeResetGoalColumn();
2589        if (mark == null) {
2590            CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
2591            addUndo(SimpleEdit.MOVE);
2592            setMarkAtDot();
2593            pageUpInternal();
2594            endCompoundEdit(compoundEdit);
2595        } else
2596            pageUpInternal();
2597        setCurrentCommand(COMMAND_PAGE_UP);
2598    }
2599
2600    private void pageDownInternal()
2601    {
2602        Debug.assertTrue(buffer.needsRenumbering == false);
2603
2604        int topLineNumber = display.getTopLineNumber();
2605        Line dotLine = getDotLine();
2606        int dotLineNumber = dot.lineNumber();
2607        int numRows = display.getRows();
2608
2609        Line[] lines = new Line[numRows];
2610        Line line = getTopLine();
2611        int dotRow = -1;
2612        for (int i = 0; i < numRows; i++) {
2613            lines[i] = line;
2614            if (line == dotLine)
2615                dotRow = i;
2616            if (line != null)
2617                line = line.nextVisible();
2618        }
2619        Line bottomLine = lines[numRows - 1];
2620
2621        if (bottomLine == null) {
2622            // We're on the last page already.
2623
if (dotRow >= 0) {
2624                Position eob = getEob();
2625                if (eob != null) {
2626                    addUndo(SimpleEdit.MOVE);
2627                    updateDotLine();
2628                    dot.setLine(eob.getLine());
2629                    moveDotToGoalCol();
2630                    updateDotLine();
2631                }
2632            }
2633            return;
2634        }
2635
2636        // Not on last page.
2637
display.setTopLine(bottomLine);
2638        setUpdateFlag(REPAINT);
2639
2640        if (dotRow >= 0) {
2641            line = getTopLine();
2642            for (int i = 0; i < dotRow; i++) {
2643                Line next = line.nextVisible();
2644                if (next == null)
2645                    break;
2646                line = next;
2647            }
2648            addUndo(SimpleEdit.MOVE);
2649            dot.setLine(line);
2650            moveDotToGoalCol();
2651        }
2652    }
2653
2654    private void pageUpInternal()
2655    {
2656        if (dot.getLine() == buffer.getFirstLine())
2657            return;
2658        Debug.assertTrue(buffer.needsRenumbering == false);
2659        int topLineNumber = display.getTopLineNumber();
2660        int dotLineNumber = dot.lineNumber();
2661        int linesToScroll = display.getRows() - 1;
2662        boolean dotLineIsVisible = false;
2663        if (dotLineNumber >= topLineNumber) {
2664            Line bottomLine = display.getBottomLine();
2665            if (bottomLine != null)
2666                if (dotLineNumber <= bottomLine.lineNumber())
2667                    dotLineIsVisible = true;
2668        }
2669        addUndo(SimpleEdit.MOVE);
2670        if (dotLineIsVisible) {
2671            for (int i = 0; i < linesToScroll; i++) {
2672                Line topLinePrevious = getTopLine().previousVisible();
2673                if (topLinePrevious != null)
2674                    display.setTopLine(topLinePrevious);
2675                Line dotLinePrevious = dot.getLine().previousVisible();
2676                if (dotLinePrevious == null)
2677                    break;
2678                dot.setLine(dotLinePrevious);
2679            }
2680        } else {
2681            for (int i = 0; i < linesToScroll; i++) {
2682                Line dotLinePrevious = dot.getLine().previousVisible();
2683                if (dotLinePrevious == null)
2684                    break;
2685                dot.setLine(dotLinePrevious);
2686            }
2687        }
2688        moveDotToGoalCol();
2689        setUpdateFlag(REPAINT);
2690    }
2691
2692    // Move dot to beginning of block, no undo.
2693
public void beginningOfBlock()
2694    {
2695        if (mark != null) {
2696            Region r = new Region(buffer, mark, dot);
2697            dot.moveTo(r.getBegin());
2698            setMark(null);
2699            moveCaretToDotCol();
2700            if (r.getBeginLine() != r.getEndLine())
2701                setUpdateFlag(REPAINT);
2702            else
2703                updateDotLine();
2704        }
2705    }
2706
2707    // Move dot to end of block, no undo.
2708
public void endOfBlock()
2709    {
2710        if (mark != null) {
2711            Region r = new Region(buffer, mark, dot);
2712            dot.moveTo(r.getEnd());
2713            setMark(null);
2714            moveCaretToDotCol();
2715            if (r.getBeginLine() != r.getEndLine())
2716                setUpdateFlag(REPAINT);
2717            else
2718                updateDotLine();
2719        }
2720    }
2721
2722    public void right()
2723    {
2724        if (dot == null)
2725            return;
2726        if (buffer.getBooleanProperty(Property.RESTRICT_CARET)) {
2727            if (mark == null && getDotOffset() >= getDotLine().length()) {
2728                // Caret is at end of line.
2729
if (getDotLine().next() != null) {
2730                    moveDotTo(getDotLine().next(), 0);
2731                    setCurrentCommand(COMMAND_RIGHT);
2732                }
2733                return;
2734            }
2735        }
2736        if (mark != null || lastCommand != COMMAND_RIGHT)
2737            addUndo(SimpleEdit.MOVE);
2738        if (mark != null)
2739            endOfBlock();
2740        else if (dot.getOffset() < dot.getLineLength()) {
2741            dot.skip(1);
2742            moveCaretToDotCol();
2743        } else {
2744            ++display.caretCol;
2745        }
2746        updateDotLine();
2747        setCurrentCommand(COMMAND_RIGHT);
2748        setUpdateFlag(REFRAME);
2749    }
2750
2751    public void selectRight()
2752    {
2753        if (dot == null)
2754            return;
2755        if (getDotOffset() < getDotLine().length()) {
2756            if (mark == null || lastCommand != COMMAND_RIGHT)
2757                addUndo(SimpleEdit.MOVE);
2758            if (mark == null)
2759                setMarkAtDot();
2760            dot.moveRight();
2761            moveCaretToDotCol();
2762        } else {
2763            // We're at or beyond the end of the line.
2764
if (buffer.getBooleanProperty(Property.RESTRICT_CARET)) {
2765                if (getDotLine().next() == null)
2766                    return;
2767                if (mark == null || lastCommand != COMMAND_RIGHT)
2768                    addUndo(SimpleEdit.MOVE);
2769                if (mark == null)
2770                    setMarkAtDot();
2771                updateDotLine();
2772                dot.moveTo(getDotLine().next(), 0);
2773                moveCaretToDotCol();
2774            } else {
2775                // Don't start a new selection, since there's no text there.
2776
if (lastCommand != COMMAND_RIGHT)
2777                    addUndo(SimpleEdit.MOVE);
2778                ++display.caretCol;
2779            }
2780        }
2781        updateDotLine();
2782        setCurrentCommand(COMMAND_RIGHT);
2783        setUpdateFlag(REFRAME);
2784    }
2785
2786    public void left()
2787    {
2788        if (dot == null)
2789            return;
2790        final Line dotLine = getDotLine();
2791        final int dotOffset = getDotOffset();
2792        final Line prevLine = dotLine.previous();
2793        if (dotOffset == 0 && prevLine == null)
2794            return;
2795        if (mark != null || lastCommand != COMMAND_LEFT)
2796            addUndo(SimpleEdit.MOVE);
2797        if (mark != null)
2798            beginningOfBlock();
2799        else {
2800            int absCaretCol = display.getCaretCol() + display.getShift();
2801            if (absCaretCol > 0 && absCaretCol <= buffer.getCol(dotLine, dotLine.length())) {
2802                // Back up one character.
2803
dot.setOffset(dotOffset - 1);
2804                moveCaretToDotCol();
2805            } else if (absCaretCol > 0) {
2806                // We're beyond the end of the text on the line.
2807
--display.caretCol;
2808            } else if (dot.getOffset() == 0) {
2809                // Back up to the end of the text on the previous line.
2810
update(dotLine);
2811                setDot(prevLine, prevLine.length());
2812                moveCaretToDotCol();
2813            } else {
2814                // There shouldn't be any other cases.
2815
Debug.assertTrue(false);
2816            }
2817        }
2818        updateDotLine();
2819        setCurrentCommand(COMMAND_LEFT);
2820        setUpdateFlag(REFRAME);
2821    }
2822
2823    public void selectLeft()
2824    {
2825        if (dot == null)
2826            return;
2827        final Line dotLine = getDotLine();
2828        final int dotOffset = getDotOffset();
2829        final Line prevLine = dotLine.previous();
2830        if (dotOffset == 0 && prevLine == null)
2831            return;
2832        if (mark == null || lastCommand != COMMAND_LEFT)
2833            addUndo(SimpleEdit.MOVE);
2834
2835        // Only start a new selection if we're over some actual text.
2836
final int absCaretCol = display.getAbsoluteCaretCol();
2837        final int end = buffer.getCol(dotLine, dotLine.length());
2838        if (mark == null && absCaretCol <= end)
2839            setMarkAtDot();
2840
2841        if (absCaretCol > 0 && absCaretCol <= end) {
2842            // Back up one character.
2843
dot.moveLeft();
2844            moveCaretToDotCol();
2845        } else if (absCaretCol > 0) {
2846            // We're beyond the end of the text on the line.
2847
--display.caretCol;
2848        } else if (dotOffset == 0) {
2849            // Back up to the end of the text on the previous line.
2850
updateDotLine();
2851            setDot(prevLine, prevLine.length());
2852            moveCaretToDotCol();
2853        } else {
2854            // There shouldn't be any other cases.
2855
Debug.assertTrue(false);
2856        }
2857        updateDotLine();
2858        setCurrentCommand(COMMAND_LEFT);
2859        setUpdateFlag(REFRAME);
2860    }
2861
2862    public final int getAbsoluteCaretCol()
2863    {
2864        return display.getAbsoluteCaretCol();
2865    }
2866
2867    public final void setAbsoluteCaretCol(int col)
2868    {
2869        display.setAbsoluteCaretCol(col);
2870    }
2871
2872    private int goalColumn;
2873
2874    public final void setGoalColumn(int col)
2875    {
2876        goalColumn = col;
2877    }
2878
2879    public void moveDotToGoalCol()
2880    {
2881        if (buffer.getBooleanProperty(Property.RESTRICT_CARET)) {
2882            final int limit =
2883                buffer.getCol(getDotLine(), getDotLine().length());
2884            moveDotToCol(goalColumn > limit ? limit : goalColumn);
2885            moveCaretToDotCol();
2886        } else {
2887            setAbsoluteCaretCol(goalColumn);
2888            moveDotToCaretCol();
2889        }
2890    }
2891
2892    // Move caret down one line, keeping it in the same column if possible.
2893
// Synchronize dot with caret.
2894
public void down()
2895    {
2896        maybeResetGoalColumn();
2897        display.down(false);
2898        setCurrentCommand(COMMAND_DOWN);
2899    }
2900
2901    public void selectDown()
2902    {
2903        maybeResetGoalColumn();
2904        display.down(true);
2905        setCurrentCommand(COMMAND_DOWN);
2906    }
2907
2908    // Move caret up one line, keeping it in the same column if possible.
2909
// Synchronize dot with caret.
2910
public void up()
2911    {
2912        maybeResetGoalColumn();
2913        display.up(false);
2914        setCurrentCommand(COMMAND_UP);
2915    }
2916
2917    public void selectUp()
2918    {
2919        maybeResetGoalColumn();
2920        display.up(true);
2921        setCurrentCommand(COMMAND_UP);
2922    }
2923
2924    private void maybeResetGoalColumn()
2925    {
2926        switch (lastCommand) {
2927            case COMMAND_UP:
2928            case COMMAND_DOWN:
2929            case COMMAND_PAGE_UP:
2930            case COMMAND_PAGE_DOWN:
2931            case COMMAND_WINDOW_UP:
2932            case COMMAND_WINDOW_DOWN:
2933                return;
2934            default:
2935                goalColumn = getAbsoluteCaretCol();
2936                return;
2937        }
2938    }
2939
2940    public void windowUp()
2941    {
2942        maybeResetGoalColumn();
2943        display.windowUp();
2944        maybeScrollCaret();
2945        setCurrentCommand(COMMAND_WINDOW_UP);
2946    }
2947
2948    public void windowDown()
2949    {
2950        maybeResetGoalColumn();
2951        display.windowDown();
2952        maybeScrollCaret();
2953        setCurrentCommand(COMMAND_WINDOW_DOWN);
2954    }
2955
2956    public void maybeScrollCaret()
2957    {
2958        if (dot == null)
2959            return;
2960        // Don't scroll the caret if a region is selected!
2961
if (mark != null)
2962            return;
2963        if (buffer.getBooleanProperty(Property.SCROLL_CARET)) {
2964            final int dotLineNumber = dot.lineNumber();
2965            final Line topLine = display.getTopLine();
2966            if (dotLineNumber < topLine.lineNumber()) {
2967                // Caret is above window.
2968
addUndo(SimpleEdit.SCROLL_CARET);
2969                dot.moveTo(topLine, 0);
2970                updateDotLine();
2971                moveCaretToDotCol();
2972                goalColumn = 0;
2973                return;
2974            }
2975            final Line bottomLine = display.getBottomLine();
2976            if (dotLineNumber > bottomLine.lineNumber()) {
2977                // Caret is below window.
2978
addUndo(SimpleEdit.SCROLL_CARET);
2979                updateDotLine();
2980                dot.moveTo(bottomLine, 0);
2981                updateDotLine();
2982                moveCaretToDotCol();
2983                goalColumn = 0;
2984            }
2985        }
2986    }
2987
2988    public void toCenter()
2989    {
2990        display.toCenter();
2991    }
2992
2993    public void toTop()
2994    {
2995        display.toTop();
2996    }
2997
2998    private void selectToPosition(Position pos)
2999    {
3000        if (!pos.equals(dot) ||
3001            buffer.getCol(pos) != display.getAbsoluteCaretCol()) {
3002            addUndo(SimpleEdit.MOVE);
3003            if (mark == null)
3004                setMarkAtDot();
3005            if (pos.getLine() != getDotLine())
3006                setUpdateFlag(REPAINT);
3007            else
3008                updateDotLine();
3009            dot.moveTo(pos);
3010            moveCaretToDotCol();
3011        }
3012    }
3013
3014    public void bol()
3015    {
3016        if (dot != null)
3017            moveDotTo(dot.getLine(), 0);
3018    }
3019
3020    public void home()
3021    {
3022        if (dot == null)
3023            return;
3024        final boolean extend = prefs.getBooleanProperty(Property.EXTEND_HOME);
3025        Position pos;
3026        if (mark != null)
3027            pos = new Position(new Region(this).getBegin());
3028        else
3029            pos = new Position(dot);
3030        int indent = pos.getLine().getIndentation();
3031        if (extend) {
3032            if (pos.getOffset() > indent)
3033                pos.setOffset(indent);
3034            else
3035                pos.setOffset(0);
3036        } else {
3037            if (pos.getOffset() > indent)
3038                pos.setOffset(indent);
3039            else if (pos.getOffset() == indent)
3040                pos.setOffset(0);
3041            else
3042                pos.setOffset(indent);
3043        }
3044        if (mark != null || !pos.equals(dot) ||
3045            buffer.getCol(pos) != display.getAbsoluteCaretCol()) {
3046            moveDotTo(pos);
3047            setCurrentCommand(COMMAND_HOME);
3048            return;
3049        }
3050        // Reaching here, caret is already in column 0.
3051
if (!extend)
3052            return;
3053        if (System.currentTimeMillis() - dispatcher.getLastEventMillis() > 1000) {
3054            // Timed out.
3055
setUpdateFlag(REFRAME);
3056            setCurrentCommand(COMMAND_HOME);
3057            return;
3058        }
3059        if (lastCommand == COMMAND_HOME_HOME)
3060            pos = new Position(buffer.getFirstLine(), 0);
3061        else if (lastCommand == COMMAND_HOME) {
3062            pos = new Position(getTopLine(), 0);
3063            setUpdateFlag(REFRAME);
3064            setCurrentCommand(COMMAND_HOME_HOME);
3065        } else {
3066            setUpdateFlag(REFRAME);
3067            setCurrentCommand(COMMAND_HOME);
3068            return;
3069        }
3070        if (!pos.equals(dot) ||
3071            buffer.getCol(pos) != display.getAbsoluteCaretCol())
3072            moveDotTo(pos);
3073    }
3074
3075    public void selectHome()
3076    {
3077        if (dot == null)
3078            return;
3079        final boolean extend = prefs.getBooleanProperty(Property.EXTEND_HOME);
3080        Position pos;
3081        if (mark != null)
3082            pos = new Position(new Region(buffer, mark, dot).getBegin());
3083        else
3084            pos = new Position(dot);
3085        int indent = pos.getLine().getIndentation();
3086        if (extend) {
3087            if (pos.getOffset() > indent)
3088                pos.setOffset(indent);
3089            else
3090                pos.setOffset(0);
3091        } else {
3092            if (pos.getOffset() > indent)
3093                pos.setOffset(indent);
3094            else if (pos.getOffset() == indent)
3095                pos.setOffset(0);
3096            else
3097                pos.setOffset(indent);
3098        }
3099        if (mark != null || !pos.equals(dot) ||
3100            buffer.getCol(pos) != display.getAbsoluteCaretCol()) {
3101            selectToPosition(pos);
3102            setUpdateFlag(REFRAME);
3103            setCurrentCommand(COMMAND_SELECT_HOME);
3104            return;
3105        }
3106        // Reaching here, caret is already in column 0.
3107
if (!prefs.getBooleanProperty(Property.EXTEND_HOME))
3108            return;
3109        if (System.currentTimeMillis() - dispatcher.getLastEventMillis() > 1000) {
3110            // Timed out.
3111
setUpdateFlag(REFRAME);
3112            setCurrentCommand(COMMAND_SELECT_HOME);
3113            return;
3114        }
3115        if (lastCommand == COMMAND_SELECT_HOME_HOME)
3116            pos = new Position(buffer.getFirstLine(), 0);
3117        else if (lastCommand == COMMAND_SELECT_HOME) {
3118            pos = new Position(getTopLine(), 0);
3119            setUpdateFlag(REFRAME);
3120            setCurrentCommand(COMMAND_SELECT_HOME_HOME);
3121        } else {
3122            setUpdateFlag(REFRAME);
3123            setCurrentCommand(COMMAND_SELECT_HOME);
3124            return;
3125        }
3126        if (!pos.equals(dot) ||
3127            buffer.getCol(pos) != display.getAbsoluteCaretCol())
3128            selectToPosition(pos);
3129    }
3130
3131    public void eol()
3132    {
3133        if (dot == null)
3134            return;
3135        if (mark != null || dot.getOffset() != dot.getLineLength() ||
3136            buffer.getCol(dot) != display.getCaretCol() + display.getShift())
3137            moveDotTo(dot.getLine(), dot.getLineLength());
3138    }
3139
3140    public void end()
3141    {
3142        if (dot == null)
3143            return;
3144        Position pos;
3145        if (mark != null)
3146            pos = new Position(new Region(buffer, mark, dot).getEnd());
3147        else
3148            pos = new Position(dot);
3149        pos.setOffset(pos.getLineLength());
3150        if (mark != null || !pos.equals(dot) ||
3151            buffer.getCol(pos) != display.getAbsoluteCaretCol()) {
3152            moveDotTo(pos);
3153            setUpdateFlag(REFRAME);
3154            setCurrentCommand(COMMAND_END);
3155            return;
3156        }
3157        // Reaching here, caret is already at end of line.
3158
if (!prefs.getBooleanProperty(Property.EXTEND_END))
3159            return;
3160        if (System.currentTimeMillis() - dispatcher.getLastEventMillis() > 1000) {
3161            // Timed out.
3162
setUpdateFlag(REFRAME);
3163            setCurrentCommand(COMMAND_END);
3164            return;
3165        }
3166        if (lastCommand == COMMAND_END_END)
3167            pos = getEob();
3168        else if (lastCommand == COMMAND_END) {
3169            Line bottomLine = display.getBottomLine();
3170            if (bottomLine != null)
3171                pos = new Position(bottomLine, bottomLine.length());
3172            else
3173                pos = getEob();
3174            setUpdateFlag(REFRAME);
3175            setCurrentCommand(COMMAND_END_END);
3176        } else {
3177            setUpdateFlag(REFRAME);
3178            setCurrentCommand(COMMAND_END);
3179            return;
3180        }
3181        if (!pos.equals(dot) ||
3182            buffer.getCol(pos) != display.getAbsoluteCaretCol())
3183            moveDotTo(pos);
3184    }
3185
3186    public void selectEnd()
3187    {
3188        if (dot == null)
3189            return;
3190        Position pos;
3191        if (mark != null)
3192            pos = new Position(new Region(buffer, mark, dot).getEnd());
3193        else
3194            pos = new Position(dot);
3195        pos.setOffset(pos.getLineLength());
3196        if (!pos.equals(dot) ||
3197            buffer.getCol(pos) != display.getAbsoluteCaretCol()) {
3198            selectToPosition(pos);
3199            setUpdateFlag(REFRAME);
3200            setCurrentCommand(COMMAND_END);
3201            return;
3202        }
3203        // Reaching here, caret is already at end of line.
3204
if (!prefs.getBooleanProperty(Property.EXTEND_END))
3205            return;
3206        if (System.currentTimeMillis() - dispatcher.getLastEventMillis() > 1000) {
3207            // Timed out.
3208
setUpdateFlag(REFRAME);
3209            setCurrentCommand(COMMAND_END);
3210            return;
3211        }
3212        if (lastCommand == COMMAND_END_END)
3213            pos = getEob();
3214        else if (lastCommand == COMMAND_END) {
3215            Line bottomLine = display.getBottomLine();
3216            if (bottomLine != null)
3217                pos = new Position(bottomLine, bottomLine.length());
3218            else
3219                pos = getEob();
3220            setUpdateFlag(REFRAME);
3221            setCurrentCommand(COMMAND_END_END);
3222        } else {
3223            setCurrentCommand(COMMAND_END);
3224            return;
3225        }
3226        if (!pos.equals(dot) ||
3227            buffer.getCol(pos) != display.getAbsoluteCaretCol())
3228            selectToPosition(pos);
3229    }
3230
3231    public void bob()
3232    {
3233        if (buffer.getFirstLine() != null)
3234            moveDotTo(buffer.getFirstLine(), 0);
3235    }
3236
3237    public void selectBob()
3238    {
3239        if (dot == null)
3240            return;
3241        if (buffer.getFirstLine() == null)
3242            return;
3243        addUndo(SimpleEdit.MOVE);
3244        if (mark == null)
3245            setMarkAtDot();
3246        dot.moveTo(buffer.getFirstLine(), 0);
3247        moveCaretToDotCol();
3248        setUpdateFlag(REPAINT);
3249    }
3250
3251    private final Position getEob()
3252    {
3253        return buffer.getEnd();
3254    }
3255
3256    public void eob()
3257    {
3258        moveDotTo(getEob());
3259    }
3260
3261    public void selectEob()
3262    {
3263        if (buffer.getFirstLine() == null)
3264            return;
3265        Line line = buffer.getFirstLine();
3266        while (line.next() != null)
3267            line = line.next();
3268        addUndo(SimpleEdit.MOVE);
3269        if (mark == null)
3270            setMarkAtDot();
3271        dot.moveTo(line, line.length());
3272        moveCaretToDotCol();
3273        setUpdateFlag(REPAINT);
3274    }
3275
3276    public void top()
3277    {
3278        if (dot == null)
3279            return;
3280        if (getDotLine() != getTopLine()) {
3281            addUndo(SimpleEdit.MOVE);
3282            updateDotLine();
3283            dot.setLine(getTopLine());
3284            moveDotToCaretCol();
3285        }
3286    }
3287
3288    public void bottom()
3289    {
3290        if (dot == null)
3291            return;
3292        Line line = display.getBottomLine();
3293        if (line != getDotLine()) {
3294            addUndo(SimpleEdit.MOVE);
3295            updateDotLine();
3296            dot.setLine(line);
3297            updateDotLine();
3298            moveDotToCaretCol();
3299        }
3300    }
3301
3302    public void selectWord()
3303    {
3304        if (dot == null)
3305            return;
3306        AWTEvent JavaDoc e = dispatcher.getLastEvent();
3307        CompoundEdit JavaDoc compoundEdit = null;
3308        if (e instanceof MouseEvent JavaDoc) {
3309            compoundEdit = beginCompoundEdit();
3310            mouseMoveDotToPoint((MouseEvent JavaDoc)e);
3311        }
3312        if (inWord()) {
3313            addUndo(SimpleEdit.MOVE);
3314            while (getDotOffset() > 0) {
3315                dot.moveLeft();
3316                if (!inWord()) {
3317                    dot.moveRight();
3318                    break;
3319                }
3320            }
3321            setMarkAtDot();
3322            final int limit = getDotLine().length();
3323            while (inWord() && getDotOffset() < limit)
3324                dot.moveRight();
3325            moveCaretToDotCol();
3326        }
3327        if (compoundEdit != null)
3328            endCompoundEdit(compoundEdit);
3329    }
3330
3331    public void mouseMoveDotToPoint()
3332    {
3333        AWTEvent JavaDoc e = dispatcher.getLastEvent();
3334        if (e instanceof MouseEvent JavaDoc)
3335            mouseMoveDotToPoint((MouseEvent JavaDoc)e);
3336    }
3337
3338    public void mouseMoveDotToPoint(MouseEvent JavaDoc e)
3339    {
3340        addUndo(SimpleEdit.MOVE);
3341        if (mark != null)
3342            unmark();
3343        display.moveCaretToPoint(e.getPoint());
3344        if (buffer.getBooleanProperty(Property.RESTRICT_CARET))
3345            moveCaretToDotCol();
3346
3347        // Dec 13 2002 6:30 PM
3348
// Without this, focus ends up in the location bar textfield if you
3349
// click in the edit window after using the openFile completion list
3350
// to open a file. Weird.
3351
Editor.restoreFocus();
3352    }
3353
3354    public void mouseSelect()
3355    {
3356        if (dot != null) {
3357            AWTEvent JavaDoc e = dispatcher.getLastEvent();
3358            if (e instanceof MouseEvent JavaDoc) {
3359                MouseEvent JavaDoc mouseEvent = (MouseEvent JavaDoc) e;
3360                Position pos = display.positionFromPoint(mouseEvent.getPoint());
3361                addUndo(SimpleEdit.MOVE);
3362                Position min, max;
3363                if (mark == null) {
3364                    // New selection.
3365
setMarkAtDot();
3366                    dot = pos;
3367                    moveCaretToDotCol();
3368                    Region r = new Region(this);
3369                    min = r.getBegin();
3370                    max = r.getEnd();
3371                } else {
3372                    // Adjust existing selection.
3373
Region r = new Region(this);
3374                    min = r.getBegin();
3375                    max = r.getEnd();
3376                    if (pos.isBefore(r.getBegin())) {
3377                        min = pos;
3378                        mark = r.getEnd();
3379                    } else if (pos.isAfter(r.getEnd())) {
3380                        max = pos;
3381                        mark = r.getBegin();
3382                    } else {
3383                        // Click was inside selected region.
3384
if (Position.getDistance(r.getBegin(), pos) < Position.getDistance(r.getEnd(), pos)) {
3385                            // Click was closer to beginning of region.
3386
mark = r.getEnd();
3387                        } else
3388                            mark = r.getBegin();
3389                    }
3390                    dot = pos;
3391                    moveCaretToDotCol();
3392                }
3393                // Minimize repaint.
3394
if (max.lineNumber() - min.lineNumber() < display.getRows()) {
3395                    Line line = min.getLine();
3396                    Line endLine = max.getLine();
3397                    while (line != null && line != endLine) {
3398                        update(line);
3399                        line = line.next();
3400                    }
3401                    update(endLine);
3402                } else
3403                    setUpdateFlag(REPAINT);
3404            }
3405        }
3406    }
3407
3408    public void mouseSelectColumn()
3409    {
3410        if (dot != null) {
3411            AWTEvent JavaDoc e = dispatcher.getLastEvent();
3412            if (e instanceof MouseEvent JavaDoc) {
3413                MouseEvent JavaDoc mouseEvent = (MouseEvent JavaDoc) e;
3414                if (getMark() == null)
3415                    setMarkAtDot();
3416                display.moveCaretToPoint(mouseEvent.getPoint());
3417                setColumnSelection(true);
3418                display.setUpdateFlag(REPAINT);
3419            }
3420        }
3421    }
3422
3423    private JPopupMenu JavaDoc popup;
3424
3425    public void mouseShowContextMenu()
3426    {
3427        AWTEvent JavaDoc e = dispatcher.getLastEvent();
3428        if (e instanceof MouseEvent JavaDoc) {
3429            MouseEvent JavaDoc mouseEvent = (MouseEvent JavaDoc) e;
3430            int x = mouseEvent.getX();
3431            int y = mouseEvent.getY();
3432            popup = buffer.getMode().getContextMenu(this);
3433            if (popup != null) {
3434                Dimension JavaDoc dimPopup = popup.getPreferredSize();
3435                Dimension JavaDoc dimDisplay = display.getSize();
3436                int xMax = dimDisplay.width - dimPopup.width - 5;
3437                int yMax = dimDisplay.height - dimPopup.height - 5;
3438                if (x > xMax)
3439                    x = xMax;
3440                else
3441                    ++x;
3442                if (y > yMax)
3443                    y = yMax;
3444                else
3445                    ++y;
3446                popup.show(mouseEvent.getComponent(), x, y);
3447            }
3448        }
3449    }
3450
3451    public final JPopupMenu JavaDoc getPopup()
3452    {
3453        return popup;
3454    }
3455
3456    public final void setPopup(JPopupMenu JavaDoc popup)
3457    {
3458        this.popup = popup;
3459    }
3460
3461    public void killPopup()
3462    {
3463        if (popup != null) {
3464            popup.setVisible(false);
3465            popup = null;
3466            restoreFocus();
3467        }
3468    }
3469
3470    private boolean inWord()
3471    {
3472        return getMode().isIdentifierPart(getDotChar());
3473    }
3474
3475    private boolean inWhitespace()
3476    {
3477        return Character.isWhitespace(getDotChar());
3478    }
3479
3480    private void skipWhitespace()
3481    {
3482        while (inWhitespace())
3483            if (!nextChar())
3484                break;
3485    }
3486
3487    private void nextWord()
3488    {
3489        if (dot == null)
3490            return;
3491        if (inWord()) {
3492            while (nextChar())
3493                if (!inWord())
3494                    break;
3495            skipWhitespace();
3496        } else if (inWhitespace()) {
3497            skipWhitespace();
3498        } else {
3499            // Not in word or whitespace.
3500
while (nextChar() && !inWord() && !inWhitespace())
3501                ;
3502            skipWhitespace();
3503        }
3504    }
3505
3506    private void prevWord()
3507    {
3508        if (dot == null)
3509            return;
3510        if (!prevChar())
3511            return;
3512        if (inWord()) {
3513            while (prevChar() && inWord())
3514                ;
3515            if (!inWord())
3516                nextChar();
3517        } else if (inWhitespace()) {
3518            while (prevChar() && inWhitespace())
3519                ;
3520            if (inWord()) {
3521                while (prevChar() && inWord())
3522                    ;
3523                if (!inWord())
3524                    nextChar();
3525            } else {
3526                while (prevChar() && !inWord() && !inWhitespace())
3527                    ;
3528                if (inWord() || inWhitespace())
3529                    nextChar();
3530            }
3531        } else {
3532            // Not in word or whitespace.
3533
while (prevChar()) {
3534                if (inWord())
3535                    break;
3536                if (inWhitespace())
3537                    break;
3538            }
3539            if (inWord() || inWhitespace())
3540                nextChar();
3541        }
3542    }
3543
3544    public void wordRight()
3545    {
3546        if (dot == null)
3547            return;
3548        updateDotLine();
3549        addUndo(SimpleEdit.MOVE);
3550        endOfBlock();
3551        nextWord();
3552        moveCaretToDotCol();
3553        updateDotLine();
3554    }
3555
3556    public void wordLeft()
3557    {
3558        if (dot == null)
3559            return;
3560        updateDotLine();
3561        addUndo(SimpleEdit.MOVE);
3562        beginningOfBlock();
3563        prevWord();
3564        moveCaretToDotCol();
3565        updateDotLine();
3566    }
3567
3568    public void selectWordRight()
3569    {
3570        if (dot == null)
3571            return;
3572        addUndo(SimpleEdit.MOVE);
3573        if (mark == null)
3574            setMarkAtDot();
3575        updateDotLine();
3576        nextWord();
3577        moveCaretToDotCol();
3578        updateDotLine();
3579    }
3580
3581    public void selectWordLeft()
3582    {
3583        if (dot == null)
3584            return;
3585        addUndo(SimpleEdit.MOVE);
3586        if (mark == null)
3587            setMarkAtDot();
3588        updateDotLine();
3589        prevWord();
3590        moveCaretToDotCol();
3591        updateDotLine();
3592    }
3593
3594    public void selectAll()
3595    {
3596        if (dot == null)
3597            return;
3598        pushPosition();
3599        addUndo(SimpleEdit.MOVE);
3600        unmark();
3601        Line line = buffer.getFirstLine();
3602        dot.moveTo(line, 0);
3603        display.setCaretCol(0);
3604        display.setShift(0);
3605        setMarkAtDot();
3606        while (line.next() != null)
3607            line = line.next();
3608        dot.moveTo(line, line.length());
3609        moveCaretToDotCol();
3610        display.setUpdateFlag(REPAINT);
3611    }
3612
3613    // Moves dot to the requested absolute column, based on the tab size of
3614
// the buffer. If the requested column is past the end of the line, dot is
3615
// moved to the end of the line.
3616
public void moveDotToCol(int goal)
3617    {
3618        if (dot == null)
3619            return;
3620
3621        dot.moveToCol(goal, buffer.getTabWidth());
3622        updateDotLine();
3623
3624        // Support tab chars in buffer. If we're not beyond the end of the
3625
// line, make sure we're on an actual character.
3626
if (dot.getOffset() < dot.getLineLength())
3627            moveCaretToDotCol();
3628    }
3629
3630    public final void moveDotToCaretCol()
3631    {
3632        moveDotToCol(display.getAbsoluteCaretCol());
3633    }
3634
3635    public final void moveCaretToDotCol()
3636    {
3637        display.moveCaretToDotCol();
3638    }
3639
3640    public final void repaintDisplay()
3641    {
3642        display.setUpdateFlag(REPAINT);
3643        display.repaint();
3644    }
3645
3646    public final void repaintNow()
3647    {
3648        display.repaintNow();
3649    }
3650
3651    // Adds whitespace to fill the area between the end of the actual text on
3652
// a line and the location of the caret, if it's beyond the end of the
3653
// text. Dot is moved to the end of the appended whitespace. Does nothing
3654
// if caret is not past end of text.
3655
public void fillToCaret()
3656    {
3657        final int where = display.getAbsoluteCaretCol();
3658        final Line dotLine = getDotLine();
3659        String JavaDoc s = getFillString(dotLine, where);
3660        if (s != null) {
3661            try {
3662                buffer.lockWrite();
3663            }
3664            catch (InterruptedException JavaDoc e) {
3665                Log.error(e);
3666                return;
3667            }
3668            try {
3669                addUndo(SimpleEdit.LINE_EDIT);
3670                dotLine.setText(dotLine.getText().concat(s));
3671                buffer.modified();
3672                dot.setOffset(dotLine.length());
3673            }
3674            finally {
3675                buffer.unlockWrite();
3676            }
3677        }
3678    }
3679
3680    private String JavaDoc getFillString(Line line, int where)
3681    {
3682        int end = buffer.getCol(line, line.length());
3683        if (where <= end)
3684            return null;
3685        final int width = where - end;
3686
3687        // For sanity, only use actual tab chars at beginning of line!
3688
if (buffer.getUseTabs() && line.length() == 0) {
3689            FastStringBuffer sb = new FastStringBuffer(width);
3690            int col = 0;
3691            final int tabWidth = buffer.getTabWidth();
3692            while (col + tabWidth <= width) {
3693                sb.append('\t');
3694                col += tabWidth;
3695            }
3696            while (col < width) {
3697                sb.append(' ');
3698                ++col;
3699            }
3700            return sb.toString();
3701        } else
3702            return Utilities.spaces(width);
3703    }
3704
3705    public final int getDotCol()
3706    {
3707        return buffer.getCol(dot);
3708    }
3709
3710    // Insert string at dot, put dot at end of inserted string.
3711
// No undo.
3712
public void insertStringInternal(String JavaDoc s)
3713    {
3714        updateInAllEditors(getDotLine());
3715        buffer.insertString(dot, s);
3716    }
3717
3718    // Fills the space (if any) between dot and caret and inserts
3719
// the char in question.
3720
public void insertChar(char c)
3721    {
3722        final Line dotLine = getDotLine();
3723        if (getDotOffset() > dotLine.length()) {
3724            // Shouldn't happen.
3725
Debug.bug();
3726            Log.error("insertChar dot offset = " + getDotOffset() +
3727                      " dotLine length = " + dotLine.length());
3728            // Enforce sanity and carry on.
3729
dot.setOffset(dotLine.length());
3730        }
3731        try {
3732            buffer.lockWrite();
3733        }
3734        catch (InterruptedException JavaDoc e) {
3735            Log.error(e);
3736            return;
3737        }
3738        try {
3739            addUndo(SimpleEdit.LINE_EDIT);
3740            fillToCaret();
3741            FastStringBuffer sb =
3742                new FastStringBuffer(dotLine.substring(0, getDotOffset()));
3743            sb.append(c);
3744            sb.append(dotLine.substring(getDotOffset()));
3745            dotLine.setText(sb.toString());
3746            dot.moveRight();
3747            moveCaretToDotCol();
3748            buffer.modified();
3749        }
3750        finally {
3751            buffer.unlockWrite();
3752        }
3753        updateInAllEditors(dotLine);
3754    }
3755
3756    public void insertChar()
3757    {
3758        if (!checkReadOnly())
3759            return;
3760        String JavaDoc input = InputDialog.showInputDialog(this, "Character:", "Insert Character");
3761        if (input == null || input.length() == 0)
3762            return;
3763        repaintNow();
3764        int c = parseNumericInput(input);
3765        if (c >= 0 && c < 0xfffe)
3766            insertChar((char) c);
3767        else
3768            MessageDialog.showMessageDialog(this, "Invalid character", "Insert Character");
3769    }
3770
3771    public void insertByte()
3772    {
3773        if (!checkReadOnly())
3774            return;
3775        String JavaDoc input = InputDialog.showInputDialog(this, "Byte:", "Insert Byte");
3776        if (input == null || input.length() == 0)
3777            return;
3778        repaintNow();
3779        int c = parseNumericInput(input);
3780        if (c >= 0 && c <= 255) {
3781            byte[] bytes = new byte[1];
3782            bytes[0] = (byte) c;
3783            String JavaDoc encoding = prefs.getStringProperty(Property.DEFAULT_ENCODING);
3784            try {
3785                String JavaDoc s = new String JavaDoc(bytes, encoding);
3786                insertChar(s.charAt(0));
3787            }
3788            catch (UnsupportedEncodingException JavaDoc e) {
3789                Log.error(e);
3790                MessageDialog.showMessageDialog(this,
3791                    "Unsupported encoding \"" + encoding + "\"", "Insert Byte");
3792            }
3793        } else
3794            MessageDialog.showMessageDialog(this,
3795                "Invalid byte \"" + input + "\"", "Insert Byte");
3796    }
3797
3798    // Used only by insertChar and insertByte. Doesn't understand a leading
3799
// minus sign.
3800
private static int parseNumericInput(String JavaDoc input)
3801    {
3802        int n = -1;
3803        input = input.trim();
3804        try {
3805            if (input.startsWith("0x") || input.startsWith("0X"))
3806                n = Integer.parseInt(input.substring(2), 16);
3807            else if (input.startsWith("0"))
3808                n = Integer.parseInt(input, 8);
3809            else
3810                n = Integer.parseInt(input, 10);
3811        }
3812        catch (NumberFormatException JavaDoc e) {
3813            Log.error(e);
3814        }
3815        return n;
3816    }
3817
3818    public void electricSemi()
3819    {
3820        if (!checkReadOnly())
3821            return;
3822        if (mark != null || getDotLine().flags() == STATE_COMMENT ||
3823            getMode().isInQuote(buffer, dot)) {
3824            insertNormalChar(';');
3825        } else {
3826            CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
3827            insertChar(';');
3828            moveCaretToDotCol();
3829            indentLine();
3830            if (buffer.getBooleanProperty(Property.AUTO_NEWLINE)) {
3831                boolean b = true;
3832                String JavaDoc s = dot.getLine().trim();
3833                if (s.startsWith("for")) {
3834                    char c = s.charAt(3);
3835                    if (c == ' ' || c == '\t' || c == '(')
3836                        b = false;
3837                }
3838                if (b)
3839                    newlineAndIndent();
3840            }
3841            endCompoundEdit(compoundEdit);
3842        }
3843    }
3844
3845    public void electricColon()
3846    {
3847        if (!checkReadOnly())
3848            return;
3849        try {
3850            buffer.lockWrite();
3851        }
3852        catch (InterruptedException JavaDoc e) {
3853            Log.error(e);
3854            return;
3855        }
3856        try {
3857            electricColonInternal();
3858        }
3859        finally {
3860            buffer.unlockWrite();
3861        }
3862    }
3863
3864    private void electricColonInternal()
3865    {
3866        final Line dotLine = getDotLine();
3867        final int dotOffset = getDotOffset();
3868        if (mark != null || dotOffset != dotLine.length()) {
3869            insertNormalChar(':');
3870            return;
3871        }
3872        if (dotLine.flags() == STATE_COMMENT || getMode().isInQuote(buffer, dot)) {
3873            insertNormalChar(':');
3874            return;
3875        }
3876        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
3877        insertChar(':');
3878        moveCaretToDotCol();
3879        indentLine();
3880        if (buffer.getBooleanProperty(Property.AUTO_NEWLINE))
3881            newlineAndIndent();
3882        endCompoundEdit(compoundEdit);
3883    }
3884
3885    public void electricStar()
3886    {
3887        if (!checkReadOnly())
3888            return;
3889
3890        // The intention here is to line up the '*' under the '*' of
3891
// the previous line if the current line is blank and if the
3892
// previous line begins with "/*".
3893
if (getDotLine().isBlank()) {
3894            if (buffer.needsParsing()) {
3895                if (getFormatter().parseBuffer())
3896                    buffer.repaint();
3897            }
3898            CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
3899            insertNormalChar('*');
3900            indentLine();
3901            endCompoundEdit(compoundEdit);
3902        } else
3903            insertNormalChar('*');
3904    }
3905
3906    public void electricPound()
3907    {
3908        if (!checkReadOnly())
3909            return;
3910        if (mark == null && getDotLine().isBlank()) {
3911            try {
3912                buffer.lockWrite();
3913            }
3914            catch (InterruptedException JavaDoc e) {
3915                Log.error(e);
3916                return;
3917            }
3918            try {
3919                addUndo(SimpleEdit.LINE_EDIT);
3920                getDotLine().setText("#");
3921                dot.setOffset(1);
3922                buffer.modified();
3923            }
3924            finally {
3925                buffer.unlockWrite();
3926            }
3927            updateInAllEditors(getDotLine());
3928            moveCaretToDotCol();
3929        } else
3930            insertNormalChar('#');
3931    }
3932
3933    public void electricOpenBrace()
3934    {
3935        electricBraceInternal('{');
3936    }
3937
3938    public void electricCloseBrace()
3939    {
3940        electricBraceInternal('}');
3941    }
3942
3943    private void electricBraceInternal(char c)
3944    {
3945        if (!checkReadOnly())
3946            return;
3947        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
3948        if (mark == null && getDotLine().isBlank()) {
3949            addUndo(SimpleEdit.LINE_EDIT);
3950            getDotLine().setText("");
3951            dot.setOffset(0);
3952            insertChar(c);
3953            indentLine();
3954            eol();
3955            if (buffer.getBooleanProperty(Property.AUTO_NEWLINE))
3956                newlineAndIndent();
3957        } else {
3958            insertNormalChar(c);
3959            indentLine();
3960        }
3961        endCompoundEdit(compoundEdit);
3962    }
3963
3964    public void electricCloseAngleBracket()
3965    {
3966        if (!checkReadOnly())
3967            return;
3968        if (mark == null) {
3969            int modeId = getModeId();
3970            if (modeId == XML_MODE || modeId == HTML_MODE) {
3971                CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
3972                insertChar('>');
3973                moveCaretToDotCol();
3974                if (buffer.getBooleanProperty(Property.AUTO_INDENT)) {
3975                    if (modeId == HTML_MODE &&
3976                        getDotLine().substring(0, getDotOffset()).endsWith(
3977                            "</pre>")) {
3978                        ; // No autoindent after "</pre>" in HTML mode.
3979
} else {
3980                        indentLine();
3981                    }
3982                }
3983                endCompoundEdit(compoundEdit);
3984                return;
3985            }
3986        }
3987        // Otherwise...
3988
insertNormalChar('>');
3989    }
3990
3991    public void gotoline(int lineNumber)
3992    {
3993        Line line = buffer.getLine(lineNumber);
3994        if (line != null)
3995            setDot(line, 0);
3996    }
3997
3998    public void saveState()
3999    {
4000        if (saveSession) {
4001            // Make sure information about current buffer is up-to-date.
4002
saveView();
4003
4004            Session.saveDefaultSession();
4005            if (sessionName != null)
4006                if (prefs.getBooleanProperty(Property.AUTOSAVE_NAMED_SESSIONS))
4007                    Session.saveCurrentSession();
4008            sessionProperties.saveWindowPlacement();
4009            sessionProperties.save();
4010        }
4011    }
4012
4013    // It might make sense to move this code into the Buffer class.
4014
public void reload(Buffer buf)
4015    {
4016        setWaitCursor();
4017        if (buf.getFile() instanceof SshFile)
4018            return; // Not supported.
4019
Debug.assertTrue(SwingUtilities.isEventDispatchThread());
4020        for (EditorIterator it = new EditorIterator(); it.hasNext();) {
4021            Editor ed = it.nextEditor();
4022            if (ed.getBuffer() == buf)
4023                ed.saveView();
4024        }
4025
4026        // May be asynchronous.
4027
buf.reload();
4028        setDefaultCursor();
4029    }
4030
4031    public void revertBuffer()
4032    {
4033        final File file = buffer.getFile();
4034        if (file instanceof SshFile)
4035            return; // Not supported.
4036
if (buffer.isModified()) {
4037            String JavaDoc prompt = "Discard changes to " + file.canonicalPath() + "?";
4038            if (!confirm("Revert Buffer", prompt))
4039                return;
4040            reload(buffer);
4041        }
4042    }
4043
4044    // Returns true if the buffer is active and there has been some change
4045
// that requires us to redraw the menus, title bar or display, false
4046
// otherwise.
4047
public boolean reactivate(Buffer buf)
4048    {
4049        if (buf instanceof ImageBuffer)
4050            return ((ImageBuffer)buf).reactivate();
4051
4052        if (buf.getType() != Buffer.TYPE_NORMAL)
4053            return false;
4054        if (buf.isUntitled())
4055            return false;
4056
4057        // BUG! Why don't we reload binary mode buffers?
4058
if (buf.getModeId() == BINARY_MODE)
4059            return false;
4060
4061        final File file = buf.getFile();
4062        if (file == null || file.isRemote() || !file.isFile())
4063            return false;
4064
4065        boolean changed = false;
4066
4067        // Check read-only status even if buffer is not loaded so buffer list
4068
// will be correct.
4069
if (buf.readOnly == file.canWrite()) {
4070            // Read-only status has changed.
4071
buf.readOnly = !buf.readOnly;
4072            changed = true;
4073            // Let the user know if the file associated with a modified buffer
4074
// is no longer writable.
4075
if (buf.readOnly && buf.isLoaded() && buf.isModified())
4076                MessageDialog.showMessageDialog(
4077                    file.canonicalPath().concat(" is no longer writable"),
4078                    "Warning");
4079        }
4080
4081        if (buf.isLoaded()) {
4082            if (file.lastModified() != buf.getLastModified()) {
4083                if (buf.isModified()) {
4084                    String JavaDoc prompt = file.canonicalPath() +
4085                        " has changed on disk. Reload and lose current changes?";
4086                    if (confirm("Reload File From Disk", prompt)) {
4087                        reload(buf);
4088                        changed = true;
4089                    } else
4090                        buf.setLastModified(file.lastModified());
4091                } else {
4092                    // No need for confirmation.
4093
reload(buf);
4094                    changed = true;
4095                }
4096            }
4097        }
4098
4099        return changed;
4100    }
4101
4102    public void setFocus(JComponent JavaDoc c)
4103    {
4104        frame.setFocus(c);
4105    }
4106
4107    public JComponent JavaDoc getFocusedComponent()
4108    {
4109        return frame.getFocusedComponent();
4110    }
4111
4112    public void setFocusToDisplay()
4113    {
4114        frame.setFocus(display);
4115    }
4116
4117    public static final void restoreFocus()
4118    {
4119        Runnable JavaDoc r = new Runnable JavaDoc() {
4120            public void run()
4121            {
4122                if (currentEditor != null)
4123                    currentEditor.setFocusToDisplay();
4124            }
4125        };
4126        SwingUtilities.invokeLater(r);
4127    }
4128
4129    public void componentHidden(ComponentEvent JavaDoc e)
4130    {
4131    }
4132
4133    public void componentMoved(ComponentEvent JavaDoc e)
4134    {
4135    }
4136
4137    public void componentResized(ComponentEvent JavaDoc e)
4138    {
4139        updateScrollBars();
4140    }
4141
4142    public void componentShown(ComponentEvent JavaDoc e)
4143    {
4144    }
4145
4146    public void mouseWheelMoved(MouseWheelEvent JavaDoc e)
4147    {
4148        // Without this, focus ends up in the location bar textfield if you use
4149
// the mouse wheel in the edit window after using the openFile
4150
// completion list to open a file (Blackdown 1.4.1-01).
4151
// See also mouseMoveDotToPoint(MouseEvent).
4152
setFocusToDisplay();
4153
4154        if (e.getWheelRotation() < 0)
4155            display.windowUp(5);
4156        else
4157            display.windowDown(5);
4158    }
4159
4160    public void ensureActive()
4161    {
4162        if (!frame.isActive()) {
4163            for (int i = 0; i < getFrameCount(); i++) {
4164                Frame f = getFrame(i);
4165                if (f.isActive()) {
4166                    f.dispatchEvent(new WindowEvent JavaDoc(f, WindowEvent.WINDOW_DEACTIVATED));
4167                    break;
4168                }
4169            }
4170            frame.dispatchEvent(new WindowEvent JavaDoc(frame, WindowEvent.WINDOW_ACTIVATED));
4171        }
4172    }
4173
4174    public void quit()
4175    {
4176        maybeExit();
4177    }
4178
4179    public void saveAllExit()
4180    {
4181        tagFileManager.setEnabled(false);
4182        saveAll();
4183        maybeExit(); // May never return.
4184
tagFileManager.setEnabled(true);
4185    }
4186
4187    private void maybeExit()
4188    {
4189        int numModifiedBuffers = 0;
4190
4191        for (BufferIterator it = new BufferIterator(); it.hasNext();) {
4192            if (it.nextBuffer().isModified())
4193                ++numModifiedBuffers;
4194        }
4195
4196        if (numModifiedBuffers > 0) {
4197            FastStringBuffer sb = new FastStringBuffer("Really exit with ");
4198            sb.append(numModifiedBuffers);
4199            sb.append(" modifed buffer");
4200            if (numModifiedBuffers > 1)
4201                sb.append('s');
4202            sb.append('?');
4203            if (!confirm("Really exit?", sb.toString()))
4204                return;
4205        }
4206
4207        setWaitCursor();
4208
4209        saveState();
4210        RecentFiles.getInstance().save();
4211
4212        // Delete all autosave files.
4213
for (BufferIterator iter = new BufferIterator(); iter.hasNext();)
4214            iter.nextBuffer().deleteAutosaveFile();
4215
4216        Autosave.deleteCatalogFile();
4217
4218        // Call dispose on all buffers.
4219
for (BufferIterator it = new BufferIterator(); it.hasNext();)
4220            it.nextBuffer().dispose();
4221
4222        // Clean up temporary directory.
4223
Directories.cleanTempDirectory();
4224
4225        // Clean up cache directory.
4226
Cache.cleanup();
4227
4228        Server.stopServer();
4229        pendingOperations.run();
4230        setDefaultCursor();
4231        System.exit(0);
4232    }
4233
4234    public void killFrame()
4235    {
4236        if (getFrameCount() == 1) {
4237            // Does not return if OK to exit.
4238
maybeExit();
4239        } else {
4240            // Move frame being closed to end of list.
4241
if (indexOf(frame) != getFrameCount() - 1) {
4242                frames.remove(frame);
4243                frames.add(frame);
4244            }
4245            sessionProperties.saveWindowPlacement();
4246            frames.remove(frame);
4247            frame.dispose();
4248
4249            Editor ed = frame.getSecondaryEditor();
4250            if (ed != null)
4251                removeEditor(ed);
4252            removeEditor(this);
4253            setCurrentEditor(getEditor(0));
4254        }
4255    }
4256
4257    // See if we have the requested file in a buffer. If not, and if the file
4258
// actually exists, make a new buffer for it.
4259
public static Buffer getBuffer(File file)
4260    {
4261        if (file == null)
4262            return null;
4263        Buffer buf = bufferList.findBuffer(file);
4264        if (buf != null)
4265            return buf;
4266        if (file.isRemote())
4267            return Buffer.createBuffer(file);
4268        if (file.isDirectory())
4269            return new Directory(file);
4270        if (file.isFile()) {
4271            if (!file.canRead()) {
4272                MessageDialog.showMessageDialog("File is not readable",
4273                    "Error");
4274                return null;
4275            }
4276            return Buffer.createBuffer(file);
4277        }
4278        if (file.exists()) {
4279            // The file exists, but it's neither a directory nor a "normal"
4280
// file. This can occur on Linux if an SMB mount goes south.
4281

4282            // Not a very informative error message, but this is what bash says
4283
// in the SMB mount case.
4284
currentEditor.status("I/O error");
4285        }
4286        return null;
4287    }
4288
4289    public void nextBuffer()
4290    {
4291        Buffer buf = bufferList.getNextPrimaryBuffer(buffer);
4292        if (buf == null)
4293            return;
4294        if (buf.isPaired()) {
4295            Buffer secondary = buf.getSecondary();
4296            if (secondary != null) {
4297                if (secondary.getLastActivated() > buf.getLastActivated())
4298                    buf = secondary;
4299            }
4300        }
4301        if (buf != buffer)
4302            switchToBuffer(buf);
4303    }
4304
4305    public void prevBuffer()
4306    {
4307        Buffer buf = bufferList.getPreviousPrimaryBuffer(buffer);
4308        if (buf == null)
4309            return;
4310        if (buf.isPaired()) {
4311            Buffer secondary = buf.getSecondary();
4312            if (secondary != null) {
4313                if (secondary.getLastActivated() > buf.getLastActivated())
4314                    buf = secondary;
4315            }
4316        }
4317        if (buf != buffer)
4318            switchToBuffer(buf);
4319    }
4320
4321    public void switchToBuffer(Buffer buf)
4322    {
4323        if (buf != null) {
4324            if (!buf.isPaired() && (buffer == null || !buffer.isPaired())) {
4325                // This is the easy case. Both the buffer we're switching in
4326
// and the buffer we're switching out are unpaired.
4327
activate(buf);
4328            } else {
4329                // We're either switching in a paired buffer or switching out
4330
// a paired buffer (or both). Delegate to our frame, since we
4331
// may end up closing this editor.
4332
frame.switchToBuffer(buf);
4333            }
4334            Sidebar sidebar = getSidebar();
4335            if (sidebar != null)
4336                sidebar.setBuffer();
4337        } else
4338            Debug.bug();
4339    }
4340
4341    public void makeNext(final Buffer buf)
4342    {
4343        bufferList.makeNext(buf, buffer);
4344    }
4345
4346    public void newBuffer()
4347    {
4348        Buffer buf = new Buffer(0);
4349        makeNext(buf);
4350        switchToBuffer(buf);
4351    }
4352
4353    public final void openFile()
4354    {
4355        AWTEvent JavaDoc e = dispatcher.getLastEvent();
4356        if (e != null && e.getSource() instanceof MenuItem) {
4357            Runnable JavaDoc r = new Runnable JavaDoc() {
4358                public void run()
4359                {
4360                    setFocusToTextField();
4361                }
4362            };
4363            SwingUtilities.invokeLater(r);
4364        } else
4365            setFocusToTextField();
4366    }
4367
4368    public void openFileInOtherWindow()
4369    {
4370        Debug.assertTrue(locationBar != null);
4371        saveView();
4372        boolean alreadySplit = (getOtherEditor() != null);
4373        if (!alreadySplit)
4374            splitWindow();
4375        final Editor ed = getOtherEditor();
4376        if (ed.getLocationBar() != null) {
4377            Runnable JavaDoc r = new Runnable JavaDoc() {
4378                public void run()
4379                {
4380                    frame.setFocus(ed.getLocationBar().getTextField());
4381                }
4382            };
4383            SwingUtilities.invokeLater(r);
4384        }
4385        setCurrentEditor(ed);
4386        if (alreadySplit) {
4387            // Current editor has changed.
4388
repaint();
4389            ed.repaint();
4390        }
4391    }
4392
4393    public Buffer openFile(File file)
4394    {
4395        Buffer buf = getBuffer(file);
4396        if (buf != null) {
4397            Debug.assertTrue(bufferList.contains(buf));
4398            return buf;
4399        }
4400        if (file.isRemote())
4401            return null;
4402        // File is local.
4403
if (!file.exists()) {
4404            if (confirm("Create file?",
4405                        file.canonicalPath() + " does not exist. Create?"))
4406                return Buffer.createBuffer(file);
4407        }
4408        return null;
4409    }
4410
4411    public Buffer openFiles(List JavaDoc list)
4412    {
4413        if (list == null)
4414            return null;
4415        final int listSize = list.size();
4416        if (listSize < 2)
4417            return null;
4418        Buffer toBeActivated = null;
4419        // First string is directory.
4420
String JavaDoc dirname = (String JavaDoc) list.get(0);
4421        File directory = File.getInstance(dirname);
4422        History openFileHistory = new History("openFile.file");
4423        int lineNumber = -1;
4424        for (int i = 1; i < listSize; i++) {
4425            String JavaDoc s = (String JavaDoc) list.get(i);
4426            if (s == null || s.length() == 0)
4427                continue;
4428            if (s.charAt(0) == '+') {
4429                try {
4430                    lineNumber = Integer.parseInt(s.substring(1)) - 1;
4431                }
4432                catch (NumberFormatException JavaDoc e) {}
4433                continue;
4434            }
4435            // Aliases.
4436
String JavaDoc value = getAlias(s);
4437            if (value != null)
4438                s = value;
4439            if (s.startsWith("pop://") || s.startsWith("{")) {
4440                MailboxURL url = MailboxURL.parse(s);
4441                if (url != null) {
4442                    Buffer buf = MailCommands.getMailboxBuffer(this, url);
4443                    if (buf != null) {
4444                        makeNext(buf);
4445                        toBeActivated = buf;
4446                    }
4447                }
4448                continue;
4449            }
4450            File file = File.getInstance(directory, s);
4451            if (file == null) {
4452                MessageDialog.showMessageDialog(this, "Invalid path ".concat(s),
4453                    "Invalid Path");
4454                continue;
4455            }
4456            if (Utilities.isFilenameAbsolute(s) || s.startsWith("./") || s.startsWith(".\\"))
4457                ; // No tricks.
4458
else if (!file.exists()) {
4459                // Look in source and include paths as appropriate.
4460
File f = Utilities.findFile(this, s);
4461                if (f != null)
4462                    file = f;
4463                else {
4464                    // Not found in source or include path.
4465
if (s.startsWith("www."))
4466                        file = File.getInstance("http://".concat(s));
4467                    else if (s.startsWith("ftp."))
4468                        file = File.getInstance("ftp://".concat(s));
4469                }
4470            }
4471            if (file.isLocal() && !file.exists()) {
4472                if (!Utilities.checkParentDirectory(file, "Open File"))
4473                    continue;
4474            }
4475            Buffer buf = openFile(file);
4476            if (buf != null) {
4477                Debug.assertTrue(bufferList.contains(buf));
4478                openFileHistory.append(file.netPath());
4479                if (lineNumber >= 0) {
4480                    // Line number was specified on command line.
4481
if (buf.isLoaded()) {
4482                        if (buf == buffer) {
4483                            // Current buffer.
4484
Line line = buffer.getLine(lineNumber);
4485                            if (line == null)
4486                                eob();
4487                            else {
4488                                addUndo(SimpleEdit.MOVE);
4489                                updateDotLine();
4490                                dot.moveTo(line, 0);
4491                                updateDotLine();
4492                                moveCaretToDotCol();
4493                            }
4494                        } else {
4495                            // Not current buffer.
4496
Buffer oldBuffer = buffer;
4497                            if (buffer != null)
4498                                saveView();
4499                            buffer = buf;
4500                            findOrCreateView(buffer);
4501                            restoreView();
4502                            addUndo(SimpleEdit.MOVE);
4503                            Line line = buffer.getLine(lineNumber);
4504                            if (line == null) {
4505                                line = buffer.getFirstLine();
4506                                while (line.next() != null)
4507                                    line = line.next();
4508                                dot.moveTo(line, line.length());
4509                            } else
4510                                dot.moveTo(line, 0);
4511                            saveView();
4512                            View view = (View) views.get(buffer);
4513                            if (view != null) {
4514                                view.shift = 0;
4515                                view.caretCol = getDotCol();
4516                            }
4517                            buffer = oldBuffer;
4518                            if (buffer != null)
4519                                restoreView();
4520                        }
4521                    } else {
4522                        // Not yet loaded.
4523
View view = findOrCreateView(buf);
4524                        view.lineNumber = lineNumber;
4525                        view.offs = 0;
4526                    }
4527                }
4528                Debug.assertTrue(bufferList.contains(buf));
4529                makeNext(buf);
4530                Debug.assertTrue(bufferList.contains(buf));
4531                toBeActivated = buf;
4532            }
4533        }
4534        openFileHistory.save();
4535        return toBeActivated;
4536    }
4537
4538    public void unmark()
4539    {
4540        if (mark != null) {
4541            setMark(null);
4542            display.setUpdateFlag(REPAINT); // BUG! Not always necessary!
4543
}
4544    }
4545
4546    public void cancelBackgroundProcess()
4547    {
4548        BackgroundProcess backgroundProcess = buffer.getBackgroundProcess();
4549        if (backgroundProcess != null)
4550            backgroundProcess.cancel();
4551    }
4552
4553    // Returns after doing exactly one thing.
4554
public void escape()
4555    {
4556        // Cancel background process (if any).
4557
BackgroundProcess backgroundProcess = buffer.getBackgroundProcess();
4558        if (backgroundProcess != null) {
4559            backgroundProcess.cancel();
4560            return;
4561        }
4562
4563        if (popup != null) {
4564            if (popup.isVisible()) {
4565                killPopup();
4566                return;
4567            }
4568            popup = null;
4569            // We haven't really done anything yet. Fall through...
4570
}
4571
4572        if (lastCommand == COMMAND_EXPAND) {
4573            Expansion expansion = Expansion.getLastExpansion();
4574            if (expansion != null) {
4575                expansion.undo(this);
4576                return;
4577            }
4578        }
4579
4580        if (buffer instanceof RemoteBuffer && buffer.isEmpty()) {
4581            killBuffer();
4582            return;
4583        }
4584
4585        if (escapeInternal())
4586            return;
4587
4588        if (selection != null && selection.getSavedDot() != null)
4589            moveDotTo(selection.getSavedDot());
4590        else if (mark != null)
4591            moveDotTo(mark);
4592    }
4593
4594    public boolean escapeInternal()
4595    {
4596        if (buffer instanceof CompilationBuffer) {
4597            if (buffer.unsplitOnClose()) {
4598                buffer.windowClosing();
4599                otherWindow();
4600                unsplitWindow();
4601            }
4602            maybeKillBuffer(buffer);
4603            restoreFocus();
4604            Sidebar.refreshSidebarInAllFrames();
4605            return true;
4606        }
4607        if (buffer.isTransient()) {
4608            if (buffer.unsplitOnClose()) {
4609                buffer.windowClosing();
4610                otherWindow();
4611                unsplitWindow();
4612            }
4613            maybeKillBuffer(buffer);
4614            restoreFocus();
4615            Sidebar.refreshSidebarInAllFrames();
4616            return true;
4617        }
4618        if (buffer.getModeId() == CHECKIN_MODE) {
4619            otherWindow();
4620            unsplitWindow();
4621            if (!buffer.isModified())
4622                maybeKillBuffer(buffer);
4623            restoreFocus();
4624            return true;
4625        }
4626        // Check for transient buffer in other editor in current frame.
4627
Editor ed = getOtherEditor();
4628        if (ed != null) {
4629            Buffer buf = ed.getBuffer();
4630            if (buf instanceof CompilationBuffer || buf.isTransient()) {
4631                if (buf.unsplitOnClose())
4632                    unsplitWindow();
4633                maybeKillBuffer(buf);
4634                if (!buf.unsplitOnClose())
4635                    ed.updateDisplay();
4636                Sidebar.refreshSidebarInAllFrames();
4637                return true;
4638            }
4639            if (buf.getModeId() == CHECKIN_MODE) {
4640                unsplitWindow();
4641                if (!buf.isModified())
4642                    maybeKillBuffer(buf);
4643                return true;
4644            }
4645        }
4646        return false;
4647    }
4648
4649    public void stamp()
4650    {
4651        if (!checkReadOnly())
4652            return;
4653        Date JavaDoc now = new Date JavaDoc(System.currentTimeMillis());
4654        String JavaDoc dateString = null;
4655        String JavaDoc stampFormat = buffer.getStringProperty(Property.STAMP_FORMAT);
4656        if (stampFormat != null) {
4657            try {
4658                SimpleDateFormat JavaDoc df = new SimpleDateFormat JavaDoc(stampFormat);
4659                dateString = df.format(now);
4660            }
4661            catch (Throwable JavaDoc t) {
4662                // Fall through...
4663
}
4664        }
4665        if (dateString == null) {
4666            SimpleDateFormat JavaDoc df = new SimpleDateFormat JavaDoc("MMM d yyyy h:mm a");
4667            dateString = df.format(now);
4668        }
4669        try {
4670            buffer.lockWrite();
4671        }
4672        catch (InterruptedException JavaDoc e) {
4673            Log.error(e);
4674            return;
4675        }
4676        try {
4677            CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
4678            if (mark != null)
4679                delete();
4680            fillToCaret();
4681            addUndo(SimpleEdit.INSERT_STRING);
4682            insertStringInternal(dateString);
4683            buffer.modified();
4684            endCompoundEdit(compoundEdit);
4685            moveCaretToDotCol();
4686            updateInAllEditors(getDotLine());
4687        }
4688        finally {
4689            buffer.unlockWrite();
4690        }
4691    }
4692
4693    public Search getSearchAtDot()
4694    {
4695        if (dot == null)
4696            return null;
4697        String JavaDoc pattern = null;
4698        boolean wholeWordsOnly = false;
4699        if (mark != null) {
4700            // No action if there's a multi-line selection.
4701
if (getMarkLine() == getDotLine())
4702                pattern = (new Region(buffer, dot, mark)).toString();
4703        } else {
4704            pattern = tokenAt(dot);
4705            wholeWordsOnly = true;
4706        }
4707        if (pattern != null && pattern.length() != 0)
4708            return new Search(pattern, false, wholeWordsOnly);
4709        else
4710            return null;
4711    }
4712
4713    // Assumes dot is on first char of found pattern.
4714
public void markFoundPattern(Search search)
4715    {
4716        if (search.isRegularExpression() && search.isMultilinePattern()) {
4717            REMatch match = search.getMatch();
4718            if (match != null) {
4719                setDot(buffer.getPosition(match.getStartIndex()));
4720                setMark(buffer.getPosition(match.getEndIndex()));
4721                final Line markLine = getMarkLine();
4722                for (Line line = getDotLine(); line != null; line = line.next()) {
4723                    update(line);
4724                    if (line == markLine)
4725                        break;
4726                }
4727                moveCaretToDotCol();
4728            }
4729        } else {
4730            final int context = 2; // This could be a preference.
4731
Position saved = dot.copy();
4732
4733            // Move dot to end of found pattern.
4734
int length;
4735            if (search.getMatch() != null)
4736                length = search.getMatch().toString().length();
4737            else
4738                length = search.getPatternLength();
4739
4740            // Found pattern might go beyond end of line.
4741
dot.setOffset(Math.min(dot.getOffset() + length, getDotLine().length()));
4742
4743            // Set mark at end of pattern.
4744
moveCaretToDotCol();
4745            setMarkAtDot();
4746
4747            // Make sure end of pattern is actually visible, with additional
4748
// context as appropriate.
4749
int absCol = getDotCol() + context;
4750            display.ensureColumnVisible(getDotLine(), absCol);
4751
4752            // Restore dot to original position at start of pattern.
4753
dot = saved;
4754
4755            // Make sure start of pattern is actually visible, with additional
4756
// context as appropriate.
4757
absCol = getDotCol() - context;
4758            if (absCol < 0)
4759                absCol = 0;
4760            display.ensureColumnVisible(getDotLine(), absCol);
4761            moveCaretToDotCol();
4762        }
4763    }
4764
4765    public void findNext()
4766    {
4767        if (lastSearch != null) {
4768            Position start;
4769            if (mark != null) {
4770                Region r = new Region(this);
4771                start = new Position(r.getBegin());
4772            } else
4773                start = new Position(dot);
4774            if (!start.next())
4775                return;
4776            setWaitCursor();
4777            Position pos = lastSearch.find(buffer, start);
4778            setDefaultCursor();
4779            if (pos != null) {
4780                moveDotTo(pos);
4781                markFoundPattern(lastSearch);
4782                if (lastSearch instanceof FindInFiles) {
4783                    if (buffer.getFile() != null) {
4784                        ListOccurrencesInFiles buf =
4785                            ((FindInFiles)lastSearch).getOutputBuffer();
4786                        if (buf != null)
4787                            buf.follow(buffer.getFile(), getDotLine());
4788                    }
4789                }
4790                return;
4791            }
4792            if (lastSearch instanceof FindInFiles) {
4793                Editor ed = getOtherEditor();
4794                if (ed != null) {
4795                    ListOccurrencesInFiles buf =
4796                        ((FindInFiles)lastSearch).getOutputBuffer();
4797                    if (ed.getBuffer() == buf) {
4798                        buf.findNextOccurrence(ed);
4799                        return;
4800                    }
4801                }
4802            }
4803            lastSearch.notFound(this);
4804        }
4805    }
4806
4807    public void findPrev()
4808    {
4809        if (lastSearch != null) {
4810            Position start;
4811            if (mark != null) {
4812                Region r = new Region(this);
4813                start = new Position(r.getBegin());
4814            } else
4815                start = new Position(dot);
4816            if (!start.prev())
4817                return;
4818            setWaitCursor();
4819            Position pos = lastSearch.reverseFind(buffer, start);
4820            setDefaultCursor();
4821            if (pos != null) {
4822                moveDotTo(pos);
4823                markFoundPattern(lastSearch);
4824                if (lastSearch instanceof FindInFiles) {
4825                    if (buffer.getFile() != null) {
4826                        ListOccurrencesInFiles buf =
4827                            ((FindInFiles)lastSearch).getOutputBuffer();
4828                        if (buf != null)
4829                            buf.follow(buffer.getFile(), getDotLine());
4830                    }
4831                }
4832                return;
4833            }
4834            if (lastSearch instanceof FindInFiles) {
4835                Editor ed = getOtherEditor();
4836                if (ed != null) {
4837                    ListOccurrencesInFiles buf =
4838                        ((FindInFiles)lastSearch).getOutputBuffer();
4839                    if (ed.getBuffer() == buf) {
4840                        buf.findPreviousOccurrence(ed);
4841                        return;
4842                    }
4843                }
4844            }
4845            lastSearch.notFound(this);
4846        }
4847    }
4848
4849    public void incrementalFind()
4850    {
4851        if (dot == null)
4852            return;
4853
4854        // Use location bar.
4855
locationBar.setLabelText(LocationBar.PROMPT_PATTERN);
4856        HistoryTextField textField = locationBar.getTextField();
4857        textField.setHandler(new IncrementalFindTextFieldHandler(this, textField));
4858        textField.setHistory(new History("incrementalFind.pattern"));
4859        textField.setText("");
4860        setFocusToTextField();
4861    }
4862
4863    public String JavaDoc getCurrentText()
4864    {
4865        String JavaDoc s = getSelectionOnCurrentLine();
4866        if (s == null)
4867            s = getTokenAtDot();
4868        return (s != null && s.length() > 0) ? s : null;
4869    }
4870
4871    public String JavaDoc getSelectionOnCurrentLine()
4872    {
4873        if (dot != null && mark != null && getMarkLine() == getDotLine())
4874            return new Region(this).toString();
4875        else
4876            return null;
4877    }
4878
4879    public String JavaDoc getFilenameAtDot()
4880    {
4881        if (dot == null)
4882            return null;
4883        Position pos;
4884        if (mark != null) {
4885            Region r = new Region(this);
4886
4887            // Trust the user if there's a highlighted selection on a single
4888
// line.
4889
if (r.getBeginLine() == r.getEndLine())
4890                return r.toString();
4891
4892            // Otherwise, we want the beginning of the marked region.
4893
pos = r.getBegin();
4894        } else
4895            pos = new Position(dot);
4896        final Line line = pos.getLine();
4897        final int limit = line.length();
4898        int offset = pos.getOffset();
4899        if (offset == limit)
4900            --offset;
4901        FastStringBuffer sb = new FastStringBuffer();
4902        if (offset >= 0 && offset < limit) {
4903            char c = line.charAt(offset);
4904            if (Utilities.isFilenameChar(c)) {
4905                while (offset > 0) {
4906                    c = line.charAt(--offset);
4907                    if (!Utilities.isFilenameChar(c)){
4908                        ++offset;
4909                        break;
4910                    }
4911                }
4912
4913                // Now we're looking at the first char of the filename.
4914
sb.append(line.charAt(offset));
4915                while (++offset < limit) {
4916                    c = line.charAt(offset);
4917                    if (Utilities.isFilenameChar(c)) {
4918                        sb.append(c);
4919                    } else if (sb.toString().startsWith("http://")) {
4920                        // Be more permissive since there may be an appended
4921
// query.
4922
if (!Character.isWhitespace(c))
4923                            sb.append(c);
4924                        else
4925                            break;
4926                    } else
4927                        break;
4928                }
4929
4930                // Now we're looking at the first char past the end of the
4931
// filename. If the filename starts with "http://", make sure
4932
// it doesn't end with normal punctuation (as is often the
4933
// case with links embedded in text).
4934
int length = sb.length();
4935                while (length > 0) {
4936                    c = sb.charAt(length-1);
4937                    if (".,:;)]>".indexOf(c) >= 0)
4938                        --length;
4939                    else
4940                        break;
4941                }
4942                sb.setLength(length);
4943
4944                RE re = new UncheckedRE(" line [0-9]+");
4945                REMatch match = re.getMatch(line.getText().substring(offset));
4946                if (match != null && match.getStartIndex() == 0)
4947                    sb.append(match.toString());
4948            }
4949        }
4950        return sb.toString();
4951    }
4952
4953    private String JavaDoc getTokenAtDot()
4954    {
4955        // If a selection is marked, return the token at the beginning of the
4956
// marked region.
4957
if (mark != null) {
4958            Region r = new Region(this);
4959            return tokenAt(r.getBegin());
4960        }
4961        return tokenAt(dot);
4962    }
4963
4964    private String JavaDoc tokenAt(Position pos)
4965    {
4966        return getMode().getIdentifier(pos);
4967    }
4968
4969    public void findNextWord()
4970    {
4971        if (dot == null)
4972            return;
4973        String JavaDoc pattern = getTokenAtDot();
4974        if (pattern == null || pattern.length() == 0)
4975            return;
4976        lastSearch = new Search(pattern, false, true);
4977        Position start;
4978        if (mark != null && dot.isBefore(mark))
4979            start = new Position(mark);
4980        else
4981            start = new Position(dot);
4982        Position pos = lastSearch.find(buffer.getMode(), start);
4983        if (pos != null && pos.equals(start)) {
4984            if (pos.next())
4985                pos = lastSearch.find(buffer.getMode(), pos);
4986        }
4987        if (pos != null && !pos.equals(start)) {
4988            moveDotTo(pos);
4989            markFoundPattern(lastSearch);
4990        } else
4991            lastSearch.notFound(this);
4992    }
4993
4994    public void findPrevWord()
4995    {
4996        if (dot == null)
4997            return;
4998        String JavaDoc pattern = getTokenAtDot();
4999        if (pattern == null || pattern.length() == 0)
5000            return;
5001        lastSearch = new Search(pattern, false, true);
5002        boolean found = false;
5003        Position start = null;
5004        if (mark != null)
5005            start = new Region(this).getBegin();
5006        else
5007            start = new Position(dot);
5008        if (start.prev()) {
5009            Position pos = lastSearch.reverseFind(buffer, start);
5010            if (pos != null && pos.getLine() == start.getLine()) {
5011                if (pos.getOffset() + lastSearch.getPatternLength() > start.getOffset()) {
5012                    // We've found the instance we started with. Keep looking.
5013
start = new Position(pos);
5014                    if (start.prev())
5015                        pos = lastSearch.reverseFind(buffer, start);
5016                    else
5017                        pos = null;
5018                }
5019            }
5020            if (pos != null) {
5021                found = true;
5022                moveDotTo(pos);
5023                markFoundPattern(lastSearch);
5024            }
5025        }
5026        if (!found)
5027            lastSearch.notFound(this);
5028    }
5029
5030    public void findFirstOccurrence()
5031    {
5032        if (dot == null)
5033            return;
5034        String JavaDoc pattern = getTokenAtDot();
5035        if (pattern == null || pattern.length() == 0)
5036            return;
5037        lastSearch = new Search(pattern, false, true);
5038        Position pos = lastSearch.find(buffer.getMode(),
5039                                       new Position(buffer.getFirstLine(), 0));
5040        if (pos != null) {
5041            moveDotTo(pos);
5042            markFoundPattern(lastSearch);
5043        } else
5044            lastSearch.notFound(this);
5045    }
5046
5047    public void copyPath()
5048    {
5049        if (buffer instanceof Directory) {
5050            String JavaDoc path = ((Directory) buffer).getPathAtDot();
5051            if (path != null) {
5052                killRing.appendNew(path);
5053                killRing.copyLastKillToSystemClipboard();
5054                status("Path copied to clipboard");
5055            }
5056        }
5057    }
5058
5059    public void copyRegion()
5060    {
5061        if (dot == null)
5062            return;
5063
5064        String JavaDoc message = null;
5065
5066        if (mark != null) {
5067            Region r = new Region(this);
5068            if (isColumnSelection()) {
5069                killedColumn = r.toString();
5070                message = "Column selection stored";
5071            } else {
5072                killRing.appendNew(r.toString());
5073                message = "Region copied to clipboard";
5074            }
5075        } else if (!getDotLine().isBlank()) {
5076            killRing.appendNew(getDotLine().getText() + System.getProperty("line.separator"));
5077            message = "Line copied to clipboard";
5078        } else
5079            return; // Nothing to do.
5080

5081        if (!isColumnSelection())
5082            killRing.copyLastKillToSystemClipboard();
5083
5084        if (message != null)
5085            status(message);
5086    }
5087
5088    public void copyAppend()
5089    {
5090        if (isColumnSelection()) {
5091            notSupportedForColumnSelections();
5092            return;
5093        }
5094
5095        if (dot == null)
5096            return;
5097
5098        String JavaDoc message = null;
5099
5100        if (mark != null) {
5101            Region r = new Region(buffer, mark, dot);
5102            killRing.appendToCurrent(r.toString());
5103            message = "Region appended to clipboard";
5104        } else if (!getDotLine().isBlank()) {
5105            killRing.appendToCurrent(getDotLine().getText() + System.getProperty("line.separator"));
5106            message = "Line appended to clipboard";
5107        } else
5108            return; // Nothing to do.
5109

5110        killRing.copyLastKillToSystemClipboard();
5111
5112        if (message != null)
5113            status(message);
5114    }
5115
5116    // Handles undo, updates display and marks buffer modified.
5117
public void deleteRegion()
5118    {
5119        if (mark == null)
5120            return;
5121        if (getMarkLine() != getDotLine() || getMarkOffset() != getDotOffset()) {
5122            try {
5123                buffer.lockWrite();
5124            }
5125            catch (InterruptedException JavaDoc e) {
5126                Log.error(e);
5127                return;
5128            }
5129            try {
5130                Region r = new Region(this);
5131                if (isColumnSelection()) {
5132                    deleteColumn(r);
5133                } else {
5134                    // A hard update is only necessary if the region spans a
5135
// line boundary.
5136
boolean hard = getDotLine() != getMarkLine();
5137
5138                    // Save undo information before calling r.delete() so
5139
// the modified flag will be correct if we revert.
5140
CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
5141                    addUndo(SimpleEdit.MOVE);
5142                    dot.moveTo(r.getBegin());
5143                    addUndoDeleteRegion(r);
5144
5145                    // Sets buffer modified flag.
5146
r.delete();
5147
5148                    endCompoundEdit(compoundEdit);
5149
5150                    if (hard)
5151                        buffer.repaint();
5152                    else
5153                        updateInAllEditors(getDotLine());
5154                }
5155            }
5156            finally {
5157                buffer.unlockWrite();
5158            }
5159            moveCaretToDotCol();
5160        }
5161        setMark(null);
5162    }
5163
5164    // Leaves dot at beginning of deleted region.
5165
private void deleteColumn(Region r)
5166    {
5167        Debug.assertTrue(r.isColumnRegion());
5168        final int beginCol = r.getBeginCol();
5169        final int endCol = r.getEndCol();
5170        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
5171        addUndo(SimpleEdit.MOVE);
5172        dot.moveTo(r.getBegin());
5173        while (true) {
5174            addUndo(SimpleEdit.LINE_EDIT);
5175            Line line = getDotLine();
5176            deleteLineRegion(line, beginCol, endCol);
5177            updateInAllEditors(line);
5178            if (line == r.getEndLine())
5179                break;
5180            if (line.next() == null)
5181                break;
5182            dot.moveTo(line.next(), 0);
5183        }
5184        addUndo(SimpleEdit.MOVE);
5185        dot.moveTo(r.getBegin());
5186        endCompoundEdit(compoundEdit);
5187        buffer.modified();
5188    }
5189
5190    private void deleteLineRegion(Line line, int beginCol, int endCol)
5191    {
5192        String JavaDoc text = Utilities.detab(line.getText(), buffer.getTabWidth());
5193        if (text.length() < beginCol)
5194            return; // No change.
5195
String JavaDoc head = text.substring(0, beginCol);
5196        if (text.length() < endCol) {
5197            line.setText(head);
5198            return;
5199        }
5200        String JavaDoc tail = text.substring(endCol);
5201        line.setText(head.concat(tail));
5202    }
5203
5204    // This really is a kill!
5205
public void killRegion()
5206    {
5207        if (!checkReadOnly())
5208            return;
5209        try {
5210            buffer.lockWrite();
5211        }
5212        catch (InterruptedException JavaDoc e) {
5213            Log.error(e);
5214            return;
5215        }
5216        try {
5217            killRegionInternal();
5218        }
5219        finally {
5220            buffer.unlockWrite();
5221        }
5222    }
5223
5224    private void killRegionInternal()
5225    {
5226        if (mark != null) {
5227            if (getMarkLine() != getDotLine() || getMarkOffset() != getDotOffset()) {
5228                // A hard update is only necessary if the region spans a line
5229
// boundary.
5230
boolean hard = getDotLine() != getMarkLine();
5231                if (isColumnSelection()) {
5232                    Region r = new Region(this);
5233                    killedColumn = r.toString();
5234                    deleteColumn(r);
5235                } else {
5236                    Region r = new Region(this);
5237                    String JavaDoc kill = r.toString();
5238                    if (lastCommand == COMMAND_KILL)
5239                        killRing.appendToCurrent(kill);
5240                    else
5241                        killRing.appendNew(kill);
5242                    killRing.copyLastKillToSystemClipboard();
5243
5244                    // Save undo information before calling Region.delete so
5245
// modified flag will be correct if we revert.
5246
CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
5247                    addUndo(SimpleEdit.MOVE);
5248                    dot.moveTo(r.getBegin());
5249                    addUndoDeleteRegion(r);
5250
5251                    // Sets buffer modified flag.
5252
r.delete();
5253
5254                    endCompoundEdit(compoundEdit);
5255                }
5256                moveCaretToDotCol();
5257                if (hard)
5258                    buffer.repaint();
5259                else
5260                    updateInAllEditors(getDotLine());
5261                setCurrentCommand(COMMAND_KILL);
5262            }
5263            setMark(null);
5264        } else {
5265            // No selection. Use current line.
5266
final Line dotLine = getDotLine();
5267            final Line nextLine = dotLine.next();
5268
5269            // Last line is a special case.
5270
if (nextLine == null) {
5271                CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
5272                dot.setOffset(0);
5273                killLine();
5274                endCompoundEdit(compoundEdit);
5275                setCurrentCommand(COMMAND_KILL);
5276                return;
5277            }
5278
5279            CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
5280            addUndo(SimpleEdit.MOVE);
5281            dot.moveTo(dotLine, 0);
5282            mark = new Position(nextLine, 0);
5283
5284            Region r = new Region(this);
5285            String JavaDoc kill = r.toString();
5286            if (lastCommand == COMMAND_KILL)
5287                killRing.appendToCurrent(kill);
5288            else
5289                killRing.appendNew(kill);
5290            killRing.copyLastKillToSystemClipboard();
5291
5292            // Save undo information before calling Region.delete so
5293
// modified flag will be correct if we revert.
5294
addUndo(SimpleEdit.MOVE);
5295            dot.moveTo(r.getBegin());
5296            addUndoDeleteRegion(r);
5297
5298            // Sets buffer modified flag.
5299
r.delete();
5300
5301            addUndo(SimpleEdit.MOVE);
5302            mark = null;
5303            endCompoundEdit(compoundEdit);
5304            moveCaretToDotCol();
5305            buffer.repaint();
5306            setCurrentCommand(COMMAND_KILL);
5307        }
5308    }
5309
5310    public void killAppend()
5311    {
5312        if (isColumnSelection()) {
5313            notSupportedForColumnSelections();
5314            return;
5315        }
5316        setLastCommand(COMMAND_KILL); // Force append.
5317
killRegion();
5318    }
5319
5320    // Copies text from dot to end of line to kill ring and then deletes that
5321
// text. If dot is already at end of line, deletes newline and copies it
5322
// to kill ring.
5323
public void killLine()
5324    {
5325        if (!checkReadOnly())
5326            return;
5327
5328        if (dot.getOffset() == dot.getLineLength() && dot.getNextLine() == null)
5329            return;
5330
5331        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
5332
5333        addUndo(SimpleEdit.MOVE);
5334        unmark();
5335
5336        if (getDotOffset() < getDotLine().length()){
5337            setMarkAtDot();
5338            if (dot.getLine().isBlank() && dot.getNextLine() != null)
5339                dot.moveTo(dot.getNextLine(), 0);
5340            else
5341                dot.setOffset(dot.getLineLength());
5342        } else if (dot.getOffset() == dot.getLineLength()) {
5343            fillToCaret(); // We might be beyond the end of the actual text on the line.
5344
setMarkAtDot();
5345            dot.moveTo(dot.getNextLine(), 0);
5346        }
5347
5348        killRegion();
5349
5350        endCompoundEdit(compoundEdit);
5351    }
5352
5353    public void deleteWordRight()
5354    {
5355        deleteOrKillWordRight(false);
5356    }
5357
5358    public void killWordRight()
5359    {
5360        deleteOrKillWordRight(true);
5361    }
5362
5363    private void deleteOrKillWordRight(boolean isKill)
5364    {
5365        if (!checkReadOnly())
5366            return;
5367        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
5368        addUndo(SimpleEdit.MOVE);
5369        unmark();
5370        fillToCaret();
5371        setMarkAtDot();
5372        if (inWord()) {
5373            while (inWord() && nextChar())
5374                ;
5375            while (inWhitespace() && nextChar())
5376                ;
5377        } else if (inWhitespace()) {
5378            while (inWhitespace() && nextChar())
5379                ;
5380        } else {
5381            while (!inWhitespace() && !inWord() && nextChar())
5382                ;
5383            while (inWhitespace() && nextChar())
5384                ;
5385        }
5386        if (isKill)
5387            killRegion();
5388        else
5389            deleteRegion();
5390        endCompoundEdit(compoundEdit);
5391    }
5392
5393    public void deleteWordLeft()
5394    {
5395        deleteOrKillWordLeft(false);
5396    }
5397
5398    public void killWordLeft()
5399    {
5400        deleteOrKillWordLeft(true);
5401    }
5402
5403    private void deleteOrKillWordLeft(boolean isKill)
5404    {
5405        if (!checkReadOnly())
5406            return;
5407        if (getDotOffset() == 0 && getDotLine().previous() == null)
5408            return;
5409        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
5410        addUndo(SimpleEdit.MOVE);
5411        unmark();
5412        setMarkAtDot();
5413        prevChar();
5414        if (inWord()) {
5415            while (getDotOffset() > 0 && inWord() && prevChar())
5416                ;
5417            if (!inWord())
5418                nextChar();
5419        } else if (inWhitespace()) {
5420            while (inWhitespace() && prevChar())
5421                ;
5422            if (!inWhitespace())
5423                nextChar();
5424        } else {
5425            while (!inWhitespace() && !inWord() && prevChar())
5426                ;
5427            while (inWhitespace() && prevChar())
5428                ;
5429            nextChar();
5430        }
5431        if (isKill)
5432            killRegion();
5433        else
5434            deleteRegion();
5435        endCompoundEdit(compoundEdit);
5436    }
5437
5438    public boolean canPaste()
5439    {
5440        if (buffer.isReadOnly())
5441            return false;
5442        if (killRing.size() > 0)
5443            return true;
5444        String JavaDoc toBeInserted = null;
5445        Transferable JavaDoc t = getToolkit().getSystemClipboard().getContents(this);
5446        if (t != null) {
5447            try {
5448                toBeInserted = (String JavaDoc) t.getTransferData(DataFlavor.stringFlavor);
5449            }
5450            catch (Exception JavaDoc e) {}
5451        }
5452        return toBeInserted != null;
5453    }
5454
5455    public void paste()
5456    {
5457        if (!checkReadOnly())
5458            return;
5459        setWaitCursor();
5460        String JavaDoc toBeInserted = null;
5461        Transferable JavaDoc t = getToolkit().getSystemClipboard().getContents(this);
5462        if (t != null) {
5463            try {
5464                toBeInserted = (String JavaDoc) t.getTransferData(DataFlavor.stringFlavor);
5465            }
5466            catch (Exception JavaDoc e) {}
5467        }
5468        if (toBeInserted != null && toBeInserted.length() > 0)
5469            killRing.appendNew(toBeInserted);
5470        // Even if we already have the text to be inserted, we MUST call
5471
// killRing.pop() here so that killRing.indexOfNextPop and
5472
// killRing.lastPaste are set correctly.
5473
toBeInserted = killRing.pop();
5474        if (toBeInserted != null) {
5475            paste(toBeInserted);
5476            setCurrentCommand(COMMAND_PASTE);
5477        }
5478        setDefaultCursor();
5479    }
5480
5481    public void cyclePaste()
5482    {
5483        if (lastCommand == COMMAND_PASTE) {
5484            setWaitCursor();
5485            String JavaDoc s = killRing.popNext();
5486            if (s != null) {
5487                undo();
5488                paste(s);
5489                setCurrentCommand(COMMAND_PASTE);
5490            }
5491            setDefaultCursor();
5492        } else
5493            paste();
5494    }
5495
5496    public void mousePaste()
5497    {
5498        if (dot == null)
5499            return;
5500        if (!checkReadOnly())
5501            return;
5502        if (isColumnSelection()) {
5503            notSupportedForColumnSelections();
5504            return;
5505        }
5506        AWTEvent JavaDoc e = dispatcher.getLastEvent();
5507        if (!(e instanceof MouseEvent JavaDoc))
5508            return;
5509        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
5510        if (mark != null) {
5511            Region r = new Region(this);
5512            killRing.appendNew(r.toString());
5513            killRing.copyLastKillToSystemClipboard();
5514            addUndo(SimpleEdit.MOVE);
5515            setMark(null);
5516        }
5517        mouseMoveDotToPoint((MouseEvent JavaDoc) e);
5518        paste();
5519        endCompoundEdit(compoundEdit);
5520    }
5521
5522    public static void promoteLastPaste()
5523    {
5524        killRing.promoteLastPaste();
5525    }
5526
5527    public void paste(String JavaDoc toBeInserted)
5528    {
5529        paste(toBeInserted, false);
5530    }
5531
5532    public void paste(String JavaDoc toBeInserted, boolean leavePasteSelected)
5533    {
5534        if (!checkReadOnly())
5535            return;
5536        if (toBeInserted == null || toBeInserted.length() == 0)
5537            return;
5538        try {
5539            buffer.lockWrite();
5540        }
5541        catch (InterruptedException JavaDoc e) {
5542            Log.error(e);
5543            return;
5544        }
5545        try {
5546            pasteInternal(toBeInserted, leavePasteSelected);
5547        }
5548        finally {
5549            buffer.unlockWrite();
5550        }
5551        setUpdateFlag(REFRAME);
5552    }
5553
5554    private void pasteInternal(String JavaDoc toBeInserted, boolean leavePasteSelected)
5555    {
5556        final Mode mode = buffer.getMode();
5557        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
5558        if (mark == null && Utilities.isLinePaste(toBeInserted) &&
5559            mode.acceptsLinePaste(this) && buffer.getBooleanProperty(Property.AUTO_PASTE_LINES))
5560        {
5561            // We want to the caret to be in the same column when we're done.
5562
final int absCaretCol = display.getAbsoluteCaretCol();
5563
5564            final Line prevLine = getDotLine().previous();
5565
5566            addUndo(SimpleEdit.MOVE);
5567            dot.setOffset(0);
5568            Position begin = dot.copy();
5569            addUndo(SimpleEdit.INSERT_STRING);
5570            insertStringInternal(toBeInserted);
5571
5572            if (prevLine != null && mode.canIndentPaste()) {
5573                // Indent inserted lines according to context.
5574

5575                // Make sure line flags are correct.
5576
if (getFormatter().parseBuffer())
5577                    buffer.repaint();
5578
5579                // Dot is at the beginning of the line following the inserted
5580
// block.
5581
Position savedDot = dot.copy();
5582
5583                // First move dot to start of inserted block.
5584
addUndo(SimpleEdit.MOVE);
5585                dot.moveTo(prevLine.next(), 0);
5586
5587                while (dot.getLine() != null && dot.getLine() != savedDot.getLine()) {
5588                    if (!dot.getLine().isBlank())
5589                        indentLineInternal();
5590                    addUndo(SimpleEdit.MOVE);
5591                    dot.moveTo(dot.getNextLine(), 0);
5592                }
5593
5594                // Restore dot.
5595
dot = savedDot;
5596            }
5597
5598            if (leavePasteSelected) {
5599                setMark(begin);
5600                final Line dotLine = getDotLine();
5601                for (Line line = begin.getLine(); line != null; line = line.nextVisible()) {
5602                    update(line);
5603                    if (line == dotLine)
5604                        break;
5605                }
5606            } else {
5607                // Restore caret column.
5608
addUndo(SimpleEdit.MOVE);
5609                display.setCaretCol(absCaretCol - display.getShift());
5610                moveDotToCaretCol();
5611            }
5612        } else {
5613            if (mark != null)
5614                deleteRegion();
5615            fillToCaret();
5616            Position begin = dot.copy();
5617            addUndo(SimpleEdit.INSERT_STRING);
5618            insertStringInternal(toBeInserted);
5619            moveCaretToDotCol();
5620            if (leavePasteSelected) {
5621                setMark(begin);
5622                final Line dotLine = getDotLine();
5623                for (Line line = begin.getLine(); line != null; line = line.nextVisible()) {
5624                    update(line);
5625                    if (line == dotLine)
5626                        break;
5627                }
5628            }
5629        }
5630        endCompoundEdit(compoundEdit);
5631        buffer.modified();
5632        if (getFormatter().parseBuffer())
5633            buffer.repaint();
5634    }
5635
5636    public void pasteColumn()
5637    {
5638        if (!checkReadOnly())
5639            return;
5640        if (killedColumn == null || killedColumn.length() == 0)
5641            return;
5642        try {
5643            buffer.lockWrite();
5644        }
5645        catch (InterruptedException JavaDoc e) {
5646            Log.error(e);
5647            return;
5648        }
5649        try {
5650            pasteColumnInternal(killedColumn);
5651        }
5652        finally {
5653            buffer.unlockWrite();
5654        }
5655    }
5656
5657    private void pasteColumnInternal(String JavaDoc toBeInserted)
5658    {
5659        Position pos = new Position(dot);
5660        final int col = display.getAbsoluteCaretCol();
5661        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
5662        while (true) {
5663            final int index = toBeInserted.indexOf('\n');
5664            final String JavaDoc s = index >= 0 ? toBeInserted.substring(0, index) : toBeInserted;
5665            if (index >= 0)
5666                toBeInserted = toBeInserted.substring(index + 1);
5667            final Line dotLine = getDotLine();
5668            String JavaDoc text = Utilities.detab(dotLine.getText(), buffer.getTabWidth());
5669            if (text.length() < col)
5670                text = text.concat(Utilities.spaces(col - text.length()));
5671            Debug.assertTrue(text.length() >= col);
5672            final String JavaDoc head = text.substring(0, col);
5673            final String JavaDoc tail = text.substring(col);
5674            addUndo(SimpleEdit.LINE_EDIT);
5675            FastStringBuffer sb = new FastStringBuffer(head);
5676            sb.append(s);
5677            sb.append(tail);
5678            dotLine.setText(sb.toString());
5679            updateInAllEditors(dotLine);
5680            pos = new Position(dotLine, head.length() + s.length());
5681            if (toBeInserted.length() == 0)
5682                break;
5683            if (dotLine.next() == null) {
5684                addUndo(SimpleEdit.MOVE);
5685                dot.setOffset(dotLine.length());
5686                addUndo(SimpleEdit.INSERT_LINE_SEP);
5687                buffer.insertLineSeparator(dot);
5688            } else {
5689                addUndo(SimpleEdit.MOVE);
5690                dot.moveTo(dotLine.next(), 0);
5691            }
5692        }
5693        addUndo(SimpleEdit.MOVE);
5694        dot.moveTo(pos);
5695        moveCaretToDotCol();
5696        endCompoundEdit(compoundEdit);
5697    }
5698
5699    public void insertString(String JavaDoc toBeInserted)
5700    {
5701        if (toBeInserted == null || toBeInserted.length() == 0)
5702            return;
5703        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
5704        if (mark != null)
5705            delete();
5706        fillToCaret();
5707        addUndo(SimpleEdit.INSERT_STRING);
5708        insertStringInternal(toBeInserted);
5709        updateInAllEditors(dot.getLine());
5710        moveCaretToDotCol();
5711        endCompoundEdit(compoundEdit);
5712        if (getFormatter().parseBuffer())
5713            buffer.repaint();
5714    }
5715
5716    public void centerDialog(JDialog JavaDoc d)
5717    {
5718        Dimension JavaDoc parent = frame.getSize();
5719        Dimension JavaDoc window = d.getSize();
5720        Point JavaDoc p = frame.getLocation();
5721        p.translate((parent.width - window.width) / 2,
5722            (parent.height - window.height) / 2);
5723        d.setLocation(p);
5724    }
5725
5726    public boolean confirm(String JavaDoc title, String JavaDoc text)
5727    {
5728        int response = ConfirmDialog.showConfirmDialog(this, text, title);
5729        repaintNow();
5730        return response == RESPONSE_YES;
5731    }
5732
5733    public int confirmAll(String JavaDoc title, String JavaDoc text)
5734    {
5735        int response = ConfirmDialog.showConfirmAllDialog(this, text, title);
5736        repaintNow();
5737        return response;
5738    }
5739
5740    public void killBuffer()
5741    {
5742        try {
5743            if (buffer.isSecondary()) {
5744                buffer.windowClosing();
5745                otherWindow();
5746                unsplitWindow();
5747                currentEditor.maybeKillBuffer(buffer);
5748                restoreFocus();
5749                return;
5750            }
5751            Buffer buf = buffer.getSecondary();
5752            if (buf != null) {
5753                unsplitWindow();
5754                maybeKillBuffer(buf);
5755                return;
5756            }
5757            // Normal buffer.
5758
maybeKillBuffer(buffer);
5759            // If we're left with two editors showing exactly the same thing,
5760
// unsplit the window.
5761
Frame frame = currentEditor.getFrame();
5762            if (frame.getEditorCount() == 2) {
5763                Editor p = frame.getPrimaryEditor();
5764                Editor s = frame.getSecondaryEditor();
5765                boolean unsplit = false;
5766                if (p.getDot() != null && p.getDot().equals(s.getDot())) {
5767                    if (p.getMark() == null && s.getMark() == null)
5768                        unsplit = true;
5769                    else if (p.getMark() != null && p.getMark().equals(s.getMark()))
5770                        unsplit = true;
5771                }
5772                if (unsplit)
5773                    unsplitWindow();
5774            }
5775        }
5776        finally {
5777            Sidebar.refreshSidebarInAllFrames();
5778        }
5779    }
5780
5781    public void maybeKillBuffer(Buffer toBeKilled)
5782    {
5783        if (!bufferList.contains(toBeKilled)) {
5784            Debug.bug("maybeKillBuffer buffer not in list " + toBeKilled);
5785            return;
5786        }
5787
5788        // Don't kill the last buffer if it's a directory.
5789
if (bufferList.size() == 1 && toBeKilled instanceof Directory)
5790            return;
5791
5792        // Cancel background process if any.
5793
BackgroundProcess backgroundProcess = toBeKilled.getBackgroundProcess();
5794        if (backgroundProcess != null) {
5795            Log.debug("maybeKillBuffer calling backgroundProcess.cancel...");
5796            backgroundProcess.cancel();
5797            // backgroundProcess.cancel() may have killed the buffer, so
5798
// verify that it's still in the list.
5799
if (!bufferList.contains(toBeKilled)) {
5800                Log.debug("maybeKillBuffer buffer is no longer in list");
5801                return;
5802            }
5803        }
5804
5805        Mode mode = toBeKilled.getMode();
5806        if (mode == null || mode.confirmClose(this, toBeKilled))
5807            toBeKilled.kill();
5808    }
5809
5810    public void clearStatusText()
5811    {
5812        StatusBar statusBar = getStatusBar();
5813        if (statusBar != null) {
5814            statusBar.setText(null);
5815            statusBar.repaint();
5816        }
5817    }
5818
5819    public void activate(Buffer buf)
5820    {
5821        if (buf == null)
5822            return;
5823        Debug.assertTrue(bufferList.contains(buf));
5824        if (buf == buffer)
5825            return;
5826        if (!buf.initialized())
5827            buf.initialize();
5828        clearStatusText();
5829        if (buffer != null && bufferList.contains(buffer)) {
5830            // Save information about buffer being deactivated.
5831
buffer.autosave();
5832            saveView();
5833            RecentFiles.getInstance().bufferDeactivated(buffer, dot);
5834        }
5835
5836        // Read-only status may have changed. (We could be switching back from
5837
// a shell buffer.)
5838
reactivate(buf);
5839
5840        buf.setLastActivated(System.currentTimeMillis());
5841        if (buf.isLoaded()) {
5842            buffer = buf;
5843            bufferActivated(false);
5844        } else {
5845            setWaitCursor();
5846            int result = LOAD_FAILED;
5847            try {
5848                result = buf.load();
5849            }
5850            catch (OutOfMemoryError JavaDoc e) {
5851                buf.kill();
5852                Sidebar.setUpdateFlagInAllFrames(SIDEBAR_ALL);
5853                MessageDialog.showMessageDialog(this,
5854                                                "Insufficient memory to load buffer",
5855                                                "Error");
5856                return;
5857            }
5858            switch (result) {
5859                case LOAD_COMPLETED:
5860                    buffer = buf;
5861                    bufferActivated(true);
5862                    break;
5863                case LOAD_PENDING:
5864                    buffer = buf;
5865                    buffer.setBusy(true);
5866                    bufferPending();
5867                    break;
5868                case LOAD_FAILED:
5869                    setDefaultCursor();
5870                    buffer = buf;
5871                    bufferActivated(true);
5872                    MessageDialog.showMessageDialog(this,
5873                                                    "Unable to load buffer",
5874                                                    "Error");
5875                    break;
5876                default:
5877                    Debug.assertTrue(false);
5878            }
5879        }
5880    }
5881
5882    public Editor activateInOtherWindow(Buffer buf)
5883    {
5884        return frame.activateInOtherWindow(this, buf);
5885    }
5886
5887    public Editor activateInOtherWindow(Buffer buf, float split)
5888    {
5889        return frame.activateInOtherWindow(this, buf, split);
5890    }
5891
5892    public Editor displayInOtherWindow(Buffer buf)
5893    {
5894        return frame.displayInOtherWindow(this, buf);
5895    }
5896
5897    public void bufferActivated(boolean firstTime)
5898    {
5899        if (buffer.getModeId() == IMAGE_MODE) {
5900            setDot(null);
5901            setMark(null);
5902            display.setTopLine(null);
5903            display.setShift(0);
5904            display.setCaretCol(0);
5905        } else {
5906            findOrCreateView(buffer);
5907            restoreView();
5908
5909            // If the buffer has already been loaded, the caret position will
5910
// be restored correctly.
5911
if (firstTime)
5912                moveCaretToDotCol();
5913        }
5914
5915        if (dot != null && dot.getOffset() > dot.getLineLength()) {
5916            dot.setOffset(dot.getLineLength());
5917            moveCaretToDotCol();
5918        }
5919
5920        frame.updateTitle();
5921        frame.setMenu();
5922        frame.setToolbar();
5923
5924        if (buffer.isBusy())
5925            setWaitCursor();
5926        else
5927            setDefaultCursor();
5928
5929        setUpdateFlag(REFRAME);
5930        reframe();
5931        setUpdateFlag(REPAINT);
5932
5933        RecentFiles.getInstance().bufferActivated(buffer);
5934
5935        if (buffer.isTaggable()) {
5936            tagFileManager.addToQueue(buffer.getCurrentDirectory(),
5937                buffer.getMode());
5938        }
5939
5940        Sidebar.setUpdateFlagInAllFrames(SIDEBAR_ALL);
5941
5942        if (isLispInitialized()) {
5943            if (firstTime)
5944                LispAPI.invokeOpenFileHook(buffer);
5945            LispAPI.invokeBufferActivatedHook(buffer);
5946        }
5947    }
5948
5949    private void bufferPending()
5950    {
5951        // Find or create a view of this buffer.
5952
findOrCreateView(buffer);
5953        restoreView();
5954
5955        frame.updateTitle();
5956        frame.setMenu();
5957        frame.setToolbar();
5958
5959        display.repaint();
5960
5961        Sidebar.setUpdateFlagInAllFrames(SIDEBAR_ALL);
5962        Sidebar sidebar = getSidebar();
5963        if (sidebar != null)
5964            sidebar.setBuffer();
5965    }
5966
5967    // Find or create another frame in which to activate the specified buffer.
5968
public Editor activateInOtherFrame(Buffer buf)
5969    {
5970        Editor ed = null;
5971        if (getEditorCount() == 1) {
5972            ed = createNewFrame();
5973            ed.activate(buf);
5974            ed.getFrame().setVisible(true);
5975            ed.updateDisplay();
5976        } else {
5977            for (int i = 0; i < getEditorCount(); i++) {
5978                ed = getEditor(i);
5979                if (ed != this) {
5980                    ed.activate(buf);
5981                    ed.getFrame().toFront();
5982                    break;
5983                }
5984            }
5985        }
5986        return ed;
5987    }
5988
5989    public void nextFrame()
5990    {
5991        int count = getEditorCount();
5992        if (count > 1) {
5993            Editor ed = null;
5994            for (int i = 0; i < count; i++) {
5995                ed = Editor.getEditor(i);
5996                if (ed == this) {
5997                    if (++i == count)
5998                        i = 0;
5999                    ed = Editor.getEditor(i);
6000                    ed.getFrame().toFront();
6001                    ed.requestFocusLater();
6002                    break;
6003                }
6004            }
6005        }
6006    }
6007
6008    private void requestFocusLater()
6009    {
6010        Runnable JavaDoc r = new Runnable JavaDoc() {
6011            public void run()
6012            {
6013                Editor.this.requestFocus();
6014            }
6015        };
6016        SwingUtilities.invokeLater(r);
6017    }
6018
6019    public void toggleSidebar()
6020    {
6021        frame.frameToggleSidebar();
6022    }
6023
6024    public void sidebarListBuffers()
6025    {
6026        ensureActive();
6027
6028        if (frame.getSidebar() == null)
6029            toggleSidebar();
6030
6031        if (frame.getSidebar() != null)
6032            frame.getSidebar().activateBufferList();
6033    }
6034
6035    public void sidebarListTags()
6036    {
6037        if (!frame.isActive())
6038            return;
6039
6040        if (getMode().getSidebarComponent(this) != null) {
6041            if (frame.getSidebar() == null)
6042                toggleSidebar();
6043            if (frame.getSidebar() != null)
6044                frame.getSidebar().activateNavigationComponent();
6045        }
6046    }
6047
6048    public void toggleToolbar()
6049    {
6050        frame.frameToggleToolbar();
6051    }
6052
6053    public final boolean addUndo(int type)
6054    {
6055        return SimpleEdit.addUndo(this, type);
6056    }
6057
6058    public final boolean addUndoDeleteRegion(Region r)
6059    {
6060        buffer.addEdit(new UndoDeleteRegion(this, r));
6061        return true;
6062    }
6063
6064    public final CompoundEdit JavaDoc beginCompoundEdit()
6065    {
6066        return buffer.beginCompoundEdit();
6067    }
6068
6069    public final void endCompoundEdit(CompoundEdit JavaDoc compoundEdit)
6070    {
6071        buffer.endCompoundEdit(compoundEdit);
6072    }
6073
6074    public void undo()
6075    {
6076        setWaitCursor();
6077        try {
6078            buffer.lockWrite();
6079        }
6080        catch (InterruptedException JavaDoc e) {
6081            Log.error(e);
6082            return;
6083        }
6084        try {
6085            buffer.undo();
6086            checkDotInOtherFrames();
6087            setCurrentCommand(COMMAND_UNDO);
6088        }
6089        catch (Throwable JavaDoc t) {
6090            Log.error(t);
6091        }
6092        finally {
6093            buffer.unlockWrite();
6094            setDefaultCursor();
6095        }
6096    }
6097
6098    public void redo()
6099    {
6100        setWaitCursor();
6101        try {
6102            buffer.lockWrite();
6103        }
6104        catch (InterruptedException JavaDoc e) {
6105            Log.error(e);
6106            return;
6107        }
6108        try {
6109            buffer.redo();
6110            checkDotInOtherFrames();
6111        }
6112        catch (Throwable JavaDoc t) {
6113            Log.error(t);
6114        }
6115        finally {
6116            buffer.unlockWrite();
6117            setDefaultCursor();
6118        }
6119    }
6120
6121    private void checkDotInOtherFrames()
6122    {
6123        if (getEditorCount() > 1) {
6124            for (int i = 0; i < getEditorCount(); i++) {
6125                Editor ed = getEditor(i);
6126                if (ed != this && ed.getBuffer() == buffer) {
6127                    if (ed.getDotOffset() > ed.getDotLine().length()) {
6128                        ed.getDot().setOffset(ed.getDotLine().length());
6129                        ed.moveCaretToDotCol();
6130                        ed.updateDotLine();
6131                    }
6132                }
6133            }
6134        }
6135    }
6136
6137    public final void jumpToLine(int lineNumber)
6138    {
6139        jumpToLine(lineNumber, 0);
6140    }
6141
6142    public void jumpToLine(int lineNumber, int offset)
6143    {
6144        Line line = buffer.getLine(lineNumber);
6145        if (line != null) {
6146            if (offset < 0)
6147                offset = 0;
6148            else if (offset > line.length())
6149                offset = line.length();
6150            moveDotTo(line, offset);
6151            setUpdateFlag(REFRAME);
6152        } else
6153            eob();
6154    }
6155
6156    public void offset()
6157    {
6158        status(String.valueOf(buffer.getAbsoluteOffset(dot)));
6159    }
6160
6161    public void executeCommand()
6162    {
6163        // Use location bar.
6164
locationBar.setLabelText(LocationBar.PROMPT_COMMAND);
6165        HistoryTextField textField = locationBar.getTextField();
6166        textField.setHandler(new ExecuteCommandTextFieldHandler(this, textField));
6167        textField.setHistory(new History("executeCommand.input", 30));
6168        textField.recallLast();
6169        textField.selectAll();
6170        AWTEvent JavaDoc e = dispatcher.getLastEvent();
6171        if (e != null && e.getSource() instanceof MenuItem) {
6172            Runnable JavaDoc r = new Runnable JavaDoc() {
6173                public void run()
6174                {
6175                    setFocusToTextField();
6176                }
6177            };
6178            SwingUtilities.invokeLater(r);
6179        } else
6180            setFocusToTextField();
6181    }
6182
6183    public void executeCommand(String JavaDoc input)
6184    {
6185        executeCommand(input, false);
6186    }
6187
6188    public void executeCommand(String JavaDoc input, final boolean interactive)
6189    {
6190        input = Utilities.trimLeading(input);
6191        if (input.length() == 0)
6192            return;
6193        if (input.charAt(0) == '(') {
6194            // Lisp form.
6195
try {
6196                String JavaDoc result = String.valueOf(Interpreter.evaluate(input));
6197                if (interactive)
6198                    status(result);
6199            }
6200            catch (Throwable JavaDoc t) {
6201                String JavaDoc message = null;
6202                if (t instanceof ConditionThrowable) {
6203                    LispObject obj = ((ConditionThrowable)t).getCondition();
6204                    if (obj instanceof Condition) {
6205                        try {
6206                            message = ((Condition)obj).getConditionReport();
6207                        }
6208                        catch (Throwable JavaDoc ignored) {
6209                            // At least we tried.
6210
}
6211                    }
6212                }
6213                if (message == null || message.length() == 0)
6214                    message = t.getMessage();
6215                if (message != null && message.length() > 0) {
6216                    FastStringBuffer sb = new FastStringBuffer(message);
6217                    sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
6218                    message = sb.toString();
6219                } else
6220                    message = String.valueOf(t);
6221                MessageDialog.showMessageDialog(this, message, "Error");
6222            }
6223            return;
6224        }
6225        int index = input.indexOf('=');
6226        if (index >= 0) {
6227            String JavaDoc key = input.substring(0, index).trim();
6228            if (key.indexOf(' ') < 0 && key.indexOf('\t') < 0) {
6229                String JavaDoc value = input.substring(index+1).trim();
6230                setProperty(key, value);
6231                return;
6232            }
6233        }
6234        String JavaDoc[] array = parseCommand(input);
6235        if (array != null) {
6236            final String JavaDoc command = array[0];
6237            final String JavaDoc parameters = array[1];
6238            Runnable JavaDoc r = new Runnable JavaDoc() {
6239                public void run()
6240                {
6241                    try {
6242                        StatusBar statusBar = getStatusBar();
6243                        statusBar.setText("");
6244                        execute(command, parameters);
6245                        if (interactive && parameters == null) {
6246                            // Suggest key binding if one is available.
6247
Object JavaDoc[] values = getKeyMapping(command);
6248                            Debug.assertTrue(values != null);
6249                            Debug.assertTrue(values.length == 2);
6250                            KeyMapping mapping = (KeyMapping) values[0];
6251                            Mode mode = (Mode) values[1];
6252                            if (mapping != null) {
6253                                String JavaDoc statusText = statusBar.getText();
6254                                boolean append =
6255                                    statusText != null && statusText.length() > 0;
6256                                FastStringBuffer sb = new FastStringBuffer();
6257                                if (append) {
6258                                    sb.append(statusText);
6259                                    sb.append(" ");
6260                                }
6261                                sb.append(command);
6262                                sb.append(" is mapped to ");
6263                                sb.append(mapping.getKeyText());
6264                                if (mode != null) {
6265                                    sb.append(" (");
6266                                    sb.append(mode);
6267                                    sb.append(" mode)");
6268                                } else
6269                                    sb.append(" (global mapping)");
6270                                status(sb.toString());
6271                            }
6272                        }
6273                    }
6274                    catch (NoSuchMethodException JavaDoc e) {
6275                        FastStringBuffer sb =
6276                            new FastStringBuffer("Unknown command \"");
6277                        sb.append(command);
6278                        sb.append('"');
6279                        MessageDialog.showMessageDialog(Editor.this,
6280                            sb.toString(), "Error");
6281                    }
6282                }
6283            };
6284            if (SwingUtilities.isEventDispatchThread()) {
6285                r.run();
6286            } else {
6287                try {
6288                    SwingUtilities.invokeAndWait(r);
6289                }
6290                catch (Throwable JavaDoc t) {
6291                    Log.debug(t);
6292                }
6293            }
6294        }
6295    }
6296
6297    private static String JavaDoc[] parseCommand(String JavaDoc command)
6298    {
6299        command = Utilities.trimLeading(command);
6300        // Command name is terminated by whitespace or '('.
6301
char delimiter = '\0';
6302        int index = -1;
6303        int commandLength = command.length();
6304        for (int i = 0; i < commandLength; i++) {
6305            char c = command.charAt(i);
6306            if (c == '(' || Character.isWhitespace(c)) {
6307                delimiter = c;
6308                index = i;
6309                break;
6310            }
6311        }
6312        String JavaDoc methodName, parameters;
6313        if (index < 0) {
6314            methodName = command;
6315            parameters = null;
6316        } else {
6317            methodName = command.substring(0, index);
6318            parameters = Utilities.trimLeading(command.substring(index));
6319            if (delimiter != '(') {
6320                if (parameters.startsWith("("))
6321                    delimiter = '(';
6322            }
6323            if (delimiter == '(') {
6324                // Strip parens.
6325
int length = parameters.length();
6326                if (length < 2)
6327                    return null; // Error.
6328
if (parameters.charAt(length - 1) != ')')
6329                    return null; // Error.
6330
parameters = parameters.substring(1, length - 1).trim();
6331                length = parameters.length();
6332                if (length == 0)
6333                    parameters = null;
6334                else {
6335                    // Strip required quotes.
6336
if (length < 2)
6337                        return null; // Error.
6338
if (parameters.charAt(0) != '"' || parameters.charAt(length - 1) != '"')
6339                        return null; // Error.
6340
parameters = parameters.substring(1, length - 1); // Done.
6341
}
6342            }
6343        }
6344        String JavaDoc[] array = new String JavaDoc[2];
6345        array[0] = methodName;
6346        array[1] = parameters;
6347        return array;
6348    }
6349
6350    // Set a buffer-specific property.
6351
private void setProperty(String JavaDoc key, String JavaDoc value)
6352    {
6353        Property property = Property.findProperty(key);
6354        if (property == null) {
6355            MessageDialog.showMessageDialog(
6356                "Property \"" + key + "\" not found",
6357                "Error");
6358            return;
6359        }
6360        final boolean succeeded;
6361        if (value.length() == 0) {
6362            succeeded = buffer.removeProperty(property);
6363        } else {
6364            succeeded = buffer.setPropertyFromString(property, value);
6365            if (!succeeded)
6366                invalidPropertyValue(property, value);
6367        }
6368        if (succeeded)
6369            buffer.saveProperties();
6370    }
6371
6372    // No error checking.
6373
public static void setGlobalProperty(String JavaDoc key, String JavaDoc value)
6374    {
6375        if (value == null || value.length() == 0)
6376            prefs.removeProperty(key);
6377        else
6378            prefs.setProperty(key, value);
6379    }
6380
6381    private void invalidPropertyValue(Property property, String JavaDoc value)
6382    {
6383        if (property.isIntegerProperty())
6384            status("Invalid integer value \"" + value + "\"");
6385        else if (property.isBooleanProperty())
6386            status("Invalid boolean value \"" + value + "\"");
6387    }
6388
6389    public void slideIn()
6390    {
6391        slide(buffer.getIndentSize());
6392    }
6393
6394    public void slideOut()
6395    {
6396        slide(-buffer.getIndentSize());
6397    }
6398
6399    private void slide(int amount)
6400    {
6401        if (!checkReadOnly())
6402            return;
6403        Region r = mark != null ? new Region(this) : null;
6404        if (r != null && (r.getBeginOffset() != 0 || r.getEndOffset() != 0))
6405            return; // If a block is marked, it must be a block of full lines.
6406
try {
6407            buffer.lockWrite();
6408        }
6409        catch (InterruptedException JavaDoc e) {
6410            Log.error(e);
6411            return;
6412        }
6413        try {
6414            if (r == null) {
6415                CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
6416                int dotCol = getDotCol();
6417                int oldIndent = buffer.getIndentation(getDotLine());
6418                int newIndent = oldIndent + amount;
6419                addUndo(SimpleEdit.LINE_EDIT);
6420                buffer.setIndentation(getDotLine(), newIndent);
6421                updateInAllEditors(getDotLine());
6422                if (dotCol < oldIndent) {
6423                    // Caret was originally in indentation area. Move caret to
6424
// start of text. This ensures that the caret is on an
6425
// actual character, in case the indentation got entabbed.
6426
moveDotToCol(newIndent);
6427                } else {
6428                    // Move caret with text.
6429
display.setCaretCol(display.getCaretCol() + amount);
6430                    moveDotToCaretCol();
6431                }
6432                endCompoundEdit(compoundEdit);
6433                buffer.modified();
6434            } else {
6435                // If a block is marked, it must be a block of full lines.
6436
CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
6437                Position saved = new Position(dot);
6438                Line line = r.getBeginLine();
6439                while (line != r.getEndLine()) {
6440                    addUndo(SimpleEdit.MOVE);
6441                    dot.moveTo(line, 0);
6442                    addUndo(SimpleEdit.LINE_EDIT);
6443                    buffer.setIndentation(getDotLine(),
6444                        buffer.getIndentation(getDotLine()) + amount);
6445                    updateInAllEditors(getDotLine());
6446                    line = line.next();
6447                }
6448                addUndo(SimpleEdit.MOVE);
6449                dot = saved;
6450                endCompoundEdit(compoundEdit);
6451                buffer.modified();
6452            }
6453        }
6454        finally {
6455            buffer.unlockWrite();
6456        }
6457    }
6458
6459    public void dirHome()
6460    {
6461        if (buffer instanceof Directory)
6462            ((Directory) buffer).home();
6463    }
6464
6465    public void dirTagFile()
6466    {
6467        if (buffer instanceof Directory)
6468            ((Directory) buffer).tagFileAtDot();
6469    }
6470
6471    public void dirBrowseFile()
6472    {
6473        if (buffer instanceof Directory && !buffer.getFile().isRemote()) {
6474            Directory d = (Directory) buffer;
6475            d.browseFileAtDot();
6476        }
6477    }
6478
6479    public void dirDeleteFiles()
6480    {
6481        if (mark != null && getMarkLine() != getDotLine()) {
6482            MessageDialog.showMessageDialog(this,
6483                "This operation is not supported with multi-line text selections.",
6484                "Delete Files");
6485            return;
6486        }
6487        if (buffer instanceof Directory) {
6488            if (buffer.getFile() instanceof SshFile) {
6489                MessageDialog.showMessageDialog(this, "Deletions are not yet supported in ssh directory buffers.", "Error");
6490                return;
6491            }
6492            ((Directory)buffer).deleteFiles();
6493        }
6494    }
6495
6496    public void dirCopyFile()
6497    {
6498        if (buffer instanceof Directory && buffer.getFile().isLocal())
6499            ((Directory) buffer).copyFileAtDot();
6500    }
6501
6502    public void dirGetFile()
6503    {
6504        if (buffer instanceof Directory && buffer.getFile() instanceof FtpFile)
6505            ((Directory) buffer).getFileAtDot();
6506    }
6507
6508    public void dirMoveFile()
6509    {
6510        if (buffer instanceof Directory && buffer.getFile().isLocal())
6511            ((Directory) buffer).moveFileAtDot();
6512    }
6513
6514    public void dirRescan()
6515    {
6516        if (buffer instanceof Directory) {
6517            setWaitCursor();
6518            ((Directory) buffer).rescan();
6519            setDefaultCursor();
6520        }
6521    }
6522
6523    public void dirHomeDir()
6524    {
6525        File homeDir = File.getInstance(Utilities.getUserHome());
6526        if (buffer instanceof Directory) {
6527            if (!buffer.getFile().equals(homeDir))
6528                ((Directory) buffer).changeDirectory(homeDir);
6529        } else {
6530            Buffer buf = getBuffer(homeDir);
6531            if (buf != null) {
6532                makeNext(buf);
6533                activate(buf);
6534            }
6535        }
6536    }
6537
6538    public void dirUpDir()
6539    {
6540        if (buffer instanceof Directory)
6541            ((Directory) buffer).upDir();
6542    }
6543
6544    public void setFocusToTextField()
6545    {
6546        frame.setFocus(locationBar.getTextField());
6547    }
6548
6549    public void wrapRegion()
6550    {
6551        if (!checkReadOnly())
6552            return;
6553        if (dot == null || mark == null)
6554            return;
6555        // Must be line block.
6556
if (dot.getOffset() != 0 || mark.getOffset() != 0)
6557            return;
6558        new WrapText(this).wrapRegion();
6559    }
6560
6561    public void wrapParagraph()
6562    {
6563        if (!checkReadOnly())
6564            return;
6565        new WrapText(this).wrapParagraph();
6566    }
6567
6568    public void unwrapParagraph()
6569    {
6570        if (!checkReadOnly())
6571            return;
6572        new WrapText(this).unwrapParagraph();
6573    }
6574
6575    public void wrapParagraphsInRegion()
6576    {
6577        if (!checkReadOnly())
6578            return;
6579        new WrapText(this).wrapParagraphsInRegion();
6580    }
6581
6582    public void visibleTabs()
6583    {
6584        tabsAreVisible = !tabsAreVisible;
6585        if (tabsAreVisible)
6586            status("Tabs are visible");
6587        else
6588            status("Tabs are not visible");
6589        for (int i = 0; i < getEditorCount(); i++) {
6590            Editor ed = getEditor(i);
6591            ed.getDisplay().repaint();
6592        }
6593    }
6594
6595    public void insertBraces()
6596    {
6597        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
6598        insertChar('{');
6599        indentLine();
6600        eol();
6601        newlineAndIndent();
6602        insertChar('}');
6603        indentLine();
6604        up();
6605        eol();
6606        newlineAndIndent();
6607        endCompoundEdit(compoundEdit);
6608    }
6609
6610    public void insertParentheses()
6611    {
6612        if (!checkReadOnly())
6613            return;
6614        boolean parensRequireSpaces =
6615            buffer.getBooleanProperty(Property.PARENS_REQUIRE_SPACES);
6616        CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
6617        if (mark != null) {
6618            Position begin, end;
6619            if (mark.isBefore(dot)) {
6620                begin = new Position(mark);
6621                end = new Position(dot);
6622            } else {
6623                begin = new Position(dot);
6624                end = new Position(mark);
6625            }
6626            addUndo(SimpleEdit.MOVE);
6627            setMark(null);
6628            dot.moveTo(end);
6629            if (parensRequireSpaces)
6630                insertChar(' ');
6631            insertChar(')');
6632            addUndo(SimpleEdit.MOVE);
6633            dot.moveTo(begin);
6634            insertChar('(');
6635            if (parensRequireSpaces)
6636                insertChar(' ');
6637        } else {
6638            fillToCaret();
6639            addUndo(SimpleEdit.INSERT_STRING);
6640            insertStringInternal(parensRequireSpaces ? "( )" : "()");
6641            addUndo(SimpleEdit.MOVE);
6642            dot.skip(parensRequireSpaces ? -2 : -1);
6643        }
6644        moveCaretToDotCol();
6645        endCompoundEdit(compoundEdit);
6646    }
6647
6648    public void movePastCloseAndReindent()
6649    {
6650        Position pos = new Position(dot);
6651        int count = 1;
6652        while (pos.next()) {
6653            char c = pos.getChar();
6654            if (c == '(')
6655                ++count;
6656            else if (c == ')')
6657                --count;
6658            if (count == 0)
6659                break;
6660        }
6661        if (count == 0) {
6662            pos.next();
6663            updateDotLine();
6664            CompoundEdit JavaDoc compoundEdit = beginCompoundEdit();
6665            addUndo(SimpleEdit.MOVE);
6666            dot.moveTo(pos);
6667            updateDotLine();
6668            newlineAndIndent();
6669            endCompoundEdit(compoundEdit);
6670        }
6671    }
6672
6673    public final void updateDotLine()
6674    {
6675        display.lineChanged(dot.getLine());
6676    }
6677
6678    // Adds line to changed line list of current frame only.
6679
public final void update(Line line)
6680    {
6681        display.lineChanged(line);
6682    }
6683
6684    /**
6685     * Adds line to changed line list of all editors in which the current
6686     * buffer is displayed.
6687     *
6688     * @param line the line
6689     */

6690    public static void updateInAllEditors(Line line)
6691    {
6692        if (line != null) {
6693            for (EditorIterator it = new EditorIterator(); it.hasNext();) {
6694                Editor ed = it.nextEditor();
6695                if (ed.getBuffer() == currentEditor.getBuffer())
6696                    ed.getDisplay().lineChanged(line);
6697            }
6698        }
6699    }
6700
6701    /**
6702     * Adds line to changed line list of all editors in which the specified
6703     * buffer is displayed.
6704     *
6705     * @param buffer the buffer
6706     * @param line the line
6707     */

6708    public static void updateInAllEditors(Buffer buffer, Line line)
6709    {
6710        if (line != null) {
6711            for (EditorIterator it = new