0001: /*
0002: * AbstractMode.java
0003: *
0004: * Copyright (C) 1998-2003 Peter Graves
0005: * $Id: AbstractMode.java,v 1.20 2003/10/15 14:52:25 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 java.awt.Color;
0025: import java.awt.event.MouseEvent;
0026: import java.util.List;
0027: import javax.swing.JCheckBoxMenuItem;
0028: import javax.swing.JMenuItem;
0029: import javax.swing.JPopupMenu;
0030:
0031: /**
0032: * <p>
0033: * AbstractMode provides an interface for implementing specific modes. A mode
0034: * is a set of rules to define the way that the editor behaves. This includes
0035: * key mappings, menu generation, context menu handling, toolbar generation, a
0036: * sidebar component, tooltips, colors, indentation, and general properties.
0037: * All modes should extend AbstractMode and are encouraged to override the
0038: * methods that are pertinent to that mode. The default implementation given
0039: * in AbstractMode will suffice for the rest.
0040: * </p>
0041: * <p>
0042: * Any class extending AbstractMode must add its id and displayName to the
0043: * <code>Constants</code> class.
0044: * </p>
0045: *
0046: * ***More on overriding AbstractMode goes here***
0047: *
0048: * @future #createBuffer(File)
0049: *
0050: * @see Constants
0051: */
0052: public abstract class AbstractMode implements Constants, Mode {
0053: private static final Preferences preferences = Editor.preferences();
0054:
0055: protected KeyMap keyMap;
0056: protected File keyMapFile;
0057: protected PropertyList properties;
0058: protected Keywords keywords;
0059:
0060: private final int id;
0061: private final String displayName;
0062:
0063: protected AbstractMode(int id, String displayName) {
0064: this .id = id;
0065: this .displayName = displayName;
0066: if (Editor.isLispInitialized()) {
0067: String hook = displayName.toLowerCase().replace(' ', '-')
0068: + "-mode-hook";
0069: Editor.invokeHook(hook);
0070: }
0071: }
0072:
0073: /**
0074: * Returns the unique identifier of this mode. All ids should be defined
0075: * in <code>Constants</code>.
0076: *
0077: * @return The unique identifier of this mode.
0078: * @see Constants
0079: */
0080: public final int getId() {
0081: return id;
0082: }
0083:
0084: /**
0085: * Returns the display name of this mode. Display names should be defined
0086: * in <code>Constants</code>.
0087: *
0088: * @return The display name of this mode.
0089: * @see Constants
0090: */
0091: public final String getDisplayName() {
0092: return displayName;
0093: }
0094:
0095: /**
0096: * Creates a Buffer for the given <code>File</code>. For now, all this
0097: * implementation does is return null.
0098: *
0099: * @future Currently most of the code for creating buffers lies in
0100: * Buffer.createBuffer. Ideally, this default implementation
0101: * would create a Buffer for the "normal" case, and each mode
0102: * that requires a specific Buffer would override this method.
0103: *
0104: * @param file
0105: * @return null
0106: * @see org.armedbear.j.mail.SendMailMode#createBuffer(File)
0107: */
0108: public Buffer createBuffer(File file) {
0109: return null;
0110: }
0111:
0112: /**
0113: * Returns a <code>Formatter</code> for the given <code>Buffer</code>.
0114: *
0115: * @param buffer The <code>Buffer</code> that is to be formatted
0116: * according to this mode.
0117: * @return A <code>Formatter</code> for the given mode.
0118: * (The default is a PlainTextFormatter.)
0119: */
0120: public Formatter getFormatter(Buffer buffer) {
0121: return new PlainTextFormatter(buffer);
0122: }
0123:
0124: /**
0125: * Returns the display name for this mode.
0126: *
0127: * @return The display name for this mode.
0128: */
0129: public final String toString() {
0130: return displayName;
0131: }
0132:
0133: // Should never return null.
0134: public synchronized final KeyMap getKeyMap() {
0135: if (keyMap == null) {
0136: if (!loadKeyMapForMode()) {
0137: keyMap = new KeyMap();
0138: setKeyMapDefaults(keyMap);
0139: }
0140: }
0141: return keyMap;
0142: }
0143:
0144: private boolean loadKeyMapForMode() {
0145: keyMap = null;
0146: String filename = preferences
0147: .getStringProperty(getFullKey("keyMap"));
0148: if (filename != null) {
0149: keyMapFile = File.getInstance(filename);
0150: if (keyMapFile != null) {
0151: keyMap = new KeyMap();
0152: if (keyMap.load(keyMapFile))
0153: return true;
0154: }
0155: }
0156: keyMap = null;
0157: keyMapFile = null;
0158: return false;
0159: }
0160:
0161: protected void setKeyMapDefaults(KeyMap km) {
0162: // Default implementation just leaves keymap empty.
0163: }
0164:
0165: public File getKeyMapFile() {
0166: return keyMapFile;
0167: }
0168:
0169: public synchronized final void useDefaultKeyMap() {
0170: keyMap = new KeyMap();
0171: setKeyMapDefaults(keyMap);
0172: }
0173:
0174: public synchronized final void deleteKeyMap() {
0175: keyMap = null;
0176: }
0177:
0178: public String getMenuName() {
0179: return "Default";
0180: }
0181:
0182: public MenuBar createMenuBar(Frame frame) {
0183: MenuBar menuBar = new MenuBar("Default");
0184: menuBar.add(new Menu("File", 'F'));
0185: menuBar.add(new Menu("Edit", 'E'));
0186: menuBar.add(new Menu("View", 'V'));
0187: menuBar.add(new Menu("Search", 'S'));
0188: menuBar.add(new Menu("Go", 'G'));
0189: menuBar.add(new Menu("Mode", 'M'));
0190: menuBar.add(new Menu("Lisp", 'L'));
0191: menuBar.add(new Menu("Help", 'H'));
0192: return menuBar;
0193: }
0194:
0195: public void populateMenu(Editor editor, Menu menu) {
0196: final String text = menu.getText();
0197: if (text == "File")
0198: populateFileMenu(editor, menu);
0199: else if (text == "Edit")
0200: populateEditMenu(editor, menu);
0201: else if (text == "View")
0202: populateViewMenu(editor, menu);
0203: else if (text == "Search")
0204: populateSearchMenu(editor, menu);
0205: else if (text == "Go")
0206: populateGoMenu(editor, menu);
0207: else if (text == "Mode") {
0208: populateModeMenu(editor, menu);
0209: if (menu.getMenuComponentCount() == 0)
0210: menu.add(new JMenuItem("This menu isn't here yet!"))
0211: .setEnabled(false);
0212: } else if (text == "Lisp")
0213: populateLispMenu(editor, menu);
0214: else if (text == "Help")
0215: populateHelpMenu(editor, menu);
0216: }
0217:
0218: private static void populateFileMenu(Editor editor, Menu menu) {
0219: final boolean isNotReadOnly = !editor.getBuffer().isReadOnly();
0220: menu.add(editor, "New", 'N', "newBuffer");
0221: menu.add(editor, "Open...", 'O', "openFile");
0222: menu.add(editor, "Recent Files...", 'R', "recentFiles");
0223: menu.addSeparator();
0224: menu.add(editor, "Save", 'S', "save", isNotReadOnly);
0225: menu.add(editor, "Save As...", 'E', "saveAs");
0226: menu.add(editor, "Save a Copy...", 'Y', "saveCopy");
0227: menu.add(editor, "Save All", 'A', "saveAll");
0228: menu.add(editor, "Close", 'C', "killBuffer");
0229: menu.add(editor, "Close All", 'L', "closeAll");
0230: menu.add(editor, "Close Others", 'H', "closeOthers");
0231: menu.add(editor, "Revert", 'V', "revertBuffer");
0232: menu.add(editor, "Set Encoding", 'G', "setEncoding");
0233: menu.addSeparator();
0234: menu.add(editor, "Properties", 'I', "properties");
0235: menu.addSeparator();
0236: menu.add(editor, "Next Buffer", 'T', "nextBuffer");
0237: menu.add(editor, "Previous Buffer", 'R', "prevBuffer");
0238: menu.addSeparator();
0239: menu.add(editor, "New Frame", 'M', "newFrame");
0240: menu.add(editor, "Execute Command...", 'D', "executeCommand");
0241: menu.addSeparator();
0242: menu.add(editor, "Print...", 'P', "print");
0243: menu.addSeparator();
0244: menu.add(editor, "Save Session", 'S', "saveSession");
0245: menu.add(editor, "Load Session...", 'L', "loadSession");
0246: menu.addSeparator();
0247: menu.add(editor, "Save All/Exit", '/', "saveAllExit");
0248: menu.add(editor, "Exit", 'X', "quit");
0249: }
0250:
0251: private static void populateEditMenu(Editor editor, Menu menu) {
0252: final boolean isNotReadOnly = !editor.getBuffer().isReadOnly();
0253: menu.add(editor, "Undo", 'U', "undo");
0254: menu.add(editor, "Redo", 'O', "redo");
0255: menu.addSeparator();
0256: menu.add(editor, "Cut", 'T', "killRegion", isNotReadOnly);
0257: menu
0258: .add(editor, "Cut Append", 'D', "killAppend",
0259: isNotReadOnly);
0260: menu.add(editor, "Copy", 'C', "copyRegion");
0261: menu.add(editor, "Copy Append", 'A', "copyAppend");
0262: menu.add(editor, "Paste", 'P', "paste", isNotReadOnly);
0263: menu.add(editor, "Cycle Paste", 'Y', "cyclePaste",
0264: isNotReadOnly);
0265: menu.addSeparator();
0266: menu.add(editor, "Cycle Tab Width", 'B', "cycleTabWidth");
0267: menu.add(editor, "Cycle Indent Size", 'N', "cycleIndentSize");
0268: menu.add(editor, "Indent", 'I', "indentLineOrRegion",
0269: isNotReadOnly);
0270: menu.addSeparator();
0271: menu.add(editor, "Upper Case", 'R', "upperCaseRegion",
0272: isNotReadOnly);
0273: menu.add(editor, "Lower Case", 'L', "lowerCaseRegion",
0274: isNotReadOnly);
0275: }
0276:
0277: private static void populateViewMenu(Editor editor, Menu menu) {
0278: JCheckBoxMenuItem toolbarMenuItem = new JCheckBoxMenuItem(
0279: "Toolbar");
0280: toolbarMenuItem.setMnemonic('T');
0281: toolbarMenuItem.setActionCommand("toggleToolbar");
0282: toolbarMenuItem.addActionListener(editor.getDispatcher());
0283: toolbarMenuItem.setSelected(editor.getFrame().getShowToolbar());
0284: menu.add(toolbarMenuItem);
0285: JCheckBoxMenuItem sidebarMenuItem = new JCheckBoxMenuItem(
0286: "Sidebar");
0287: sidebarMenuItem.setMnemonic('S');
0288: sidebarMenuItem.setActionCommand("toggleSidebar");
0289: sidebarMenuItem.addActionListener(editor.getDispatcher());
0290: sidebarMenuItem.setSelected(editor.getSidebar() != null);
0291: menu.add(sidebarMenuItem);
0292: menu.addSeparator();
0293: menu.add(editor, "Split Window", 'W', "splitWindow");
0294: menu.add(editor, "Unsplit Window", 'U', "unsplitWindow");
0295: menu.add(editor, "Close Window", 'C', "killWindow");
0296: }
0297:
0298: protected void populateSearchMenu(Editor editor, Menu menu) {
0299: final File dir = editor.getCurrentDirectory();
0300: final boolean local = (dir != null && dir.isLocal());
0301: if (Editor.preferences().getBooleanProperty(
0302: Property.USE_INCREMENTAL_FIND))
0303: menu.add(editor, "Incremental Find...", 'I',
0304: "incrementalFind");
0305: menu.add(editor, "Find...", 'F', "find");
0306: menu.add(editor, "Find Next", 'T', "findNext");
0307: menu.add(editor, "Find Previous", 'R', "findPrev");
0308: menu.add(editor, "Find in Files...", 'S', "findInFiles", local);
0309: menu.addSeparator();
0310: menu.add(editor, "List Occurrences of Last Pattern", 'L',
0311: "listOccurrences", editor.getLastSearch() != null);
0312: menu.add(editor, "List Occurrences of Pattern in Files", 'O',
0313: "listFiles", FindInFiles.getFindInFiles() != null);
0314: menu.addSeparator();
0315: final boolean isNotReadOnly = !editor.getBuffer().isReadOnly();
0316: if (!(editor.getBuffer() instanceof Directory))
0317: menu.add(editor, "Replace...", 'P', "replace",
0318: isNotReadOnly);
0319: menu.add(editor, "Replace in Files...", 'E', "replaceInFiles",
0320: local);
0321: if (!(editor.getBuffer() instanceof Directory)) {
0322: menu.addSeparator();
0323: menu.add(editor, "Find Tag...", 'A', "findTag");
0324: }
0325: }
0326:
0327: private static void populateGoMenu(Editor editor, Menu menu) {
0328: menu.add(editor, "Go to Line...", 'L', "jumpToLine");
0329: menu.add(editor, "Go to Column...", 'C', "jumpToColumn");
0330: menu.add(editor, "Go to Offset...", 'O', "jumpToOffset");
0331: if (editor.getModeId() == HTML_MODE)
0332: menu.add(editor, "Go to Matching HTML", 'M',
0333: "htmlFindMatch");
0334: else
0335: menu.add(editor, "Go to Matching Character", 'M',
0336: "findMatchingChar");
0337: menu.add(editor, "Go to Tag", 'A', "findTagAtDot");
0338: menu.add(editor, "Go to Next Occurrence of Word", 'T',
0339: "findNextWord");
0340: menu.add(editor, "Go to Previous Occurrence of Word", 'R',
0341: "findPrevWord");
0342: if (editor.getBuffer().getBooleanProperty(
0343: Property.SHOW_CHANGE_MARKS)) {
0344: menu.add(editor, "Go to Next Change", 'H', "nextChange");
0345: menu.add(editor, "Go to Previous Change", 'G',
0346: "previousChange");
0347: }
0348: menu.addSeparator();
0349: menu.add(editor, "Push Position", 'U', "pushPosition");
0350: menu.add(editor, "Pop Position", 'P', "popPosition");
0351: }
0352:
0353: public void populateModeMenu(Editor editor, Menu menu) {
0354: }
0355:
0356: public void populateLispMenu(Editor editor, Menu menu) {
0357: menu.add(editor, "Run Lisp as Separate Process", 'L', "lisp");
0358: menu.add(editor, "Run Embedded Lisp", 'E', "jlisp");
0359: }
0360:
0361: private static void populateHelpMenu(Editor editor, Menu menu) {
0362: menu.add(editor, "Help", 'P', "help");
0363: menu.add(editor, "Apropos...", 'A', "apropos");
0364: menu.add(editor, "Key Bindings", 'B', "listBindings");
0365: menu.add(editor, "Describe Key...", 'K', "describeKey");
0366: menu.add(editor, "Where is...", 'W', "whereIs");
0367: menu.add(editor, "About J", 'O', "about");
0368: }
0369:
0370: public JPopupMenu getContextMenu(Editor editor) {
0371: final JPopupMenu popup = new JPopupMenu();
0372: addDefaultContextMenuItems(editor, popup);
0373: popup.pack();
0374: return popup;
0375: }
0376:
0377: protected void addDefaultContextMenuItems(Editor editor,
0378: JPopupMenu popup) {
0379: final Buffer buffer = editor.getBuffer();
0380: final Dispatcher dispatcher = editor.getDispatcher();
0381:
0382: JMenuItem menuItem;
0383:
0384: if (buffer.supportsUndo()) {
0385: menuItem = new JMenuItem();
0386: menuItem.setText("Undo");
0387: menuItem.setActionCommand("undo");
0388: menuItem.addActionListener(dispatcher);
0389: if (!buffer.canUndo())
0390: menuItem.setEnabled(false);
0391: popup.add(menuItem);
0392:
0393: menuItem = new JMenuItem();
0394: menuItem.setText("Redo");
0395: menuItem.setActionCommand("redo");
0396: menuItem.addActionListener(dispatcher);
0397: if (!buffer.canRedo())
0398: menuItem.setEnabled(false);
0399: popup.add(menuItem);
0400:
0401: popup.addSeparator();
0402: }
0403:
0404: menuItem = new JMenuItem();
0405: menuItem.setText(editor.getMark() == null ? "Cut line" : "Cut");
0406: menuItem.setActionCommand("killRegion");
0407: menuItem.addActionListener(dispatcher);
0408: if (buffer.isReadOnly())
0409: menuItem.setEnabled(false);
0410: popup.add(menuItem);
0411:
0412: menuItem = new JMenuItem();
0413: menuItem.setText(editor.getMark() == null ? "Copy line"
0414: : "Copy");
0415: menuItem.setActionCommand("copyRegion");
0416: menuItem.addActionListener(dispatcher);
0417: if (editor.getMark() == null && editor.getDotLine().isBlank())
0418: menuItem.setEnabled(false);
0419: popup.add(menuItem);
0420:
0421: menuItem = new JMenuItem("Paste");
0422: menuItem.setActionCommand("paste");
0423: menuItem.addActionListener(dispatcher);
0424: if (!editor.canPaste())
0425: menuItem.setEnabled(false);
0426: popup.add(menuItem);
0427:
0428: // List occurrences.
0429: menuItem = new JMenuItem();
0430: menuItem.setActionCommand("listOccurrencesOfPatternAtDot");
0431: menuItem.addActionListener(dispatcher);
0432: Search search = editor.getSearchAtDot();
0433: if (search != null) {
0434: if (editor.getMark() != null)
0435: menuItem.setText("List occurrences of selected text");
0436: else
0437: menuItem.setText("List occurrences of \""
0438: + search.getPattern() + "\"");
0439: } else {
0440: menuItem.setText("List occurences of pattern under cursor");
0441: menuItem.setEnabled(false);
0442: }
0443: popup.addSeparator();
0444: popup.add(menuItem);
0445:
0446: // Find tag.
0447: if (buffer.isTaggable()) {
0448: menuItem = new JMenuItem();
0449: menuItem.setActionCommand("findTagAtDot");
0450: menuItem.addActionListener(dispatcher);
0451: if (editor.getMark() == null) {
0452: Expression expr = getExpressionAtDot(editor, false);
0453: if (expr != null)
0454: menuItem.setText("Find tag \"" + expr.getName()
0455: + "\"");
0456: } else {
0457: menuItem.setText("Find tag under cursor");
0458: menuItem.setEnabled(false);
0459: }
0460: popup.add(menuItem);
0461: }
0462:
0463: // Folding.
0464: popup.addSeparator();
0465: addContextMenuItem("Fold", "fold", popup, dispatcher);
0466: addContextMenuItem("Unfold", "unfold", popup, dispatcher);
0467: addContextMenuItem("Unfold all", "unfoldAll", popup, dispatcher);
0468:
0469: // Properties.
0470: if (buffer.getFile() != null && !(buffer instanceof Directory)) {
0471: popup.addSeparator();
0472: addContextMenuItem("Properties", "properties", popup,
0473: dispatcher);
0474: }
0475: }
0476:
0477: protected JMenuItem addContextMenuItem(String text, String command,
0478: JPopupMenu popup, Dispatcher dispatcher) {
0479: JMenuItem menuItem = new JMenuItem(text);
0480: menuItem.setActionCommand(command);
0481: menuItem.addActionListener(dispatcher);
0482: popup.add(menuItem);
0483: return menuItem;
0484: }
0485:
0486: public ToolBar getToolBar(Frame frame) {
0487: ToolBar tb = getCustomToolBar(frame);
0488: if (tb != null)
0489: return tb;
0490: return getDefaultToolBar(frame);
0491: }
0492:
0493: protected ToolBar getCustomToolBar(Frame frame) {
0494: String filename = Editor.preferences().getStringProperty(
0495: getFullKey("toolbar"));
0496: if (filename != null) {
0497: File file = File.getInstance(filename);
0498: if (file != null && file.isFile()) {
0499: ToolBar tb = ToolBar.createToolBar(frame, file);
0500: if (tb != null)
0501: return tb;
0502: }
0503: }
0504: return null;
0505: }
0506:
0507: protected ToolBar getDefaultToolBar(Frame frame) {
0508: return frame.getDefaultToolBar();
0509: }
0510:
0511: public NavigationComponent getSidebarComponent(Editor editor) {
0512: if (isTaggable())
0513: return new SidebarTagList(editor.getSidebar(), editor);
0514: else
0515: return null;
0516: }
0517:
0518: /**
0519: * {@inheritDoc}
0520: * The default is <code>null</code>.
0521: *
0522: * @param buffer {@inheritDoc}
0523: * @return {@inheritDoc}
0524: */
0525: public Tagger getTagger(SystemBuffer buffer) {
0526: return null;
0527: }
0528:
0529: /**
0530: * {@inheritDoc}
0531: * The default is <code>false</code>.
0532: *
0533: * @return {@inheritDoc}
0534: */
0535: public boolean isTaggable() {
0536: return false;
0537: }
0538:
0539: /**
0540: * {@inheritDoc}
0541: * The default is <code>false</code>.
0542: *
0543: * @return {@inheritDoc}
0544: */
0545: public boolean hasQualifiedNames() {
0546: return false;
0547: }
0548:
0549: /**
0550: * {@inheritDoc}
0551: * The default is <code>true</code> if <code>s</code> contains either
0552: * a period '.' or a double colon "::".
0553: *
0554: * @param s {@inheritDoc}
0555: * @return {@inheritDoc}
0556: */
0557: public boolean isQualifiedName(String s) {
0558: return s.indexOf('.') >= 0 || s.indexOf("::") >= 0;
0559: }
0560:
0561: /**
0562: * {@inheritDoc}
0563: * The default is <code>false</code>.
0564: *
0565: * @return {@inheritDoc}
0566: */
0567: public boolean canIndent() {
0568: return false;
0569: }
0570:
0571: /**
0572: * {@inheritDoc}
0573: * The default is <code>false</code>.
0574: *
0575: * @return {@inheritDoc}
0576: */
0577: public boolean canIndentPaste() {
0578: return canIndent();
0579: }
0580:
0581: public boolean acceptsLinePaste(Editor editor) {
0582: return true;
0583: }
0584:
0585: /**
0586: * {@inheritDoc}
0587: * The default is 0.
0588: *
0589: * @param line {@inheritDoc}
0590: * @param buffer {@inheritDoc}
0591: * @return {@inheritDoc}
0592: */
0593: public int getCorrectIndentation(Line line, Buffer buffer) {
0594: return 0;
0595: }
0596:
0597: /**
0598: * {@inheritDoc}
0599: * The default is to return an instance of
0600: * {@link DefaultSyntaxIterator DefaultSyntaxIterator}.
0601: *
0602: * @param pos {@inheritDoc}
0603: * @return {@inheritDoc}
0604: */
0605: public SyntaxIterator getSyntaxIterator(Position pos) {
0606: return new DefaultSyntaxIterator(pos);
0607: }
0608:
0609: /**
0610: * {@inheritDoc}
0611: * The default is <code>null</code>.
0612: *
0613: * @return {@inheritDoc}
0614: */
0615: public String getCommentStart() {
0616: return null;
0617: }
0618:
0619: /**
0620: * {@inheritDoc}
0621: * The default is <code>null</code>.
0622: *
0623: * @return {@inheritDoc}
0624: */
0625: public String getCommentEnd() {
0626: return null;
0627: }
0628:
0629: public boolean getBooleanProperty(Property property) {
0630: String key = property.key();
0631:
0632: // Look for mode-specific setting in preferences.
0633: String s = preferences.getStringProperty(getFullKey(key));
0634:
0635: if (s == null) {
0636: // No mode-specific setting in preferences.
0637: // Look in property list for mode.
0638: // (Property list for mode overrides global preference!)
0639: if (properties != null) {
0640: Object value = properties.getProperty(property);
0641: if (value instanceof Boolean)
0642: return ((Boolean) value).booleanValue();
0643: }
0644:
0645: // Not in property list for mode.
0646: // Look for global setting in preferences.
0647: s = preferences.getStringProperty(key);
0648: }
0649:
0650: if (s != null) {
0651: s = s.trim();
0652: if (s.equals("true") || s.equals("1"))
0653: return true;
0654: if (s.equals("false") || s.equals("0"))
0655: return false;
0656: }
0657:
0658: // Not in preferences or property list for mode.
0659: // Use hard-coded default.
0660: return ((Boolean) getDefaultValue(property)).booleanValue();
0661: }
0662:
0663: public int getIntegerProperty(Property property) {
0664: String key = property.key();
0665:
0666: // Look for mode-specific setting in preferences.
0667: String s = preferences.getStringProperty(getFullKey(key));
0668:
0669: if (s == null) {
0670: // No mode-specific setting in preferences.
0671: // Look in property list for mode.
0672: // (Property list for mode overrides global preference!)
0673: if (properties != null) {
0674: Object value = properties.getProperty(property);
0675: if (value instanceof Integer)
0676: return ((Integer) value).intValue();
0677: }
0678:
0679: // Not in property list for mode.
0680: // Look for global setting in preferences.
0681: s = preferences.getStringProperty(key);
0682: }
0683:
0684: if (s != null) {
0685: try {
0686: return Integer.parseInt(s.trim());
0687: } catch (NumberFormatException e) {
0688: }
0689: }
0690:
0691: // Not in preferences or property list for mode.
0692: // Use hard-coded default.
0693: return ((Integer) getDefaultValue(property)).intValue();
0694: }
0695:
0696: public String getStringProperty(Property property) {
0697: String key = property.key();
0698:
0699: // Look for mode-specific setting in preferences.
0700: String s = preferences.getStringProperty(getFullKey(key));
0701:
0702: if (s == null) {
0703: // No mode-specific setting in preferences.
0704: // Look in property list for mode.
0705: // (Property list for mode overrides global preference!)
0706: if (properties != null) {
0707: Object value = properties.getProperty(property);
0708: if (value instanceof String)
0709: return (String) value;
0710: }
0711:
0712: // Not in property list for mode.
0713: // Look for global setting in preferences.
0714: s = preferences.getStringProperty(key);
0715: }
0716:
0717: if (s != null)
0718: return s;
0719:
0720: // Not in preferences or property list for mode.
0721: // Use hard-coded default.
0722: return (String) getDefaultValue(property); // May be null.
0723: }
0724:
0725: public Color getColorProperty(Property property) {
0726: String key = property.key();
0727:
0728: // Look for mode-specific setting in preferences.
0729: String value = preferences.getStringProperty(getFullKey(key));
0730:
0731: if (value == null) {
0732: // We don't check the mode-specific properties list here. Is that
0733: // correct?
0734:
0735: // Look for global setting in preferences.
0736: value = preferences.getStringProperty(key);
0737: }
0738:
0739: if (value != null)
0740: return Utilities.getColor(value);
0741: else
0742: return null;
0743: }
0744:
0745: /**
0746: * Sets the given property name to the given property value. The property
0747: * can then be accessed by calling
0748: * {@link getStringProperty(Property) getStringProperty}.
0749: *
0750: * @param property the property to set.
0751: * @param value the value to set it to.
0752: */
0753: public void setProperty(Property property, String value) {
0754: if (properties == null)
0755: properties = new PropertyList();
0756: properties.setProperty(property, value);
0757: }
0758:
0759: /**
0760: * Sets the given property name to the given property value. The property
0761: * can then be accessed by calling
0762: * {@link getBooleanProperty(Property) getBooleanProperty}.
0763: *
0764: * @param property the property to set.
0765: * @param value the value to set it to.
0766: */
0767: public void setProperty(Property property, boolean value) {
0768: if (properties == null)
0769: properties = new PropertyList();
0770: properties.setProperty(property, value);
0771: }
0772:
0773: /**
0774: * Sets the given property name to the given property value. The property
0775: * can then be accessed by calling
0776: * {@link getIntegerProperty(Property) getIntegerProperty}.
0777: *
0778: * @param property the property to set.
0779: * @param value the value to set it to.
0780: */
0781: public void setProperty(Property property, int value) {
0782: if (properties == null)
0783: properties = new PropertyList();
0784: properties.setProperty(property, value);
0785: }
0786:
0787: /**
0788: * Returns the default value for the given <code>Property</code>.
0789: *
0790: * @param property the <code>Property</code> to get the default value for.
0791: * @return the default value.
0792: */
0793: protected Object getDefaultValue(Property property) {
0794: return property.getDefaultValue();
0795: }
0796:
0797: public final boolean accepts(String filename) {
0798: return Editor.getModeList().modeAccepts(id, filename);
0799: }
0800:
0801: protected String getFullKey(String key) {
0802: FastStringBuffer sb = new FastStringBuffer(this .getClass()
0803: .getName());
0804: sb.append('.');
0805: sb.append(key);
0806: final String fullKey = sb.toString().toLowerCase();
0807: if (fullKey.startsWith("org.armedbear.j.mail."))
0808: return fullKey.substring(21);
0809: else if (fullKey.startsWith("org.armedbear.j."))
0810: return fullKey.substring(16);
0811: else
0812: return fullKey;
0813: }
0814:
0815: /**
0816: * {@inheritDoc}
0817: * The default is the result of Character.isJavaIdentifierStart(char).
0818: *
0819: * @return {@inheritDoc}
0820: */
0821: public boolean isIdentifierStart(char c) {
0822: return Character.isJavaIdentifierStart(c);
0823: }
0824:
0825: /**
0826: * {@inheritDoc}
0827: * The default is the result of Character.isJavaIdentifierPart(char).
0828: *
0829: * @return {@inheritDoc}
0830: */
0831: public boolean isIdentifierPart(char c) {
0832: return Character.isJavaIdentifierPart(c);
0833: }
0834:
0835: /**
0836: * {@inheritDoc}
0837: * The default implementation considers both single and double quotes
0838: * (which is wrong for Lisp) and only looks at the current line (which
0839: * is wrong for C and C++).
0840: *
0841: * @param buffer {@inheritDoc}
0842: * @param pos {@inheritDoc}
0843: * @return {@inheritDoc}
0844: */
0845: public boolean isInQuote(Buffer buffer, Position pos) {
0846: // The default implementation considers both single and double quotes
0847: // (which is wrong for Lisp) and only looks at the current line (which
0848: // is wrong for C and C++).
0849: Line line = pos.getLine();
0850: int offset = pos.getOffset();
0851: boolean inQuote = false;
0852: char quoteChar = '\0';
0853: for (int i = 0; i < offset; i++) {
0854: char c = line.charAt(i);
0855: if (c == '\\') {
0856: // Escape.
0857: ++i;
0858: } else if (inQuote) {
0859: if (c == quoteChar)
0860: inQuote = false;
0861: } else {
0862: if (c == '"' || c == '\'') {
0863: inQuote = true;
0864: quoteChar = c;
0865: }
0866: }
0867: }
0868: return inQuote;
0869: }
0870:
0871: /**
0872: * {@inheritDoc}
0873: * The default is <code>false</code>.
0874: *
0875: * @param buffer {@inheritDoc}
0876: * @param pos {@inheritDoc}
0877: * @return {@inheritDoc}
0878: */
0879: public boolean isInComment(Buffer buffer, Position pos) {
0880: return false;
0881: }
0882:
0883: /**
0884: * {@inheritDoc}
0885: * The default is <code>false</code>.
0886: *
0887: * @param line {@inheritDoc}
0888: * @return {@inheritDoc}
0889: */
0890: public boolean isCommentLine(Line line) {
0891: return false;
0892: }
0893:
0894: /**
0895: * {@inheritDoc}
0896: * The default is to return the original character.
0897: *
0898: * @param editor {@inheritDoc}
0899: * @param c {@inheritDoc}
0900: * @return {@inheritDoc}
0901: */
0902: public char fixCase(Editor editor, char c) {
0903: return c;
0904: }
0905:
0906: /**
0907: * {@inheritDoc}
0908: * The default is to return the tag before the cursor position, if the
0909: * buffer has tags.
0910: *
0911: * @param editor {@inheritDoc}
0912: * @param verbose {@inheritDoc}
0913: * @return {@inheritDoc}
0914: */
0915: public String getContextString(Editor editor, boolean verbose) {
0916: final List tags = editor.getBuffer().getTags();
0917: if (tags != null) {
0918: Position pos = editor.getDot();
0919: if (pos != null) {
0920: LocalTag tag = null;
0921: // Find the tag before the cursor position.
0922: final int target = pos.lineNumber();
0923: final int limit = tags.size();
0924: for (int i = 0; i < limit; i++) {
0925: LocalTag nextTag = (LocalTag) tags.get(i);
0926: if (nextTag.lineNumber() > target)
0927: break;
0928: else
0929: tag = nextTag;
0930: }
0931: if (tag != null)
0932: return verbose ? tag.getLongName() : tag
0933: .getMethodName();
0934: }
0935: }
0936: return null;
0937: }
0938:
0939: /**
0940: * {@inheritDoc}
0941: * The default is <code>null</code>.
0942: *
0943: * @param editor {@inheritDoc}
0944: * @param pos {@inheritDoc}
0945: * @return {@inheritDoc}
0946: */
0947: public String getMouseMovedContextString(Editor editor, Position pos) {
0948: return null;
0949: }
0950:
0951: /**
0952: * {@inheritDoc}
0953: * The default is <code>null</code>.
0954: *
0955: * @param editor {@inheritDoc}
0956: * @param e {@inheritDoc}
0957: * @return {@inheritDoc}
0958: */
0959: public String getToolTipText(Editor editor, MouseEvent e) {
0960: return null;
0961: }
0962:
0963: /**
0964: * {@inheritDoc}
0965: * The default is to do nothing.
0966: *
0967: * @param buffer {@inheritDoc}
0968: * @param file {@inheritDoc}
0969: * @return {@inheritDoc}
0970: */
0971: public void loadFile(Buffer buffer, File file) {
0972: }
0973:
0974: public boolean confirmClose(Editor editor, Buffer buffer) {
0975: if (!buffer.isModified())
0976: return true;
0977: if (buffer.getFile() == null)
0978: return true;
0979: return CloseBufferConfirmationDialog.confirmClose(editor,
0980: buffer);
0981: }
0982:
0983: public boolean isKeyword(String s) {
0984: if (keywords != null)
0985: return keywords.isKeyword(s);
0986: return false;
0987: }
0988:
0989: public Expression getExpressionAtDot(Editor editor, boolean exact) {
0990: if (editor.getDot() == null)
0991: return null;
0992: final Position begin;
0993: if (editor.getMark() != null) {
0994: // Start at beginning of marked block.
0995: Region r = new Region(editor);
0996: begin = r.getBegin();
0997: } else
0998: begin = editor.getDot();
0999: Line line = begin.getLine();
1000: int offset = begin.getOffset();
1001: if (offset < line.length()
1002: && isIdentifierStart(line.charAt(offset))) {
1003: String identifier = getIdentifier(line, offset);
1004: if (identifier != null)
1005: return new Expression(identifier);
1006: }
1007: // Try moving to the left.
1008: while (--offset >= 0) {
1009: char c = line.charAt(offset);
1010: if (isIdentifierStart(c)) {
1011: String identifier = getIdentifier(line, offset);
1012: if (identifier != null)
1013: return new Expression(identifier);
1014: }
1015: }
1016: // Nothing there. Go back to starting point and try moving right.
1017: offset = begin.getOffset();
1018: while (++offset < line.length()) {
1019: char c = line.charAt(offset);
1020: if (isIdentifierStart(c)) {
1021: String identifier = getIdentifier(line, offset);
1022: if (identifier != null)
1023: return new Expression(identifier);
1024: }
1025: }
1026: return null;
1027: }
1028:
1029: public final String getIdentifier(Position pos) {
1030: return getIdentifier(pos.getLine(), pos.getOffset());
1031: }
1032:
1033: public final String getIdentifier(Line line, int offset) {
1034: int limit = line.length();
1035: if (offset < limit) {
1036: char c = line.charAt(offset);
1037: if (isIdentifierPart(c)) {
1038: while (offset > 0) {
1039: --offset;
1040: c = line.charAt(offset);
1041: if (!isIdentifierPart(c)) {
1042: ++offset;
1043: break;
1044: }
1045: }
1046: // Now we're looking at the first character of the identifier.
1047: c = line.charAt(offset);
1048: if (isIdentifierStart(c)) {
1049: FastStringBuffer sb = new FastStringBuffer(c);
1050: while (++offset < limit) {
1051: c = line.charAt(offset);
1052: if (isIdentifierPart(c))
1053: sb.append(c);
1054: else
1055: break;
1056: }
1057: return sb.toString();
1058: }
1059: }
1060: }
1061: return null;
1062: }
1063:
1064: public Position findIdentifierStart(Line line, int offset) {
1065: if (!isIdentifierPart(line.charAt(offset)))
1066: return null;
1067: int start = offset;
1068: while (--offset >= 0) {
1069: if (!isIdentifierPart(line.charAt(offset)))
1070: break;
1071: start = offset;
1072: }
1073: return new Position(line, start);
1074: }
1075: }
|