0001: /*
0002: * Editor.java
0003: *
0004: * Copyright (C) 1998-2004 Peter Graves
0005: * $Id: Editor.java,v 1.130 2004/09/19 18:27:45 piso Exp $
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License
0009: * as published by the Free Software Foundation; either version 2
0010: * of the License, or (at your option) any later version.
0011: *
0012: * This program is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015: * GNU General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * along with this program; if not, write to the Free Software
0019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0020: */
0021:
0022: package org.armedbear.j;
0023:
0024: import gnu.regexp.RE;
0025: import gnu.regexp.REException;
0026: import gnu.regexp.REMatch;
0027: import gnu.regexp.UncheckedRE;
0028: import java.awt.AWTEvent;
0029: import java.awt.BorderLayout;
0030: import java.awt.Cursor;
0031: import java.awt.Dimension;
0032: import java.awt.Point;
0033: import java.awt.Rectangle;
0034: import java.awt.Toolkit;
0035: import java.awt.datatransfer.DataFlavor;
0036: import java.awt.datatransfer.Transferable;
0037: import java.awt.dnd.DropTarget;
0038: import java.awt.event.ComponentEvent;
0039: import java.awt.event.ComponentListener;
0040: import java.awt.event.KeyEvent;
0041: import java.awt.event.MouseEvent;
0042: import java.awt.event.MouseWheelEvent;
0043: import java.awt.event.MouseWheelListener;
0044: import java.awt.event.WindowEvent;
0045: import java.io.BufferedReader;
0046: import java.io.BufferedWriter;
0047: import java.io.IOException;
0048: import java.io.InputStreamReader;
0049: import java.io.OutputStream;
0050: import java.io.OutputStreamWriter;
0051: import java.io.StringReader;
0052: import java.io.UnsupportedEncodingException;
0053: import java.lang.reflect.Method;
0054: import java.net.ConnectException;
0055: import java.net.MalformedURLException;
0056: import java.net.Socket;
0057: import java.text.SimpleDateFormat;
0058: import java.util.ArrayList;
0059: import java.util.Date;
0060: import java.util.Enumeration;
0061: import java.util.Hashtable;
0062: import java.util.List;
0063: import java.util.Properties;
0064: import java.util.Stack;
0065: import java.util.StringTokenizer;
0066: import java.util.Vector;
0067: import javax.swing.Box;
0068: import javax.swing.BoxLayout;
0069: import javax.swing.FocusManager;
0070: import javax.swing.JComponent;
0071: import javax.swing.JDialog;
0072: import javax.swing.JLabel;
0073: import javax.swing.JMenuBar;
0074: import javax.swing.JPanel;
0075: import javax.swing.JPopupMenu;
0076: import javax.swing.SwingUtilities;
0077: import javax.swing.undo.CompoundEdit;
0078: import org.armedbear.j.mail.MailCommands;
0079: import org.armedbear.j.mail.MailboxURL;
0080: import org.armedbear.lisp.Condition;
0081: import org.armedbear.lisp.ConditionThrowable;
0082: import org.armedbear.lisp.Interpreter;
0083: import org.armedbear.lisp.Lisp;
0084: import org.armedbear.lisp.LispObject;
0085: import org.armedbear.lisp.LispThread;
0086:
0087: public final class Editor extends JPanel implements Constants,
0088: ComponentListener, MouseWheelListener {
0089: private static final long startTimeMillis = System
0090: .currentTimeMillis();
0091:
0092: private static boolean debug = false;
0093: private static boolean saveSession = true;
0094:
0095: static File portfile;
0096:
0097: private static EditorList editorList = new EditorList();
0098:
0099: private static PendingOperations pendingOperations = new PendingOperations();
0100:
0101: private static Editor currentEditor;
0102:
0103: private static KillRing killRing = new KillRing();
0104:
0105: public static final KillRing getKillRing() {
0106: return killRing;
0107: }
0108:
0109: private static String killedColumn;
0110:
0111: private static SessionProperties sessionProperties;
0112:
0113: public static SessionProperties getSessionProperties() {
0114: return sessionProperties;
0115: }
0116:
0117: private static final Preferences prefs = new Preferences();
0118:
0119: public static final Preferences preferences() {
0120: return prefs;
0121: }
0122:
0123: private static boolean isRecordingMacro;
0124:
0125: public static synchronized boolean isRecordingMacro() {
0126: return isRecordingMacro;
0127: }
0128:
0129: public static synchronized void setRecordingMacro(boolean b) {
0130: isRecordingMacro = b;
0131: }
0132:
0133: static String lookAndFeel;
0134:
0135: private Buffer buffer;
0136:
0137: private final Display display;
0138: private final Dispatcher dispatcher;
0139: private final Frame frame;
0140:
0141: private Search lastSearch;
0142:
0143: public final Search getLastSearch() {
0144: return lastSearch;
0145: }
0146:
0147: public final void setLastSearch(Search search) {
0148: lastSearch = search;
0149: }
0150:
0151: // The current position in the buffer (that is, in the actual text).
0152: private Position dot;
0153:
0154: // The position of the other end of the selection, if any,
0155: private Position mark;
0156:
0157: private Selection selection;
0158: private boolean isColumnSelection;
0159:
0160: Hashtable views = new Hashtable();
0161:
0162: // BUG! This stuff should be factored somehow...
0163: private int currentCommand = COMMAND_NOTHING;
0164: private int lastCommand = COMMAND_NOTHING;
0165:
0166: public final int getCurrentCommand() {
0167: return currentCommand;
0168: }
0169:
0170: public final void setCurrentCommand(int command) {
0171: currentCommand = command;
0172: }
0173:
0174: public final int getLastCommand() {
0175: return lastCommand;
0176: }
0177:
0178: public final void setLastCommand(int command) {
0179: lastCommand = command;
0180: }
0181:
0182: private static Marker[] bookmarks = new Marker[11];
0183:
0184: private static TagFileManager tagFileManager;
0185:
0186: private static boolean tabsAreVisible = false;
0187:
0188: public static final boolean tabsAreVisible() {
0189: return tabsAreVisible;
0190: }
0191:
0192: static boolean isMenuSelected = false;
0193:
0194: DirectoryTree localDirectoryTree;
0195:
0196: private static ModeList modeList;
0197:
0198: public static final ModeList getModeList() {
0199: if (modeList == null)
0200: modeList = ModeList.getInstance();
0201: return modeList;
0202: }
0203:
0204: private static final BufferList bufferList = new BufferList();
0205:
0206: public static final BufferList getBufferList() {
0207: return bufferList;
0208: }
0209:
0210: public static long getStartTimeMillis() {
0211: return startTimeMillis;
0212: }
0213:
0214: private static String when() {
0215: return String.valueOf(System.currentTimeMillis()
0216: - startTimeMillis)
0217: + " ms";
0218: }
0219:
0220: public static void main(String[] args) {
0221: final File currentDir = File.getInstance(System
0222: .getProperty("user.dir"));
0223: boolean forceNewInstance = false;
0224: boolean restoreSession = true;
0225: boolean startServer = true;
0226: int quick = 0;
0227: File userHomeDir = null;
0228: List files = null;
0229:
0230: // Process command line.
0231: for (int i = 0; i < args.length; i++) {
0232: final String arg = args[i];
0233: if (arg.startsWith("-")) {
0234: if (arg.equals("-h") || arg.equals("-help")
0235: || arg.equals("--help")) {
0236: usage();
0237: System.exit(0);
0238: }
0239: if (arg.equals("-version")) {
0240: version();
0241: System.exit(0);
0242: }
0243: if (arg.equals("-d") || arg.equals("--debug")) {
0244: debug = true;
0245: continue;
0246: }
0247: if (arg.equals("-q")) {
0248: if (quick < 1)
0249: quick = 1;
0250: continue;
0251: }
0252: if (arg.equals("-n") || arg.equals("--no-restore")) {
0253: restoreSession = false;
0254: continue;
0255: }
0256: if (arg.equals("-session")) {
0257: if (i < args.length - 1)
0258: sessionName = args[++i];
0259: continue;
0260: }
0261: if (arg.equals("--force-new-instance")) {
0262: forceNewInstance = true;
0263: continue;
0264: }
0265: if (arg.equals("--no-session")) {
0266: restoreSession = false;
0267: saveSession = false;
0268: continue;
0269: }
0270: if (arg.equals("--no-server")) {
0271: startServer = false;
0272: continue;
0273: }
0274: if (arg.startsWith("--home")) {
0275: String home = null;
0276: if (arg.equals("--home")) {
0277: if (i < args.length - 1)
0278: home = args[++i];
0279: } else if (arg.startsWith("--home="))
0280: home = arg.substring(7);
0281: else
0282: unknown(arg);
0283:
0284: if (home == null || home.length() == 0)
0285: fatal("Option \"--home\" requires an argument.");
0286:
0287: userHomeDir = File.getInstance(currentDir, home);
0288:
0289: if (userHomeDir == null
0290: || !userHomeDir.isDirectory()) {
0291: fatal("Specified home directory \""
0292: + userHomeDir.canonicalPath()
0293: + "\" does not exist.");
0294: }
0295:
0296: if (!userHomeDir.canWrite()) {
0297: fatal("Specified home directory \""
0298: + userHomeDir.canonicalPath()
0299: + "\" is not writable.");
0300: }
0301:
0302: // Specified directory is OK.
0303: Utilities.setUserHome(userHomeDir.canonicalPath());
0304:
0305: continue;
0306: }
0307: // If we get here, it's an unknown option.
0308: unknown(arg);
0309: } else {
0310: // It's a file to be opened.
0311: if (files == null)
0312: files = new ArrayList();
0313: files.add(arg);
0314: }
0315: }
0316:
0317: // At this point the user has had a chance to tell us where his home
0318: // directory is.
0319: Directories.initialize(userHomeDir);
0320:
0321: boolean alreadyRunning = false;
0322: portfile = File.getInstance(Directories.getEditorDirectory(),
0323: "port");
0324: if (portfile.exists()) {
0325: try {
0326: BufferedReader in = new BufferedReader(
0327: new InputStreamReader(portfile.getInputStream()));
0328: String s = in.readLine();
0329: in.close();
0330:
0331: int port = Integer.parseInt(s);
0332:
0333: Socket socket = new Socket("localhost", port);
0334:
0335: // No ConnectException. We found a running instance.
0336: alreadyRunning = true;
0337:
0338: if (!forceNewInstance) {
0339: BufferedWriter out = new BufferedWriter(
0340: new OutputStreamWriter(socket
0341: .getOutputStream()));
0342: File dir = File.getInstance(System
0343: .getProperty("user.dir"));
0344: out.write(dir.canonicalPath());
0345: out.newLine();
0346: if (files != null) {
0347: for (int i = 0; i < files.size(); i++) {
0348: out.write((String) files.get(i));
0349: out.newLine();
0350: }
0351: }
0352: out.flush();
0353: out.close();
0354: socket.close();
0355: System.exit(0);
0356: }
0357: } catch (ConnectException e) {
0358: portfile.delete();
0359: } catch (IOException e) {
0360: Log.error(e);
0361: }
0362: }
0363:
0364: loadPreferences();
0365: Log.initialize();
0366: Directories.moveUnsentMessagesToDraftsFolder();
0367: loadExtensions();
0368: if (quick == 0) {
0369: runStartupScript();
0370: }
0371: DefaultLookAndFeel.setLookAndFeel();
0372:
0373: sessionProperties = new SessionProperties();
0374:
0375: if (!alreadyRunning)
0376: Autosave.recover();
0377:
0378: tagFileManager = new TagFileManager();
0379:
0380: setCurrentEditor(new Editor(null));
0381:
0382: currentEditor.getFrame().updateControls();
0383:
0384: // With Java 1.4, we only need to do this to support the key-pressed
0385: // hook.
0386: FocusManager.setCurrentManager(new CustomFocusManager());
0387:
0388: currentEditor.getFrame().placeWindow();
0389:
0390: Buffer toBeActivated = null;
0391:
0392: if (restoreSession) {
0393: Session session = null;
0394: if (sessionName != null)
0395: session = Session.getSession(sessionName);
0396: if (session == null)
0397: session = Session.getDefaultSession();
0398: toBeActivated = session.restore();
0399: }
0400:
0401: if (files != null) {
0402: ArrayList list = new ArrayList();
0403: list.add(currentDir.canonicalPath());
0404: for (int i = 0; i < files.size(); i++)
0405: list.add(files.get(i));
0406: Buffer buf = currentEditor.openFiles(list);
0407: if (buf != null) {
0408: Debug.assertTrue(bufferList.contains(buf));
0409: toBeActivated = buf;
0410: }
0411: }
0412:
0413: if (toBeActivated == null)
0414: toBeActivated = new Directory(currentDir);
0415:
0416: currentEditor.activate(toBeActivated);
0417:
0418: if (startServer)
0419: Server.startServer();
0420:
0421: Runnable r = new Runnable() {
0422: public void run() {
0423: currentEditor.getFrame().setVisible(true);
0424: Sidebar sidebar = currentEditor.getSidebar();
0425: if (sidebar != null)
0426: sidebar.setUpdateFlag(SIDEBAR_ALL);
0427: }
0428: };
0429: SwingUtilities.invokeLater(r);
0430:
0431: Log.debug("leaving main " + when());
0432: }
0433:
0434: private static final void usage() {
0435: version();
0436: System.out.println("Usage: j [options] [+linenum] file");
0437: System.out.println("Options:");
0438: System.out.println(" -h, -help");
0439: System.out.println(" -d, --debug");
0440: System.out.println(" -n, --no-restore");
0441: System.out.println(" -version");
0442: System.out.println(" --force-new-instance");
0443: System.out.println(" --no-session");
0444: System.out.println(" --no-server");
0445: System.out.println(" --home=directory");
0446: }
0447:
0448: private static final void version() {
0449: String longVersionString = Version.getLongVersionString();
0450: if (longVersionString != null)
0451: System.out.println(longVersionString);
0452: String snapshotInformation = Version.getSnapshotInformation();
0453: if (snapshotInformation != null)
0454: System.out.println(snapshotInformation);
0455: }
0456:
0457: public static final void fatal(String message) {
0458: System.err.println(message);
0459: System.exit(1);
0460: }
0461:
0462: private static final void unknown(String arg) {
0463: usage();
0464: fatal("Unknown option \"" + arg + "\"");
0465: }
0466:
0467: public Editor(Frame f) {
0468: display = new Display(this );
0469: dispatcher = new Dispatcher(this );
0470: init();
0471: frame = f != null ? f : new Frame(this );
0472: }
0473:
0474: private void init() {
0475: // Add this editor to the global editor list.
0476: editorList.add(this );
0477:
0478: setLayout(new BorderLayout());
0479: display.setDoubleBuffered(true);
0480: add(display, BorderLayout.CENTER);
0481:
0482: new DropTarget(display, dispatcher);
0483:
0484: addLocationBar();
0485: addVerticalScrollBar();
0486: addHorizontalScrollBar();
0487:
0488: display.addKeyListener(dispatcher);
0489: display.addMouseListener(dispatcher);
0490: display.addMouseMotionListener(dispatcher);
0491:
0492: addMouseWheelListener(this );
0493: addComponentListener(this );
0494: }
0495:
0496: public static final boolean isDebugEnabled() {
0497: return debug;
0498: }
0499:
0500: public static final boolean isMailEnabled() {
0501: if (!prefs
0502: .getBooleanProperty(Property.ENABLE_EXPERIMENTAL_FEATURES))
0503: return false;
0504: if (!prefs.getBooleanProperty(Property.ENABLE_MAIL))
0505: return false;
0506: // Mail address must be configured!
0507: if (prefs.getStringProperty(Property.USER_MAIL_ADDRESS) == null)
0508: return false;
0509: return true;
0510: }
0511:
0512: private LocationBar locationBar;
0513:
0514: public final LocationBar getLocationBar() {
0515: return locationBar;
0516: }
0517:
0518: public final HistoryTextField getLocationBarTextField() {
0519: return locationBar == null ? null : locationBar.getTextField();
0520: }
0521:
0522: public void addLocationBar() {
0523: if (locationBar == null) {
0524: locationBar = new LocationBar(this );
0525: add(locationBar, BorderLayout.NORTH);
0526: }
0527: }
0528:
0529: public void removeLocationBar() {
0530: if (locationBar != null) {
0531: remove(locationBar);
0532: locationBar = null;
0533: }
0534: }
0535:
0536: public void updateLocation() {
0537: if (locationBar != null) {
0538: HistoryTextField textField = locationBar.getTextField();
0539: if (textField == null
0540: || textField != frame.getFocusedComponent())
0541: locationBar.update();
0542: } else
0543: Debug.bug();
0544: }
0545:
0546: public boolean isPrimaryEditor() {
0547: return frame.isPrimaryEditor(this );
0548: }
0549:
0550: public final Editor getOtherEditor() {
0551: return isPrimaryEditor() ? frame.getSecondaryEditor() : frame
0552: .getPrimaryEditor();
0553: }
0554:
0555: public static int indexOf(Editor editor) {
0556: for (int i = getEditorCount() - 1; i >= 0; i--) {
0557: if (editor == getEditor(i))
0558: return i;
0559: }
0560:
0561: return -1;
0562: }
0563:
0564: public static final EditorList getEditorList() {
0565: return editorList;
0566: }
0567:
0568: public static final int getEditorCount() {
0569: return editorList.size();
0570: }
0571:
0572: public static final Editor getEditor(int i) {
0573: return editorList.get(i);
0574: }
0575:
0576: public static final void removeEditor(Editor editor) {
0577: editorList.remove(editor);
0578: }
0579:
0580: public final Frame getFrame() {
0581: return frame;
0582: }
0583:
0584: static Vector frames = new Vector();
0585:
0586: public static int indexOf(Frame frame) {
0587: for (int i = getFrameCount() - 1; i >= 0; i--) {
0588: if (frame == getFrame(i))
0589: return i;
0590: }
0591:
0592: return -1;
0593: }
0594:
0595: public static final int getFrameCount() {
0596: return frames.size();
0597: }
0598:
0599: public static final Frame getFrame(int i) {
0600: if (i >= 0 && i < frames.size())
0601: return (Frame) frames.get(i);
0602: return null;
0603: }
0604:
0605: public final Buffer getBuffer() {
0606: return buffer;
0607: }
0608:
0609: public final Mode getMode() {
0610: return buffer.getMode();
0611: }
0612:
0613: public final int getModeId() {
0614: return buffer.getModeId();
0615: }
0616:
0617: public final Formatter getFormatter() {
0618: return buffer.getFormatter();
0619: }
0620:
0621: public final Display getDisplay() {
0622: return display;
0623: }
0624:
0625: public final Dispatcher getDispatcher() {
0626: return dispatcher;
0627: }
0628:
0629: public static final TagFileManager getTagFileManager() {
0630: return tagFileManager;
0631: }
0632:
0633: public final Sidebar getSidebar() {
0634: return frame.getSidebar();
0635: }
0636:
0637: public final StatusBar getStatusBar() {
0638: return frame.getStatusBar();
0639: }
0640:
0641: public static final PendingOperations getPendingOperations() {
0642: return pendingOperations;
0643: }
0644:
0645: private VerticalScrollBar verticalScrollBar;
0646:
0647: private VerticalScrollBarListener verticalScrollBarListener;
0648:
0649: public void addVerticalScrollBar() {
0650: if (verticalScrollBar == null) {
0651: verticalScrollBar = new VerticalScrollBar(this );
0652: verticalScrollBar.setMinimum(0);
0653: add(verticalScrollBar, BorderLayout.EAST);
0654: verticalScrollBarListener = new VerticalScrollBarListener(
0655: this , verticalScrollBar);
0656: verticalScrollBar
0657: .addAdjustmentListener(verticalScrollBarListener);
0658: }
0659: }
0660:
0661: public void removeVerticalScrollBar() {
0662: if (verticalScrollBar != null) {
0663: if (verticalScrollBarListener == null) {
0664: verticalScrollBar
0665: .removeAdjustmentListener(verticalScrollBarListener);
0666: verticalScrollBarListener = null;
0667: }
0668: remove(verticalScrollBar);
0669: verticalScrollBar = null;
0670: }
0671: }
0672:
0673: private HorizontalScrollBar horizontalScrollBar;
0674:
0675: private HorizontalScrollBarListener horizontalScrollBarListener;
0676:
0677: public void addHorizontalScrollBar() {
0678: if (horizontalScrollBar == null) {
0679: horizontalScrollBar = new HorizontalScrollBar(this );
0680: horizontalScrollBar.setMinimum(0);
0681: JPanel panel = new JPanel();
0682: panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
0683: panel.add(horizontalScrollBar);
0684: final int height = horizontalScrollBar.getPreferredSize().height;
0685: panel.add(Box
0686: .createRigidArea(new Dimension(height, height)));
0687: add(panel, BorderLayout.SOUTH);
0688: horizontalScrollBarListener = new HorizontalScrollBarListener(
0689: this );
0690: horizontalScrollBar
0691: .addAdjustmentListener(horizontalScrollBarListener);
0692: }
0693: }
0694:
0695: public void removeHorizontalScrollBar() {
0696: if (horizontalScrollBar != null) {
0697: if (horizontalScrollBarListener == null) {
0698: horizontalScrollBar
0699: .removeAdjustmentListener(horizontalScrollBarListener);
0700: horizontalScrollBarListener = null;
0701: }
0702: // Remove JPanel containing scroll bar.
0703: remove(horizontalScrollBar.getParent());
0704: horizontalScrollBar = null;
0705: }
0706: }
0707:
0708: public static synchronized final Editor currentEditor() {
0709: return currentEditor;
0710: }
0711:
0712: public static synchronized final void setCurrentEditor(Editor editor) {
0713: Editor oldCurrentEditor = currentEditor;
0714: currentEditor = editor;
0715: editor.getFrame().setCurrentEditor(editor);
0716: if (currentEditor != oldCurrentEditor) {
0717: if (currentEditor != null)
0718: currentEditor.getLocationBar().repaint();
0719: if (oldCurrentEditor != null)
0720: oldCurrentEditor.getLocationBar().repaint();
0721: if (isLispInitialized())
0722: LispAPI.invokeBufferActivatedHook(currentEditor
0723: .getBuffer());
0724: }
0725: }
0726:
0727: public static synchronized final Buffer currentBuffer() {
0728: return currentEditor.buffer;
0729: }
0730:
0731: public static synchronized final Frame getCurrentFrame() {
0732: return currentEditor.getFrame();
0733: }
0734:
0735: public static Editor createNewFrame() {
0736: Editor ed = new Editor(null);
0737: ed.getFrame().updateControls();
0738: ed.getFrame().placeWindow();
0739: return ed;
0740: }
0741:
0742: public void newFrame() {
0743: saveView();
0744: Editor ed = createNewFrame();
0745: ed.activate(buffer);
0746: ed.getFrame().setVisible(true);
0747: setCurrentEditor(ed);
0748: ed.updateDisplay();
0749: display.repaint();
0750: Runnable r = new Runnable() {
0751: public void run() {
0752: currentEditor.setFocusToDisplay();
0753: }
0754: };
0755: SwingUtilities.invokeLater(r);
0756: }
0757:
0758: public View getCurrentView() {
0759: return (View) views.get(buffer);
0760: }
0761:
0762: // Might return null.
0763: public final View getView(SystemBuffer buf) {
0764: return (View) views.get(buf);
0765: }
0766:
0767: public final void setView(SystemBuffer buf, View view) {
0768: views.put(buf, view);
0769: }
0770:
0771: public void removeView(SystemBuffer buf) {
0772: views.remove(buf);
0773: }
0774:
0775: // Find or create a view of buf.
0776: private View findOrCreateView(Buffer buf) {
0777: View view = (View) views.get(buf);
0778: if (view == null) {
0779: view = buf.getLastView();
0780: if (view == null)
0781: view = buf.getInitialView();
0782: views.put(buf, view);
0783: }
0784: return view;
0785: }
0786:
0787: public final void saveView() {
0788: buffer.saveView(this );
0789: }
0790:
0791: private final void restoreView() {
0792: buffer.restoreView(this );
0793: }
0794:
0795: public final Position getDot() {
0796: return dot;
0797: }
0798:
0799: public final Position getDotCopy() {
0800: return dot != null ? new Position(dot) : null;
0801: }
0802:
0803: public final void setDot(Position pos) {
0804: dot = pos;
0805: }
0806:
0807: public final void setDot(Line line, int offset) {
0808: dot = new Position(line, offset);
0809: }
0810:
0811: public void setDot(int lineNumber, int offset) {
0812: Line line = buffer.getLine(lineNumber);
0813: if (line != null)
0814: setDot(line, offset);
0815: }
0816:
0817: public final Line getDotLine() {
0818: return dot.getLine();
0819: }
0820:
0821: public final int getDotLineNumber() {
0822: return dot.lineNumber();
0823: }
0824:
0825: public final int getDotOffset() {
0826: return dot.getOffset();
0827: }
0828:
0829: public final Selection getSelection() {
0830: return selection;
0831: }
0832:
0833: public final void setSelection(Selection selection) {
0834: this .selection = selection;
0835: }
0836:
0837: public final Position getMark() {
0838: return mark;
0839: }
0840:
0841: public void setMarkAtDot() {
0842: setMark(new Position(dot));
0843: selection = new Selection();
0844: }
0845:
0846: public void setMark(Position pos) {
0847: mark = pos;
0848: if (mark == null) {
0849: selection = null;
0850: setColumnSelection(false);
0851: }
0852: }
0853:
0854: public void setMark(int lineNumber, int offset) {
0855: Line line = buffer.getLine(lineNumber);
0856: if (line != null)
0857: setMark(new Position(line, offset));
0858: }
0859:
0860: public final void setColumnSelection(boolean b) {
0861: isColumnSelection = b;
0862: }
0863:
0864: public final boolean isColumnSelection() {
0865: return isColumnSelection;
0866: }
0867:
0868: public final void notSupportedForColumnSelections() {
0869: MessageDialog.showMessageDialog(this ,
0870: "Operation not supported for column selections",
0871: "Error");
0872: }
0873:
0874: public final Line getMarkLine() {
0875: return mark.getLine();
0876: }
0877:
0878: public final int getMarkLineNumber() {
0879: return mark.lineNumber();
0880: }
0881:
0882: public final int getMarkOffset() {
0883: return mark.getOffset();
0884: }
0885:
0886: public File getCurrentDirectory() {
0887: return buffer.getCurrentDirectory();
0888: }
0889:
0890: public File getCompletionDirectory() {
0891: return buffer.getCompletionDirectory();
0892: }
0893:
0894: // Cycle through the most plausible possibilities for the tab width of the
0895: // current buffer.
0896: public void cycleTabWidth() {
0897: switch (buffer.getTabWidth()) {
0898: case 2:
0899: buffer.setTabWidth(4);
0900: break;
0901: case 4:
0902: buffer.setTabWidth(8);
0903: break;
0904: case 8:
0905: default:
0906: buffer.setTabWidth(2);
0907: break;
0908: }
0909: buffer.saveProperties();
0910: status("Tab width set to " + buffer.getTabWidth());
0911: buffer.repaint();
0912: }
0913:
0914: // Cycle through the most plausible possibilities for the indent size of
0915: // the current buffer.
0916: public void cycleIndentSize() {
0917: switch (buffer.getIndentSize()) {
0918: case 2:
0919: buffer.setIndentSize(3);
0920: break;
0921: case 3:
0922: buffer.setIndentSize(4);
0923: break;
0924: case 4:
0925: buffer.setIndentSize(8);
0926: break;
0927: case 8:
0928: default:
0929: buffer.setIndentSize(2);
0930: break;
0931: }
0932: buffer.saveProperties();
0933: status("Indent size set to " + buffer.getIndentSize());
0934: }
0935:
0936: public void adjustMarkers(Line line) {
0937: if (line == null)
0938: return;
0939: Position pos = null; // Where all displaced markers go.
0940: if (line.next() != null)
0941: pos = new Position(line.next(), 0);
0942: else if (line.previous() != null)
0943: pos = new Position(line.previous(), line.previous()
0944: .length());
0945: if (pos == null)
0946: return;
0947: for (int i = 0; i < Editor.getEditorCount(); i++) {
0948: Editor ed = Editor.getEditor(i);
0949: if (ed.getBuffer() == buffer) {
0950: if (ed.getDotLine() == line) {
0951: ed.getDot().moveTo(pos);
0952: ed.moveCaretToDotCol();
0953: }
0954: if (ed.getMark() != null && ed.getMarkLine() == line)
0955: ed.setMark(null); // Take no chances.
0956: if (ed.getTopLine() == line) {
0957: ed.setTopLine(pos.getLine());
0958: ed.setUpdateFlag(REPAINT);
0959: }
0960: } else {
0961: // Not presently displayed, but possibly in a stored view.
0962: View view = (View) ed.views.get(buffer);
0963: if (view != null) {
0964: if (view.dot != null && view.dot.getLine() == line)
0965: view.dot.moveTo(pos);
0966: if (view.mark != null
0967: && view.mark.getLine() == line)
0968: view.mark = null;
0969: if (view.topLine == line)
0970: view.topLine = pos.getLine();
0971: }
0972: }
0973: }
0974: for (int i = 0; i < bookmarks.length; i++) {
0975: Marker m = bookmarks[i];
0976: if (m != null && m.getLine() == line)
0977: m.setPosition(pos);
0978: }
0979: }
0980:
0981: public static Marker[] getBookmarks() {
0982: return bookmarks;
0983: }
0984:
0985: public void dropBookmark() {
0986: AWTEvent e = dispatcher.getLastEvent();
0987: if (e != null && e.getID() == KeyEvent.KEY_PRESSED) {
0988: int keyCode = ((KeyEvent) e).getKeyCode();
0989: int index = keyCode - KeyEvent.VK_0;
0990: if (index >= 0 && index < bookmarks.length) {
0991: if (bookmarks[index] == null
0992: || confirm("Drop Bookmark",
0993: "Overwrite existing bookmark?")) {
0994: bookmarks[index] = new Marker(buffer, dot);
0995: status("Bookmark dropped");
0996: }
0997: }
0998: }
0999: }
1000:
1001: public void gotoBookmark() {
1002: AWTEvent e = dispatcher.getLastEvent();
1003: if (e != null && e.getID() == KeyEvent.KEY_PRESSED) {
1004: int keyCode = ((KeyEvent) e).getKeyCode();
1005: int index = keyCode - KeyEvent.VK_0;
1006: if (index >= 0 && index < bookmarks.length) {
1007: Marker m = bookmarks[index];
1008: if (m != null)
1009: m.gotoMarker(this );
1010: }
1011: }
1012: }
1013:
1014: // Drop a temporary bookmark, overwriting the existing temporary bookmark
1015: // if one exists.
1016: public void dropTemporaryMarker() {
1017: bookmarks[10] = new Marker(buffer, dot);
1018: status("Temporary marker dropped");
1019: }
1020:
1021: public void gotoTemporaryMarker() {
1022: Marker m = bookmarks[10];
1023: if (m != null)
1024: m.gotoMarker(this );
1025: }
1026:
1027: public void deleteLineSeparator() {
1028: final Line dotLine = getDotLine();
1029: final Line nextLine = dotLine.next();
1030: if (nextLine == null)
1031: return;
1032: try {
1033: buffer.lockWrite();
1034: } catch (InterruptedException e) {
1035: Log.error(e);
1036: return;
1037: }
1038: try {
1039: if (dotLine.length() == 0) {
1040: adjustMarkers(dotLine);
1041: // Save original text.
1042: FastStringBuffer sb = new FastStringBuffer();
1043: if (dotLine.getOriginalText() != null)
1044: sb.append(dotLine.getOriginalText());
1045: sb.append('\n');
1046: if (nextLine.getOriginalText() != null)
1047: sb.append(nextLine.getOriginalText());
1048: else
1049: sb.append(nextLine.getText());
1050: nextLine.setOriginalText(sb.toString());
1051: // Unlink the current line.
1052: final Line prevLine = dotLine.previous();
1053: if (prevLine != null)
1054: prevLine.setNext(nextLine);
1055: nextLine.setPrevious(prevLine);
1056: if (dotLine == buffer.getFirstLine()) {
1057: Log
1058: .debug("deleteLineSeparator calling buffer.setFirstLine()");
1059: buffer.setFirstLine(nextLine);
1060: Log.debug("first line = |"
1061: + buffer.getFirstLine().getText() + "|");
1062: }
1063: if (dotLine == display.getTopLine())
1064: display.setTopLine(nextLine);
1065: dot.moveTo(nextLine, 0);
1066: } else {
1067: // Append the next line's text to end of this line.
1068: dotLine.setText(dotLine.getText() + nextLine.getText());
1069: // Save original text.
1070: FastStringBuffer sb = new FastStringBuffer();
1071: if (dotLine.getOriginalText() != null)
1072: sb.append(dotLine.getOriginalText());
1073: else
1074: sb.append(dotLine.getText());
1075: if (!nextLine.isNew()) {
1076: sb.append('\n');
1077: if (nextLine.getOriginalText() != null)
1078: sb.append(nextLine.getOriginalText());
1079: else
1080: sb.append(nextLine.getText());
1081: }
1082: dotLine.setOriginalText(sb.toString());
1083: // Move any markers that might be on the next line.
1084: adjustMarkers(nextLine);
1085: // Unlink the next line.
1086: if (nextLine.next() != null)
1087: nextLine.next().setPrevious(dotLine);
1088: dotLine.setNext(nextLine.next());
1089: }
1090: buffer.repaint();
1091: setUpdateFlag(REFRAME);
1092: buffer.needsRenumbering = true;
1093: buffer.modified();
1094: } finally {
1095: buffer.unlockWrite();
1096: }
1097: }
1098:
1099: private void deleteNormalChar() {
1100: addUndo(SimpleEdit.LINE_EDIT);
1101: final Line dotLine = getDotLine();
1102: final int dotOffset = getDotOffset();
1103: String head = dotLine.substring(0, dotOffset);
1104: String tail = "";
1105: if (dotOffset < dotLine.length() - 1)
1106: tail = dotLine.substring(dotOffset + 1);
1107: dotLine.setText(head.concat(tail));
1108: buffer.modified();
1109: updateInAllEditors(dotLine);
1110: }
1111:
1112: // A deletion, not a kill!
1113: public void delete() {
1114: if (!checkReadOnly())
1115: return;
1116: try {
1117: buffer.lockWrite();
1118: } catch (InterruptedException e) {
1119: Log.error(e);
1120: return;
1121: }
1122: try {
1123: if (mark != null) {
1124: deleteRegion();
1125: } else {
1126: final Line dotLine = getDotLine();
1127: final int dotOffset = getDotOffset();
1128: final int length = dotLine.length();
1129: if (dotOffset < length) {
1130: deleteNormalChar();
1131: } else if (dotOffset == length) {
1132: if (dotLine.next() != null) {
1133: CompoundEdit compoundEdit = beginCompoundEdit();
1134: fillToCaret();
1135: addUndo(SimpleEdit.DELETE_LINE_SEP);
1136: deleteLineSeparator();
1137: endCompoundEdit(compoundEdit);
1138: } else
1139: status("End of buffer");
1140: } else {
1141: // Shouldn't happen.
1142: Debug.bug();
1143: }
1144: }
1145: } finally {
1146: buffer.unlockWrite();
1147: }
1148: }
1149:
1150: // A deletion, not a kill!
1151: public void backspace() {
1152: if (!checkReadOnly())
1153: return;
1154: try {
1155: buffer.lockWrite();
1156: } catch (InterruptedException e) {
1157: Log.error(e);
1158: return;
1159: }
1160: try {
1161: if (mark != null) {
1162: delete();
1163: } else if (display.getCaretCol() > buffer.getCol(
1164: getDotLine(), getDotLine().length())) {
1165: // The caret is beyond the end of the actual text on the current line.
1166: addUndo(SimpleEdit.MOVE);
1167: --display.caretCol;
1168: updateDotLine();
1169: } else if (dot.getOffset() > 0) {
1170: addUndo(SimpleEdit.LINE_EDIT);
1171: dot.moveLeft();
1172: deleteNormalChar();
1173: moveCaretToDotCol();
1174: } else if (getDotLine().previous() != null) {
1175: CompoundEdit compoundEdit = beginCompoundEdit();
1176: addUndo(SimpleEdit.MOVE);
1177: dot.moveTo(getDotLine().previous(), getDotLine()
1178: .previous().length());
1179: addUndo(SimpleEdit.DELETE_LINE_SEP);
1180: deleteLineSeparator();
1181: endCompoundEdit(compoundEdit);
1182: moveCaretToDotCol();
1183: }
1184: } finally {
1185: buffer.unlockWrite();
1186: }
1187: }
1188:
1189: private boolean nextChar() {
1190: if (getDotOffset() < getDotLine().length()) {
1191: dot.skip(1);
1192: return true;
1193: }
1194: if (getDotLine().next() != null) {
1195: dot.moveTo(getDotLine().next(), 0);
1196: return true;
1197: }
1198: return false;
1199: }
1200:
1201: private boolean prevChar() {
1202: if (getDotOffset() > 0) {
1203: dot.skip(-1);
1204: return true;
1205: }
1206: final Line previous = getDotLine().previous();
1207: if (previous != null) {
1208: dot.moveTo(previous, previous.length());
1209: return true;
1210: }
1211: return false;
1212: }
1213:
1214: public char getDotChar() {
1215: Debug.assertTrue(dot != null);
1216: return dot.getChar();
1217: }
1218:
1219: public void cppFindMatch() {
1220: if (getDotLine().trim().startsWith("#")) {
1221: Line line = CMode.findMatchPreprocessor(getDotLine());
1222: if (line != null)
1223: moveDotTo(line, 0);
1224: else
1225: status("No match");
1226: } else
1227: findMatchingChar();
1228: }
1229:
1230: // If numLines is non-zero, limit the search to that many lines either
1231: // forward or backward in the buffer.
1232: public Position findMatchInternal(Position start, int numLines) {
1233: if (start == null)
1234: return null;
1235: final String s1 = new String("{([})]");
1236: final char origChar = start.getChar();
1237: int index = s1.indexOf(origChar);
1238: if (index < 0)
1239: return null;
1240: final Mode mode = getMode();
1241: if (mode.isInComment(getBuffer(), start))
1242: return null;
1243: boolean inQuote = mode.isInQuote(buffer, start);
1244: if (inQuote)
1245: return null;
1246: final String s2 = new String("})]{([");
1247: final char match = s2.charAt(index);
1248: final boolean searchBackwards = index > 2;
1249: int stopLineNumber = searchBackwards ? 0 : buffer
1250: .getLineCount();
1251: if (numLines != 0)
1252: stopLineNumber = searchBackwards ? start.lineNumber()
1253: - numLines : start.lineNumber() + numLines;
1254: int count = 1;
1255: final SyntaxIterator it = mode.getSyntaxIterator(start);
1256: while (true) {
1257: char c;
1258: Position pos = it.getPosition();
1259: if (searchBackwards) {
1260: if (pos.lineNumber() < stopLineNumber)
1261: return null;
1262: else
1263: c = it.prevChar();
1264: } else {
1265: if (pos.lineNumber() > stopLineNumber)
1266: return null;
1267: else
1268: c = it.nextChar();
1269: }
1270: if (c == SyntaxIterator.DONE)
1271: return null;
1272: if (inQuote && c == '"')
1273: return null;
1274: if (c == origChar)
1275: ++count;
1276: else if (c == match)
1277: --count;
1278: if (count == 0) {
1279: // Found it!
1280: return it.getPosition();
1281: }
1282: }
1283: }
1284:
1285: public void findMatchingChar() {
1286: setWaitCursor();
1287: Position pos = findDelimiterNearDot();
1288: if (pos != null) {
1289: Position match = findMatchInternal(pos, 0);
1290: if (match != null) {
1291: // If the match is a right delimiter, we want to put the caret
1292: // beyond it.
1293: if ("})]".indexOf(match.getChar()) >= 0)
1294: match.next();
1295: addUndo(SimpleEdit.MOVE);
1296: unmark();
1297: updateDotLine();
1298: dot.moveTo(match);
1299: updateDotLine();
1300: moveCaretToDotCol();
1301: } else
1302: status("No match");
1303: }
1304: setDefaultCursor();
1305: }
1306:
1307: public void selectSyntax() {
1308: setWaitCursor();
1309: Position pos = findDelimiterNearDot();
1310: if (pos != null) {
1311: Position match = findMatchInternal(pos, 0);
1312: if (match != null) {
1313: if ("})]".indexOf(pos.getChar()) >= 0)
1314: pos.next();
1315: else if ("})]".indexOf(match.getChar()) >= 0)
1316: match.next();
1317: if (pos.getLine() != match.getLine()) {
1318: // Extend selection to full lines if possible.
1319: Region r = new Region(buffer, pos, match);
1320: Position begin = r.getBegin();
1321: String trim = begin.getLine().substring(0,
1322: begin.getOffset()).trim();
1323: if (trim.length() == 0) {
1324: Position end = r.getEnd();
1325: trim = end.getLine().substring(end.getOffset())
1326: .trim();
1327: if (trim.length() == 0) {
1328: // Extend selection to complete lines.
1329: begin.setOffset(0);
1330: if (end.getNextLine() != null)
1331: end.moveTo(end.getNextLine(), 0);
1332: else
1333: end.setOffset(end.getLineLength());
1334: if (pos.isBefore(match)) {
1335: pos = begin;
1336: match = end;
1337: } else {
1338: match = begin;
1339: pos = end;
1340: }
1341: }
1342: }
1343: }
1344: addUndo(SimpleEdit.MOVE);
1345: unmark();
1346: dot.moveTo(pos);
1347: setMarkAtDot();
1348: updateDotLine();
1349: dot.moveTo(match);
1350: updateDotLine();
1351: moveCaretToDotCol();
1352: if (dot.getLine() != mark.getLine())
1353: setUpdateFlag(REPAINT);
1354: } else
1355: status("No match");
1356: }
1357: setDefaultCursor();
1358: }
1359:
1360: private Position findDelimiterNearDot() {
1361: Position pos = dot.copy();
1362: if ("{([".indexOf(pos.getChar()) >= 0) {
1363: // The character to the right of the caret is a left delimiter.
1364: return pos;
1365: }
1366: Position saved = dot.copy();
1367: if (pos.getOffset() > 0) {
1368: pos.prev();
1369: if ("})]".indexOf(pos.getChar()) >= 0) {
1370: // The character to the left of the caret is a right delimiter.
1371: return pos;
1372: }
1373: }
1374: // There's no delimiter at the exact location of the caret.
1375: final String delimiters = "{([})]";
1376: pos.moveTo(saved);
1377: while (pos.getOffset() > 0) {
1378: // Look at previous char.
1379: pos.prev();
1380: char c = pos.getChar();
1381: if (delimiters.indexOf(c) >= 0)
1382: return pos;
1383: if (!Character.isWhitespace(c) && c != ';')
1384: break;
1385: }
1386: pos.moveTo(saved);
1387: final int limit = pos.getLineLength();
1388: while (pos.getOffset() < limit) {
1389: char c = pos.getChar();
1390: if (delimiters.indexOf(c) >= 0)
1391: return pos;
1392: if (!Character.isWhitespace(c))
1393: return null;
1394: // Look at next char.
1395: pos.next();
1396: }
1397: return null;
1398: }
1399:
1400: public void closeParen() {
1401: insertNormalChar(')');
1402: if (prefs
1403: .getBooleanProperty(Property.HIGHLIGHT_MATCHING_BRACKET)
1404: || prefs
1405: .getBooleanProperty(Property.HIGHLIGHT_BRACKETS))
1406: return;
1407: // Limit search to 50 lines.
1408: Position match = findMatchInternal(new Position(getDotLine(),
1409: getDotOffset() - 1), 50);
1410: if (match == null)
1411: return;
1412: // We don't want to reframe.
1413: if (match.lineNumber() < display.getTopLineNumber())
1414: return;
1415: if (buffer.getCol(match) < display.getShift())
1416: return;
1417: // Move caret to match momentarily and then return.
1418: Position saved = new Position(dot);
1419: updateDotLine();
1420: dot.moveTo(match);
1421: updateDotLine();
1422: moveCaretToDotCol();
1423: display.repaintChangedLines();
1424: try {
1425: Thread.sleep(300);
1426: } catch (InterruptedException e) {
1427: }
1428: updateDotLine();
1429: dot.moveTo(saved);
1430: moveCaretToDotCol();
1431: updateDotLine();
1432: }
1433:
1434: // No undo.
1435: public void insertLineSeparator() {
1436: Debug.assertTrue(mark == null);
1437: try {
1438: buffer.lockWrite();
1439: } catch (InterruptedException e) {
1440: Log.error(e);
1441: return;
1442: }
1443: try {
1444: buffer.insertLineSeparator(dot);
1445: } finally {
1446: buffer.unlockWrite();
1447: }
1448: final Line dotLine = getDotLine();
1449: for (int i = 0; i < getEditorCount(); i++) {
1450: Editor ed = getEditor(i);
1451: if (ed.getTopLine() == dotLine)
1452: ed.setTopLine(dotLine.previous());
1453: }
1454: }
1455:
1456: public void newline() {
1457: if (!checkReadOnly())
1458: return;
1459: CompoundEdit compoundEdit = beginCompoundEdit();
1460: if (mark != null)
1461: deleteRegion();
1462: addUndo(SimpleEdit.INSERT_LINE_SEP);
1463: insertLineSeparator();
1464: moveCaretToDotCol();
1465: endCompoundEdit(compoundEdit);
1466: }
1467:
1468: public void newlineAndIndent() {
1469: if (isColumnSelection()) {
1470: notSupportedForColumnSelections();
1471: return;
1472: }
1473: if (!checkReadOnly())
1474: return;
1475: try {
1476: buffer.lockWrite();
1477: } catch (InterruptedException e) {
1478: Log.error(e);
1479: return;
1480: }
1481: try {
1482: CompoundEdit compoundEdit = beginCompoundEdit();
1483: if (mark != null)
1484: deleteRegion();
1485: addUndo(SimpleEdit.INSERT_LINE_SEP);
1486: insertLineSeparator();
1487: final Mode mode = getMode();
1488: final Line dotLine = getDotLine();
1489: int indent;
1490: if (mode.canIndent()) {
1491: if (buffer.needsRenumbering())
1492: buffer.renumber();
1493: getFormatter().parseBuffer();
1494: indent = mode.getCorrectIndentation(dotLine, buffer);
1495: } else {
1496: // Can't indent according to context. Match indentation of previous line.
1497: indent = buffer.getIndentation(dotLine.previous());
1498: }
1499: if (indent != buffer.getIndentation(dotLine)) {
1500: addUndo(SimpleEdit.LINE_EDIT);
1501: buffer.setIndentation(dotLine, indent);
1502: }
1503: if (dotLine.length() > 0) {
1504: moveDotToIndentation();
1505: moveCaretToDotCol();
1506: } else {
1507: display.setCaretCol(indent - display.getShift());
1508: if (buffer.getBooleanProperty(Property.RESTRICT_CARET))
1509: fillToCaret();
1510: }
1511: endCompoundEdit(compoundEdit);
1512: } finally {
1513: buffer.unlockWrite();
1514: }
1515: setUpdateFlag(REFRAME);
1516: }
1517:
1518: public void insertNormalChar(char c) {
1519: if (isColumnSelection()) {
1520: notSupportedForColumnSelections();
1521: return;
1522: }
1523: if (!checkReadOnly())
1524: return;
1525: try {
1526: buffer.lockWrite();
1527: } catch (InterruptedException e) {
1528: Log.error(e);
1529: return;
1530: }
1531: try {
1532: c = getMode().fixCase(this , c);
1533: if (mark != null) {
1534: CompoundEdit compoundEdit = beginCompoundEdit();
1535: deleteRegion();
1536: insertChar(c);
1537: endCompoundEdit(compoundEdit);
1538: } else {
1539: // No selection.
1540: if (buffer.getBooleanProperty(Property.WRAP)
1541: && getDotCol() >= buffer
1542: .getIntegerProperty(Property.WRAP_COL)) {
1543: CompoundEdit compoundEdit = beginCompoundEdit();
1544: insertChar(c);
1545: new WrapText(this ).wrapLine();
1546: endCompoundEdit(compoundEdit);
1547: } else
1548: insertChar(c);
1549: }
1550: } finally {
1551: buffer.unlockWrite();
1552: }
1553: moveCaretToDotCol();
1554: }
1555:
1556: public void tab() {
1557: if (isColumnSelection()) {
1558: notSupportedForColumnSelections();
1559: return;
1560: }
1561: if (buffer.getBooleanProperty(Property.TAB_ALWAYS_INDENT)) {
1562: indentLineOrRegion();
1563: return;
1564: }
1565: if (mark == null) {
1566: // No selection.
1567: if (dot.getOffset() <= dot.getLine().getIndentation())
1568: indentLine();
1569: else
1570: insertTab();
1571: return;
1572: }
1573: if (getMarkLine() != getDotLine()) {
1574: // Multi-line selection.
1575: indentRegion();
1576: return;
1577: }
1578: // Single-line selection.
1579: Region r = new Region(this );
1580: if (r.getBeginOffset() <= getDotLine().getIndentation())
1581: indentLine();
1582: else
1583: insertTab();
1584: }
1585:
1586: public void insertTab() {
1587: if (isColumnSelection()) {
1588: notSupportedForColumnSelections();
1589: return;
1590: }
1591:
1592: if (!checkReadOnly())
1593: return;
1594:
1595: try {
1596: buffer.lockWrite();
1597: } catch (InterruptedException e) {
1598: Log.error(e);
1599: return;
1600: }
1601: try {
1602: CompoundEdit compoundEdit = beginCompoundEdit();
1603: if (mark != null)
1604: deleteRegion();
1605: if (buffer.getUseTabs())
1606: insertChar('\t');
1607: else {
1608: fillToCaret();
1609: int tabWidth = buffer.getTabWidth();
1610: addUndo(SimpleEdit.LINE_EDIT);
1611: buffer.insertChars(dot, Utilities.spaces(tabWidth
1612: - getDotCol() % tabWidth));
1613: updateInAllEditors(getDotLine());
1614: }
1615: moveCaretToDotCol();
1616: endCompoundEdit(compoundEdit);
1617: } finally {
1618: buffer.unlockWrite();
1619: }
1620: }
1621:
1622: public void moveDotToIndentation() {
1623: final Line dotLine = getDotLine();
1624: final int limit = dotLine.length();
1625: int i;
1626: for (i = 0; i < limit; i++) {
1627: if (!Character.isWhitespace(dotLine.charAt(i)))
1628: break;
1629: }
1630: dot.setOffset(i);
1631: }
1632:
1633: public void indentLineOrRegion() {
1634: if (isColumnSelection()) {
1635: notSupportedForColumnSelections();
1636: return;
1637: }
1638: if (getMode().canIndent()) {
1639: if (mark != null && getMarkLine() != getDotLine()) {
1640: indentRegion();
1641: } else {
1642: unmark();
1643: indentLine();
1644: }
1645: }
1646: }
1647:
1648: public void indentRegion() {
1649: if (isColumnSelection()) {
1650: notSupportedForColumnSelections();
1651: return;
1652: }
1653: if (getMode().canIndent() && mark != null) {
1654: Region r = new Region(this );
1655: if (r.getBeginLine() == r.getEndLine()) {
1656: indentLine();
1657: } else {
1658: setWaitCursor();
1659: Position savedDot = new Position(dot);
1660: try {
1661: buffer.lockWrite();
1662: } catch (InterruptedException e) {
1663: Log.error(e);
1664: return;
1665: }
1666: try {
1667: if (buffer.needsParsing()) {
1668: if (getFormatter().parseBuffer())
1669: buffer.repaint();
1670: }
1671: CompoundEdit compoundEdit = beginCompoundEdit();
1672: addUndo(SimpleEdit.MOVE);
1673: dot.moveTo(r.getBeginLine(), 0);
1674: do {
1675: if (!dot.getLine().isBlank())
1676: indentLineInternal();
1677: dot.moveTo(dot.getNextLine(), 0);
1678: } while (dot.getLine() != r.getEndLine());
1679: addUndo(SimpleEdit.MOVE);
1680: dot = savedDot;
1681: if (dot.getOffset() > getDotLine().length())
1682: dot.setOffset(getDotLine().length());
1683: if (mark.getOffset() > getMarkLine().length())
1684: mark.setOffset(getMarkLine().length());
1685: moveCaretToDotCol();
1686: endCompoundEdit(compoundEdit);
1687: } finally {
1688: buffer.unlockWrite();
1689: }
1690: setUpdateFlag(REFRAME);
1691: setDefaultCursor();
1692: }
1693: }
1694: }
1695:
1696: public void commentRegion() {
1697: if (isColumnSelection()) {
1698: notSupportedForColumnSelections();
1699: return;
1700: }
1701: commentRegion(true);
1702: }
1703:
1704: public void uncommentRegion() {
1705: if (isColumnSelection()) {
1706: notSupportedForColumnSelections();
1707: return;
1708: }
1709: commentRegion(false);
1710: }
1711:
1712: // If argument is false, uncomment the region.
1713: private void commentRegion(boolean comment) {
1714: if (!checkReadOnly())
1715: return;
1716: if (dot == null)
1717: return;
1718: try {
1719: buffer.lockWrite();
1720: } catch (InterruptedException e) {
1721: Log.error(e);
1722: return;
1723: }
1724: try {
1725: commentRegionInternal(comment);
1726: } finally {
1727: buffer.unlockWrite();
1728: }
1729: }
1730:
1731: // If argument is false, uncomment the region.
1732: private void commentRegionInternal(boolean comment) {
1733: String commentStart = buffer.getCommentStart();
1734: if (commentStart == null)
1735: return;
1736: String commentEnd = buffer.getCommentEnd();
1737: Position savedDot = new Position(dot);
1738: Line beginLine, endLine;
1739: if (mark != null) {
1740: Region r = new Region(buffer, dot, mark);
1741: beginLine = r.getBeginLine();
1742: endLine = r.getEndLine();
1743: } else {
1744: beginLine = getDotLine();
1745: endLine = getDotLine().next();
1746: }
1747: if (endLine == beginLine)
1748: endLine = beginLine.next();
1749:
1750: CompoundEdit compoundEdit = beginCompoundEdit();
1751:
1752: if (mark != null) {
1753: addUndo(SimpleEdit.MOVE);
1754: setMark(null);
1755: setUpdateFlag(REPAINT);
1756: }
1757:
1758: if (getDotLine() != beginLine) {
1759: addUndo(SimpleEdit.MOVE);
1760: dot.moveTo(beginLine, 0);
1761: }
1762:
1763: boolean modified = false;
1764:
1765: while (true) {
1766: Line dotLine = getDotLine();
1767: String trim = dotLine.trim();
1768: if (comment) {
1769: if (trim.length() != 0) {
1770: addUndo(SimpleEdit.LINE_EDIT);
1771: if (commentEnd != null)
1772: dotLine.setText(commentStart
1773: + dotLine.getText() + commentEnd);
1774: else
1775: dotLine.setText(commentStart
1776: + dotLine.getText());
1777: modified = true;
1778: if (dotLine == savedDot.getLine())
1779: savedDot.setOffset(savedDot.getOffset()
1780: + commentStart.length());
1781: updateInAllEditors(dotLine);
1782: }
1783: } else {
1784: // Uncomment.
1785: if (trim.startsWith(commentStart)) {
1786: if (commentEnd == null || trim.endsWith(commentEnd)) {
1787: addUndo(SimpleEdit.LINE_EDIT);
1788: int index = dotLine.getText().indexOf(
1789: commentStart)
1790: + commentStart.length();
1791: dotLine.setText(dotLine.substring(index));
1792: if (commentEnd != null)
1793: dotLine.setText(dotLine.substring(0,
1794: dotLine.length()
1795: - commentEnd.length()));
1796: modified = true;
1797: int dotCol = 0;
1798: if (dotLine == savedDot.getLine()) {
1799: // Adjust saved position.
1800: savedDot.setOffset(savedDot.getOffset()
1801: - index);
1802: if (savedDot.getOffset() < 0)
1803: savedDot.setOffset(0);
1804: dotCol = buffer.getCol(dotLine, savedDot
1805: .getOffset());
1806: }
1807:
1808: int oldIndent = buffer.getIndentation(dotLine);
1809: int indent = getMode().getCorrectIndentation(
1810: dotLine, buffer);
1811: if (indent != oldIndent) {
1812: buffer.setIndentation(dotLine, indent);
1813: if (dotLine == savedDot.getLine()) {
1814: // Adjust saved position.
1815: if (dotCol >= oldIndent) {
1816: dotCol += indent - oldIndent;
1817: dot.moveToCol(dotCol, buffer
1818: .getTabWidth());
1819: savedDot.setOffset(dot.getOffset());
1820: }
1821: }
1822: }
1823: updateInAllEditors(dotLine);
1824: }
1825: }
1826: }
1827:
1828: if (dotLine.next() == endLine)
1829: break;
1830:
1831: addUndo(SimpleEdit.MOVE);
1832: dot.moveTo(dotLine.next(), 0);
1833: }
1834:
1835: addUndo(SimpleEdit.MOVE);
1836: setDot(savedDot);
1837: moveCaretToDotCol();
1838:
1839: if (modified)
1840: buffer.modified();
1841:
1842: endCompoundEdit(compoundEdit);
1843: }
1844:
1845: public final void moveDotTo(Position pos) {
1846: if (pos != null)
1847: moveDotTo(pos.getLine(), pos.getOffset());
1848: }
1849:
1850: public void moveDotTo(Line line, int offset) {
1851: if (dot == null)
1852: return;
1853: addUndo(SimpleEdit.MOVE);
1854: if (mark != null) {
1855: setMark(null);
1856: dot.moveTo(line, offset);
1857: setUpdateFlag(REPAINT);
1858: } else {
1859: updateDotLine();
1860: dot.moveTo(line, offset);
1861: updateDotLine();
1862: }
1863: moveCaretToDotCol();
1864: }
1865:
1866: public void indentLine() {
1867: if (isColumnSelection()) {
1868: notSupportedForColumnSelections();
1869: return;
1870: }
1871: if (!checkReadOnly())
1872: return;
1873: if (!getMode().canIndent())
1874: return; // No change.
1875: try {
1876: buffer.lockWrite();
1877: } catch (InterruptedException e) {
1878: Log.error(e);
1879: return;
1880: }
1881: try {
1882: if (buffer.needsParsing()) {
1883: if (getFormatter().parseBuffer())
1884: buffer.repaint();
1885: }
1886: indentLineInternal();
1887: } finally {
1888: buffer.unlockWrite();
1889: }
1890: setUpdateFlag(REFRAME);
1891: }
1892:
1893: private void indentLineInternal() {
1894: final Line dotLine = getDotLine();
1895: final int indent = getMode().getCorrectIndentation(dotLine,
1896: buffer);
1897: final int shift = display.getShift();
1898:
1899: if (dotLine.isBlank()) {
1900: // Put the caret where it needs to go...
1901: addUndo(SimpleEdit.LINE_EDIT);
1902: dotLine.setText("");
1903: dot.setOffset(0);
1904: display.setCaretCol(indent - shift);
1905: // Fill if necessary.
1906: if (buffer.getBooleanProperty(Property.RESTRICT_CARET))
1907: fillToCaret();
1908: updateInAllEditors(dotLine);
1909: return;
1910: }
1911:
1912: // Line is not blank. Figure out current indentation.
1913: final int oldIndent = buffer.getIndentation(dotLine);
1914:
1915: FastStringBuffer sb = null;
1916:
1917: if (indent == oldIndent) {
1918: boolean ok = false;
1919: if (buffer
1920: .getBooleanProperty(Property.INDENT_LINE_FIX_WHITESPACE)) {
1921: sb = buffer.getCorrectIndentationString(indent);
1922: if (dotLine.getText().startsWith(sb.toString()))
1923: ok = true;
1924: } else
1925: ok = true;
1926:
1927: if (ok) {
1928: // Current indentation is correct. If the caret is in the
1929: // indentation area, move it to the start of the non-blank
1930: // text.
1931: if (display.getCaretCol() + shift < indent) {
1932: addUndo(SimpleEdit.MOVE);
1933: display.setCaretCol(indent - shift);
1934: moveDotToCaretCol();
1935: }
1936: return;
1937: }
1938: }
1939:
1940: // We need to fix the indentation. Figure out where we want to put the
1941: // caret when we're done. We want to maintain the existing offset from
1942: // the start of the non-blank text.
1943: final int existing = display.getCaretCol() + shift - oldIndent;
1944: final int goal = existing < 0 ? indent : existing + indent;
1945:
1946: // Strip existing indentation.
1947: int i = 0;
1948: while (i < dotLine.length()
1949: && Character.isWhitespace(dotLine.charAt(i)))
1950: ++i;
1951: String nonBlank = dotLine.substring(i);
1952:
1953: // Get the correct indentation string.
1954: if (sb == null)
1955: sb = buffer.getCorrectIndentationString(indent);
1956:
1957: // Add the rest of the line.
1958: sb.append(nonBlank);
1959:
1960: // Replace the existing text.
1961: addUndo(SimpleEdit.LINE_EDIT);
1962: dotLine.setText(sb.toString());
1963: buffer.modified();
1964: updateInAllEditors(dotLine);
1965:
1966: // Put the caret where we want it.
1967: display.setCaretCol(goal - shift);
1968: moveDotToCaretCol();
1969: }
1970:
1971: public void save() {
1972: save(buffer);
1973: }
1974:
1975: public void save(Buffer toBeSaved) {
1976: if (toBeSaved.isLocked())
1977: return;
1978: if (toBeSaved.getType() == Buffer.TYPE_NORMAL) {
1979: if (toBeSaved.isModified()) {
1980: if (toBeSaved.isUntitled()) {
1981: saveAs(toBeSaved);
1982: } else {
1983: setWaitCursor();
1984: status("Saving...");
1985: if (toBeSaved
1986: .getBooleanProperty(Property.REMOVE_TRAILING_WHITESPACE))
1987: toBeSaved.removeTrailingWhitespace();
1988: if (toBeSaved.save())
1989: status("Saving...done");
1990: else
1991: status("Save failed");
1992: setDefaultCursor();
1993: }
1994: } else
1995: status("Not modified");
1996: }
1997: }
1998:
1999: public void saveAs() {
2000: saveAs(buffer);
2001: }
2002:
2003: private void saveAs(Buffer toBeSaved) {
2004: if (toBeSaved.isLocked())
2005: return;
2006: if (toBeSaved.getType() == Buffer.TYPE_NORMAL) {
2007: final String dialogTitle = "Save As";
2008: File destination = SaveFileDialog.getSaveFile(this ,
2009: dialogTitle);
2010: if (destination == null)
2011: return;
2012:
2013: // At this point, if the target file exists, the user has said
2014: // it's OK to overwrite it.
2015: repaintNow();
2016:
2017: // Do we have the target file in a buffer?
2018: Buffer buf = bufferList.findBuffer(destination);
2019: if (buf != null) {
2020: // We do. Can we just get rid of it?
2021: if (!buf.isModified()) {
2022: buf.deleteAutosaveFile();
2023: bufferList.remove(buf);
2024: } else {
2025: // Buffer is modified. Make user deal with it.
2026: setDefaultCursor();
2027: String message = "Target file is in an active buffer. Please take care of that first.";
2028: MessageDialog.showMessageDialog(this , message,
2029: dialogTitle);
2030: return;
2031: }
2032: }
2033:
2034: toBeSaved.saveAs(destination);
2035: }
2036: }
2037:
2038: public void saveCopy() {
2039: if (buffer.isLocked())
2040: return;
2041: if (buffer.getType() == Buffer.TYPE_NORMAL) {
2042: final String dialogTitle = "Save Copy";
2043: final File destination = SaveFileDialog.getSaveFile(this ,
2044: dialogTitle);
2045: if (destination == null)
2046: return;
2047:
2048: repaintNow();
2049:
2050: // Do we have the target file in a buffer?
2051: Buffer buf = bufferList.findBuffer(destination);
2052: if (buf != null) {
2053: // We do. Do we care?
2054: if (buf.isModified()) {
2055: // Buffer is modified. Make user deal with it.
2056: setDefaultCursor();
2057: String message = "Target file is in an active buffer. Please take care of that first.";
2058: MessageDialog.showMessageDialog(this , message,
2059: "Save Copy");
2060: return;
2061: }
2062: }
2063:
2064: buffer.saveCopy(destination);
2065: if (buf != null && buf.isLoaded())
2066: reload(buf);
2067: }
2068: }
2069:
2070: public void saveAll() {
2071: setWaitCursor();
2072: int numModified = 0;
2073: int numErrors = 0;
2074: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
2075: Buffer buf = it.nextBuffer();
2076: if (buf.getModeId() == CHECKIN_MODE)
2077: continue;
2078: if (buf.isUntitled()) {
2079: setDefaultCursor();
2080: makeNext(buf);
2081: activate(buf);
2082: saveAs();
2083: setWaitCursor();
2084: } else if (buf.isModified()) {
2085: status("Saving modified buffers...");
2086: ++numModified;
2087: if (buffer.getFile() != null)
2088: if (buffer
2089: .getBooleanProperty(Property.REMOVE_TRAILING_WHITESPACE))
2090: buffer.removeTrailingWhitespace();
2091: if (!buf.save())
2092: ++numErrors;
2093: }
2094: }
2095: if (numModified == 0)
2096: status("No modified buffers");
2097: else if (numErrors == 0)
2098: status("Saving modified buffers...done");
2099: else {
2100: // User will already have seen detailed error information from Buffer.save().
2101: status("Unable to save all modified buffers");
2102: }
2103: setDefaultCursor();
2104: }
2105:
2106: public boolean okToClose(Buffer buf) {
2107: if (buf.getType() != Buffer.TYPE_NORMAL)
2108: return true;
2109: if (buf.isUntitled() || buf.isModified()) {
2110: makeNext(buf);
2111: activate(buf);
2112: repaintNow();
2113: if (!CloseBufferConfirmationDialog.confirmClose(this , buf))
2114: return false;
2115: }
2116: return true;
2117: }
2118:
2119: public void closeAll() {
2120: repaintNow();
2121:
2122: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
2123: if (!okToClose(it.nextBuffer()))
2124: return;
2125: }
2126:
2127: Marker.invalidateAllMarkers();
2128:
2129: Buffer toBeActivated = null;
2130:
2131: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
2132: Buffer buf = it.nextBuffer();
2133: if (buf instanceof Directory
2134: && buf.getFile().equals(getCurrentDirectory())) {
2135: toBeActivated = buf;
2136: break;
2137: }
2138: }
2139:
2140: if (toBeActivated == null)
2141: toBeActivated = new Directory(getCurrentDirectory());
2142:
2143: setWaitCursor();
2144:
2145: for (int i = 0; i < getEditorCount(); i++) {
2146: Editor ed = getEditor(i);
2147: ed.activate(toBeActivated);
2148: }
2149:
2150: for (BufferIterator iter = new BufferIterator(); iter.hasNext();) {
2151: Buffer buf = iter.nextBuffer();
2152: if (buf != toBeActivated) {
2153: for (EditorIterator it = new EditorIterator(); it
2154: .hasNext();) {
2155: Editor ed = it.nextEditor();
2156: ed.views.remove(buf);
2157: }
2158: buf.deleteAutosaveFile();
2159: iter.remove();
2160: buf.dispose();
2161: }
2162: }
2163:
2164: Sidebar.setUpdateFlagInAllFrames(SIDEBAR_BUFFER_LIST_ALL);
2165: Sidebar.refreshSidebarInAllFrames();
2166: setDefaultCursor();
2167: }
2168:
2169: public void closeOthers() {
2170: repaintNow();
2171:
2172: Buffer toBeActivated = buffer;
2173:
2174: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
2175: Buffer buf = it.nextBuffer();
2176: if (buf != buffer && !okToClose(buf))
2177: return;
2178: }
2179:
2180: List markers = Marker.getAllMarkers();
2181: for (int i = 0; i < markers.size(); i++) {
2182: Marker m = (Marker) markers.get(i);
2183: if (m != null && m.getBuffer() != buffer)
2184: m.invalidate();
2185: }
2186:
2187: setWaitCursor();
2188:
2189: for (EditorIterator it = new EditorIterator(); it.hasNext();)
2190: it.nextEditor().activate(toBeActivated);
2191:
2192: for (BufferIterator iter = new BufferIterator(); iter.hasNext();) {
2193: Buffer buf = iter.nextBuffer();
2194: if (buf != buffer) {
2195: for (EditorIterator it = new EditorIterator(); it
2196: .hasNext();) {
2197: Editor ed = it.nextEditor();
2198: ed.views.remove(buf);
2199: }
2200: buf.deleteAutosaveFile();
2201: iter.remove();
2202: buf.dispose();
2203: }
2204: }
2205:
2206: Sidebar.setUpdateFlagInAllFrames(SIDEBAR_BUFFER_LIST_ALL);
2207: Sidebar.refreshSidebarInAllFrames();
2208: setDefaultCursor();
2209: }
2210:
2211: public boolean execute(String command) throws NoSuchMethodException {
2212: String[] array = parseCommand(command);
2213: if (array == null)
2214: return false;
2215: String methodName = array[0];
2216: String parameters = array[1];
2217: return execute(methodName, parameters);
2218: }
2219:
2220: public boolean execute(String commandName, String parameters)
2221: throws NoSuchMethodException {
2222: if (commandName == null)
2223: return false;
2224:
2225: try {
2226: String className = null;
2227: String methodName = null;
2228: Method method = null;
2229:
2230: Class[] parameterTypes;
2231: if (parameters == null)
2232: parameterTypes = new Class[0];
2233: else {
2234: parameterTypes = new Class[1];
2235: parameterTypes[0] = Class.forName("java.lang.String");
2236: }
2237:
2238: Command command = CommandTable.getCommand(commandName);
2239: if (command != null) {
2240: method = command.getMethod();
2241: if (method != null) {
2242: try {
2243: invoke(method, parameters);
2244: return true;
2245: } catch (IllegalArgumentException e) {
2246: // The cached method requires different arguments.
2247: // Fall through.
2248: }
2249: }
2250: // Method is not cached yet.
2251: className = command.getClassName();
2252: methodName = command.getMethodName();
2253: if (className == null) {
2254: // Special case. Command is implemented in org.armedbear.j.Editor.
2255: method = Editor.class.getMethod(methodName,
2256: parameterTypes);
2257: } else {
2258: Class c = Class.forName("org.armedbear.j."
2259: + className);
2260: if (c != null)
2261: method = c
2262: .getMethod(methodName, parameterTypes);
2263: }
2264: if (method != null) {
2265: // Cache the method for nest time.
2266: command.setMethod(method);
2267: invoke(method, parameters);
2268: return true;
2269: }
2270: } else {
2271: // This is the old code path.
2272: Debug.assertTrue(className == null);
2273: Debug.assertTrue(methodName == null);
2274: int index = commandName.indexOf('.');
2275: if (index < 0) {
2276: // No class name. Must be a method in the Editor class.
2277: method = Editor.class.getMethod(commandName,
2278: parameterTypes);
2279: } else if (commandName.length() > index + 1) {
2280: // Class name was provided.
2281: className = commandName.substring(0, index);
2282: methodName = commandName.substring(index + 1);
2283: Class c = null;
2284: try {
2285: c = Class.forName("org.armedbear.j."
2286: + className);
2287: } catch (ClassNotFoundException e) {
2288: }
2289: if (c == null) {
2290: // Check for extension class.
2291: c = loadExtensionClass(className);
2292: }
2293: if (c != null) {
2294: // Might throw NoSuchMethodException.
2295: method = c
2296: .getMethod(methodName, parameterTypes);
2297: }
2298: }
2299: if (method != null) {
2300: invoke(method, parameters);
2301: return true;
2302: }
2303: }
2304: } catch (NoSuchMethodException e) {
2305: throw e;
2306: } catch (Throwable t) {
2307: Log.error(t);
2308: }
2309: return false;
2310: }
2311:
2312: private void invoke(Method method, String parameters)
2313: throws IllegalArgumentException {
2314: Object[] args;
2315: if (parameters == null)
2316: args = new Object[0]; // No arguments.
2317: else {
2318: args = new Object[1];
2319: args[0] = parameters;
2320: }
2321: try {
2322: method.invoke(this , args);
2323: } catch (IllegalArgumentException e) {
2324: throw e;
2325: } catch (Throwable t) {
2326: Log.error(t);
2327: }
2328: }
2329:
2330: public boolean handleKeyEvent(char keyChar, int keyCode,
2331: int modifiers) {
2332: if (insertingKeyText) {
2333: insertKeyTextInternal(keyChar, keyCode, modifiers);
2334: return true;
2335: }
2336: KeyMapping mapping = getKeyMapping(keyChar, keyCode, modifiers);
2337: if (mapping != null) {
2338: Object command = mapping.getCommand();
2339: if (isRecordingMacro()) {
2340: if (command != "recordMacro"
2341: && command != "playbackMacro")
2342: Macro.record(this , command);
2343: }
2344: if (command instanceof String) {
2345: String commandString = (String) command;
2346: if (commandString.length() > 0
2347: && commandString.charAt(0) == '(') {
2348: // Lisp form.
2349: executeCommand(commandString);
2350: return true;
2351: }
2352: String[] array = parseCommand(commandString);
2353: if (array != null) {
2354: String methodName = array[0];
2355: String parameters = array[1];
2356: try {
2357: return execute(methodName, parameters);
2358: } catch (NoSuchMethodException e) {
2359: }
2360: }
2361: } else if (command instanceof LispObject) {
2362: try {
2363: Lisp.funcall0(Lisp
2364: .coerceToFunction((LispObject) command),
2365: LispThread.currentThread());
2366: } catch (Throwable t) {
2367: Log.error(t);
2368: }
2369: return true;
2370: }
2371: }
2372: return false;
2373: }
2374:
2375: public KeyMapping getKeyMapping(char keyChar, int keyCode,
2376: int modifiers) {
2377: // Look in mode-specific key map.
2378: KeyMapping mapping = buffer.getMode().getKeyMap().lookup(
2379: keyChar, keyCode, modifiers);
2380: if (mapping != null)
2381: return mapping;
2382: // Look in global key map.
2383: return KeyMap.getGlobalKeyMap().lookup(keyChar, keyCode,
2384: modifiers);
2385: }
2386:
2387: // Returns multiple values: mapping, mode.
2388: // mode == null means it's a global mapping.
2389: public Object[] getKeyMapping(String command) {
2390: Mode mode = null;
2391: // Look in buffer-local keymap first.
2392: KeyMapping mapping = buffer.getKeyMapForMode().getKeyMapping(
2393: command);
2394: // If not found there, try global keymap.
2395: if (mapping == null) {
2396: mapping = KeyMap.getGlobalKeyMap().getKeyMapping(command);
2397: // Don't let a global mapping hide a different mapping of the same
2398: // keystroke in the buffer-local keymap!
2399: if (mapping != null) {
2400: javax.swing.KeyStroke keyStroke = javax.swing.KeyStroke
2401: .getKeyStroke(mapping.getKeyCode(), mapping
2402: .getModifiers());
2403: if (buffer.getKeyMapForMode().lookup(keyStroke) != null)
2404: mapping = null;
2405: }
2406: } else
2407: mode = getMode();
2408: Object[] values = new Object[2];
2409: values[0] = mapping;
2410: values[1] = mode;
2411: return values;
2412: }
2413:
2414: public void pageDown() {
2415: if (dot == null)
2416: return;
2417: maybeResetGoalColumn();
2418: if (mark != null) {
2419: CompoundEdit compoundEdit = beginCompoundEdit();
2420: addUndo(SimpleEdit.MOVE);
2421: setMark(null);
2422: setUpdateFlag(REPAINT);
2423: pageDownInternal();
2424: endCompoundEdit(compoundEdit);
2425: } else
2426: pageDownInternal();
2427: setCurrentCommand(COMMAND_PAGE_DOWN);
2428: }
2429:
2430: public void pageDownOtherWindow() {
2431: final Editor ed = frame.getOtherEditor();
2432: if (ed != null) {
2433: ed.pageDown();
2434: ed.updateDisplay();
2435: }
2436: }
2437:
2438: public void selectPageDown() {
2439: if (dot == null)
2440: return;
2441: maybeResetGoalColumn();
2442: if (mark == null) {
2443: CompoundEdit compoundEdit = beginCompoundEdit();
2444: addUndo(SimpleEdit.MOVE);
2445: setMarkAtDot();
2446: pageDownInternal();
2447: endCompoundEdit(compoundEdit);
2448: } else
2449: pageDownInternal();
2450: setCurrentCommand(COMMAND_PAGE_DOWN);
2451: }
2452:
2453: public void pageUp() {
2454: if (dot == null)
2455: return;
2456: maybeResetGoalColumn();
2457: if (mark != null) {
2458: CompoundEdit compoundEdit = beginCompoundEdit();
2459: addUndo(SimpleEdit.MOVE);
2460: setMark(null);
2461: setUpdateFlag(REPAINT);
2462: pageUpInternal();
2463: endCompoundEdit(compoundEdit);
2464: } else
2465: pageUpInternal();
2466: setCurrentCommand(COMMAND_PAGE_UP);
2467: }
2468:
2469: public void pageUpOtherWindow() {
2470: final Editor ed = frame.getOtherEditor();
2471: if (ed != null) {
2472: ed.pageUp();
2473: ed.updateDisplay();
2474: }
2475: }
2476:
2477: public void selectPageUp() {
2478: if (dot == null)
2479: return;
2480: maybeResetGoalColumn();
2481: if (mark == null) {
2482: CompoundEdit compoundEdit = beginCompoundEdit();
2483: addUndo(SimpleEdit.MOVE);
2484: setMarkAtDot();
2485: pageUpInternal();
2486: endCompoundEdit(compoundEdit);
2487: } else
2488: pageUpInternal();
2489: setCurrentCommand(COMMAND_PAGE_UP);
2490: }
2491:
2492: private void pageDownInternal() {
2493: Debug.assertTrue(buffer.needsRenumbering == false);
2494:
2495: int topLineNumber = display.getTopLineNumber();
2496: Line dotLine = getDotLine();
2497: int dotLineNumber = dot.lineNumber();
2498: int numRows = display.getRows();
2499:
2500: Line[] lines = new Line[numRows];
2501: Line line = getTopLine();
2502: int dotRow = -1;
2503: for (int i = 0; i < numRows; i++) {
2504: lines[i] = line;
2505: if (line == dotLine)
2506: dotRow = i;
2507: if (line != null)
2508: line = line.nextVisible();
2509: }
2510: Line bottomLine = lines[numRows - 1];
2511:
2512: if (bottomLine == null) {
2513: // We're on the last page already.
2514: if (dotRow >= 0) {
2515: Position eob = getEob();
2516: if (eob != null) {
2517: addUndo(SimpleEdit.MOVE);
2518: updateDotLine();
2519: dot.setLine(eob.getLine());
2520: moveDotToGoalCol();
2521: updateDotLine();
2522: }
2523: }
2524: return;
2525: }
2526:
2527: // Not on last page.
2528: display.setTopLine(bottomLine);
2529: setUpdateFlag(REPAINT);
2530:
2531: if (dotRow >= 0) {
2532: line = getTopLine();
2533: for (int i = 0; i < dotRow; i++) {
2534: Line next = line.nextVisible();
2535: if (next == null)
2536: break;
2537: line = next;
2538: }
2539: addUndo(SimpleEdit.MOVE);
2540: dot.setLine(line);
2541: moveDotToGoalCol();
2542: }
2543: }
2544:
2545: private void pageUpInternal() {
2546: if (dot.getLine() == buffer.getFirstLine())
2547: return;
2548: Debug.assertTrue(buffer.needsRenumbering == false);
2549: int topLineNumber = display.getTopLineNumber();
2550: int dotLineNumber = dot.lineNumber();
2551: int linesToScroll = display.getRows() - 1;
2552: boolean dotLineIsVisible = false;
2553: if (dotLineNumber >= topLineNumber) {
2554: Line bottomLine = display.getBottomLine();
2555: if (bottomLine != null)
2556: if (dotLineNumber <= bottomLine.lineNumber())
2557: dotLineIsVisible = true;
2558: }
2559: addUndo(SimpleEdit.MOVE);
2560: if (dotLineIsVisible) {
2561: for (int i = 0; i < linesToScroll; i++) {
2562: Line topLinePrevious = getTopLine().previousVisible();
2563: if (topLinePrevious != null)
2564: display.setTopLine(topLinePrevious);
2565: Line dotLinePrevious = dot.getLine().previousVisible();
2566: if (dotLinePrevious == null)
2567: break;
2568: dot.setLine(dotLinePrevious);
2569: }
2570: } else {
2571: for (int i = 0; i < linesToScroll; i++) {
2572: Line dotLinePrevious = dot.getLine().previousVisible();
2573: if (dotLinePrevious == null)
2574: break;
2575: dot.setLine(dotLinePrevious);
2576: }
2577: }
2578: moveDotToGoalCol();
2579: setUpdateFlag(REPAINT);
2580: }
2581:
2582: // Move dot to beginning of block, no undo.
2583: public void beginningOfBlock() {
2584: if (mark != null) {
2585: Region r = new Region(buffer, mark, dot);
2586: dot.moveTo(r.getBegin());
2587: setMark(null);
2588: moveCaretToDotCol();
2589: if (r.getBeginLine() != r.getEndLine())
2590: setUpdateFlag(REPAINT);
2591: else
2592: updateDotLine();
2593: }
2594: }
2595:
2596: // Move dot to end of block, no undo.
2597: public void endOfBlock() {
2598: if (mark != null) {
2599: Region r = new Region(buffer, mark, dot);
2600: dot.moveTo(r.getEnd());
2601: setMark(null);
2602: moveCaretToDotCol();
2603: if (r.getBeginLine() != r.getEndLine())
2604: setUpdateFlag(REPAINT);
2605: else
2606: updateDotLine();
2607: }
2608: }
2609:
2610: public void right() {
2611: if (dot == null)
2612: return;
2613: if (buffer.getBooleanProperty(Property.RESTRICT_CARET)) {
2614: if (mark == null && getDotOffset() >= getDotLine().length()) {
2615: // Caret is at end of line.
2616: if (getDotLine().next() != null) {
2617: moveDotTo(getDotLine().next(), 0);
2618: setCurrentCommand(COMMAND_RIGHT);
2619: }
2620: return;
2621: }
2622: }
2623: if (mark != null || lastCommand != COMMAND_RIGHT)
2624: addUndo(SimpleEdit.MOVE);
2625: if (mark != null)
2626: endOfBlock();
2627: else if (dot.getOffset() < dot.getLineLength()) {
2628: dot.skip(1);
2629: moveCaretToDotCol();
2630: } else {
2631: ++display.caretCol;
2632: }
2633: updateDotLine();
2634: setCurrentCommand(COMMAND_RIGHT);
2635: setUpdateFlag(REFRAME);
2636: }
2637:
2638: public void selectRight() {
2639: if (dot == null)
2640: return;
2641: if (getDotOffset() < getDotLine().length()) {
2642: if (mark == null || lastCommand != COMMAND_RIGHT)
2643: addUndo(SimpleEdit.MOVE);
2644: if (mark == null)
2645: setMarkAtDot();
2646: dot.moveRight();
2647: moveCaretToDotCol();
2648: } else {
2649: // We're at or beyond the end of the line.
2650: if (buffer.getBooleanProperty(Property.RESTRICT_CARET)) {
2651: if (getDotLine().next() == null)
2652: return;
2653: if (mark == null || lastCommand != COMMAND_RIGHT)
2654: addUndo(SimpleEdit.MOVE);
2655: if (mark == null)
2656: setMarkAtDot();
2657: updateDotLine();
2658: dot.moveTo(getDotLine().next(), 0);
2659: moveCaretToDotCol();
2660: } else {
2661: // Don't start a new selection, since there's no text there.
2662: if (lastCommand != COMMAND_RIGHT)
2663: addUndo(SimpleEdit.MOVE);
2664: ++display.caretCol;
2665: }
2666: }
2667: updateDotLine();
2668: setCurrentCommand(COMMAND_RIGHT);
2669: setUpdateFlag(REFRAME);
2670: }
2671:
2672: public void left() {
2673: if (dot == null)
2674: return;
2675: final Line dotLine = getDotLine();
2676: final int dotOffset = getDotOffset();
2677: final Line prevLine = dotLine.previous();
2678: if (dotOffset == 0 && prevLine == null)
2679: return;
2680: if (mark != null || lastCommand != COMMAND_LEFT)
2681: addUndo(SimpleEdit.MOVE);
2682: if (mark != null)
2683: beginningOfBlock();
2684: else {
2685: int absCaretCol = display.getCaretCol()
2686: + display.getShift();
2687: if (absCaretCol > 0
2688: && absCaretCol <= buffer.getCol(dotLine, dotLine
2689: .length())) {
2690: // Back up one character.
2691: dot.setOffset(dotOffset - 1);
2692: moveCaretToDotCol();
2693: } else if (absCaretCol > 0) {
2694: // We're beyond the end of the text on the line.
2695: --display.caretCol;
2696: } else if (dot.getOffset() == 0) {
2697: // Back up to the end of the text on the previous line.
2698: update(dotLine);
2699: setDot(prevLine, prevLine.length());
2700: moveCaretToDotCol();
2701: } else {
2702: // There shouldn't be any other cases.
2703: Debug.assertTrue(false);
2704: }
2705: }
2706: updateDotLine();
2707: setCurrentCommand(COMMAND_LEFT);
2708: setUpdateFlag(REFRAME);
2709: }
2710:
2711: public void selectLeft() {
2712: if (dot == null)
2713: return;
2714: final Line dotLine = getDotLine();
2715: final int dotOffset = getDotOffset();
2716: final Line prevLine = dotLine.previous();
2717: if (dotOffset == 0 && prevLine == null)
2718: return;
2719: if (mark == null || lastCommand != COMMAND_LEFT)
2720: addUndo(SimpleEdit.MOVE);
2721:
2722: // Only start a new selection if we're over some actual text.
2723: final int absCaretCol = display.getAbsoluteCaretCol();
2724: final int end = buffer.getCol(dotLine, dotLine.length());
2725: if (mark == null && absCaretCol <= end)
2726: setMarkAtDot();
2727:
2728: if (absCaretCol > 0 && absCaretCol <= end) {
2729: // Back up one character.
2730: dot.moveLeft();
2731: moveCaretToDotCol();
2732: } else if (absCaretCol > 0) {
2733: // We're beyond the end of the text on the line.
2734: --display.caretCol;
2735: } else if (dotOffset == 0) {
2736: // Back up to the end of the text on the previous line.
2737: updateDotLine();
2738: setDot(prevLine, prevLine.length());
2739: moveCaretToDotCol();
2740: } else {
2741: // There shouldn't be any other cases.
2742: Debug.assertTrue(false);
2743: }
2744: updateDotLine();
2745: setCurrentCommand(COMMAND_LEFT);
2746: setUpdateFlag(REFRAME);
2747: }
2748:
2749: public final int getAbsoluteCaretCol() {
2750: return display.getAbsoluteCaretCol();
2751: }
2752:
2753: public final void setAbsoluteCaretCol(int col) {
2754: display.setAbsoluteCaretCol(col);
2755: }
2756:
2757: private int goalColumn;
2758:
2759: public final void setGoalColumn(int col) {
2760: goalColumn = col;
2761: }
2762:
2763: public void moveDotToGoalCol() {
2764: if (buffer.getBooleanProperty(Property.RESTRICT_CARET)) {
2765: final int limit = buffer.getCol(getDotLine(), getDotLine()
2766: .length());
2767: moveDotToCol(goalColumn > limit ? limit : goalColumn);
2768: moveCaretToDotCol();
2769: } else {
2770: setAbsoluteCaretCol(goalColumn);
2771: moveDotToCaretCol();
2772: }
2773: }
2774:
2775: // Move caret down one line, keeping it in the same column if possible.
2776: // Synchronize dot with caret.
2777: public void down() {
2778: maybeResetGoalColumn();
2779: display.down(false);
2780: setCurrentCommand(COMMAND_DOWN);
2781: }
2782:
2783: public void selectDown() {
2784: maybeResetGoalColumn();
2785: display.down(true);
2786: setCurrentCommand(COMMAND_DOWN);
2787: }
2788:
2789: // Move caret up one line, keeping it in the same column if possible.
2790: // Synchronize dot with caret.
2791: public void up() {
2792: maybeResetGoalColumn();
2793: display.up(false);
2794: setCurrentCommand(COMMAND_UP);
2795: }
2796:
2797: public void selectUp() {
2798: maybeResetGoalColumn();
2799: display.up(true);
2800: setCurrentCommand(COMMAND_UP);
2801: }
2802:
2803: private void maybeResetGoalColumn() {
2804: switch (lastCommand) {
2805: case COMMAND_UP:
2806: case COMMAND_DOWN:
2807: case COMMAND_PAGE_UP:
2808: case COMMAND_PAGE_DOWN:
2809: case COMMAND_WINDOW_UP:
2810: case COMMAND_WINDOW_DOWN:
2811: return;
2812: default:
2813: goalColumn = getAbsoluteCaretCol();
2814: return;
2815: }
2816: }
2817:
2818: public void windowUp() {
2819: maybeResetGoalColumn();
2820: display.windowUp();
2821: maybeScrollCaret();
2822: setCurrentCommand(COMMAND_WINDOW_UP);
2823: }
2824:
2825: public void windowDown() {
2826: maybeResetGoalColumn();
2827: display.windowDown();
2828: maybeScrollCaret();
2829: setCurrentCommand(COMMAND_WINDOW_DOWN);
2830: }
2831:
2832: public void maybeScrollCaret() {
2833: if (dot == null)
2834: return;
2835: // Don't scroll the caret if a region is selected!
2836: if (mark != null)
2837: return;
2838: if (buffer.getBooleanProperty(Property.SCROLL_CARET)) {
2839: final int dotLineNumber = dot.lineNumber();
2840: final Line topLine = display.getTopLine();
2841: if (dotLineNumber < topLine.lineNumber()) {
2842: // Caret is above window.
2843: addUndo(SimpleEdit.SCROLL_CARET);
2844: dot.moveTo(topLine, 0);
2845: updateDotLine();
2846: moveCaretToDotCol();
2847: goalColumn = 0;
2848: return;
2849: }
2850: final Line bottomLine = display.getBottomLine();
2851: if (dotLineNumber > bottomLine.lineNumber()) {
2852: // Caret is below window.
2853: addUndo(SimpleEdit.SCROLL_CARET);
2854: updateDotLine();
2855: dot.moveTo(bottomLine, 0);
2856: updateDotLine();
2857: moveCaretToDotCol();
2858: goalColumn = 0;
2859: }
2860: }
2861: }
2862:
2863: public void toCenter() {
2864: display.toCenter();
2865: }
2866:
2867: public void toTop() {
2868: display.toTop();
2869: }
2870:
2871: private void selectToPosition(Position pos) {
2872: if (!pos.equals(dot)
2873: || buffer.getCol(pos) != display.getAbsoluteCaretCol()) {
2874: addUndo(SimpleEdit.MOVE);
2875: if (mark == null)
2876: setMarkAtDot();
2877: if (pos.getLine() != getDotLine())
2878: setUpdateFlag(REPAINT);
2879: else
2880: updateDotLine();
2881: dot.moveTo(pos);
2882: moveCaretToDotCol();
2883: }
2884: }
2885:
2886: public void bol() {
2887: if (dot != null)
2888: moveDotTo(dot.getLine(), 0);
2889: }
2890:
2891: public void home() {
2892: if (dot == null)
2893: return;
2894: final boolean extend = prefs
2895: .getBooleanProperty(Property.EXTEND_HOME);
2896: Position pos;
2897: if (mark != null)
2898: pos = new Position(new Region(this ).getBegin());
2899: else
2900: pos = new Position(dot);
2901: int indent = pos.getLine().getIndentation();
2902: if (extend) {
2903: if (pos.getOffset() > indent)
2904: pos.setOffset(indent);
2905: else
2906: pos.setOffset(0);
2907: } else {
2908: if (pos.getOffset() > indent)
2909: pos.setOffset(indent);
2910: else if (pos.getOffset() == indent)
2911: pos.setOffset(0);
2912: else
2913: pos.setOffset(indent);
2914: }
2915: if (mark != null || !pos.equals(dot)
2916: || buffer.getCol(pos) != display.getAbsoluteCaretCol()) {
2917: moveDotTo(pos);
2918: setCurrentCommand(COMMAND_HOME);
2919: return;
2920: }
2921: // Reaching here, caret is already in column 0.
2922: if (!extend)
2923: return;
2924: if (System.currentTimeMillis()
2925: - dispatcher.getLastEventMillis() > 1000) {
2926: // Timed out.
2927: setUpdateFlag(REFRAME);
2928: setCurrentCommand(COMMAND_HOME);
2929: return;
2930: }
2931: if (lastCommand == COMMAND_HOME_HOME)
2932: pos = new Position(buffer.getFirstLine(), 0);
2933: else if (lastCommand == COMMAND_HOME) {
2934: pos = new Position(getTopLine(), 0);
2935: setUpdateFlag(REFRAME);
2936: setCurrentCommand(COMMAND_HOME_HOME);
2937: } else {
2938: setUpdateFlag(REFRAME);
2939: setCurrentCommand(COMMAND_HOME);
2940: return;
2941: }
2942: if (!pos.equals(dot)
2943: || buffer.getCol(pos) != display.getAbsoluteCaretCol())
2944: moveDotTo(pos);
2945: }
2946:
2947: public void selectHome() {
2948: if (dot == null)
2949: return;
2950: final boolean extend = prefs
2951: .getBooleanProperty(Property.EXTEND_HOME);
2952: Position pos;
2953: if (mark != null)
2954: pos = new Position(new Region(buffer, mark, dot).getBegin());
2955: else
2956: pos = new Position(dot);
2957: int indent = pos.getLine().getIndentation();
2958: if (extend) {
2959: if (pos.getOffset() > indent)
2960: pos.setOffset(indent);
2961: else
2962: pos.setOffset(0);
2963: } else {
2964: if (pos.getOffset() > indent)
2965: pos.setOffset(indent);
2966: else if (pos.getOffset() == indent)
2967: pos.setOffset(0);
2968: else
2969: pos.setOffset(indent);
2970: }
2971: if (mark != null || !pos.equals(dot)
2972: || buffer.getCol(pos) != display.getAbsoluteCaretCol()) {
2973: selectToPosition(pos);
2974: setUpdateFlag(REFRAME);
2975: setCurrentCommand(COMMAND_SELECT_HOME);
2976: return;
2977: }
2978: // Reaching here, caret is already in column 0.
2979: if (!prefs.getBooleanProperty(Property.EXTEND_HOME))
2980: return;
2981: if (System.currentTimeMillis()
2982: - dispatcher.getLastEventMillis() > 1000) {
2983: // Timed out.
2984: setUpdateFlag(REFRAME);
2985: setCurrentCommand(COMMAND_SELECT_HOME);
2986: return;
2987: }
2988: if (lastCommand == COMMAND_SELECT_HOME_HOME)
2989: pos = new Position(buffer.getFirstLine(), 0);
2990: else if (lastCommand == COMMAND_SELECT_HOME) {
2991: pos = new Position(getTopLine(), 0);
2992: setUpdateFlag(REFRAME);
2993: setCurrentCommand(COMMAND_SELECT_HOME_HOME);
2994: } else {
2995: setUpdateFlag(REFRAME);
2996: setCurrentCommand(COMMAND_SELECT_HOME);
2997: return;
2998: }
2999: if (!pos.equals(dot)
3000: || buffer.getCol(pos) != display.getAbsoluteCaretCol())
3001: selectToPosition(pos);
3002: }
3003:
3004: public void eol() {
3005: if (dot == null)
3006: return;
3007: if (mark != null
3008: || dot.getOffset() != dot.getLineLength()
3009: || buffer.getCol(dot) != display.getCaretCol()
3010: + display.getShift())
3011: moveDotTo(dot.getLine(), dot.getLineLength());
3012: }
3013:
3014: public void end() {
3015: if (dot == null)
3016: return;
3017: Position pos;
3018: if (mark != null)
3019: pos = new Position(new Region(buffer, mark, dot).getEnd());
3020: else
3021: pos = new Position(dot);
3022: pos.setOffset(pos.getLineLength());
3023: if (mark != null || !pos.equals(dot)
3024: || buffer.getCol(pos) != display.getAbsoluteCaretCol()) {
3025: moveDotTo(pos);
3026: setUpdateFlag(REFRAME);
3027: setCurrentCommand(COMMAND_END);
3028: return;
3029: }
3030: // Reaching here, caret is already at end of line.
3031: if (!prefs.getBooleanProperty(Property.EXTEND_END))
3032: return;
3033: if (System.currentTimeMillis()
3034: - dispatcher.getLastEventMillis() > 1000) {
3035: // Timed out.
3036: setUpdateFlag(REFRAME);
3037: setCurrentCommand(COMMAND_END);
3038: return;
3039: }
3040: if (lastCommand == COMMAND_END_END)
3041: pos = getEob();
3042: else if (lastCommand == COMMAND_END) {
3043: Line bottomLine = display.getBottomLine();
3044: if (bottomLine != null)
3045: pos = new Position(bottomLine, bottomLine.length());
3046: else
3047: pos = getEob();
3048: setUpdateFlag(REFRAME);
3049: setCurrentCommand(COMMAND_END_END);
3050: } else {
3051: setUpdateFlag(REFRAME);
3052: setCurrentCommand(COMMAND_END);
3053: return;
3054: }
3055: if (!pos.equals(dot)
3056: || buffer.getCol(pos) != display.getAbsoluteCaretCol())
3057: moveDotTo(pos);
3058: }
3059:
3060: public void selectEnd() {
3061: if (dot == null)
3062: return;
3063: Position pos;
3064: if (mark != null)
3065: pos = new Position(new Region(buffer, mark, dot).getEnd());
3066: else
3067: pos = new Position(dot);
3068: pos.setOffset(pos.getLineLength());
3069: if (!pos.equals(dot)
3070: || buffer.getCol(pos) != display.getAbsoluteCaretCol()) {
3071: selectToPosition(pos);
3072: setUpdateFlag(REFRAME);
3073: setCurrentCommand(COMMAND_END);
3074: return;
3075: }
3076: // Reaching here, caret is already at end of line.
3077: if (!prefs.getBooleanProperty(Property.EXTEND_END))
3078: return;
3079: if (System.currentTimeMillis()
3080: - dispatcher.getLastEventMillis() > 1000) {
3081: // Timed out.
3082: setUpdateFlag(REFRAME);
3083: setCurrentCommand(COMMAND_END);
3084: return;
3085: }
3086: if (lastCommand == COMMAND_END_END)
3087: pos = getEob();
3088: else if (lastCommand == COMMAND_END) {
3089: Line bottomLine = display.getBottomLine();
3090: if (bottomLine != null)
3091: pos = new Position(bottomLine, bottomLine.length());
3092: else
3093: pos = getEob();
3094: setUpdateFlag(REFRAME);
3095: setCurrentCommand(COMMAND_END_END);
3096: } else {
3097: setCurrentCommand(COMMAND_END);
3098: return;
3099: }
3100: if (!pos.equals(dot)
3101: || buffer.getCol(pos) != display.getAbsoluteCaretCol())
3102: selectToPosition(pos);
3103: }
3104:
3105: public void bob() {
3106: if (buffer.getFirstLine() != null)
3107: moveDotTo(buffer.getFirstLine(), 0);
3108: }
3109:
3110: public void selectBob() {
3111: if (dot == null)
3112: return;
3113: if (buffer.getFirstLine() == null)
3114: return;
3115: addUndo(SimpleEdit.MOVE);
3116: if (mark == null)
3117: setMarkAtDot();
3118: dot.moveTo(buffer.getFirstLine(), 0);
3119: moveCaretToDotCol();
3120: setUpdateFlag(REPAINT);
3121: }
3122:
3123: private final Position getEob() {
3124: return buffer.getEnd();
3125: }
3126:
3127: public void eob() {
3128: moveDotTo(getEob());
3129: }
3130:
3131: public void selectEob() {
3132: if (buffer.getFirstLine() == null)
3133: return;
3134: Line line = buffer.getFirstLine();
3135: while (line.next() != null)
3136: line = line.next();
3137: addUndo(SimpleEdit.MOVE);
3138: if (mark == null)
3139: setMarkAtDot();
3140: dot.moveTo(line, line.length());
3141: moveCaretToDotCol();
3142: setUpdateFlag(REPAINT);
3143: }
3144:
3145: public void top() {
3146: if (dot == null)
3147: return;
3148: if (getDotLine() != getTopLine()) {
3149: addUndo(SimpleEdit.MOVE);
3150: updateDotLine();
3151: dot.setLine(getTopLine());
3152: moveDotToCaretCol();
3153: }
3154: }
3155:
3156: public void bottom() {
3157: if (dot == null)
3158: return;
3159: Line line = display.getBottomLine();
3160: if (line != getDotLine()) {
3161: addUndo(SimpleEdit.MOVE);
3162: updateDotLine();
3163: dot.setLine(line);
3164: updateDotLine();
3165: moveDotToCaretCol();
3166: }
3167: }
3168:
3169: public void selectWord() {
3170: if (dot == null)
3171: return;
3172: AWTEvent e = dispatcher.getLastEvent();
3173: CompoundEdit compoundEdit = null;
3174: if (e instanceof MouseEvent) {
3175: compoundEdit = beginCompoundEdit();
3176: mouseMoveDotToPoint((MouseEvent) e);
3177: }
3178: if (inWord()) {
3179: addUndo(SimpleEdit.MOVE);
3180: while (getDotOffset() > 0) {
3181: dot.moveLeft();
3182: if (!inWord()) {
3183: dot.moveRight();
3184: break;
3185: }
3186: }
3187: setMarkAtDot();
3188: final int limit = getDotLine().length();
3189: while (inWord() && getDotOffset() < limit)
3190: dot.moveRight();
3191: moveCaretToDotCol();
3192: }
3193: if (compoundEdit != null)
3194: endCompoundEdit(compoundEdit);
3195: }
3196:
3197: public void mouseMoveDotToPoint() {
3198: AWTEvent e = dispatcher.getLastEvent();
3199: if (e instanceof MouseEvent)
3200: mouseMoveDotToPoint((MouseEvent) e);
3201: }
3202:
3203: public void mouseMoveDotToPoint(MouseEvent e) {
3204: addUndo(SimpleEdit.MOVE);
3205: if (mark != null)
3206: unmark();
3207: display.moveCaretToPoint(e.getPoint());
3208: if (buffer.getBooleanProperty(Property.RESTRICT_CARET))
3209: moveCaretToDotCol();
3210:
3211: // Dec 13 2002 6:30 PM
3212: // Without this, focus ends up in the location bar textfield if you
3213: // click in the edit window after using the openFile completion list
3214: // to open a file. Weird.
3215: Editor.restoreFocus();
3216: }
3217:
3218: public void mouseSelect() {
3219: if (dot != null) {
3220: AWTEvent e = dispatcher.getLastEvent();
3221: if (e instanceof MouseEvent) {
3222: MouseEvent mouseEvent = (MouseEvent) e;
3223: Position pos = display.positionFromPoint(mouseEvent
3224: .getPoint());
3225: addUndo(SimpleEdit.MOVE);
3226: Position min, max;
3227: if (mark == null) {
3228: // New selection.
3229: setMarkAtDot();
3230: dot = pos;
3231: moveCaretToDotCol();
3232: Region r = new Region(this );
3233: min = r.getBegin();
3234: max = r.getEnd();
3235: } else {
3236: // Adjust existing selection.
3237: Region r = new Region(this );
3238: min = r.getBegin();
3239: max = r.getEnd();
3240: if (pos.isBefore(r.getBegin())) {
3241: min = pos;
3242: mark = r.getEnd();
3243: } else if (pos.isAfter(r.getEnd())) {
3244: max = pos;
3245: mark = r.getBegin();
3246: } else {
3247: // Click was inside selected region.
3248: if (Position.getDistance(r.getBegin(), pos) < Position
3249: .getDistance(r.getEnd(), pos)) {
3250: // Click was closer to beginning of region.
3251: mark = r.getEnd();
3252: } else
3253: mark = r.getBegin();
3254: }
3255: dot = pos;
3256: moveCaretToDotCol();
3257: }
3258: // Minimize repaint.
3259: if (max.lineNumber() - min.lineNumber() < display
3260: .getRows()) {
3261: Line line = min.getLine();
3262: Line endLine = max.getLine();
3263: while (line != null && line != endLine) {
3264: update(line);
3265: line = line.next();
3266: }
3267: update(endLine);
3268: } else
3269: setUpdateFlag(REPAINT);
3270: }
3271: }
3272: }
3273:
3274: public void mouseSelectColumn() {
3275: if (dot != null) {
3276: AWTEvent e = dispatcher.getLastEvent();
3277: if (e instanceof MouseEvent) {
3278: MouseEvent mouseEvent = (MouseEvent) e;
3279: if (getMark() == null)
3280: setMarkAtDot();
3281: display.moveCaretToPoint(mouseEvent.getPoint());
3282: setColumnSelection(true);
3283: display.setUpdateFlag(REPAINT);
3284: }
3285: }
3286: }
3287:
3288: private JPopupMenu popup;
3289:
3290: public void mouseShowContextMenu() {
3291: AWTEvent e = dispatcher.getLastEvent();
3292: if (e instanceof MouseEvent) {
3293: MouseEvent mouseEvent = (MouseEvent) e;
3294: int x = mouseEvent.getX();
3295: int y = mouseEvent.getY();
3296: popup = buffer.getMode().getContextMenu(this );
3297: if (popup != null) {
3298: Dimension dimPopup = popup.getPreferredSize();
3299: Dimension dimDisplay = display.getSize();
3300: int xMax = dimDisplay.width - dimPopup.width - 5;
3301: int yMax = dimDisplay.height - dimPopup.height - 5;
3302: if (x > xMax)
3303: x = xMax;
3304: else
3305: ++x;
3306: if (y > yMax)
3307: y = yMax;
3308: else
3309: ++y;
3310: popup.show(mouseEvent.getComponent(), x, y);
3311: }
3312: }
3313: }
3314:
3315: public final JPopupMenu getPopup() {
3316: return popup;
3317: }
3318:
3319: public final void setPopup(JPopupMenu popup) {
3320: this .popup = popup;
3321: }
3322:
3323: public void killPopup() {
3324: if (popup != null) {
3325: popup.setVisible(false);
3326: popup = null;
3327: restoreFocus();
3328: }
3329: }
3330:
3331: private boolean inWord() {
3332: return getMode().isIdentifierPart(getDotChar());
3333: }
3334:
3335: private boolean inWhitespace() {
3336: return Character.isWhitespace(getDotChar());
3337: }
3338:
3339: private void skipWhitespace() {
3340: while (inWhitespace())
3341: if (!nextChar())
3342: break;
3343: }
3344:
3345: private void nextWord() {
3346: if (dot == null)
3347: return;
3348: if (inWord()) {
3349: while (nextChar())
3350: if (!inWord())
3351: break;
3352: skipWhitespace();
3353: } else if (inWhitespace()) {
3354: skipWhitespace();
3355: } else {
3356: // Not in word or whitespace.
3357: while (nextChar() && !inWord() && !inWhitespace())
3358: ;
3359: skipWhitespace();
3360: }
3361: }
3362:
3363: private void prevWord() {
3364: if (dot == null)
3365: return;
3366: if (!prevChar())
3367: return;
3368: if (inWord()) {
3369: while (prevChar() && inWord())
3370: ;
3371: if (!inWord())
3372: nextChar();
3373: } else if (inWhitespace()) {
3374: while (prevChar() && inWhitespace())
3375: ;
3376: if (inWord()) {
3377: while (prevChar() && inWord())
3378: ;
3379: if (!inWord())
3380: nextChar();
3381: } else {
3382: while (prevChar() && !inWord() && !inWhitespace())
3383: ;
3384: if (inWord() || inWhitespace())
3385: nextChar();
3386: }
3387: } else {
3388: // Not in word or whitespace.
3389: while (prevChar()) {
3390: if (inWord())
3391: break;
3392: if (inWhitespace())
3393: break;
3394: }
3395: if (inWord() || inWhitespace())
3396: nextChar();
3397: }
3398: }
3399:
3400: public void wordRight() {
3401: if (dot == null)
3402: return;
3403: updateDotLine();
3404: addUndo(SimpleEdit.MOVE);
3405: endOfBlock();
3406: nextWord();
3407: moveCaretToDotCol();
3408: updateDotLine();
3409: }
3410:
3411: public void wordLeft() {
3412: if (dot == null)
3413: return;
3414: updateDotLine();
3415: addUndo(SimpleEdit.MOVE);
3416: beginningOfBlock();
3417: prevWord();
3418: moveCaretToDotCol();
3419: updateDotLine();
3420: }
3421:
3422: public void selectWordRight() {
3423: if (dot == null)
3424: return;
3425: addUndo(SimpleEdit.MOVE);
3426: if (mark == null)
3427: setMarkAtDot();
3428: updateDotLine();
3429: nextWord();
3430: moveCaretToDotCol();
3431: updateDotLine();
3432: }
3433:
3434: public void selectWordLeft() {
3435: if (dot == null)
3436: return;
3437: addUndo(SimpleEdit.MOVE);
3438: if (mark == null)
3439: setMarkAtDot();
3440: updateDotLine();
3441: prevWord();
3442: moveCaretToDotCol();
3443: updateDotLine();
3444: }
3445:
3446: public void selectAll() {
3447: if (dot == null)
3448: return;
3449: pushPosition();
3450: addUndo(SimpleEdit.MOVE);
3451: unmark();
3452: Line line = buffer.getFirstLine();
3453: dot.moveTo(line, 0);
3454: display.setCaretCol(0);
3455: display.setShift(0);
3456: setMarkAtDot();
3457: while (line.next() != null)
3458: line = line.next();
3459: dot.moveTo(line, line.length());
3460: moveCaretToDotCol();
3461: display.setUpdateFlag(REPAINT);
3462: }
3463:
3464: // Moves dot to the requested absolute column, based on the tab size of
3465: // the buffer. If the requested column is past the end of the line, dot is
3466: // moved to the end of the line.
3467: public void moveDotToCol(int goal) {
3468: if (dot == null)
3469: return;
3470:
3471: dot.moveToCol(goal, buffer.getTabWidth());
3472: updateDotLine();
3473:
3474: // Support tab chars in buffer. If we're not beyond the end of the
3475: // line, make sure we're on an actual character.
3476: if (dot.getOffset() < dot.getLineLength())
3477: moveCaretToDotCol();
3478: }
3479:
3480: public final void moveDotToCaretCol() {
3481: moveDotToCol(display.getAbsoluteCaretCol());
3482: }
3483:
3484: public final void moveCaretToDotCol() {
3485: display.moveCaretToDotCol();
3486: }
3487:
3488: public final void repaintDisplay() {
3489: display.setUpdateFlag(REPAINT);
3490: display.repaint();
3491: }
3492:
3493: public final void repaintNow() {
3494: display.repaintNow();
3495: }
3496:
3497: // Adds whitespace to fill the area between the end of the actual text on
3498: // a line and the location of the caret, if it's beyond the end of the
3499: // text. Dot is moved to the end of the appended whitespace. Does nothing
3500: // if caret is not past end of text.
3501: public void fillToCaret() {
3502: final int where = display.getAbsoluteCaretCol();
3503: final Line dotLine = getDotLine();
3504: String s = getFillString(dotLine, where);
3505: if (s != null) {
3506: try {
3507: buffer.lockWrite();
3508: } catch (InterruptedException e) {
3509: Log.error(e);
3510: return;
3511: }
3512: try {
3513: addUndo(SimpleEdit.LINE_EDIT);
3514: dotLine.setText(dotLine.getText().concat(s));
3515: buffer.modified();
3516: dot.setOffset(dotLine.length());
3517: } finally {
3518: buffer.unlockWrite();
3519: }
3520: }
3521: }
3522:
3523: private String getFillString(Line line, int where) {
3524: int end = buffer.getCol(line, line.length());
3525: if (where <= end)
3526: return null;
3527: final int width = where - end;
3528:
3529: // For sanity, only use actual tab chars at beginning of line!
3530: if (buffer.getUseTabs() && line.length() == 0) {
3531: FastStringBuffer sb = new FastStringBuffer(width);
3532: int col = 0;
3533: final int tabWidth = buffer.getTabWidth();
3534: while (col + tabWidth <= width) {
3535: sb.append('\t');
3536: col += tabWidth;
3537: }
3538: while (col < width) {
3539: sb.append(' ');
3540: ++col;
3541: }
3542: return sb.toString();
3543: } else
3544: return Utilities.spaces(width);
3545: }
3546:
3547: public final int getDotCol() {
3548: return buffer.getCol(dot);
3549: }
3550:
3551: // Insert string at dot, put dot at end of inserted string.
3552: // No undo.
3553: public void insertStringInternal(String s) {
3554: updateInAllEditors(getDotLine());
3555: buffer.insertString(dot, s);
3556: }
3557:
3558: // Fills the space (if any) between dot and caret and inserts
3559: // the char in question.
3560: public void insertChar(char c) {
3561: final Line dotLine = getDotLine();
3562: if (getDotOffset() > dotLine.length()) {
3563: // Shouldn't happen.
3564: Debug.bug();
3565: Log.error("insertChar dot offset = " + getDotOffset()
3566: + " dotLine length = " + dotLine.length());
3567: // Enforce sanity and carry on.
3568: dot.setOffset(dotLine.length());
3569: }
3570: try {
3571: buffer.lockWrite();
3572: } catch (InterruptedException e) {
3573: Log.error(e);
3574: return;
3575: }
3576: try {
3577: addUndo(SimpleEdit.LINE_EDIT);
3578: fillToCaret();
3579: FastStringBuffer sb = new FastStringBuffer(dotLine
3580: .substring(0, getDotOffset()));
3581: sb.append(c);
3582: sb.append(dotLine.substring(getDotOffset()));
3583: dotLine.setText(sb.toString());
3584: dot.moveRight();
3585: moveCaretToDotCol();
3586: buffer.modified();
3587: } finally {
3588: buffer.unlockWrite();
3589: }
3590: updateInAllEditors(dotLine);
3591: }
3592:
3593: public void insertChar() {
3594: if (!checkReadOnly())
3595: return;
3596: String input = InputDialog.showInputDialog(this , "Character:",
3597: "Insert Character");
3598: if (input == null || input.length() == 0)
3599: return;
3600: repaintNow();
3601: int c = parseNumericInput(input);
3602: if (c >= 0 && c < 0xfffe)
3603: insertChar((char) c);
3604: else
3605: MessageDialog.showMessageDialog(this , "Invalid character",
3606: "Insert Character");
3607: }
3608:
3609: public void insertByte() {
3610: if (!checkReadOnly())
3611: return;
3612: String input = InputDialog.showInputDialog(this , "Byte:",
3613: "Insert Byte");
3614: if (input == null || input.length() == 0)
3615: return;
3616: repaintNow();
3617: int c = parseNumericInput(input);
3618: if (c >= 0 && c <= 255) {
3619: byte[] bytes = new byte[1];
3620: bytes[0] = (byte) c;
3621: String encoding = prefs
3622: .getStringProperty(Property.DEFAULT_ENCODING);
3623: try {
3624: String s = new String(bytes, encoding);
3625: insertChar(s.charAt(0));
3626: } catch (UnsupportedEncodingException e) {
3627: Log.error(e);
3628: MessageDialog.showMessageDialog(this ,
3629: "Unsupported encoding \"" + encoding + "\"",
3630: "Insert Byte");
3631: }
3632: } else
3633: MessageDialog.showMessageDialog(this , "Invalid byte \""
3634: + input + "\"", "Insert Byte");
3635: }
3636:
3637: // Used only by insertChar and insertByte. Doesn't understand a leading
3638: // minus sign.
3639: private static int parseNumericInput(String input) {
3640: int n = -1;
3641: input = input.trim();
3642: try {
3643: if (input.startsWith("0x") || input.startsWith("0X"))
3644: n = Integer.parseInt(input.substring(2), 16);
3645: else if (input.startsWith("0"))
3646: n = Integer.parseInt(input, 8);
3647: else
3648: n = Integer.parseInt(input, 10);
3649: } catch (NumberFormatException e) {
3650: Log.error(e);
3651: }
3652: return n;
3653: }
3654:
3655: public void electricSemi() {
3656: if (!checkReadOnly())
3657: return;
3658: if (mark != null || getDotLine().flags() == STATE_COMMENT
3659: || getMode().isInQuote(buffer, dot)) {
3660: insertNormalChar(';');
3661: } else {
3662: CompoundEdit compoundEdit = beginCompoundEdit();
3663: insertChar(';');
3664: moveCaretToDotCol();
3665: indentLine();
3666: if (buffer.getBooleanProperty(Property.AUTO_NEWLINE)) {
3667: boolean b = true;
3668: String s = dot.getLine().trim();
3669: if (s.startsWith("for")) {
3670: char c = s.charAt(3);
3671: if (c == ' ' || c == '\t' || c == '(')
3672: b = false;
3673: }
3674: if (b)
3675: newlineAndIndent();
3676: }
3677: endCompoundEdit(compoundEdit);
3678: }
3679: }
3680:
3681: public void electricColon() {
3682: if (!checkReadOnly())
3683: return;
3684: try {
3685: buffer.lockWrite();
3686: } catch (InterruptedException e) {
3687: Log.error(e);
3688: return;
3689: }
3690: try {
3691: electricColonInternal();
3692: } finally {
3693: buffer.unlockWrite();
3694: }
3695: }
3696:
3697: private void electricColonInternal() {
3698: final Line dotLine = getDotLine();
3699: final int dotOffset = getDotOffset();
3700: if (mark != null || dotOffset != dotLine.length()) {
3701: insertNormalChar(':');
3702: return;
3703: }
3704: if (dotLine.flags() == STATE_COMMENT
3705: || getMode().isInQuote(buffer, dot)) {
3706: insertNormalChar(':');
3707: return;
3708: }
3709: CompoundEdit compoundEdit = beginCompoundEdit();
3710: insertChar(':');
3711: moveCaretToDotCol();
3712: indentLine();
3713: if (buffer.getBooleanProperty(Property.AUTO_NEWLINE))
3714: newlineAndIndent();
3715: endCompoundEdit(compoundEdit);
3716: }
3717:
3718: public void electricStar() {
3719: if (!checkReadOnly())
3720: return;
3721:
3722: // The intention here is to line up the '*' under the '*' of
3723: // the previous line if the current line is blank and if the
3724: // previous line begins with "/*".
3725: if (getDotLine().isBlank()) {
3726: if (buffer.needsParsing()) {
3727: if (getFormatter().parseBuffer())
3728: buffer.repaint();
3729: }
3730: CompoundEdit compoundEdit = beginCompoundEdit();
3731: insertNormalChar('*');
3732: indentLine();
3733: endCompoundEdit(compoundEdit);
3734: } else
3735: insertNormalChar('*');
3736: }
3737:
3738: public void electricPound() {
3739: if (!checkReadOnly())
3740: return;
3741: if (mark == null && getDotLine().isBlank()) {
3742: try {
3743: buffer.lockWrite();
3744: } catch (InterruptedException e) {
3745: Log.error(e);
3746: return;
3747: }
3748: try {
3749: addUndo(SimpleEdit.LINE_EDIT);
3750: getDotLine().setText("#");
3751: dot.setOffset(1);
3752: buffer.modified();
3753: } finally {
3754: buffer.unlockWrite();
3755: }
3756: updateInAllEditors(getDotLine());
3757: moveCaretToDotCol();
3758: } else
3759: insertNormalChar('#');
3760: }
3761:
3762: public void electricOpenBrace() {
3763: electricBraceInternal('{');
3764: }
3765:
3766: public void electricCloseBrace() {
3767: electricBraceInternal('}');
3768: }
3769:
3770: private void electricBraceInternal(char c) {
3771: if (!checkReadOnly())
3772: return;
3773: CompoundEdit compoundEdit = beginCompoundEdit();
3774: if (mark == null && getDotLine().isBlank()) {
3775: addUndo(SimpleEdit.LINE_EDIT);
3776: getDotLine().setText("");
3777: dot.setOffset(0);
3778: insertChar(c);
3779: indentLine();
3780: eol();
3781: if (buffer.getBooleanProperty(Property.AUTO_NEWLINE))
3782: newlineAndIndent();
3783: } else {
3784: insertNormalChar(c);
3785: indentLine();
3786: }
3787: endCompoundEdit(compoundEdit);
3788: }
3789:
3790: public void electricCloseAngleBracket() {
3791: if (!checkReadOnly())
3792: return;
3793: if (mark == null) {
3794: int modeId = getModeId();
3795: if (modeId == XML_MODE || modeId == HTML_MODE) {
3796: CompoundEdit compoundEdit = beginCompoundEdit();
3797: insertChar('>');
3798: moveCaretToDotCol();
3799: if (buffer.getBooleanProperty(Property.AUTO_INDENT)) {
3800: if (modeId == HTML_MODE
3801: && getDotLine()
3802: .substring(0, getDotOffset())
3803: .endsWith("</pre>")) {
3804: ; // No autoindent after "</pre>" in HTML mode.
3805: } else {
3806: indentLine();
3807: }
3808: }
3809: endCompoundEdit(compoundEdit);
3810: return;
3811: }
3812: }
3813: // Otherwise...
3814: insertNormalChar('>');
3815: }
3816:
3817: public void gotoline(int lineNumber) {
3818: Line line = buffer.getLine(lineNumber);
3819: if (line != null)
3820: setDot(line, 0);
3821: }
3822:
3823: public void saveState() {
3824: if (saveSession) {
3825: // Make sure information about current buffer is up-to-date.
3826: saveView();
3827:
3828: Session.saveDefaultSession();
3829: if (sessionName != null)
3830: if (prefs
3831: .getBooleanProperty(Property.AUTOSAVE_NAMED_SESSIONS))
3832: Session.saveCurrentSession();
3833: sessionProperties.saveWindowPlacement();
3834: sessionProperties.save();
3835: }
3836: }
3837:
3838: // It might make sense to move this code into the Buffer class.
3839: public void reload(Buffer buf) {
3840: setWaitCursor();
3841: if (buf.getFile() instanceof SshFile)
3842: return; // Not supported.
3843: Debug.assertTrue(SwingUtilities.isEventDispatchThread());
3844: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
3845: Editor ed = it.nextEditor();
3846: if (ed.getBuffer() == buf)
3847: ed.saveView();
3848: }
3849:
3850: // May be asynchronous.
3851: buf.reload();
3852: setDefaultCursor();
3853: }
3854:
3855: public void revertBuffer() {
3856: final File file = buffer.getFile();
3857: if (file instanceof SshFile)
3858: return; // Not supported.
3859: if (buffer.isModified()) {
3860: String prompt = "Discard changes to "
3861: + file.canonicalPath() + "?";
3862: if (!confirm("Revert Buffer", prompt))
3863: return;
3864: reload(buffer);
3865: }
3866: }
3867:
3868: // Returns true if the buffer is active and there has been some change
3869: // that requires us to redraw the menus, title bar or display, false
3870: // otherwise.
3871: public boolean reactivate(Buffer buf) {
3872: if (buf instanceof ImageBuffer)
3873: return ((ImageBuffer) buf).reactivate();
3874:
3875: if (buf.getType() != Buffer.TYPE_NORMAL)
3876: return false;
3877: if (buf.isUntitled())
3878: return false;
3879:
3880: // BUG! Why don't we reload binary mode buffers?
3881: if (buf.getModeId() == BINARY_MODE)
3882: return false;
3883:
3884: final File file = buf.getFile();
3885: if (file == null || file.isRemote() || !file.isFile())
3886: return false;
3887:
3888: boolean changed = false;
3889:
3890: // Check read-only status even if buffer is not loaded so buffer list
3891: // will be correct.
3892: if (buf.readOnly == file.canWrite()) {
3893: // Read-only status has changed.
3894: buf.readOnly = !buf.readOnly;
3895: changed = true;
3896: // Let the user know if the file associated with a modified buffer
3897: // is no longer writable.
3898: if (buf.readOnly && buf.isLoaded() && buf.isModified())
3899: MessageDialog.showMessageDialog(file.canonicalPath()
3900: .concat(" is no longer writable"), "Warning");
3901: }
3902:
3903: if (buf.isLoaded()) {
3904: if (file.lastModified() != buf.getLastModified()) {
3905: if (buf.isModified()) {
3906: String prompt = file.canonicalPath()
3907: + " has changed on disk. Reload and lose current changes?";
3908: if (confirm("Reload File From Disk", prompt)) {
3909: reload(buf);
3910: changed = true;
3911: } else
3912: buf.setLastModified(file.lastModified());
3913: } else {
3914: // No need for confirmation.
3915: reload(buf);
3916: changed = true;
3917: }
3918: }
3919: }
3920:
3921: return changed;
3922: }
3923:
3924: public void setFocus(JComponent c) {
3925: frame.setFocus(c);
3926: }
3927:
3928: public JComponent getFocusedComponent() {
3929: return frame.getFocusedComponent();
3930: }
3931:
3932: public void setFocusToDisplay() {
3933: frame.setFocus(display);
3934: }
3935:
3936: public static final void restoreFocus() {
3937: Runnable r = new Runnable() {
3938: public void run() {
3939: if (currentEditor != null)
3940: currentEditor.setFocusToDisplay();
3941: }
3942: };
3943: SwingUtilities.invokeLater(r);
3944: }
3945:
3946: public void componentHidden(ComponentEvent e) {
3947: }
3948:
3949: public void componentMoved(ComponentEvent e) {
3950: }
3951:
3952: public void componentResized(ComponentEvent e) {
3953: updateScrollBars();
3954: }
3955:
3956: public void componentShown(ComponentEvent e) {
3957: }
3958:
3959: public void mouseWheelMoved(MouseWheelEvent e) {
3960: // Without this, focus ends up in the location bar textfield if you use
3961: // the mouse wheel in the edit window after using the openFile
3962: // completion list to open a file (Blackdown 1.4.1-01).
3963: // See also mouseMoveDotToPoint(MouseEvent).
3964: setFocusToDisplay();
3965:
3966: if (e.getWheelRotation() < 0)
3967: display.windowUp(5);
3968: else
3969: display.windowDown(5);
3970: }
3971:
3972: public void ensureActive() {
3973: if (!frame.isActive()) {
3974: for (int i = 0; i < getFrameCount(); i++) {
3975: Frame f = getFrame(i);
3976: if (f.isActive()) {
3977: f.dispatchEvent(new WindowEvent(f,
3978: WindowEvent.WINDOW_DEACTIVATED));
3979: break;
3980: }
3981: }
3982: frame.dispatchEvent(new WindowEvent(frame,
3983: WindowEvent.WINDOW_ACTIVATED));
3984: }
3985: }
3986:
3987: public void quit() {
3988: maybeExit();
3989: }
3990:
3991: public void saveAllExit() {
3992: tagFileManager.setEnabled(false);
3993: saveAll();
3994: maybeExit(); // May never return.
3995: tagFileManager.setEnabled(true);
3996: }
3997:
3998: private void maybeExit() {
3999: int numModifiedBuffers = 0;
4000:
4001: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
4002: if (it.nextBuffer().isModified())
4003: ++numModifiedBuffers;
4004: }
4005:
4006: if (numModifiedBuffers > 0) {
4007: FastStringBuffer sb = new FastStringBuffer(
4008: "Really exit with ");
4009: sb.append(numModifiedBuffers);
4010: sb.append(" modifed buffer");
4011: if (numModifiedBuffers > 1)
4012: sb.append('s');
4013: sb.append('?');
4014: if (!confirm("Really exit?", sb.toString()))
4015: return;
4016: }
4017:
4018: setWaitCursor();
4019:
4020: saveState();
4021: RecentFiles.getInstance().save();
4022:
4023: // Delete all autosave files.
4024: for (BufferIterator iter = new BufferIterator(); iter.hasNext();)
4025: iter.nextBuffer().deleteAutosaveFile();
4026:
4027: Autosave.deleteCatalogFile();
4028:
4029: // Call dispose on all buffers.
4030: for (BufferIterator it = new BufferIterator(); it.hasNext();)
4031: it.nextBuffer().dispose();
4032:
4033: // Clean up temporary directory.
4034: Directories.cleanTempDirectory();
4035:
4036: // Clean up cache directory.
4037: Cache.cleanup();
4038:
4039: Server.stopServer();
4040: pendingOperations.run();
4041: setDefaultCursor();
4042: System.exit(0);
4043: }
4044:
4045: public void killFrame() {
4046: if (getFrameCount() == 1) {
4047: // Does not return if OK to exit.
4048: maybeExit();
4049: } else {
4050: // Move frame being closed to end of list.
4051: if (indexOf(frame) != getFrameCount() - 1) {
4052: frames.remove(frame);
4053: frames.add(frame);
4054: }
4055: sessionProperties.saveWindowPlacement();
4056: frames.remove(frame);
4057: frame.dispose();
4058:
4059: Editor ed = frame.getSecondaryEditor();
4060: if (ed != null)
4061: removeEditor(ed);
4062: removeEditor(this );
4063: setCurrentEditor(getEditor(0));
4064: }
4065: }
4066:
4067: // See if we have the requested file in a buffer. If not, and if the file
4068: // actually exists, make a new buffer for it.
4069: public static Buffer getBuffer(File file) {
4070: if (file == null)
4071: return null;
4072: Buffer buf = bufferList.findBuffer(file);
4073: if (buf != null)
4074: return buf;
4075: if (file.isRemote())
4076: return Buffer.createBuffer(file);
4077: if (file.isDirectory())
4078: return new Directory(file);
4079: if (file.isFile()) {
4080: if (!file.canRead()) {
4081: MessageDialog.showMessageDialog("File is not readable",
4082: "Error");
4083: return null;
4084: }
4085: return Buffer.createBuffer(file);
4086: }
4087: if (file.exists()) {
4088: // The file exists, but it's neither a directory nor a "normal"
4089: // file. This can occur on Linux if an SMB mount goes south.
4090:
4091: // Not a very informative error message, but this is what bash says
4092: // in the SMB mount case.
4093: currentEditor.status("I/O error");
4094: }
4095: return null;
4096: }
4097:
4098: public void nextBuffer() {
4099: Buffer buf = bufferList.getNextPrimaryBuffer(buffer);
4100: if (buf == null)
4101: return;
4102: if (buf.isPaired()) {
4103: Buffer secondary = buf.getSecondary();
4104: if (secondary != null) {
4105: if (secondary.getLastActivated() > buf
4106: .getLastActivated())
4107: buf = secondary;
4108: }
4109: }
4110: if (buf != buffer)
4111: switchToBuffer(buf);
4112: }
4113:
4114: public void prevBuffer() {
4115: Buffer buf = bufferList.getPreviousPrimaryBuffer(buffer);
4116: if (buf == null)
4117: return;
4118: if (buf.isPaired()) {
4119: Buffer secondary = buf.getSecondary();
4120: if (secondary != null) {
4121: if (secondary.getLastActivated() > buf
4122: .getLastActivated())
4123: buf = secondary;
4124: }
4125: }
4126: if (buf != buffer)
4127: switchToBuffer(buf);
4128: }
4129:
4130: public void switchToBuffer(Buffer buf) {
4131: if (buf != null) {
4132: if (!buf.isPaired()
4133: && (buffer == null || !buffer.isPaired())) {
4134: // This is the easy case. Both the buffer we're switching in
4135: // and the buffer we're switching out are unpaired.
4136: activate(buf);
4137: } else {
4138: // We're either switching in a paired buffer or switching out
4139: // a paired buffer (or both). Delegate to our frame, since we
4140: // may end up closing this editor.
4141: frame.switchToBuffer(buf);
4142: }
4143: Sidebar sidebar = getSidebar();
4144: if (sidebar != null)
4145: sidebar.setBuffer();
4146: } else
4147: Debug.bug();
4148: }
4149:
4150: public void makeNext(final Buffer buf) {
4151: bufferList.makeNext(buf, buffer);
4152: }
4153:
4154: public void newBuffer() {
4155: Buffer buf = new Buffer(0);
4156: makeNext(buf);
4157: switchToBuffer(buf);
4158: }
4159:
4160: public final void openFile() {
4161: AWTEvent e = dispatcher.getLastEvent();
4162: if (e != null && e.getSource() instanceof MenuItem) {
4163: Runnable r = new Runnable() {
4164: public void run() {
4165: setFocusToTextField();
4166: }
4167: };
4168: SwingUtilities.invokeLater(r);
4169: } else
4170: setFocusToTextField();
4171: }
4172:
4173: public void openFileInOtherWindow() {
4174: Debug.assertTrue(locationBar != null);
4175: saveView();
4176: boolean alreadySplit = (getOtherEditor() != null);
4177: if (!alreadySplit)
4178: splitWindow();
4179: final Editor ed = getOtherEditor();
4180: if (ed.getLocationBar() != null) {
4181: Runnable r = new Runnable() {
4182: public void run() {
4183: frame.setFocus(ed.getLocationBar().getTextField());
4184: }
4185: };
4186: SwingUtilities.invokeLater(r);
4187: }
4188: setCurrentEditor(ed);
4189: if (alreadySplit) {
4190: // Current editor has changed.
4191: repaint();
4192: ed.repaint();
4193: }
4194: }
4195:
4196: public Buffer openFile(File file) {
4197: Buffer buf = getBuffer(file);
4198: if (buf != null) {
4199: Debug.assertTrue(bufferList.contains(buf));
4200: return buf;
4201: }
4202: if (file.isRemote())
4203: return null;
4204: // File is local.
4205: if (!file.exists()) {
4206: if (confirm("Create file?", file.canonicalPath()
4207: + " does not exist. Create?"))
4208: return Buffer.createBuffer(file);
4209: }
4210: return null;
4211: }
4212:
4213: public Buffer openFiles(List list) {
4214: if (list == null)
4215: return null;
4216: final int listSize = list.size();
4217: if (listSize < 2)
4218: return null;
4219: Buffer toBeActivated = null;
4220: // First string is directory.
4221: String dirname = (String) list.get(0);
4222: File directory = File.getInstance(dirname);
4223: History openFileHistory = new History("openFile.file");
4224: int lineNumber = -1;
4225: for (int i = 1; i < listSize; i++) {
4226: String s = (String) list.get(i);
4227: if (s == null || s.length() == 0)
4228: continue;
4229: if (s.charAt(0) == '+') {
4230: try {
4231: lineNumber = Integer.parseInt(s.substring(1)) - 1;
4232: } catch (NumberFormatException e) {
4233: }
4234: continue;
4235: }
4236: // Aliases.
4237: String value = getAlias(s);
4238: if (value != null)
4239: s = value;
4240: if (s.startsWith("pop://") || s.startsWith("{")) {
4241: MailboxURL url = MailboxURL.parse(s);
4242: if (url != null) {
4243: Buffer buf = MailCommands.getMailboxBuffer(this ,
4244: url);
4245: if (buf != null) {
4246: makeNext(buf);
4247: toBeActivated = buf;
4248: }
4249: }
4250: continue;
4251: }
4252: File file = File.getInstance(directory, s);
4253: if (file == null) {
4254: MessageDialog.showMessageDialog(this , "Invalid path "
4255: .concat(s), "Invalid Path");
4256: continue;
4257: }
4258: if (Utilities.isFilenameAbsolute(s) || s.startsWith("./")
4259: || s.startsWith(".\\"))
4260: ; // No tricks.
4261: else if (!file.exists()) {
4262: // Look in source and include paths as appropriate.
4263: File f = Utilities.findFile(this , s);
4264: if (f != null)
4265: file = f;
4266: else {
4267: // Not found in source or include path.
4268: if (s.startsWith("www."))
4269: file = File.getInstance("http://".concat(s));
4270: else if (s.startsWith("ftp."))
4271: file = File.getInstance("ftp://".concat(s));
4272: }
4273: }
4274: if (file.isLocal() && !file.exists()) {
4275: if (!Utilities.checkParentDirectory(file, "Open File"))
4276: continue;
4277: }
4278: Buffer buf = openFile(file);
4279: if (buf != null) {
4280: Debug.assertTrue(bufferList.contains(buf));
4281: openFileHistory.append(file.netPath());
4282: if (lineNumber >= 0) {
4283: // Line number was specified on command line.
4284: if (buf.isLoaded()) {
4285: if (buf == buffer) {
4286: // Current buffer.
4287: Line line = buffer.getLine(lineNumber);
4288: if (line == null)
4289: eob();
4290: else {
4291: addUndo(SimpleEdit.MOVE);
4292: updateDotLine();
4293: dot.moveTo(line, 0);
4294: updateDotLine();
4295: moveCaretToDotCol();
4296: }
4297: } else {
4298: // Not current buffer.
4299: Buffer oldBuffer = buffer;
4300: if (buffer != null)
4301: saveView();
4302: buffer = buf;
4303: findOrCreateView(buffer);
4304: restoreView();
4305: addUndo(SimpleEdit.MOVE);
4306: Line line = buffer.getLine(lineNumber);
4307: if (line == null) {
4308: line = buffer.getFirstLine();
4309: while (line.next() != null)
4310: line = line.next();
4311: dot.moveTo(line, line.length());
4312: } else
4313: dot.moveTo(line, 0);
4314: saveView();
4315: View view = (View) views.get(buffer);
4316: if (view != null) {
4317: view.shift = 0;
4318: view.caretCol = getDotCol();
4319: }
4320: buffer = oldBuffer;
4321: if (buffer != null)
4322: restoreView();
4323: }
4324: } else {
4325: // Not yet loaded.
4326: View view = findOrCreateView(buf);
4327: view.lineNumber = lineNumber;
4328: view.offs = 0;
4329: }
4330: }
4331: Debug.assertTrue(bufferList.contains(buf));
4332: makeNext(buf);
4333: Debug.assertTrue(bufferList.contains(buf));
4334: toBeActivated = buf;
4335: }
4336: }
4337: openFileHistory.save();
4338: return toBeActivated;
4339: }
4340:
4341: public void unmark() {
4342: if (mark != null) {
4343: setMark(null);
4344: display.setUpdateFlag(REPAINT); // BUG! Not always necessary!
4345: }
4346: }
4347:
4348: public void cancelBackgroundProcess() {
4349: BackgroundProcess backgroundProcess = buffer
4350: .getBackgroundProcess();
4351: if (backgroundProcess != null)
4352: backgroundProcess.cancel();
4353: }
4354:
4355: // Returns after doing exactly one thing.
4356: public void escape() {
4357: // Cancel background process (if any).
4358: BackgroundProcess backgroundProcess = buffer
4359: .getBackgroundProcess();
4360: if (backgroundProcess != null) {
4361: backgroundProcess.cancel();
4362: return;
4363: }
4364:
4365: if (popup != null) {
4366: if (popup.isVisible()) {
4367: killPopup();
4368: return;
4369: }
4370: popup = null;
4371: // We haven't really done anything yet. Fall through...
4372: }
4373:
4374: if (lastCommand == COMMAND_EXPAND) {
4375: Expansion expansion = Expansion.getLastExpansion();
4376: if (expansion != null) {
4377: expansion.undo(this );
4378: return;
4379: }
4380: }
4381:
4382: if (buffer instanceof RemoteBuffer && buffer.isEmpty()) {
4383: killBuffer();
4384: return;
4385: }
4386:
4387: if (escapeInternal())
4388: return;
4389:
4390: if (selection != null && selection.getSavedDot() != null)
4391: moveDotTo(selection.getSavedDot());
4392: else if (mark != null)
4393: moveDotTo(mark);
4394: }
4395:
4396: public boolean escapeInternal() {
4397: if (buffer instanceof CompilationBuffer) {
4398: if (buffer.unsplitOnClose()) {
4399: buffer.windowClosing();
4400: otherWindow();
4401: unsplitWindow();
4402: }
4403: maybeKillBuffer(buffer);
4404: restoreFocus();
4405: Sidebar.refreshSidebarInAllFrames();
4406: return true;
4407: }
4408: if (buffer.isTransient()) {
4409: if (buffer.unsplitOnClose()) {
4410: buffer.windowClosing();
4411: otherWindow();
4412: unsplitWindow();
4413: }
4414: maybeKillBuffer(buffer);
4415: restoreFocus();
4416: Sidebar.refreshSidebarInAllFrames();
4417: return true;
4418: }
4419: if (buffer.getModeId() == CHECKIN_MODE) {
4420: otherWindow();
4421: unsplitWindow();
4422: if (!buffer.isModified())
4423: maybeKillBuffer(buffer);
4424: restoreFocus();
4425: return true;
4426: }
4427: // Check for transient buffer in other editor in current frame.
4428: Editor ed = getOtherEditor();
4429: if (ed != null) {
4430: Buffer buf = ed.getBuffer();
4431: if (buf instanceof CompilationBuffer || buf.isTransient()) {
4432: if (buf.unsplitOnClose())
4433: unsplitWindow();
4434: maybeKillBuffer(buf);
4435: if (!buf.unsplitOnClose())
4436: ed.updateDisplay();
4437: Sidebar.refreshSidebarInAllFrames();
4438: return true;
4439: }
4440: if (buf.getModeId() == CHECKIN_MODE) {
4441: unsplitWindow();
4442: if (!buf.isModified())
4443: maybeKillBuffer(buf);
4444: return true;
4445: }
4446: }
4447: return false;
4448: }
4449:
4450: public void stamp() {
4451: if (!checkReadOnly())
4452: return;
4453: Date now = new Date(System.currentTimeMillis());
4454: String dateString = null;
4455: String stampFormat = buffer
4456: .getStringProperty(Property.STAMP_FORMAT);
4457: if (stampFormat != null) {
4458: try {
4459: SimpleDateFormat df = new SimpleDateFormat(stampFormat);
4460: dateString = df.format(now);
4461: } catch (Throwable t) {
4462: // Fall through...
4463: }
4464: }
4465: if (dateString == null) {
4466: SimpleDateFormat df = new SimpleDateFormat(
4467: "MMM d yyyy h:mm a");
4468: dateString = df.format(now);
4469: }
4470: try {
4471: buffer.lockWrite();
4472: } catch (InterruptedException e) {
4473: Log.error(e);
4474: return;
4475: }
4476: try {
4477: CompoundEdit compoundEdit = beginCompoundEdit();
4478: if (mark != null)
4479: delete();
4480: fillToCaret();
4481: addUndo(SimpleEdit.INSERT_STRING);
4482: insertStringInternal(dateString);
4483: buffer.modified();
4484: endCompoundEdit(compoundEdit);
4485: moveCaretToDotCol();
4486: updateInAllEditors(getDotLine());
4487: } finally {
4488: buffer.unlockWrite();
4489: }
4490: }
4491:
4492: public Search getSearchAtDot() {
4493: if (dot == null)
4494: return null;
4495: String pattern = null;
4496: boolean wholeWordsOnly = false;
4497: if (mark != null) {
4498: // No action if there's a multi-line selection.
4499: if (getMarkLine() == getDotLine())
4500: pattern = (new Region(buffer, dot, mark)).toString();
4501: } else {
4502: pattern = tokenAt(dot);
4503: wholeWordsOnly = true;
4504: }
4505: if (pattern != null && pattern.length() != 0)
4506: return new Search(pattern, false, wholeWordsOnly);
4507: else
4508: return null;
4509: }
4510:
4511: // Assumes dot is on first char of found pattern.
4512: public void markFoundPattern(Search search) {
4513: if (search.isRegularExpression() && search.isMultilinePattern()) {
4514: REMatch match = search.getMatch();
4515: if (match != null) {
4516: setDot(buffer.getPosition(match.getStartIndex()));
4517: setMark(buffer.getPosition(match.getEndIndex()));
4518: final Line markLine = getMarkLine();
4519: for (Line line = getDotLine(); line != null; line = line
4520: .next()) {
4521: update(line);
4522: if (line == markLine)
4523: break;
4524: }
4525: moveCaretToDotCol();
4526: }
4527: } else {
4528: final int context = 2; // This could be a preference.
4529: Position saved = dot.copy();
4530:
4531: // Move dot to end of found pattern.
4532: int length;
4533: if (search.getMatch() != null)
4534: length = search.getMatch().toString().length();
4535: else
4536: length = search.getPatternLength();
4537:
4538: // Found pattern might go beyond end of line.
4539: dot.setOffset(Math.min(dot.getOffset() + length,
4540: getDotLine().length()));
4541:
4542: // Set mark at end of pattern.
4543: moveCaretToDotCol();
4544: setMarkAtDot();
4545:
4546: // Make sure end of pattern is actually visible, with additional
4547: // context as appropriate.
4548: int absCol = getDotCol() + context;
4549: display.ensureColumnVisible(getDotLine(), absCol);
4550:
4551: // Restore dot to original position at start of pattern.
4552: dot = saved;
4553:
4554: // Make sure start of pattern is actually visible, with additional
4555: // context as appropriate.
4556: absCol = getDotCol() - context;
4557: if (absCol < 0)
4558: absCol = 0;
4559: display.ensureColumnVisible(getDotLine(), absCol);
4560: moveCaretToDotCol();
4561: }
4562: }
4563:
4564: public void findNext() {
4565: if (lastSearch != null) {
4566: Position start;
4567: if (mark != null) {
4568: Region r = new Region(this );
4569: start = new Position(r.getBegin());
4570: } else
4571: start = new Position(dot);
4572: if (!start.next())
4573: return;
4574: setWaitCursor();
4575: Position pos = lastSearch.find(buffer, start);
4576: setDefaultCursor();
4577: if (pos != null) {
4578: moveDotTo(pos);
4579: markFoundPattern(lastSearch);
4580: if (lastSearch instanceof FindInFiles) {
4581: if (buffer.getFile() != null) {
4582: ListOccurrencesInFiles buf = ((FindInFiles) lastSearch)
4583: .getOutputBuffer();
4584: if (buf != null)
4585: buf.follow(buffer.getFile(), getDotLine());
4586: }
4587: }
4588: return;
4589: }
4590: if (lastSearch instanceof FindInFiles) {
4591: Editor ed = getOtherEditor();
4592: if (ed != null) {
4593: ListOccurrencesInFiles buf = ((FindInFiles) lastSearch)
4594: .getOutputBuffer();
4595: if (ed.getBuffer() == buf) {
4596: buf.findNextOccurrence(ed);
4597: return;
4598: }
4599: }
4600: }
4601: lastSearch.notFound(this );
4602: }
4603: }
4604:
4605: public void findPrev() {
4606: if (lastSearch != null) {
4607: Position start;
4608: if (mark != null) {
4609: Region r = new Region(this );
4610: start = new Position(r.getBegin());
4611: } else
4612: start = new Position(dot);
4613: if (!start.prev())
4614: return;
4615: setWaitCursor();
4616: Position pos = lastSearch.reverseFind(buffer, start);
4617: setDefaultCursor();
4618: if (pos != null) {
4619: moveDotTo(pos);
4620: markFoundPattern(lastSearch);
4621: if (lastSearch instanceof FindInFiles) {
4622: if (buffer.getFile() != null) {
4623: ListOccurrencesInFiles buf = ((FindInFiles) lastSearch)
4624: .getOutputBuffer();
4625: if (buf != null)
4626: buf.follow(buffer.getFile(), getDotLine());
4627: }
4628: }
4629: return;
4630: }
4631: if (lastSearch instanceof FindInFiles) {
4632: Editor ed = getOtherEditor();
4633: if (ed != null) {
4634: ListOccurrencesInFiles buf = ((FindInFiles) lastSearch)
4635: .getOutputBuffer();
4636: if (ed.getBuffer() == buf) {
4637: buf.findPreviousOccurrence(ed);
4638: return;
4639: }
4640: }
4641: }
4642: lastSearch.notFound(this );
4643: }
4644: }
4645:
4646: public void incrementalFind() {
4647: if (dot == null)
4648: return;
4649:
4650: // Use location bar.
4651: locationBar.setLabelText(LocationBar.PROMPT_PATTERN);
4652: HistoryTextField textField = locationBar.getTextField();
4653: textField.setHandler(new IncrementalFindTextFieldHandler(this ,
4654: textField));
4655: textField.setHistory(new History("incrementalFind.pattern"));
4656: textField.setText("");
4657: setFocusToTextField();
4658: }
4659:
4660: public String getCurrentText() {
4661: String s = getSelectionOnCurrentLine();
4662: if (s == null)
4663: s = getTokenAtDot();
4664: return (s != null && s.length() > 0) ? s : null;
4665: }
4666:
4667: public String getSelectionOnCurrentLine() {
4668: if (dot != null && mark != null
4669: && getMarkLine() == getDotLine())
4670: return new Region(this ).toString();
4671: else
4672: return null;
4673: }
4674:
4675: public String getFilenameAtDot() {
4676: if (dot == null)
4677: return null;
4678: Position pos;
4679: if (mark != null) {
4680: Region r = new Region(this );
4681:
4682: // Trust the user if there's a highlighted selection on a single
4683: // line.
4684: if (r.getBeginLine() == r.getEndLine())
4685: return r.toString();
4686:
4687: // Otherwise, we want the beginning of the marked region.
4688: pos = r.getBegin();
4689: } else
4690: pos = new Position(dot);
4691: final Line line = pos.getLine();
4692: final int limit = line.length();
4693: int offset = pos.getOffset();
4694: if (offset == limit)
4695: --offset;
4696: FastStringBuffer sb = new FastStringBuffer();
4697: if (offset >= 0 && offset < limit) {
4698: char c = line.charAt(offset);
4699: if (Utilities.isFilenameChar(c)) {
4700: while (offset > 0) {
4701: c = line.charAt(--offset);
4702: if (!Utilities.isFilenameChar(c)) {
4703: ++offset;
4704: break;
4705: }
4706: }
4707:
4708: // Now we're looking at the first char of the filename.
4709: sb.append(line.charAt(offset));
4710: while (++offset < limit) {
4711: c = line.charAt(offset);
4712: if (Utilities.isFilenameChar(c)) {
4713: sb.append(c);
4714: } else if (sb.toString().startsWith("http://")) {
4715: // Be more permissive since there may be an appended
4716: // query.
4717: if (!Character.isWhitespace(c))
4718: sb.append(c);
4719: else
4720: break;
4721: } else
4722: break;
4723: }
4724:
4725: // Now we're looking at the first char past the end of the
4726: // filename. If the filename starts with "http://", make sure
4727: // it doesn't end with normal punctuation (as is often the
4728: // case with links embedded in text).
4729: int length = sb.length();
4730: while (length > 0) {
4731: c = sb.charAt(length - 1);
4732: if (".,:;)]>".indexOf(c) >= 0)
4733: --length;
4734: else
4735: break;
4736: }
4737: sb.setLength(length);
4738:
4739: RE re = new UncheckedRE(" line [0-9]+");
4740: REMatch match = re.getMatch(line.getText().substring(
4741: offset));
4742: if (match != null && match.getStartIndex() == 0)
4743: sb.append(match.toString());
4744: }
4745: }
4746: return sb.toString();
4747: }
4748:
4749: private String getTokenAtDot() {
4750: // If a selection is marked, return the token at the beginning of the
4751: // marked region.
4752: if (mark != null) {
4753: Region r = new Region(this );
4754: return tokenAt(r.getBegin());
4755: }
4756: return tokenAt(dot);
4757: }
4758:
4759: private String tokenAt(Position pos) {
4760: return getMode().getIdentifier(pos);
4761: }
4762:
4763: public void findNextWord() {
4764: if (dot == null)
4765: return;
4766: String pattern = getTokenAtDot();
4767: if (pattern == null || pattern.length() == 0)
4768: return;
4769: lastSearch = new Search(pattern, false, true);
4770: Position start;
4771: if (mark != null && dot.isBefore(mark))
4772: start = new Position(mark);
4773: else
4774: start = new Position(dot);
4775: Position pos = lastSearch.find(buffer.getMode(), start);
4776: if (pos != null && pos.equals(start)) {
4777: if (pos.next())
4778: pos = lastSearch.find(buffer.getMode(), pos);
4779: }
4780: if (pos != null && !pos.equals(start)) {
4781: moveDotTo(pos);
4782: markFoundPattern(lastSearch);
4783: } else
4784: lastSearch.notFound(this );
4785: }
4786:
4787: public void findPrevWord() {
4788: if (dot == null)
4789: return;
4790: String pattern = getTokenAtDot();
4791: if (pattern == null || pattern.length() == 0)
4792: return;
4793: lastSearch = new Search(pattern, false, true);
4794: boolean found = false;
4795: Position start = null;
4796: if (mark != null)
4797: start = new Region(this ).getBegin();
4798: else
4799: start = new Position(dot);
4800: if (start.prev()) {
4801: Position pos = lastSearch.reverseFind(buffer, start);
4802: if (pos != null && pos.getLine() == start.getLine()) {
4803: if (pos.getOffset() + lastSearch.getPatternLength() > start
4804: .getOffset()) {
4805: // We've found the instance we started with. Keep looking.
4806: start = new Position(pos);
4807: if (start.prev())
4808: pos = lastSearch.reverseFind(buffer, start);
4809: else
4810: pos = null;
4811: }
4812: }
4813: if (pos != null) {
4814: found = true;
4815: moveDotTo(pos);
4816: markFoundPattern(lastSearch);
4817: }
4818: }
4819: if (!found)
4820: lastSearch.notFound(this );
4821: }
4822:
4823: public void findFirstOccurrence() {
4824: if (dot == null)
4825: return;
4826: String pattern = getTokenAtDot();
4827: if (pattern == null || pattern.length() == 0)
4828: return;
4829: lastSearch = new Search(pattern, false, true);
4830: Position pos = lastSearch.find(buffer.getMode(), new Position(
4831: buffer.getFirstLine(), 0));
4832: if (pos != null) {
4833: moveDotTo(pos);
4834: markFoundPattern(lastSearch);
4835: } else
4836: lastSearch.notFound(this );
4837: }
4838:
4839: public void copyPath() {
4840: if (buffer instanceof Directory) {
4841: String path = ((Directory) buffer).getPathAtDot();
4842: if (path != null) {
4843: killRing.appendNew(path);
4844: killRing.copyLastKillToSystemClipboard();
4845: status("Path copied to clipboard");
4846: }
4847: }
4848: }
4849:
4850: public void copyRegion() {
4851: if (dot == null)
4852: return;
4853:
4854: String message = null;
4855:
4856: if (mark != null) {
4857: Region r = new Region(this );
4858: if (isColumnSelection()) {
4859: killedColumn = r.toString();
4860: message = "Column selection stored";
4861: } else {
4862: killRing.appendNew(r.toString());
4863: message = "Region copied to clipboard";
4864: }
4865: } else if (!getDotLine().isBlank()) {
4866: killRing.appendNew(getDotLine().getText()
4867: + System.getProperty("line.separator"));
4868: message = "Line copied to clipboard";
4869: } else
4870: return; // Nothing to do.
4871:
4872: if (!isColumnSelection())
4873: killRing.copyLastKillToSystemClipboard();
4874:
4875: if (message != null)
4876: status(message);
4877: }
4878:
4879: public void copyAppend() {
4880: if (isColumnSelection()) {
4881: notSupportedForColumnSelections();
4882: return;
4883: }
4884:
4885: if (dot == null)
4886: return;
4887:
4888: String message = null;
4889:
4890: if (mark != null) {
4891: Region r = new Region(buffer, mark, dot);
4892: killRing.appendToCurrent(r.toString());
4893: message = "Region appended to clipboard";
4894: } else if (!getDotLine().isBlank()) {
4895: killRing.appendToCurrent(getDotLine().getText()
4896: + System.getProperty("line.separator"));
4897: message = "Line appended to clipboard";
4898: } else
4899: return; // Nothing to do.
4900:
4901: killRing.copyLastKillToSystemClipboard();
4902:
4903: if (message != null)
4904: status(message);
4905: }
4906:
4907: // Handles undo, updates display and marks buffer modified.
4908: public void deleteRegion() {
4909: if (mark == null)
4910: return;
4911: if (getMarkLine() != getDotLine()
4912: || getMarkOffset() != getDotOffset()) {
4913: try {
4914: buffer.lockWrite();
4915: } catch (InterruptedException e) {
4916: Log.error(e);
4917: return;
4918: }
4919: try {
4920: Region r = new Region(this );
4921: if (isColumnSelection()) {
4922: deleteColumn(r);
4923: } else {
4924: // A hard update is only necessary if the region spans a
4925: // line boundary.
4926: boolean hard = getDotLine() != getMarkLine();
4927:
4928: // Save undo information before calling r.delete() so
4929: // the modified flag will be correct if we revert.
4930: CompoundEdit compoundEdit = beginCompoundEdit();
4931: addUndo(SimpleEdit.MOVE);
4932: dot.moveTo(r.getBegin());
4933: addUndoDeleteRegion(r);
4934:
4935: // Sets buffer modified flag.
4936: r.delete();
4937:
4938: endCompoundEdit(compoundEdit);
4939:
4940: if (hard)
4941: buffer.repaint();
4942: else
4943: updateInAllEditors(getDotLine());
4944: }
4945: } finally {
4946: buffer.unlockWrite();
4947: }
4948: moveCaretToDotCol();
4949: }
4950: setMark(null);
4951: }
4952:
4953: // Leaves dot at beginning of deleted region.
4954: private void deleteColumn(Region r) {
4955: Debug.assertTrue(r.isColumnRegion());
4956: final int beginCol = r.getBeginCol();
4957: final int endCol = r.getEndCol();
4958: CompoundEdit compoundEdit = beginCompoundEdit();
4959: addUndo(SimpleEdit.MOVE);
4960: dot.moveTo(r.getBegin());
4961: while (true) {
4962: addUndo(SimpleEdit.LINE_EDIT);
4963: Line line = getDotLine();
4964: deleteLineRegion(line, beginCol, endCol);
4965: updateInAllEditors(line);
4966: if (line == r.getEndLine())
4967: break;
4968: if (line.next() == null)
4969: break;
4970: dot.moveTo(line.next(), 0);
4971: }
4972: addUndo(SimpleEdit.MOVE);
4973: dot.moveTo(r.getBegin());
4974: endCompoundEdit(compoundEdit);
4975: buffer.modified();
4976: }
4977:
4978: private void deleteLineRegion(Line line, int beginCol, int endCol) {
4979: String text = Utilities.detab(line.getText(), buffer
4980: .getTabWidth());
4981: if (text.length() < beginCol)
4982: return; // No change.
4983: String head = text.substring(0, beginCol);
4984: if (text.length() < endCol) {
4985: line.setText(head);
4986: return;
4987: }
4988: String tail = text.substring(endCol);
4989: line.setText(head.concat(tail));
4990: }
4991:
4992: // This really is a kill!
4993: public void killRegion() {
4994: if (!checkReadOnly())
4995: return;
4996: try {
4997: buffer.lockWrite();
4998: } catch (InterruptedException e) {
4999: Log.error(e);
5000: return;
5001: }
5002: try {
5003: killRegionInternal();
5004: } finally {
5005: buffer.unlockWrite();
5006: }
5007: }
5008:
5009: private void killRegionInternal() {
5010: if (mark != null) {
5011: if (getMarkLine() != getDotLine()
5012: || getMarkOffset() != getDotOffset()) {
5013: // A hard update is only necessary if the region spans a line
5014: // boundary.
5015: boolean hard = getDotLine() != getMarkLine();
5016: if (isColumnSelection()) {
5017: Region r = new Region(this );
5018: killedColumn = r.toString();
5019: deleteColumn(r);
5020: } else {
5021: Region r = new Region(this );
5022: String kill = r.toString();
5023: if (lastCommand == COMMAND_KILL)
5024: killRing.appendToCurrent(kill);
5025: else
5026: killRing.appendNew(kill);
5027: killRing.copyLastKillToSystemClipboard();
5028:
5029: // Save undo information before calling Region.delete so
5030: // modified flag will be correct if we revert.
5031: CompoundEdit compoundEdit = beginCompoundEdit();
5032: addUndo(SimpleEdit.MOVE);
5033: dot.moveTo(r.getBegin());
5034: addUndoDeleteRegion(r);
5035:
5036: // Sets buffer modified flag.
5037: r.delete();
5038:
5039: endCompoundEdit(compoundEdit);
5040: }
5041: moveCaretToDotCol();
5042: if (hard)
5043: buffer.repaint();
5044: else
5045: updateInAllEditors(getDotLine());
5046: setCurrentCommand(COMMAND_KILL);
5047: }
5048: setMark(null);
5049: } else {
5050: // No selection. Use current line.
5051: final Line dotLine = getDotLine();
5052: final Line nextLine = dotLine.next();
5053:
5054: // Last line is a special case.
5055: if (nextLine == null) {
5056: CompoundEdit compoundEdit = beginCompoundEdit();
5057: dot.setOffset(0);
5058: killLine();
5059: endCompoundEdit(compoundEdit);
5060: setCurrentCommand(COMMAND_KILL);
5061: return;
5062: }
5063:
5064: CompoundEdit compoundEdit = beginCompoundEdit();
5065: addUndo(SimpleEdit.MOVE);
5066: dot.moveTo(dotLine, 0);
5067: mark = new Position(nextLine, 0);
5068:
5069: Region r = new Region(this );
5070: String kill = r.toString();
5071: if (lastCommand == COMMAND_KILL)
5072: killRing.appendToCurrent(kill);
5073: else
5074: killRing.appendNew(kill);
5075: killRing.copyLastKillToSystemClipboard();
5076:
5077: // Save undo information before calling Region.delete so
5078: // modified flag will be correct if we revert.
5079: addUndo(SimpleEdit.MOVE);
5080: dot.moveTo(r.getBegin());
5081: addUndoDeleteRegion(r);
5082:
5083: // Sets buffer modified flag.
5084: r.delete();
5085:
5086: addUndo(SimpleEdit.MOVE);
5087: mark = null;
5088: endCompoundEdit(compoundEdit);
5089: moveCaretToDotCol();
5090: buffer.repaint();
5091: setCurrentCommand(COMMAND_KILL);
5092: }
5093: }
5094:
5095: public void killAppend() {
5096: if (isColumnSelection()) {
5097: notSupportedForColumnSelections();
5098: return;
5099: }
5100: setLastCommand(COMMAND_KILL); // Force append.
5101: killRegion();
5102: }
5103:
5104: // Copies text from dot to end of line to kill ring and then deletes that
5105: // text. If dot is already at end of line, deletes newline and copies it
5106: // to kill ring.
5107: public void killLine() {
5108: if (!checkReadOnly())
5109: return;
5110:
5111: if (dot.getOffset() == dot.getLineLength()
5112: && dot.getNextLine() == null)
5113: return;
5114:
5115: CompoundEdit compoundEdit = beginCompoundEdit();
5116:
5117: addUndo(SimpleEdit.MOVE);
5118: unmark();
5119:
5120: if (getDotOffset() < getDotLine().length()) {
5121: setMarkAtDot();
5122: if (dot.getLine().isBlank() && dot.getNextLine() != null)
5123: dot.moveTo(dot.getNextLine(), 0);
5124: else
5125: dot.setOffset(dot.getLineLength());
5126: } else if (dot.getOffset() == dot.getLineLength()) {
5127: fillToCaret(); // We might be beyond the end of the actual text on the line.
5128: setMarkAtDot();
5129: dot.moveTo(dot.getNextLine(), 0);
5130: }
5131:
5132: killRegion();
5133:
5134: endCompoundEdit(compoundEdit);
5135: }
5136:
5137: public void deleteWordRight() {
5138: deleteOrKillWordRight(false);
5139: }
5140:
5141: public void killWordRight() {
5142: deleteOrKillWordRight(true);
5143: }
5144:
5145: private void deleteOrKillWordRight(boolean isKill) {
5146: if (!checkReadOnly())
5147: return;
5148: CompoundEdit compoundEdit = beginCompoundEdit();
5149: addUndo(SimpleEdit.MOVE);
5150: unmark();
5151: fillToCaret();
5152: setMarkAtDot();
5153: if (inWord()) {
5154: while (inWord() && nextChar())
5155: ;
5156: while (inWhitespace() && nextChar())
5157: ;
5158: } else if (inWhitespace()) {
5159: while (inWhitespace() && nextChar())
5160: ;
5161: } else {
5162: while (!inWhitespace() && !inWord() && nextChar())
5163: ;
5164: while (inWhitespace() && nextChar())
5165: ;
5166: }
5167: if (isKill)
5168: killRegion();
5169: else
5170: deleteRegion();
5171: endCompoundEdit(compoundEdit);
5172: }
5173:
5174: public void deleteWordLeft() {
5175: deleteOrKillWordLeft(false);
5176: }
5177:
5178: public void killWordLeft() {
5179: deleteOrKillWordLeft(true);
5180: }
5181:
5182: private void deleteOrKillWordLeft(boolean isKill) {
5183: if (!checkReadOnly())
5184: return;
5185: if (getDotOffset() == 0 && getDotLine().previous() == null)
5186: return;
5187: CompoundEdit compoundEdit = beginCompoundEdit();
5188: addUndo(SimpleEdit.MOVE);
5189: unmark();
5190: setMarkAtDot();
5191: prevChar();
5192: if (inWord()) {
5193: while (getDotOffset() > 0 && inWord() && prevChar())
5194: ;
5195: if (!inWord())
5196: nextChar();
5197: } else if (inWhitespace()) {
5198: while (inWhitespace() && prevChar())
5199: ;
5200: if (!inWhitespace())
5201: nextChar();
5202: } else {
5203: while (!inWhitespace() && !inWord() && prevChar())
5204: ;
5205: while (inWhitespace() && prevChar())
5206: ;
5207: nextChar();
5208: }
5209: if (isKill)
5210: killRegion();
5211: else
5212: deleteRegion();
5213: endCompoundEdit(compoundEdit);
5214: }
5215:
5216: public boolean canPaste() {
5217: if (buffer.isReadOnly())
5218: return false;
5219: if (killRing.size() > 0)
5220: return true;
5221: String toBeInserted = null;
5222: Transferable t = getToolkit().getSystemClipboard().getContents(
5223: this );
5224: if (t != null) {
5225: try {
5226: toBeInserted = (String) t
5227: .getTransferData(DataFlavor.stringFlavor);
5228: } catch (Exception e) {
5229: }
5230: }
5231: return toBeInserted != null;
5232: }
5233:
5234: public void paste() {
5235: if (!checkReadOnly())
5236: return;
5237: setWaitCursor();
5238: String toBeInserted = null;
5239: Transferable t = getToolkit().getSystemClipboard().getContents(
5240: this );
5241: if (t != null) {
5242: try {
5243: toBeInserted = (String) t
5244: .getTransferData(DataFlavor.stringFlavor);
5245: } catch (Exception e) {
5246: }
5247: }
5248: if (toBeInserted != null && toBeInserted.length() > 0)
5249: killRing.appendNew(toBeInserted);
5250: // Even if we already have the text to be inserted, we MUST call
5251: // killRing.pop() here so that killRing.indexOfNextPop and
5252: // killRing.lastPaste are set correctly.
5253: toBeInserted = killRing.pop();
5254: if (toBeInserted != null) {
5255: paste(toBeInserted);
5256: setCurrentCommand(COMMAND_PASTE);
5257: }
5258: setDefaultCursor();
5259: }
5260:
5261: public void cyclePaste() {
5262: if (lastCommand == COMMAND_PASTE) {
5263: setWaitCursor();
5264: String s = killRing.popNext();
5265: if (s != null) {
5266: undo();
5267: paste(s);
5268: setCurrentCommand(COMMAND_PASTE);
5269: }
5270: setDefaultCursor();
5271: } else
5272: paste();
5273: }
5274:
5275: public void mousePaste() {
5276: if (dot == null)
5277: return;
5278: if (!checkReadOnly())
5279: return;
5280: if (isColumnSelection()) {
5281: notSupportedForColumnSelections();
5282: return;
5283: }
5284: AWTEvent e = dispatcher.getLastEvent();
5285: if (!(e instanceof MouseEvent))
5286: return;
5287: CompoundEdit compoundEdit = beginCompoundEdit();
5288: if (mark != null) {
5289: Region r = new Region(this );
5290: killRing.appendNew(r.toString());
5291: killRing.copyLastKillToSystemClipboard();
5292: addUndo(SimpleEdit.MOVE);
5293: setMark(null);
5294: }
5295: mouseMoveDotToPoint((MouseEvent) e);
5296: paste();
5297: endCompoundEdit(compoundEdit);
5298: }
5299:
5300: public static void promoteLastPaste() {
5301: killRing.promoteLastPaste();
5302: }
5303:
5304: public void paste(String toBeInserted) {
5305: paste(toBeInserted, false);
5306: }
5307:
5308: public void paste(String toBeInserted, boolean leavePasteSelected) {
5309: if (!checkReadOnly())
5310: return;
5311: if (toBeInserted == null || toBeInserted.length() == 0)
5312: return;
5313: try {
5314: buffer.lockWrite();
5315: } catch (InterruptedException e) {
5316: Log.error(e);
5317: return;
5318: }
5319: try {
5320: pasteInternal(toBeInserted, leavePasteSelected);
5321: } finally {
5322: buffer.unlockWrite();
5323: }
5324: setUpdateFlag(REFRAME);
5325: }
5326:
5327: private void pasteInternal(String toBeInserted,
5328: boolean leavePasteSelected) {
5329: final Mode mode = buffer.getMode();
5330: CompoundEdit compoundEdit = beginCompoundEdit();
5331: if (mark == null && Utilities.isLinePaste(toBeInserted)
5332: && mode.acceptsLinePaste(this )
5333: && buffer.getBooleanProperty(Property.AUTO_PASTE_LINES)) {
5334: // We want to the caret to be in the same column when we're done.
5335: final int absCaretCol = display.getAbsoluteCaretCol();
5336:
5337: final Line prevLine = getDotLine().previous();
5338:
5339: addUndo(SimpleEdit.MOVE);
5340: dot.setOffset(0);
5341: Position begin = dot.copy();
5342: addUndo(SimpleEdit.INSERT_STRING);
5343: insertStringInternal(toBeInserted);
5344:
5345: if (prevLine != null && mode.canIndentPaste()) {
5346: // Indent inserted lines according to context.
5347:
5348: // Make sure line flags are correct.
5349: if (getFormatter().parseBuffer())
5350: buffer.repaint();
5351:
5352: // Dot is at the beginning of the line following the inserted
5353: // block.
5354: Position savedDot = dot.copy();
5355:
5356: // First move dot to start of inserted block.
5357: addUndo(SimpleEdit.MOVE);
5358: dot.moveTo(prevLine.next(), 0);
5359:
5360: while (dot.getLine() != null
5361: && dot.getLine() != savedDot.getLine()) {
5362: if (!dot.getLine().isBlank())
5363: indentLineInternal();
5364: addUndo(SimpleEdit.MOVE);
5365: dot.moveTo(dot.getNextLine(), 0);
5366: }
5367:
5368: // Restore dot.
5369: dot = savedDot;
5370: }
5371:
5372: if (leavePasteSelected) {
5373: setMark(begin);
5374: final Line dotLine = getDotLine();
5375: for (Line line = begin.getLine(); line != null; line = line
5376: .nextVisible()) {
5377: update(line);
5378: if (line == dotLine)
5379: break;
5380: }
5381: } else {
5382: // Restore caret column.
5383: addUndo(SimpleEdit.MOVE);
5384: display.setCaretCol(absCaretCol - display.getShift());
5385: moveDotToCaretCol();
5386: }
5387: } else {
5388: if (mark != null)
5389: deleteRegion();
5390: fillToCaret();
5391: Position begin = dot.copy();
5392: addUndo(SimpleEdit.INSERT_STRING);
5393: insertStringInternal(toBeInserted);
5394: moveCaretToDotCol();
5395: if (leavePasteSelected) {
5396: setMark(begin);
5397: final Line dotLine = getDotLine();
5398: for (Line line = begin.getLine(); line != null; line = line
5399: .nextVisible()) {
5400: update(line);
5401: if (line == dotLine)
5402: break;
5403: }
5404: }
5405: }
5406: endCompoundEdit(compoundEdit);
5407: buffer.modified();
5408: if (getFormatter().parseBuffer())
5409: buffer.repaint();
5410: }
5411:
5412: public void pasteColumn() {
5413: if (!checkReadOnly())
5414: return;
5415: if (killedColumn == null || killedColumn.length() == 0)
5416: return;
5417: try {
5418: buffer.lockWrite();
5419: } catch (InterruptedException e) {
5420: Log.error(e);
5421: return;
5422: }
5423: try {
5424: pasteColumnInternal(killedColumn);
5425: } finally {
5426: buffer.unlockWrite();
5427: }
5428: }
5429:
5430: private void pasteColumnInternal(String toBeInserted) {
5431: Position pos = new Position(dot);
5432: final int col = display.getAbsoluteCaretCol();
5433: CompoundEdit compoundEdit = beginCompoundEdit();
5434: while (true) {
5435: final int index = toBeInserted.indexOf('\n');
5436: final String s = index >= 0 ? toBeInserted.substring(0,
5437: index) : toBeInserted;
5438: if (index >= 0)
5439: toBeInserted = toBeInserted.substring(index + 1);
5440: final Line dotLine = getDotLine();
5441: String text = Utilities.detab(dotLine.getText(), buffer
5442: .getTabWidth());
5443: if (text.length() < col)
5444: text = text.concat(Utilities
5445: .spaces(col - text.length()));
5446: Debug.assertTrue(text.length() >= col);
5447: final String head = text.substring(0, col);
5448: final String tail = text.substring(col);
5449: addUndo(SimpleEdit.LINE_EDIT);
5450: FastStringBuffer sb = new FastStringBuffer(head);
5451: sb.append(s);
5452: sb.append(tail);
5453: dotLine.setText(sb.toString());
5454: updateInAllEditors(dotLine);
5455: pos = new Position(dotLine, head.length() + s.length());
5456: if (toBeInserted.length() == 0)
5457: break;
5458: if (dotLine.next() == null) {
5459: addUndo(SimpleEdit.MOVE);
5460: dot.setOffset(dotLine.length());
5461: addUndo(SimpleEdit.INSERT_LINE_SEP);
5462: buffer.insertLineSeparator(dot);
5463: } else {
5464: addUndo(SimpleEdit.MOVE);
5465: dot.moveTo(dotLine.next(), 0);
5466: }
5467: }
5468: addUndo(SimpleEdit.MOVE);
5469: dot.moveTo(pos);
5470: moveCaretToDotCol();
5471: endCompoundEdit(compoundEdit);
5472: }
5473:
5474: public void insertString(String toBeInserted) {
5475: if (toBeInserted == null || toBeInserted.length() == 0)
5476: return;
5477: CompoundEdit compoundEdit = beginCompoundEdit();
5478: if (mark != null)
5479: delete();
5480: fillToCaret();
5481: addUndo(SimpleEdit.INSERT_STRING);
5482: insertStringInternal(toBeInserted);
5483: updateInAllEditors(dot.getLine());
5484: moveCaretToDotCol();
5485: endCompoundEdit(compoundEdit);
5486: if (getFormatter().parseBuffer())
5487: buffer.repaint();
5488: }
5489:
5490: public void centerDialog(JDialog d) {
5491: Dimension parent = frame.getSize();
5492: Dimension window = d.getSize();
5493: Point p = frame.getLocation();
5494: p.translate((parent.width - window.width) / 2,
5495: (parent.height - window.height) / 2);
5496: d.setLocation(p);
5497: }
5498:
5499: public boolean confirm(String title, String text) {
5500: int response = ConfirmDialog.showConfirmDialog(this , text,
5501: title);
5502: repaintNow();
5503: return response == RESPONSE_YES;
5504: }
5505:
5506: public int confirmAll(String title, String text) {
5507: int response = ConfirmDialog.showConfirmAllDialog(this , text,
5508: title);
5509: repaintNow();
5510: return response;
5511: }
5512:
5513: public void killBuffer() {
5514: try {
5515: if (buffer.isSecondary()) {
5516: buffer.windowClosing();
5517: otherWindow();
5518: unsplitWindow();
5519: currentEditor.maybeKillBuffer(buffer);
5520: restoreFocus();
5521: return;
5522: }
5523: Buffer buf = buffer.getSecondary();
5524: if (buf != null) {
5525: unsplitWindow();
5526: maybeKillBuffer(buf);
5527: return;
5528: }
5529: // Normal buffer.
5530: maybeKillBuffer(buffer);
5531: // If we're left with two editors showing exactly the same thing,
5532: // unsplit the window.
5533: Frame frame = currentEditor.getFrame();
5534: if (frame.getEditorCount() == 2) {
5535: Editor p = frame.getPrimaryEditor();
5536: Editor s = frame.getSecondaryEditor();
5537: boolean unsplit = false;
5538: if (p.getDot() != null && p.getDot().equals(s.getDot())) {
5539: if (p.getMark() == null && s.getMark() == null)
5540: unsplit = true;
5541: else if (p.getMark() != null
5542: && p.getMark().equals(s.getMark()))
5543: unsplit = true;
5544: }
5545: if (unsplit)
5546: unsplitWindow();
5547: }
5548: } finally {
5549: Sidebar.refreshSidebarInAllFrames();
5550: }
5551: }
5552:
5553: public void maybeKillBuffer(Buffer toBeKilled) {
5554: if (!bufferList.contains(toBeKilled)) {
5555: Debug.bug("maybeKillBuffer buffer not in list "
5556: + toBeKilled);
5557: return;
5558: }
5559:
5560: // Don't kill the last buffer if it's a directory.
5561: if (bufferList.size() == 1 && toBeKilled instanceof Directory)
5562: return;
5563:
5564: // Cancel background process if any.
5565: BackgroundProcess backgroundProcess = toBeKilled
5566: .getBackgroundProcess();
5567: if (backgroundProcess != null) {
5568: Log
5569: .debug("maybeKillBuffer calling backgroundProcess.cancel...");
5570: backgroundProcess.cancel();
5571: // backgroundProcess.cancel() may have killed the buffer, so
5572: // verify that it's still in the list.
5573: if (!bufferList.contains(toBeKilled)) {
5574: Log
5575: .debug("maybeKillBuffer buffer is no longer in list");
5576: return;
5577: }
5578: }
5579:
5580: Mode mode = toBeKilled.getMode();
5581: if (mode == null || mode.confirmClose(this , toBeKilled))
5582: toBeKilled.kill();
5583: }
5584:
5585: public void clearStatusText() {
5586: StatusBar statusBar = getStatusBar();
5587: if (statusBar != null) {
5588: statusBar.setText(null);
5589: statusBar.repaint();
5590: }
5591: }
5592:
5593: public void activate(Buffer buf) {
5594: if (buf == null)
5595: return;
5596: Debug.assertTrue(bufferList.contains(buf));
5597: if (buf == buffer)
5598: return;
5599: if (!buf.initialized())
5600: buf.initialize();
5601: clearStatusText();
5602: if (buffer != null && bufferList.contains(buffer)) {
5603: // Save information about buffer being deactivated.
5604: buffer.autosave();
5605: saveView();
5606: RecentFiles.getInstance().bufferDeactivated(buffer, dot);
5607: }
5608:
5609: // Read-only status may have changed. (We could be switching back from
5610: // a shell buffer.)
5611: reactivate(buf);
5612:
5613: buf.setLastActivated(System.currentTimeMillis());
5614: if (buf.isLoaded()) {
5615: buffer = buf;
5616: bufferActivated(false);
5617: } else {
5618: setWaitCursor();
5619: int result = LOAD_FAILED;
5620: try {
5621: result = buf.load();
5622: } catch (OutOfMemoryError e) {
5623: buf.kill();
5624: Sidebar.setUpdateFlagInAllFrames(SIDEBAR_ALL);
5625: MessageDialog.showMessageDialog(this ,
5626: "Insufficient memory to load buffer", "Error");
5627: return;
5628: }
5629: switch (result) {
5630: case LOAD_COMPLETED:
5631: buffer = buf;
5632: bufferActivated(true);
5633: break;
5634: case LOAD_PENDING:
5635: buffer = buf;
5636: buffer.setBusy(true);
5637: bufferPending();
5638: break;
5639: case LOAD_FAILED:
5640: setDefaultCursor();
5641: buffer = buf;
5642: bufferActivated(true);
5643: MessageDialog.showMessageDialog(this ,
5644: "Unable to load buffer", "Error");
5645: break;
5646: default:
5647: Debug.assertTrue(false);
5648: }
5649: }
5650: }
5651:
5652: public Editor activateInOtherWindow(Buffer buf) {
5653: return frame.activateInOtherWindow(this , buf);
5654: }
5655:
5656: public Editor activateInOtherWindow(Buffer buf, float split) {
5657: return frame.activateInOtherWindow(this , buf, split);
5658: }
5659:
5660: public Editor displayInOtherWindow(Buffer buf) {
5661: return frame.displayInOtherWindow(this , buf);
5662: }
5663:
5664: public void bufferActivated(boolean firstTime) {
5665: if (buffer.getModeId() == IMAGE_MODE) {
5666: setDot(null);
5667: setMark(null);
5668: display.setTopLine(null);
5669: display.setShift(0);
5670: display.setCaretCol(0);
5671: } else {
5672: findOrCreateView(buffer);
5673: restoreView();
5674:
5675: // If the buffer has already been loaded, the caret position will
5676: // be restored correctly.
5677: if (firstTime)
5678: moveCaretToDotCol();
5679: }
5680:
5681: if (dot != null && dot.getOffset() > dot.getLineLength()) {
5682: dot.setOffset(dot.getLineLength());
5683: moveCaretToDotCol();
5684: }
5685:
5686: frame.updateTitle();
5687: frame.setMenu();
5688: frame.setToolbar();
5689:
5690: if (buffer.isBusy())
5691: setWaitCursor();
5692: else
5693: setDefaultCursor();
5694:
5695: setUpdateFlag(REFRAME);
5696: reframe();
5697: setUpdateFlag(REPAINT);
5698:
5699: RecentFiles.getInstance().bufferActivated(buffer);
5700:
5701: if (buffer.isTaggable()) {
5702: tagFileManager.addToQueue(buffer.getCurrentDirectory(),
5703: buffer.getMode());
5704: }
5705:
5706: Sidebar.setUpdateFlagInAllFrames(SIDEBAR_ALL);
5707:
5708: if (isLispInitialized()) {
5709: if (firstTime)
5710: LispAPI.invokeOpenFileHook(buffer);
5711: LispAPI.invokeBufferActivatedHook(buffer);
5712: }
5713: }
5714:
5715: private void bufferPending() {
5716: // Find or create a view of this buffer.
5717: findOrCreateView(buffer);
5718: restoreView();
5719:
5720: frame.updateTitle();
5721: frame.setMenu();
5722: frame.setToolbar();
5723:
5724: display.repaint();
5725:
5726: Sidebar.setUpdateFlagInAllFrames(SIDEBAR_ALL);
5727: Sidebar sidebar = getSidebar();
5728: if (sidebar != null)
5729: sidebar.setBuffer();
5730: }
5731:
5732: // Find or create another frame in which to activate the specified buffer.
5733: public Editor activateInOtherFrame(Buffer buf) {
5734: Editor ed = null;
5735: if (getEditorCount() == 1) {
5736: ed = createNewFrame();
5737: ed.activate(buf);
5738: ed.getFrame().setVisible(true);
5739: ed.updateDisplay();
5740: } else {
5741: for (int i = 0; i < getEditorCount(); i++) {
5742: ed = getEditor(i);
5743: if (ed != this ) {
5744: ed.activate(buf);
5745: ed.getFrame().toFront();
5746: break;
5747: }
5748: }
5749: }
5750: return ed;
5751: }
5752:
5753: public void nextFrame() {
5754: int count = getEditorCount();
5755: if (count > 1) {
5756: Editor ed = null;
5757: for (int i = 0; i < count; i++) {
5758: ed = Editor.getEditor(i);
5759: if (ed == this ) {
5760: if (++i == count)
5761: i = 0;
5762: ed = Editor.getEditor(i);
5763: ed.getFrame().toFront();
5764: ed.requestFocusLater();
5765: break;
5766: }
5767: }
5768: }
5769: }
5770:
5771: private void requestFocusLater() {
5772: Runnable r = new Runnable() {
5773: public void run() {
5774: Editor.this .requestFocus();
5775: }
5776: };
5777: SwingUtilities.invokeLater(r);
5778: }
5779:
5780: public void toggleSidebar() {
5781: frame.frameToggleSidebar();
5782: }
5783:
5784: public void sidebarListBuffers() {
5785: ensureActive();
5786:
5787: if (frame.getSidebar() == null)
5788: toggleSidebar();
5789:
5790: if (frame.getSidebar() != null)
5791: frame.getSidebar().activateBufferList();
5792: }
5793:
5794: public void sidebarListTags() {
5795: if (!frame.isActive())
5796: return;
5797:
5798: if (getMode().getSidebarComponent(this ) != null) {
5799: if (frame.getSidebar() == null)
5800: toggleSidebar();
5801: if (frame.getSidebar() != null)
5802: frame.getSidebar().activateNavigationComponent();
5803: }
5804: }
5805:
5806: public void toggleToolbar() {
5807: frame.frameToggleToolbar();
5808: }
5809:
5810: public final boolean addUndo(int type) {
5811: return SimpleEdit.addUndo(this , type);
5812: }
5813:
5814: public final boolean addUndoDeleteRegion(Region r) {
5815: buffer.addEdit(new UndoDeleteRegion(this , r));
5816: return true;
5817: }
5818:
5819: public final CompoundEdit beginCompoundEdit() {
5820: return buffer.beginCompoundEdit();
5821: }
5822:
5823: public final void endCompoundEdit(CompoundEdit compoundEdit) {
5824: buffer.endCompoundEdit(compoundEdit);
5825: }
5826:
5827: public void undo() {
5828: setWaitCursor();
5829: try {
5830: buffer.lockWrite();
5831: } catch (InterruptedException e) {
5832: Log.error(e);
5833: return;
5834: }
5835: try {
5836: buffer.undo();
5837: checkDotInOtherFrames();
5838: setCurrentCommand(COMMAND_UNDO);
5839: } catch (Throwable t) {
5840: Log.error(t);
5841: } finally {
5842: buffer.unlockWrite();
5843: setDefaultCursor();
5844: }
5845: }
5846:
5847: public void redo() {
5848: setWaitCursor();
5849: try {
5850: buffer.lockWrite();
5851: } catch (InterruptedException e) {
5852: Log.error(e);
5853: return;
5854: }
5855: try {
5856: buffer.redo();
5857: checkDotInOtherFrames();
5858: } catch (Throwable t) {
5859: Log.error(t);
5860: } finally {
5861: buffer.unlockWrite();
5862: setDefaultCursor();
5863: }
5864: }
5865:
5866: private void checkDotInOtherFrames() {
5867: if (getEditorCount() > 1) {
5868: for (int i = 0; i < getEditorCount(); i++) {
5869: Editor ed = getEditor(i);
5870: if (ed != this && ed.getBuffer() == buffer) {
5871: if (ed.getDotOffset() > ed.getDotLine().length()) {
5872: ed.getDot().setOffset(ed.getDotLine().length());
5873: ed.moveCaretToDotCol();
5874: ed.updateDotLine();
5875: }
5876: }
5877: }
5878: }
5879: }
5880:
5881: public final void jumpToLine(int lineNumber) {
5882: jumpToLine(lineNumber, 0);
5883: }
5884:
5885: public void jumpToLine(int lineNumber, int offset) {
5886: Line line = buffer.getLine(lineNumber);
5887: if (line != null) {
5888: if (offset < 0)
5889: offset = 0;
5890: else if (offset > line.length())
5891: offset = line.length();
5892: moveDotTo(line, offset);
5893: setUpdateFlag(REFRAME);
5894: } else
5895: eob();
5896: }
5897:
5898: public void offset() {
5899: status(String.valueOf(buffer.getAbsoluteOffset(dot)));
5900: }
5901:
5902: public void executeCommand() {
5903: // Use location bar.
5904: locationBar.setLabelText(LocationBar.PROMPT_COMMAND);
5905: HistoryTextField textField = locationBar.getTextField();
5906: textField.setHandler(new ExecuteCommandTextFieldHandler(this ,
5907: textField));
5908: textField.setHistory(new History("executeCommand.input", 30));
5909: textField.recallLast();
5910: textField.selectAll();
5911: AWTEvent e = dispatcher.getLastEvent();
5912: if (e != null && e.getSource() instanceof MenuItem) {
5913: Runnable r = new Runnable() {
5914: public void run() {
5915: setFocusToTextField();
5916: }
5917: };
5918: SwingUtilities.invokeLater(r);
5919: } else
5920: setFocusToTextField();
5921: }
5922:
5923: public void executeCommand(String input) {
5924: executeCommand(input, false);
5925: }
5926:
5927: public void executeCommand(String input, final boolean interactive) {
5928: input = Utilities.trimLeading(input);
5929: if (input.length() == 0)
5930: return;
5931: if (input.charAt(0) == '(') {
5932: // Lisp form.
5933: try {
5934: String result = String.valueOf(Interpreter
5935: .evaluate(input));
5936: if (interactive)
5937: status(result);
5938: } catch (Throwable t) {
5939: String message = null;
5940: if (t instanceof ConditionThrowable) {
5941: LispObject obj = ((ConditionThrowable) t)
5942: .getCondition();
5943: if (obj instanceof Condition) {
5944: try {
5945: message = ((Condition) obj)
5946: .getConditionReport();
5947: } catch (Throwable ignored) {
5948: // At least we tried.
5949: }
5950: }
5951: }
5952: if (message == null || message.length() == 0)
5953: message = t.getMessage();
5954: if (message != null && message.length() > 0) {
5955: FastStringBuffer sb = new FastStringBuffer(message);
5956: sb
5957: .setCharAt(0, Character.toUpperCase(sb
5958: .charAt(0)));
5959: message = sb.toString();
5960: } else
5961: message = String.valueOf(t);
5962: MessageDialog.showMessageDialog(this , message, "Error");
5963: }
5964: return;
5965: }
5966: int index = input.indexOf('=');
5967: if (index >= 0) {
5968: String key = input.substring(0, index).trim();
5969: if (key.indexOf(' ') < 0 && key.indexOf('\t') < 0) {
5970: String value = input.substring(index + 1).trim();
5971: setProperty(key, value);
5972: return;
5973: }
5974: }
5975: String[] array = parseCommand(input);
5976: if (array != null) {
5977: final String command = array[0];
5978: final String parameters = array[1];
5979: Runnable r = new Runnable() {
5980: public void run() {
5981: try {
5982: StatusBar statusBar = getStatusBar();
5983: statusBar.setText("");
5984: execute(command, parameters);
5985: if (interactive && parameters == null) {
5986: // Suggest key binding if one is available.
5987: Object[] values = getKeyMapping(command);
5988: Debug.assertTrue(values != null);
5989: Debug.assertTrue(values.length == 2);
5990: KeyMapping mapping = (KeyMapping) values[0];
5991: Mode mode = (Mode) values[1];
5992: if (mapping != null) {
5993: String statusText = statusBar.getText();
5994: boolean append = statusText != null
5995: && statusText.length() > 0;
5996: FastStringBuffer sb = new FastStringBuffer();
5997: if (append) {
5998: sb.append(statusText);
5999: sb.append(" ");
6000: }
6001: sb.append(command);
6002: sb.append(" is mapped to ");
6003: sb.append(mapping.getKeyText());
6004: if (mode != null) {
6005: sb.append(" (");
6006: sb.append(mode);
6007: sb.append(" mode)");
6008: } else
6009: sb.append(" (global mapping)");
6010: status(sb.toString());
6011: }
6012: }
6013: } catch (NoSuchMethodException e) {
6014: FastStringBuffer sb = new FastStringBuffer(
6015: "Unknown command \"");
6016: sb.append(command);
6017: sb.append('"');
6018: MessageDialog.showMessageDialog(Editor.this , sb
6019: .toString(), "Error");
6020: }
6021: }
6022: };
6023: if (SwingUtilities.isEventDispatchThread()) {
6024: r.run();
6025: } else {
6026: try {
6027: SwingUtilities.invokeAndWait(r);
6028: } catch (Throwable t) {
6029: Log.debug(t);
6030: }
6031: }
6032: }
6033: }
6034:
6035: private static String[] parseCommand(String command) {
6036: command = Utilities.trimLeading(command);
6037: // Command name is terminated by whitespace or '('.
6038: char delimiter = '\0';
6039: int index = -1;
6040: int commandLength = command.length();
6041: for (int i = 0; i < commandLength; i++) {
6042: char c = command.charAt(i);
6043: if (c == '(' || Character.isWhitespace(c)) {
6044: delimiter = c;
6045: index = i;
6046: break;
6047: }
6048: }
6049: String methodName, parameters;
6050: if (index < 0) {
6051: methodName = command;
6052: parameters = null;
6053: } else {
6054: methodName = command.substring(0, index);
6055: parameters = Utilities
6056: .trimLeading(command.substring(index));
6057: if (delimiter != '(') {
6058: if (parameters.startsWith("("))
6059: delimiter = '(';
6060: }
6061: if (delimiter == '(') {
6062: // Strip parens.
6063: int length = parameters.length();
6064: if (length < 2)
6065: return null; // Error.
6066: if (parameters.charAt(length - 1) != ')')
6067: return null; // Error.
6068: parameters = parameters.substring(1, length - 1).trim();
6069: length = parameters.length();
6070: if (length == 0)
6071: parameters = null;
6072: else {
6073: // Strip required quotes.
6074: if (length < 2)
6075: return null; // Error.
6076: if (parameters.charAt(0) != '"'
6077: || parameters.charAt(length - 1) != '"')
6078: return null; // Error.
6079: parameters = parameters.substring(1, length - 1); // Done.
6080: }
6081: }
6082: }
6083: String[] array = new String[2];
6084: array[0] = methodName;
6085: array[1] = parameters;
6086: return array;
6087: }
6088:
6089: // Set a buffer-specific property.
6090: private void setProperty(String key, String value) {
6091: Property property = Property.findProperty(key);
6092: if (property == null) {
6093: MessageDialog.showMessageDialog("Property \"" + key
6094: + "\" not found", "Error");
6095: return;
6096: }
6097: final boolean succeeded;
6098: if (value.length() == 0) {
6099: succeeded = buffer.removeProperty(property);
6100: } else {
6101: succeeded = buffer.setPropertyFromString(property, value);
6102: if (!succeeded)
6103: invalidPropertyValue(property, value);
6104: }
6105: if (succeeded)
6106: buffer.saveProperties();
6107: }
6108:
6109: // No error checking.
6110: public static void setGlobalProperty(String key, String value) {
6111: if (value == null || value.length() == 0)
6112: prefs.removeProperty(key);
6113: else
6114: prefs.setProperty(key, value);
6115: }
6116:
6117: private void invalidPropertyValue(Property property, String value) {
6118: if (property.isIntegerProperty())
6119: status("Invalid integer value \"" + value + "\"");
6120: else if (property.isBooleanProperty())
6121: status("Invalid boolean value \"" + value + "\"");
6122: }
6123:
6124: public void slideIn() {
6125: slide(buffer.getIndentSize());
6126: }
6127:
6128: public void slideOut() {
6129: slide(-buffer.getIndentSize());
6130: }
6131:
6132: private void slide(int amount) {
6133: if (!checkReadOnly())
6134: return;
6135: Region r = mark != null ? new Region(this ) : null;
6136: if (r != null
6137: && (r.getBeginOffset() != 0 || r.getEndOffset() != 0))
6138: return; // If a block is marked, it must be a block of full lines.
6139: try {
6140: buffer.lockWrite();
6141: } catch (InterruptedException e) {
6142: Log.error(e);
6143: return;
6144: }
6145: try {
6146: if (r == null) {
6147: CompoundEdit compoundEdit = beginCompoundEdit();
6148: int dotCol = getDotCol();
6149: int oldIndent = buffer.getIndentation(getDotLine());
6150: int newIndent = oldIndent + amount;
6151: addUndo(SimpleEdit.LINE_EDIT);
6152: buffer.setIndentation(getDotLine(), newIndent);
6153: updateInAllEditors(getDotLine());
6154: if (dotCol < oldIndent) {
6155: // Caret was originally in indentation area. Move caret to
6156: // start of text. This ensures that the caret is on an
6157: // actual character, in case the indentation got entabbed.
6158: moveDotToCol(newIndent);
6159: } else {
6160: // Move caret with text.
6161: display.setCaretCol(display.getCaretCol() + amount);
6162: moveDotToCaretCol();
6163: }
6164: endCompoundEdit(compoundEdit);
6165: buffer.modified();
6166: } else {
6167: // If a block is marked, it must be a block of full lines.
6168: CompoundEdit compoundEdit = beginCompoundEdit();
6169: Position saved = new Position(dot);
6170: Line line = r.getBeginLine();
6171: while (line != r.getEndLine()) {
6172: addUndo(SimpleEdit.MOVE);
6173: dot.moveTo(line, 0);
6174: addUndo(SimpleEdit.LINE_EDIT);
6175: buffer.setIndentation(getDotLine(), buffer
6176: .getIndentation(getDotLine())
6177: + amount);
6178: updateInAllEditors(getDotLine());
6179: line = line.next();
6180: }
6181: addUndo(SimpleEdit.MOVE);
6182: dot = saved;
6183: endCompoundEdit(compoundEdit);
6184: buffer.modified();
6185: }
6186: } finally {
6187: buffer.unlockWrite();
6188: }
6189: }
6190:
6191: public void dirHome() {
6192: if (buffer instanceof Directory)
6193: ((Directory) buffer).home();
6194: }
6195:
6196: public void dirTagFile() {
6197: if (buffer instanceof Directory)
6198: ((Directory) buffer).tagFileAtDot();
6199: }
6200:
6201: public void dirBrowseFile() {
6202: if (buffer instanceof Directory && !buffer.getFile().isRemote()) {
6203: Directory d = (Directory) buffer;
6204: d.browseFileAtDot();
6205: }
6206: }
6207:
6208: public void dirDeleteFiles() {
6209: if (mark != null && getMarkLine() != getDotLine()) {
6210: MessageDialog
6211: .showMessageDialog(
6212: this ,
6213: "This operation is not supported with multi-line text selections.",
6214: "Delete Files");
6215: return;
6216: }
6217: if (buffer instanceof Directory) {
6218: if (buffer.getFile() instanceof SshFile) {
6219: MessageDialog
6220: .showMessageDialog(
6221: this ,
6222: "Deletions are not yet supported in ssh directory buffers.",
6223: "Error");
6224: return;
6225: }
6226: ((Directory) buffer).deleteFiles();
6227: }
6228: }
6229:
6230: public void dirCopyFile() {
6231: if (buffer instanceof Directory && buffer.getFile().isLocal())
6232: ((Directory) buffer).copyFileAtDot();
6233: }
6234:
6235: public void dirGetFile() {
6236: if (buffer instanceof Directory
6237: && buffer.getFile() instanceof FtpFile)
6238: ((Directory) buffer).getFileAtDot();
6239: }
6240:
6241: public void dirMoveFile() {
6242: if (buffer instanceof Directory && buffer.getFile().isLocal())
6243: ((Directory) buffer).moveFileAtDot();
6244: }
6245:
6246: public void dirRescan() {
6247: if (buffer instanceof Directory) {
6248: setWaitCursor();
6249: ((Directory) buffer).rescan();
6250: setDefaultCursor();
6251: }
6252: }
6253:
6254: public void dirHomeDir() {
6255: File homeDir = File.getInstance(Utilities.getUserHome());
6256: if (buffer instanceof Directory) {
6257: if (!buffer.getFile().equals(homeDir))
6258: ((Directory) buffer).changeDirectory(homeDir);
6259: } else {
6260: Buffer buf = getBuffer(homeDir);
6261: if (buf != null) {
6262: makeNext(buf);
6263: activate(buf);
6264: }
6265: }
6266: }
6267:
6268: public void dirUpDir() {
6269: if (buffer instanceof Directory)
6270: ((Directory) buffer).upDir();
6271: }
6272:
6273: public void setFocusToTextField() {
6274: frame.setFocus(locationBar.getTextField());
6275: }
6276:
6277: public void wrapRegion() {
6278: if (!checkReadOnly())
6279: return;
6280: if (dot == null || mark == null)
6281: return;
6282: // Must be line block.
6283: if (dot.getOffset() != 0 || mark.getOffset() != 0)
6284: return;
6285: new WrapText(this ).wrapRegion();
6286: }
6287:
6288: public void wrapParagraph() {
6289: if (!checkReadOnly())
6290: return;
6291: new WrapText(this ).wrapParagraph();
6292: }
6293:
6294: public void unwrapParagraph() {
6295: if (!checkReadOnly())
6296: return;
6297: new WrapText(this ).unwrapParagraph();
6298: }
6299:
6300: public void wrapParagraphsInRegion() {
6301: if (!checkReadOnly())
6302: return;
6303: new WrapText(this ).wrapParagraphsInRegion();
6304: }
6305:
6306: public void visibleTabs() {
6307: tabsAreVisible = !tabsAreVisible;
6308: if (tabsAreVisible)
6309: status("Tabs are visible");
6310: else
6311: status("Tabs are not visible");
6312: for (int i = 0; i < getEditorCount(); i++) {
6313: Editor ed = getEditor(i);
6314: ed.getDisplay().repaint();
6315: }
6316: }
6317:
6318: public void insertBraces() {
6319: CompoundEdit compoundEdit = beginCompoundEdit();
6320: insertChar('{');
6321: indentLine();
6322: eol();
6323: newlineAndIndent();
6324: insertChar('}');
6325: indentLine();
6326: up();
6327: eol();
6328: newlineAndIndent();
6329: endCompoundEdit(compoundEdit);
6330: }
6331:
6332: public void insertParentheses() {
6333: if (!checkReadOnly())
6334: return;
6335: boolean parensRequireSpaces = buffer
6336: .getBooleanProperty(Property.PARENS_REQUIRE_SPACES);
6337: CompoundEdit compoundEdit = beginCompoundEdit();
6338: if (mark != null) {
6339: Position begin, end;
6340: if (mark.isBefore(dot)) {
6341: begin = new Position(mark);
6342: end = new Position(dot);
6343: } else {
6344: begin = new Position(dot);
6345: end = new Position(mark);
6346: }
6347: addUndo(SimpleEdit.MOVE);
6348: setMark(null);
6349: dot.moveTo(end);
6350: if (parensRequireSpaces)
6351: insertChar(' ');
6352: insertChar(')');
6353: addUndo(SimpleEdit.MOVE);
6354: dot.moveTo(begin);
6355: insertChar('(');
6356: if (parensRequireSpaces)
6357: insertChar(' ');
6358: } else {
6359: fillToCaret();
6360: addUndo(SimpleEdit.INSERT_STRING);
6361: insertStringInternal(parensRequireSpaces ? "( )" : "()");
6362: addUndo(SimpleEdit.MOVE);
6363: dot.skip(parensRequireSpaces ? -2 : -1);
6364: }
6365: moveCaretToDotCol();
6366: endCompoundEdit(compoundEdit);
6367: }
6368:
6369: public void movePastCloseAndReindent() {
6370: Position pos = new Position(dot);
6371: int count = 1;
6372: while (pos.next()) {
6373: char c = pos.getChar();
6374: if (c == '(')
6375: ++count;
6376: else if (c == ')')
6377: --count;
6378: if (count == 0)
6379: break;
6380: }
6381: if (count == 0) {
6382: pos.next();
6383: updateDotLine();
6384: CompoundEdit compoundEdit = beginCompoundEdit();
6385: addUndo(SimpleEdit.MOVE);
6386: dot.moveTo(pos);
6387: updateDotLine();
6388: newlineAndIndent();
6389: endCompoundEdit(compoundEdit);
6390: }
6391: }
6392:
6393: public final void updateDotLine() {
6394: display.lineChanged(dot.getLine());
6395: }
6396:
6397: // Adds line to changed line list of current frame only.
6398: public final void update(Line line) {
6399: display.lineChanged(line);
6400: }
6401:
6402: /**
6403: * Adds line to changed line list of all editors in which the current
6404: * buffer is displayed.
6405: *
6406: * @param line the line
6407: */
6408: public static void updateInAllEditors(Line line) {
6409: if (line != null) {
6410: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
6411: Editor ed = it.nextEditor();
6412: if (ed.getBuffer() == currentEditor.getBuffer())
6413: ed.getDisplay().lineChanged(line);
6414: }
6415: }
6416: }
6417:
6418: /**
6419: * Adds line to changed line list of all editors in which the specified
6420: * buffer is displayed.
6421: *
6422: * @param buffer the buffer
6423: * @param line the line
6424: */
6425: public static void updateInAllEditors(Buffer buffer, Line line) {
6426: if (line != null) {
6427: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
6428: Editor ed = it.nextEditor();
6429: if (ed.getBuffer() == buffer)
6430: ed.getDisplay().lineChanged(line);
6431: }
6432: }
6433: }
6434:
6435: public void updateScrollBars() {
6436: if (buffer == null)
6437: return; // Avoid NPE.
6438: if (!displayReady)
6439: return;
6440: updateVerticalScrollBar();
6441: updateHorizontalScrollBar();
6442: }
6443:
6444: boolean inScrollBarUpdate = false;
6445:
6446: public void updateVerticalScrollBar() {
6447: if (verticalScrollBar != null) {
6448: inScrollBarUpdate = true;
6449: int y;
6450: if (getTopLine() != null)
6451: y = buffer.getY(getTopLine())
6452: + display.getPixelsAboveTopLine();
6453: else
6454: y = display.getPixelsAboveTopLine();
6455: verticalScrollBar.setValues(y, display.getHeight(), 0,
6456: buffer.getDisplayHeight());
6457: inScrollBarUpdate = false;
6458: }
6459: }
6460:
6461: public void updateHorizontalScrollBar() {
6462: if (horizontalScrollBar != null)
6463: horizontalScrollBar.setValues(display.getShift()
6464: * Display.getCharWidth(), display.getWidth(), 0,
6465: buffer.getDisplayWidth());
6466: }
6467:
6468: public void updateDisplay() {
6469: if (dot != null) {
6470: if (dot.isHidden()) {
6471: buffer.appendUndoFold(this );
6472: show(getDotLine());
6473: }
6474: reframe();
6475: }
6476: display.repaintChangedLines();
6477: updateScrollBars();
6478: Sidebar sidebar = getSidebar();
6479: if (sidebar != null)
6480: sidebar.setUpdateFlag(SIDEBAR_POSITION);
6481: frame.repaintStatusBar();
6482: if (buffer.isBusy())
6483: setWaitCursor();
6484: else
6485: setDefaultCursor();
6486: }
6487:
6488: public void updateDisplayLater() {
6489: Runnable r = new Runnable() {
6490: public void run() {
6491: updateDisplay();
6492: }
6493: };
6494: SwingUtilities.invokeLater(r);
6495: }
6496:
6497: // Update display of buf in all windows showing it.
6498: public static void updateDisplayLater(final Buffer buf) {
6499: Runnable r = new Runnable() {
6500: public void run() {
6501: for (int i = 0; i < getEditorCount(); i++) {
6502: Editor ed = getEditor(i);
6503: if (ed.getBuffer() == buf)
6504: ed.updateDisplay();
6505: }
6506: }
6507: };
6508: SwingUtilities.invokeLater(r);
6509: }
6510:
6511: public final Line getTopLine() {
6512: return display.getTopLine();
6513: }
6514:
6515: public final void setTopLine(Line line) {
6516: display.setTopLine(line);
6517: }
6518:
6519: public final void setUpdateFlag(int mask) {
6520: display.setUpdateFlag(mask);
6521: }
6522:
6523: public final void reframe() {
6524: display.reframe();
6525: }
6526:
6527: public boolean checkReadOnly() {
6528: boolean readOnly = buffer.isReadOnly();
6529: if (readOnly
6530: && buffer.getBooleanProperty(Property.P4_AUTO_EDIT)) {
6531: if (buffer.getType() == Buffer.TYPE_NORMAL) {
6532: File file = buffer.getFile();
6533: if (file != null && file.isLocal() && file.isFile())
6534: if (P4.autoEdit(this ))
6535: readOnly = buffer.isReadOnly();
6536: }
6537: }
6538: if (readOnly) {
6539: status("Buffer is read only");
6540: return false;
6541: }
6542: if (buffer.isLocked())
6543: return false;
6544: if (dot == null)
6545: return false;
6546: return true;
6547: }
6548:
6549: public static boolean checkExperimental() {
6550: return prefs
6551: .getBooleanProperty(Property.ENABLE_EXPERIMENTAL_FEATURES);
6552: }
6553:
6554: public void status(String s) {
6555: frame.setStatusText(s);
6556: }
6557:
6558: private static boolean displayReady;
6559:
6560: public static final boolean displayReady() {
6561: return displayReady;
6562: }
6563:
6564: public static final void setDisplayReady(boolean b) {
6565: displayReady = b;
6566: }
6567:
6568: private static final Cursor waitCursor = Cursor
6569: .getPredefinedCursor(Cursor.WAIT_CURSOR);
6570:
6571: public final void setWaitCursor() {
6572: display.setCursor(waitCursor);
6573: }
6574:
6575: public final void setDefaultCursor() {
6576: final Cursor cursor;
6577: if (displayReady && buffer != null)
6578: cursor = buffer.getDefaultCursor();
6579: else
6580: cursor = waitCursor;
6581: display.setCursor(cursor);
6582: }
6583:
6584: public final void setCursor(Cursor cursor) {
6585: display.setCursor(cursor);
6586: }
6587:
6588: public static void loadPreferences() {
6589: prefs.reload();
6590: debug = prefs.getBooleanProperty(Property.DEBUG);
6591: }
6592:
6593: private boolean insertingKeyText = false;
6594:
6595: public void insertKeyText() {
6596: if (!checkReadOnly())
6597: return;
6598: insertingKeyText = true; // The real work is done in handleKeyEvent.
6599: }
6600:
6601: private void insertKeyTextInternal(char keyChar, int keyCode,
6602: int modifiers) {
6603: Log.debug("keycode = 0x" + Integer.toString(keyCode, 16));
6604: Log.debug("modifiers = 0x" + Integer.toString(modifiers, 16));
6605: Log.debug("character = " + String.valueOf(keyChar));
6606: Log.debug("character = 0x"
6607: + Integer.toString((int) keyChar, 16));
6608:
6609: insertingKeyText = false;
6610:
6611: try {
6612: buffer.lockWrite();
6613: } catch (InterruptedException e) {
6614: Log.error(e);
6615: return;
6616: }
6617: try {
6618: KeyMapping km;
6619: if (keyCode != 0)
6620: km = new KeyMapping(keyCode, modifiers, null);
6621: else
6622: km = new KeyMapping(keyChar, null);
6623:
6624: CompoundEdit compoundEdit = beginCompoundEdit();
6625: if (mark != null)
6626: delete();
6627: fillToCaret();
6628: addUndo(SimpleEdit.INSERT_STRING);
6629: insertStringInternal(km.toString());
6630: buffer.modified();
6631: moveCaretToDotCol();
6632: endCompoundEdit(compoundEdit);
6633: } finally {
6634: buffer.unlockWrite();
6635: }
6636: }
6637:
6638: public void whatChar() {
6639: if (dot.getOffset() < dot.getLineLength()) {
6640: char c = getDotChar();
6641: FastStringBuffer sb = new FastStringBuffer(Integer
6642: .toString(c));
6643: sb.append(" 0x");
6644: sb.append(Integer.toHexString(c));
6645: if (c >= ' ' && c < 0x7f) {
6646: sb.append(" '");
6647: if (c == '\'')
6648: sb.append('\\');
6649: sb.append(c);
6650: sb.append('\'');
6651: }
6652: status(sb.toString());
6653: }
6654: }
6655:
6656: public void jmips() {
6657: setWaitCursor();
6658: long loopsPerSecond = 0;
6659: long loops = 1;
6660: Thread thread = Thread.currentThread();
6661: int oldPriority = thread.getPriority();
6662: thread.setPriority(Thread.MAX_PRIORITY);
6663: do {
6664: long start = System.currentTimeMillis();
6665: for (long i = loops; i >= 0; i--)
6666: ;
6667: long elapsed = System.currentTimeMillis() - start;
6668: if (elapsed >= 1000) {
6669: loopsPerSecond = (loops / elapsed) * 1000;
6670: break;
6671: }
6672: loops *= 2;
6673: } while (true);
6674: thread.setPriority(oldPriority);
6675: String s = String.valueOf(((float) loopsPerSecond) / 500000);
6676: currentEditor.status(s);
6677: setDefaultCursor();
6678: }
6679:
6680: public void httpDeleteCookies() {
6681: Cookie.deleteCookies();
6682: }
6683:
6684: private static Class extensionClass = null;
6685:
6686: private static void loadExtensions() {
6687: String extension = prefs.getStringProperty(Property.EXTENSION);
6688: if (extension != null) {
6689: Log.debug("loading extension " + extension);
6690: try {
6691: ExtensionClassLoader loader = new ExtensionClassLoader();
6692: extensionClass = loader.loadClass(extension, true);
6693: if (extensionClass != null) {
6694: Method method = extensionClass.getMethod("run",
6695: new Class[0]);
6696: if (method != null)
6697: method.invoke(extensionClass.newInstance(),
6698: new Class[0]);
6699: } else
6700: Log.error("extension " + extension + " not found");
6701: } catch (Exception e) {
6702: Log.error(e);
6703: }
6704: }
6705: }
6706:
6707: private static Class loadExtensionClass(String className) {
6708: Class c = null;
6709: try {
6710: ExtensionClassLoader loader = new ExtensionClassLoader();
6711: c = loader.loadClass(className, true);
6712: } catch (Exception e) {
6713: Log.error(e);
6714: }
6715: return c;
6716: }
6717:
6718: private static void runStartupScript() {
6719: File file = File.getInstance(Directories.getEditorDirectory(),
6720: "init.lisp");
6721: if (file != null && file.isFile()) {
6722: try {
6723: long start = System.currentTimeMillis();
6724: JLisp.runStartupScript(file);
6725: long elapsed = System.currentTimeMillis() - start;
6726: FastStringBuffer sb = new FastStringBuffer("loaded ");
6727: sb.append(file.canonicalPath());
6728: sb.append(" (");
6729: sb.append(elapsed);
6730: sb.append(" ms)");
6731: Log.info(sb.toString());
6732: } catch (Throwable t) {
6733: Log.error(t);
6734: Log.error("error loading " + file.canonicalPath());
6735: }
6736: }
6737: }
6738:
6739: public static void runLispCommand(String command) {
6740: try {
6741: JLisp.runLispCommand(command);
6742: } catch (Throwable t) {
6743: Log.error(t);
6744: }
6745: }
6746:
6747: private static boolean isLispInitialized;
6748:
6749: public static synchronized boolean isLispInitialized() {
6750: return isLispInitialized;
6751: }
6752:
6753: public static synchronized void setLispInitialized(boolean b) {
6754: isLispInitialized = b;
6755: }
6756:
6757: public static void invokeHook(String hook) {
6758: invokeHook(hook, null);
6759: }
6760:
6761: public static void invokeHook(String hook, String args) {
6762: FastStringBuffer sb = new FastStringBuffer("(invoke-hook '");
6763: sb.append(hook);
6764: if (args != null && args.length() > 0) {
6765: sb.append(' ');
6766: sb.append(args);
6767: }
6768: sb.append(')');
6769: runLispCommand(sb.toString());
6770: }
6771:
6772: public void mode() {
6773: String modeName = InputDialog.showInputDialog(this ,
6774: "New mode:", "Change Mode");
6775: if (modeName != null) {
6776: modeName = modeName.trim();
6777: if (modeName.length() > 0) {
6778: repaintNow();
6779: mode(modeName);
6780: }
6781: }
6782: }
6783:
6784: public void mode(String modeName) {
6785: int modeId = getModeList().getModeIdFromModeName(modeName);
6786: if (modeId < 0) {
6787: MessageDialog.showMessageDialog("Unknown mode \""
6788: + modeName + '"', "Error");
6789: } else if (modeId != buffer.getMode().getId()) {
6790: if (buffer.isModified() && modeId == BINARY_MODE) {
6791: String prompt = "Buffer will be reloaded in binary mode; discard changes?";
6792: if (!confirm("Change Mode", prompt))
6793: return;
6794: }
6795: Mode mode = getModeList().getMode(modeId);
6796: if (mode != null) {
6797: setWaitCursor();
6798: buffer.changeMode(mode);
6799: buffer.saveProperties();
6800: setDefaultCursor();
6801: }
6802: }
6803: }
6804:
6805: public void defaultMode() {
6806: Mode mode = buffer.getDefaultMode();
6807: if (mode != null && mode != buffer.getMode()) {
6808: if (buffer.isModified()) {
6809: FastStringBuffer sb = new FastStringBuffer(
6810: "Buffer will be reloaded in ");
6811: sb.append(mode.toString());
6812: sb.append(" mode; discard changes?");
6813: if (!confirm("Change Mode", sb.toString()))
6814: return;
6815: }
6816: setWaitCursor();
6817: buffer.changeMode(mode);
6818: setDefaultCursor();
6819: }
6820: }
6821:
6822: public void textMode() {
6823: if (buffer.getModeId() == BINARY_MODE) {
6824: if (buffer.isModified()) {
6825: String prompt = "Buffer will be reloaded in text mode; discard changes?";
6826: if (!confirm("Change Mode", prompt))
6827: return;
6828: }
6829: setWaitCursor();
6830: buffer.changeMode(modeList.getMode(PLAIN_TEXT_MODE));
6831: setDefaultCursor();
6832: }
6833: }
6834:
6835: public void splitWindow() {
6836: currentEditor.getFrame().splitWindow();
6837: }
6838:
6839: public void unsplitWindow() {
6840: IdleThread.killFollowContextTask();
6841: frame.unsplitWindow();
6842: }
6843:
6844: public void killWindow() {
6845: frame.unsplitWindowKeepOther();
6846: Sidebar sidebar = getSidebar();
6847: if (sidebar != null) {
6848: sidebar.setUpdateFlag(SIDEBAR_ALL);
6849: sidebar.refreshSidebar();
6850: }
6851: }
6852:
6853: public void otherWindow() {
6854: final Editor ed = frame.getOtherEditor();
6855: if (ed != null) {
6856: saveView();
6857: setCurrentEditor(ed);
6858: ed.getBuffer().setLastActivated(System.currentTimeMillis());
6859: ed.setFocusToDisplay();
6860: if (ed.getDot() != null) {
6861: ed.update(ed.getDotLine());
6862: ed.getDisplay().repaintChangedLines();
6863: }
6864: if (dot != null) {
6865: updateDotLine();
6866: display.repaintChangedLines();
6867: }
6868: frame.setMenu();
6869: frame.setToolbar();
6870: Sidebar sidebar = getSidebar();
6871: if (sidebar != null) {
6872: sidebar.setUpdateFlag(SIDEBAR_ALL);
6873: sidebar.refreshSidebar();
6874: }
6875: }
6876: }
6877:
6878: public void fold() {
6879: if (dot == null)
6880: return;
6881: if (!foldRegionInternal() && !foldExplicit())
6882: foldNearLine(getDotLine());
6883: }
6884:
6885: public void foldRegion() {
6886: if (dot == null)
6887: return;
6888: foldRegionInternal();
6889: }
6890:
6891: private boolean foldRegionInternal() {
6892: if (mark == null)
6893: return false;
6894: if (dot.getLine() == mark.getLine())
6895: return false;
6896: if (dot.getOffset() > 0)
6897: return false;
6898: if (mark.getOffset() > 0)
6899: return false;
6900: Region r = new Region(buffer, mark, dot);
6901: Line begin = r.getBegin().getLine().next();
6902: Line end = r.getEnd().getLine();
6903: addUndo(SimpleEdit.FOLD);
6904: setMark(null);
6905: for (Line line = begin; line != end; line = line.next())
6906: line.hide();
6907: buffer.renumber();
6908: unhideDotInAllFrames(buffer);
6909: return true;
6910: }
6911:
6912: private boolean foldExplicit() {
6913: final Line dotLine = getDotLine();
6914: String text = dotLine.getText();
6915: Line begin = null;
6916: Line end = null;
6917: if (text.indexOf(EXPLICIT_FOLD_END) >= 0) {
6918: // Current line contains an end marker.
6919: int count = 1;
6920: end = dotLine.next();
6921: begin = dotLine.previous();
6922: while (begin != null) {
6923: text = begin.getText();
6924: if (text.indexOf(EXPLICIT_FOLD_START) >= 0) {
6925: --count;
6926: if (count == 0) {
6927: begin = begin.next();
6928: break;
6929: }
6930: } else if (text.indexOf(EXPLICIT_FOLD_END) >= 0) {
6931: ++count;
6932: }
6933: begin = begin.previous();
6934: }
6935: } else if (text.indexOf(EXPLICIT_FOLD_START) >= 0) {
6936: int count = 1;
6937: begin = dotLine.next();
6938: if (begin == null)
6939: return false;
6940: end = begin.next();
6941: while (end != null) {
6942: text = end.getText();
6943: if (text.indexOf(EXPLICIT_FOLD_START) >= 0) {
6944: ++count;
6945: } else if (text.indexOf(EXPLICIT_FOLD_END) >= 0) {
6946: --count;
6947: if (count == 0) {
6948: end = end.next();
6949: break;
6950: }
6951: }
6952: end = end.next();
6953: }
6954: }
6955: if (begin == null)
6956: return false;
6957: addUndo(SimpleEdit.FOLD);
6958: for (Line line = begin; line != end; line = line.next())
6959: line.hide();
6960: buffer.renumber();
6961: unhideDotInAllFrames(buffer);
6962: return true;
6963: }
6964:
6965: public void foldNearLine(Line line) {
6966: while (line != null && line.isBlank())
6967: line = line.previous();
6968: if (line == null)
6969: return;
6970: setWaitCursor();
6971: Line next = line.next();
6972: while (next != null && next.isBlank())
6973: next = next.next();
6974: if (next != null) {
6975: final String trim = line.trim();
6976: switch (getModeId()) {
6977: case JAVA_MODE:
6978: case JAVASCRIPT_MODE:
6979: case C_MODE:
6980: case CPP_MODE:
6981: case PERL_MODE:
6982: case PHP_MODE:
6983: if (trim.endsWith("{")) {
6984: if (!next.isHidden()) {
6985: fold(next);
6986: return;
6987: }
6988: }
6989: if (trim.startsWith("}")) {
6990: // We're at the end of a code block. Find the start of
6991: // the block and fold from there.
6992: Position end = new Position(line, line.getText()
6993: .indexOf('}'));
6994: Position start = findMatchInternal(end, 0);
6995: if (start != null) {
6996: foldNearLine(start.getLine());
6997: return;
6998: }
6999: }
7000: if (next.trim().startsWith("}")) {
7001: // Fold block containing current line.
7002: Position end = new Position(next, next.getText()
7003: .indexOf('}'));
7004: Position start = findMatchInternal(end, 0);
7005: if (start != null) {
7006: foldNearLine(start.getLine());
7007: return;
7008: }
7009: } else if (next.trim().endsWith("{")) {
7010: Line nextNext = next.next();
7011: while (nextNext != null && nextNext.isBlank())
7012: nextNext = nextNext.next();
7013: if (nextNext != null && !nextNext.isHidden()) {
7014: fold(nextNext);
7015: return;
7016: }
7017: }
7018: break;
7019: case XML_MODE: {
7020: if (trim.startsWith("/>") || trim.startsWith("</")) {
7021: Line prev = line.previous();
7022: while (prev != null && prev.isBlank())
7023: prev = prev.previous();
7024: if (prev != null) {
7025: int indent = buffer.getCol(line, line
7026: .getIndentation());
7027: int prevIndent = buffer.getCol(prev, prev
7028: .getIndentation());
7029: if (indent < prevIndent) {
7030: fold(prev);
7031: return;
7032: }
7033: }
7034: } else if (trim.startsWith("<")) {
7035: int indent = buffer.getCol(line, line
7036: .getIndentation());
7037: int nextIndent = buffer.getCol(next, next
7038: .getIndentation());
7039: if (indent < nextIndent) {
7040: fold(next);
7041: return;
7042: }
7043: }
7044: }
7045: default:
7046: break;
7047: }
7048: }
7049: fold(line);
7050: }
7051:
7052: private void fold(Line target) {
7053: if (target == null)
7054: return;
7055: int indent = buffer.getCol(target, target.getIndentation());
7056: if (indent == 0)
7057: return;
7058: Line begin = target;
7059: while (true) {
7060: Line prev = begin.previous();
7061: if (prev == null)
7062: break;
7063: if (prev.isBlank() || getMode().isCommentLine(prev)
7064: || isLabelLine(prev) || isPreprocessorLine(prev)) {
7065: begin = prev;
7066: continue;
7067: }
7068: if (buffer.getCol(prev, prev.getIndentation()) < indent)
7069: break;
7070: if (prev.getText().endsWith("{"))
7071: break;
7072: begin = prev;
7073: }
7074: Line end = target.next();
7075: while (end != null) {
7076: if (end.isBlank() || getMode().isCommentLine(end)
7077: || isLabelLine(end) || isPreprocessorLine(end)) {
7078: end = end.next();
7079: continue;
7080: }
7081: if (buffer.getCol(end, end.getIndentation()) < indent)
7082: break;
7083: end = end.next();
7084: }
7085: addUndo(SimpleEdit.FOLD);
7086: for (Line line = begin; line != end; line = line.next())
7087: line.hide();
7088: buffer.renumber();
7089: unhideDotInAllFrames(buffer);
7090: }
7091:
7092: private static RE labelRE = new UncheckedRE("^\\s*\\w+:");
7093:
7094: private boolean isLabelLine(Line line) {
7095: Mode mode = getMode();
7096: if (mode instanceof JavaMode || mode instanceof PerlMode)
7097: return labelRE.getMatch(line.getText()) != null;
7098: return false;
7099: }
7100:
7101: private boolean isPreprocessorLine(Line line) {
7102: if (getMode() instanceof CMode)
7103: if (line.trim().startsWith("#"))
7104: return true;
7105:
7106: return false;
7107: }
7108:
7109: // BUG! This method does more than its name suggests...
7110: public static void unhideDotInAllFrames(Buffer buffer) {
7111: for (int i = 0; i < Editor.getEditorCount(); i++) {
7112: Editor ed = Editor.getEditor(i);
7113: if (ed.getBuffer() == buffer) {
7114: // Make sure dot is visible.
7115: if (ed.getDot().isHidden()) {
7116: ed.setMark(null);
7117: Line line = ed.getDotLine().previousVisible();
7118: if (line != null) {
7119: ed.setDot(line, 0);
7120: ed.moveCaretToDotCol();
7121: }
7122: }
7123: ed.setUpdateFlag(REFRAME);
7124: ed.reframe();
7125: ed.getDisplay().repaint();
7126: }
7127: }
7128: }
7129:
7130: public void unfold() {
7131: if (dot == null)
7132: return;
7133: Line dotLine = getDotLine();
7134: Line begin = null;
7135: if (dotLine.isHidden()) {
7136: begin = dotLine;
7137: while (begin.previous() != null
7138: && begin.previous().isHidden())
7139: begin = begin.previous();
7140: } else {
7141: // Look for next fold.
7142: begin = dotLine.next();
7143: while (begin != null && !begin.isHidden())
7144: begin = begin.next();
7145: }
7146: if (begin == null)
7147: return;
7148: if (!begin.isHidden())
7149: return;
7150: Line end = begin;
7151: while (true) {
7152: Line line = end.next();
7153: if (line == null)
7154: break;
7155: if (!line.isHidden())
7156: break;
7157: end = line;
7158: }
7159: if (begin != null && end != null) {
7160: addUndo(SimpleEdit.FOLD);
7161: for (Line line = begin; line != end.next(); line = line
7162: .next())
7163: line.unhide();
7164: buffer.renumber();
7165: for (int i = 0; i < Editor.getEditorCount(); i++) {
7166: Editor ed = Editor.getEditor(i);
7167: if (ed.getBuffer() == buffer)
7168: ed.getDisplay().repaint();
7169: }
7170: }
7171: }
7172:
7173: public void unfold(Line line) {
7174: if (line == null)
7175: return;
7176: if (!line.isHidden())
7177: return;
7178: Line begin = line;
7179: while (begin.previous() != null && begin.previous().isHidden())
7180: begin = begin.previous();
7181: Line end = line.next();
7182: while (end != null && end.isHidden())
7183: end = end.next();
7184: addUndo(SimpleEdit.FOLD);
7185: for (Line l = begin; l != end; l = l.next())
7186: l.unhide();
7187: buffer.renumber();
7188: for (int i = 0; i < Editor.getEditorCount(); i++) {
7189: Editor ed = Editor.getEditor(i);
7190: if (ed.getBuffer() == buffer)
7191: ed.getDisplay().repaint();
7192: }
7193: }
7194:
7195: private void show(Line target) {
7196: if (target == null)
7197: return;
7198: if (!target.isHidden())
7199: return;
7200: Line begin = target;
7201: while (begin.previous() != null && begin.previous().isHidden())
7202: begin = begin.previous();
7203: Line end = target.next();
7204: while (end != null && end.isHidden())
7205: end = end.next();
7206: for (Line line = begin; line != end; line = line.next())
7207: line.show();
7208: buffer.renumber();
7209: for (int i = 0; i < Editor.getEditorCount(); i++) {
7210: Editor ed = Editor.getEditor(i);
7211: if (ed.getBuffer() == buffer)
7212: ed.getDisplay().repaint();
7213: }
7214: }
7215:
7216: public void unfoldAll() {
7217: addUndo(SimpleEdit.FOLD);
7218: for (Line line = buffer.getFirstLine(); line != null; line = line
7219: .next())
7220: line.show();
7221: buffer.renumber();
7222: for (int i = 0; i < Editor.getEditorCount(); i++) {
7223: Editor ed = Editor.getEditor(i);
7224: if (ed.getBuffer() == buffer)
7225: ed.getDisplay().repaint();
7226: }
7227: }
7228:
7229: public void foldMethods() {
7230: Mode mode = getMode();
7231: if (mode instanceof JavaMode || mode instanceof PerlMode) {
7232: setWaitCursor();
7233: List tags = buffer.getTags();
7234: if (tags != null) {
7235: addUndo(SimpleEdit.FOLD);
7236: for (Line line = buffer.getFirstLine(); line != null; line = line
7237: .next())
7238: line.show();
7239: for (int i = 0; i < tags.size(); i++) {
7240: LocalTag tag = (LocalTag) tags.get(i);
7241: if (tag.getType() == TAG_METHOD)
7242: foldMethod(tag.getLine());
7243: }
7244: unhideDotInAllFrames(buffer);
7245: }
7246: }
7247: }
7248:
7249: private final void foldMethod(Line line) {
7250: foldOrUnfoldMethod(line, true);
7251: }
7252:
7253: public final void unfoldMethod(Line line) {
7254: foldOrUnfoldMethod(line, false);
7255: }
7256:
7257: private void foldOrUnfoldMethod(Line line, boolean fold) {
7258: Mode mode = getMode();
7259: while (line != null) {
7260: String s = line.trim();
7261: if (mode instanceof PerlMode)
7262: s = PerlMode.trimSyntacticWhitespace(s);
7263: else if (mode instanceof JavaMode)
7264: s = JavaMode.trimSyntacticWhitespace(s);
7265: if (s.endsWith("{"))
7266: break;
7267: line = line.next();
7268: }
7269: if (line == null)
7270: return;
7271: if (line.next() == null)
7272: return; // Nothing to fold.
7273: Position start = new Position(line, line.length());
7274: Line begin = line.next();
7275: final SyntaxIterator it = mode.getSyntaxIterator(start);
7276: Position match = null;
7277: int count = 1;
7278: while (true) {
7279: char c = it.nextChar();
7280: if (c == SyntaxIterator.DONE)
7281: break;
7282: if (c == '{')
7283: ++count;
7284: else if (c == '}')
7285: --count;
7286: if (count == 0) {
7287: // Found it!
7288: match = it.getPosition();
7289: break;
7290: }
7291: }
7292: if (match == null)
7293: return;
7294: Line end = match.getLine();
7295: if (fold) {
7296: for (Line toBeHidden = begin; toBeHidden != end
7297: && toBeHidden != null; toBeHidden = toBeHidden
7298: .next())
7299: toBeHidden.hide();
7300: } else {
7301: for (Line toBeShown = begin; toBeShown != end
7302: && toBeShown != null; toBeShown = toBeShown.next())
7303: toBeShown.show();
7304: }
7305: buffer.needsRenumbering = true;
7306: }
7307:
7308: private static Aliases aliases;
7309:
7310: private static final Aliases getAliases() {
7311: if (aliases == null)
7312: aliases = new Aliases();
7313: return aliases;
7314: }
7315:
7316: public static final File getAliasesFile() {
7317: return aliases != null ? aliases.getFile() : null;
7318: }
7319:
7320: public static final void reloadAliases() {
7321: if (aliases != null)
7322: aliases.reload();
7323: }
7324:
7325: public final String getAlias(String alias) {
7326: return getAliases().get(alias);
7327: }
7328:
7329: public final void setAlias(String alias, String value) {
7330: getAliases().setAlias(alias, value);
7331: }
7332:
7333: public final void setAliasForBuffer(String alias, Buffer buf) {
7334: getAliases().setAliasForBuffer(alias, buf);
7335: }
7336:
7337: public final void removeAlias(String alias) {
7338: getAliases().remove(alias);
7339: }
7340:
7341: public void setEncoding() {
7342: File file = buffer.getFile();
7343: if (file != null) {
7344: InputDialog d = new InputDialog(this , "Encoding:",
7345: "Set Encoding", buffer.getSaveEncoding());
7346: d.setHistory(new History("setEncoding"));
7347: centerDialog(d);
7348: d.show();
7349: String encoding = d.getInput();
7350: if (encoding != null)
7351: setEncoding(encoding);
7352: }
7353: }
7354:
7355: public void setEncoding(String encoding) {
7356: File file = buffer.getFile();
7357: if (file != null) {
7358: if (Utilities.isSupportedEncoding(encoding)) {
7359: file.setEncoding(encoding);
7360: buffer.saveProperties();
7361: } else {
7362: FastStringBuffer sb = new FastStringBuffer(
7363: "Unsupported encoding \"");
7364: sb.append(encoding);
7365: sb.append('"');
7366: MessageDialog.showMessageDialog(this , sb.toString(),
7367: "Error");
7368: }
7369: }
7370: }
7371:
7372: private static final ArrayList protectList = new ArrayList();
7373:
7374: // Add reference to global list to protect obj from garbage collection.
7375: public static synchronized void protect(Object obj) {
7376: protectList.add(obj);
7377: }
7378:
7379: private static String sessionName;
7380:
7381: /**
7382: * Returns the name of the active named session (if any).
7383: *
7384: * @return the name of the active named session, or <code>null</code> if
7385: * there is no named session.
7386: */
7387: public static String getSessionName() {
7388: return sessionName;
7389: }
7390:
7391: /**
7392: * Sets the name of the active named session.
7393: *
7394: * @param name the name of the session
7395: * @see Session#loadSession
7396: * @see Session#saveSession
7397: */
7398: public static void setSessionName(String name) {
7399: sessionName = name;
7400: // The session name is displayed in the frame's title bar.
7401: for (int i = 0; i < getFrameCount(); i++) {
7402: Frame frame = getFrame(i);
7403: if (frame != null)
7404: frame.titleChanged();
7405: }
7406: }
7407:
7408: // Position stack.
7409: private static Stack positionStack = new Stack();
7410:
7411: public static List getPositionStack() {
7412: return positionStack;
7413: }
7414:
7415: public void pushPosition() {
7416: if (buffer.getFile() != null) {
7417: pushMarker(new Marker(buffer, dot));
7418: status("Position saved");
7419: }
7420: }
7421:
7422: public void popPosition() {
7423: if (positionStack.empty()) {
7424: status("Position stack is empty");
7425: } else {
7426: Marker m = (Marker) positionStack.pop();
7427: if (m != null)
7428: m.gotoMarker(this );
7429: }
7430: }
7431:
7432: public static void pushMarker(Marker m) {
7433: while (positionStack.size() >= 30)
7434: positionStack.removeElementAt(0);
7435: positionStack.push(m);
7436: }
7437:
7438: public static void resetDisplay() {
7439: if (!displayReady())
7440: return;
7441: // Force formatters to be re-initialized.
7442: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
7443: Buffer buf = it.nextBuffer();
7444: if (buf.getFormatter() != null)
7445: buf.getFormatter().reset();
7446: }
7447: Display.initializeStaticValues();
7448: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
7449: Display display = it.nextEditor().getDisplay();
7450: display.initialize();
7451: display.repaint();
7452: }
7453: Editor.restoreFocus();
7454: }
7455: }
|