/* * jEdit.java - Main class of the jEdit editor * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 1998, 2005 Slava Pestov * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit; //{{{ Imports import java.io.Closeable; import java.io.IOException; import java.io.UnsupportedEncodingException; import org.gjt.sp.jedit.datatransfer.JEditTransferableService; import org.gjt.sp.jedit.gui.tray.JTrayIconManager; import org.gjt.sp.util.StringList; import org.jedit.core.MigrationService; import org.jedit.migration.OneTimeMigrationService; import org.jedit.keymap.KeymapManager; import org.jedit.keymap.KeymapManagerImpl; import org.gjt.sp.jedit.visitors.JEditVisitor; import java.awt.*; import org.gjt.sp.jedit.View.ViewConfig; import org.gjt.sp.jedit.bsh.UtilEvalError; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.swing.*; import java.awt.event.*; import java.io.*; import java.lang.reflect.Field; import java.net.*; import java.text.MessageFormat; import java.util.*; import java.util.List; import java.lang.reflect.InvocationTargetException; import org.xml.sax.SAXParseException; import org.gjt.sp.jedit.bufferio.BufferIORequest; import org.gjt.sp.jedit.buffer.KillRing; import org.gjt.sp.jedit.buffer.JEditBuffer; import org.gjt.sp.jedit.buffer.FoldHandler; import org.gjt.sp.jedit.msg.*; import org.gjt.sp.jedit.gui.*; import org.gjt.sp.jedit.help.HelpViewer; import org.gjt.sp.jedit.io.*; import org.gjt.sp.jedit.pluginmgr.PluginManager; import org.gjt.sp.jedit.search.SearchAndReplace; import org.gjt.sp.jedit.syntax.Chunk; import org.gjt.sp.jedit.syntax.ModeProvider; import org.gjt.sp.jedit.syntax.TokenMarker; import org.gjt.sp.jedit.syntax.XModeHandler; import org.gjt.sp.jedit.textarea.*; import org.gjt.sp.jedit.visitors.SaveCaretInfoVisitor; import org.gjt.sp.jedit.bufferset.BufferSetManager; import org.gjt.sp.jedit.bufferset.BufferSet; import org.gjt.sp.util.AwtRunnableQueue; import org.gjt.sp.util.Log; import org.gjt.sp.util.StandardUtilities; import org.gjt.sp.util.TaskManager; import org.gjt.sp.util.XMLUtilities; import org.gjt.sp.util.IOUtilities; import org.gjt.sp.util.SyntaxUtilities; //}}} /** * The main class of the jEdit text editor. * @author Slava Pestov * @version $Id: jEdit.java 23896 2015-04-03 17:26:36Z kerik-sf $ */ public class jEdit { //{{{ getVersion() method /** * Returns the jEdit version as a human-readable string. */ public static String getVersion() { return MiscUtilities.buildToVersion(getBuild()); } //}}} //{{{ getBuild() method /** * Returns the internal version. MiscUtilities.compareStrings() can be used * to compare different internal versions. */ public static String getBuild() { // (major).(minor).(<99 = preX, 99 = "final").(bug fix) return "05.03.01.00"; } //}}} //{{{ main() method /** * The main method of the jEdit application. * This should never be invoked directly. * @param args The command line arguments */ public static void main(String[] args) { StringList slargs = new StringList(args); //{{{ Check for Java 1.7 or later String javaVersion = System.getProperty("java.version"); if(javaVersion.compareTo("1.7") < 0) { System.err.println("You are running Java version " + javaVersion + '.'); System.err.println("jEdit requires Java 1.7 or later."); System.exit(1); } //}}} startupDone.add(false); // later on we need to know if certain code is called from // the main thread mainThread = Thread.currentThread(); settingsDirectory = MiscUtilities.constructPath( System.getProperty("user.home"), ".jedit"); // On mac, different rules (should) apply if(OperatingSystem.isMacOS()) settingsDirectory = MiscUtilities.constructPath( System.getProperty("user.home"), "Library/jEdit" ); else if (OperatingSystem.isWindows()) { String appData = System.getenv("APPDATA"); if (appData != null) settingsDirectory = MiscUtilities.constructPath( appData, "jEdit"); } // MacOS users expect the app to keep running after all windows // are closed background = OperatingSystem.isMacOS(); // Fix X11 windows class if (OperatingSystem.isX11()) { try { Toolkit xToolkit = Toolkit.getDefaultToolkit(); Field awtAppClassNameField = xToolkit.getClass().getDeclaredField("awtAppClassName"); awtAppClassNameField.setAccessible(true); awtAppClassNameField.set(xToolkit, System.getProperty("x11.wmclass", "jedit")); } catch (Exception e) { Log.log(Log.ERROR, jEdit.class, e); } } //{{{ Parse command line boolean endOpts = false; int level = Log.WARNING; String portFile = "server"; boolean restore = true; boolean newView = true; boolean newPlainView = false; boolean gui = true; // open initial view? boolean loadPlugins = true; boolean runStartupScripts = true; boolean quit = false; boolean wait = false; boolean shouldRelocateSettings = true; String userDir = System.getProperty("user.dir"); boolean splash = true; // script to run String scriptFile = null; for(int i = 0; i < args.length; i++) { String arg = args[i]; if(arg == null) continue; else if(arg.length() == 0) args[i] = null; else if(arg.startsWith("-") && !endOpts) { if(arg.equals("--")) endOpts = true; else if(arg.equals("-usage")) { version(); System.err.println(); usage(); System.exit(1); } else if(arg.equals("-version")) { version(); System.exit(1); } else if(arg.startsWith("-log=")) { try { level = Integer.parseInt(arg.substring("-log=".length())); } catch(NumberFormatException nf) { System.err.println("Malformed option: " + arg); } } else if(arg.equals("-nosettings")) settingsDirectory = null; else if(arg.startsWith("-settings=")) { settingsDirectory = arg.substring(10); shouldRelocateSettings = false; } else if(arg.startsWith("-noserver")) portFile = null; else if(arg.equals("-server")) portFile = "server"; else if(arg.startsWith("-server=")) portFile = arg.substring(8); else if(arg.startsWith("-background")) background = true; else if(arg.startsWith("-nobackground")) background = false; else if(arg.equals("-gui")) gui = true; else if(arg.equals("-nogui")) gui = false; else if(arg.equals("-newview")) newView = true; else if(arg.equals("-newplainview")) newPlainView = true; else if(arg.equals("-reuseview")) newPlainView = newView = false; else if(arg.equals("-restore")) restore = true; else if(arg.equals("-norestore")) restore = false; else if(arg.equals("-plugins")) loadPlugins = true; else if(arg.equals("-noplugins")) loadPlugins = false; else if(arg.equals("-startupscripts")) runStartupScripts = true; else if(arg.equals("-nostartupscripts")) runStartupScripts = false; else if(arg.startsWith("-run=")) scriptFile = arg.substring(5); else if(arg.equals("-wait")) wait = true; else if(arg.equals("-quit")) quit = true; else if(arg.equals("-nosplash")) splash = false; else { System.err.println("Unknown option: " + arg); usage(); System.exit(1); } args[i] = null; } } //}}} JTrayIconManager.setTrayIconArgs(restore, userDir, args); //{{{ We need these initializations very early on if(settingsDirectory != null) { settingsDirectory = MiscUtilities.resolveSymlinks( settingsDirectory); } if(settingsDirectory != null && portFile != null) portFile = MiscUtilities.constructPath(settingsDirectory,portFile); else portFile = null; Log.init(true,level); Log.log(Log.MESSAGE,jEdit.class, "starting with command line arguments: " + slargs.join(" ")); //}}} //{{{ Try connecting to another running jEdit instance if(portFile != null && new File(portFile).exists()) { BufferedReader in = null; DataOutputStream out = null; try { in = new BufferedReader(new FileReader(portFile)); String check = in.readLine(); if(!"b".equals(check)) throw new IllegalArgumentException("Wrong port file format"); int port = Integer.parseInt(in.readLine()); int key = Integer.parseInt(in.readLine()); // socket is closed via BeanShell script below @SuppressWarnings("resource") Socket socket = new Socket(InetAddress.getByName(null),port); out = new DataOutputStream(socket.getOutputStream()); out.writeInt(key); String script; if(quit) { script = "socket.close();\n" + "jEdit.exit(null,true);\n"; } else { script = makeServerScript(wait,restore, newView,newPlainView,args, scriptFile); } out.writeUTF(script); Log.log(Log.DEBUG,jEdit.class,"Waiting for server"); // block until its closed socket.getInputStream().read(); System.exit(0); } catch(Exception e) { // ok, this one seems to confuse newbies // endlessly, so log it as NOTICE, not // ERROR Log.log(Log.NOTICE,jEdit.class,"An error occurred" + " while connecting to the jEdit server instance."); Log.log(Log.NOTICE,jEdit.class,"This probably means that" + " jEdit crashed and/or exited abnormally"); Log.log(Log.NOTICE,jEdit.class,"the last time it was run."); Log.log(Log.NOTICE,jEdit.class,"If you don't" + " know what this means, don't worry."); Log.log(Log.NOTICE,jEdit.class,e); } finally { if(in != null) try { in.close(); } catch (IOException e) {} if(out != null) try { out.close(); } catch (IOException e) {} } } if(quit) { // if no server running and user runs jedit -quit, // just exit System.exit(0); } //}}} // don't show splash screen if there is a file named // 'nosplash' in the settings directory logTime("before splash screen activation"); if(splash && (!new File(settingsDirectory,"nosplash").exists())) GUIUtilities.showSplashScreen(); logTime("after splash screen activation"); //{{{ Settings migration code. // Windows check introduced in 5.0pre1. // MacOS check introduced in 4.3. if((OperatingSystem.isMacOS() || OperatingSystem.isWindows()) && shouldRelocateSettings && settingsDirectory != null) { relocateSettings(); } // }}} //{{{ Initialize settings directory Writer stream; if(settingsDirectory != null) { File _settingsDirectory = new File(settingsDirectory); if(!_settingsDirectory.exists()) _settingsDirectory.mkdirs(); File _macrosDirectory = new File(settingsDirectory,"macros"); if(!_macrosDirectory.exists()) _macrosDirectory.mkdir(); String logPath = MiscUtilities.constructPath( settingsDirectory,"activity.log"); backupSettingsFile(new File(logPath)); try { stream = new BufferedWriter(new FileWriter(logPath)); // Write a warning message: String lineSep = System.getProperty("line.separator"); stream.write("Log file created on " + new Date()); stream.write(lineSep); stream.write("IMPORTANT:"); stream.write(lineSep); stream.write("Because updating this file after " + "every log message would kill"); stream.write(lineSep); stream.write("performance, it will be *incomplete* " + "unless you invoke the"); stream.write(lineSep); stream.write("Utilities->Troubleshooting->Update " + "Activity Log on Disk command!"); stream.write(lineSep); } catch(Exception e) { e.printStackTrace(); stream = null; } } else { stream = null; } //}}} Log.setLogWriter(stream); Log.log(Log.NOTICE,jEdit.class,"jEdit version " + getVersion()); Log.log(Log.MESSAGE,jEdit.class,"Settings directory is " + settingsDirectory); //{{{ Get things rolling GUIUtilities.advanceSplashProgress("init"); initMisc(); GUIUtilities.advanceSplashProgress("init system properties"); initSystemProperties(); GUIUtilities.advanceSplashProgress("init beanshell"); BeanShell.init(); GUIUtilities.advanceSplashProgress("loading site properties"); if(jEditHome != null) initSiteProperties(); GUIUtilities.advanceSplashProgress("loading user properties"); initUserProperties(); initLocalizationProperties(); GUIUtilities.advanceSplashProgress("init GUI"); GUIUtilities.init(); bufferSetManager = new BufferSetManager(); //}}} //{{{ Initialize server if(portFile != null) { GUIUtilities.advanceSplashProgress("init server"); server = new EditServer(portFile); if(!server.isOK()) server = null; } else { GUIUtilities.advanceSplashProgress(); if(background) { background = false; Log.log(Log.WARNING,jEdit.class,"You cannot specify both the" + " -background and -noserver switches"); } } //}}} //{{{ Do more stuff GUIUtilities.advanceSplashProgress("init look and feel"); initPLAF(); GUIUtilities.advanceSplashProgress("init VFS Manager"); VFSManager.init(); GUIUtilities.advanceSplashProgress("init resources"); initResources(); if (settingsDirectory != null) { GUIUtilities.advanceSplashProgress("Migrate keymaps"); MigrationService keymapMigration = ServiceManager.getService(MigrationService.class, "keymap"); keymapMigration.migrate(); } SearchAndReplace.load(); if(loadPlugins) { GUIUtilities.advanceSplashProgress("init plugins"); initPlugins(); } else GUIUtilities.advanceSplashProgress(); Registers.setSaver(new JEditRegisterSaver()); Registers.setListener(new JEditRegistersListener()); GUIUtilities.advanceSplashProgress("init history model"); HistoryModel.setSaver(new JEditHistoryModelSaver()); HistoryModel.loadHistory(); GUIUtilities.advanceSplashProgress("init buffer history"); BufferHistory.load(); GUIUtilities.advanceSplashProgress("init killring"); KillRing.setInstance(new JEditKillRing()); KillRing.getInstance().load(); GUIUtilities.advanceSplashProgress("init various properties"); // other one-time migration services. OneTimeMigrationService.execute(); propertiesChanged(); GUIUtilities.advanceSplashProgress("init modes"); // Buffer sort sortBuffers = getBooleanProperty("sortBuffers"); sortByName = getBooleanProperty("sortByName"); reloadModes(); GUIUtilities.advanceSplashProgress("activate plugins"); //}}} //{{{ Activate plugins that must be activated at startup for(int i = 0; i < jars.size(); i++) { jars.elementAt(i).activatePluginIfNecessary(); } //}}} String[] serviceNames = ServiceManager.getServiceNames(JEditTransferableService.class); for (String serviceName : serviceNames) { JEditTransferableService service = ServiceManager.getService(JEditTransferableService.class, serviceName); org.gjt.sp.jedit.datatransfer.TransferHandler.getInstance().registerTransferableService(service); } //{{{ Load macros and run startup scripts, after plugins and settings are loaded GUIUtilities.advanceSplashProgress("init macros"); Macros.loadMacros(); Macros.getMacroActionSet().initKeyBindings(); if(runStartupScripts && jEditHome != null) { String path = MiscUtilities.constructPath(jEditHome,"startup"); File file = new File(path); if(file.exists()) { runStartupScripts(file); } else GUIUtilities.advanceSplashProgress(); } else GUIUtilities.advanceSplashProgress("run startup scripts"); if(runStartupScripts && settingsDirectory != null) { String path = MiscUtilities.constructPath(settingsDirectory,"startup"); File file = new File(path); if (file.exists()) { GUIUtilities.advanceSplashProgress("run startup scripts"); runStartupScripts(file); } else { GUIUtilities.advanceSplashProgress(); file.mkdirs(); } } else { GUIUtilities.advanceSplashProgress(); } //}}} //{{{ Run script specified with -run= parameter if(scriptFile != null) { GUIUtilities.advanceSplashProgress("run script file"); scriptFile = MiscUtilities.constructPath(userDir,scriptFile); try { BeanShell.getNameSpace().setVariable("args",args); } catch(UtilEvalError e) { Log.log(Log.ERROR,jEdit.class,e); } BeanShell.runScript(null,scriptFile,null,false); } else { GUIUtilities.advanceSplashProgress(); } //}}} GUIUtilities.advanceSplashProgress(); // Create dynamic actions for switching to saved layouts. // The list of saved layouts is retrieved from the docking framework, // which can be provided by a plugin, so this must be called only after // the plugins are loaded. DockingLayoutManager.init(); // Open files, create the view and hide the splash screen. SyntaxUtilities.propertyManager = jEdit.propertyManager; finishStartup(gui,restore,newPlainView,userDir,args); logTime("main done"); } //}}} //{{{ Property methods //{{{ getCurrentLanguage() method /** * Returns the current language used by jEdit. * * @return the current language, never null * @since jEdit 5.0pre1 */ public static String getCurrentLanguage() { String language; if (getBooleanProperty("lang.usedefaultlocale")) { language = Locale.getDefault().getLanguage(); } else { language = getProperty("lang.current", "en"); } return language; } //}}} //{{{ getProperties() method /** * Returns the properties object which contains all known * jEdit properties. Note that as of jEdit 4.2pre10, this returns a * new collection, not the existing properties instance. * @since jEdit 3.1pre4 */ public static Properties getProperties() { return propMgr.getProperties(); } //}}} //{{{ getProperty() method /** * Fetches a property, returning null if it's not defined. * @param name The property */ public static String getProperty(String name) { return propMgr.getProperty(name); } //}}} //{{{ getProperty() method /** * Fetches a property, returning the default value if it's not * defined. * @param name The property * @param def The default value */ public static String getProperty(String name, String def) { String value = propMgr.getProperty(name); if(value == null) return def; else return value; } //}}} //{{{ getProperty() method /** * Returns the property with the specified name.<p> * * The elements of the <code>args</code> array are substituted * into the value of the property in place of strings of the * form <code>{<i>n</i>}</code>, where <code><i>n</i></code> is an index * in the array.<p> * * You can find out more about this feature by reading the * documentation for the <code>format</code> method of the * <code>java.text.MessageFormat</code> class. * * @param name The property * @param args The positional parameters */ public static String getProperty(String name, Object[] args) { if(name == null) return null; if(args == null) return getProperty(name); else { String value = getProperty(name); if(value == null) return null; else return MessageFormat.format(value,args); } } //}}} //{{{ getBooleanProperty() method /** * Returns the value of a boolean property. * @param name The property */ public static boolean getBooleanProperty(String name) { return getBooleanProperty(name,false); } //}}} //{{{ getBooleanProperty() method /** * Returns the value of a boolean property. * @param name The property * @param def The default value */ public static boolean getBooleanProperty(String name, boolean def) { String value = getProperty(name); return StandardUtilities.getBoolean(value, def); } //}}} //{{{ getIntegerProperty() method /** * Returns the value of an integer property. * @param name The property */ public static int getIntegerProperty(String name) { return getIntegerProperty(name,0); } //}}} //{{{ getIntegerProperty() method /** * Returns the value of an integer property. * @param name The property * @param def The default value * @since jEdit 4.0pre1 */ public static int getIntegerProperty(String name, int def) { String value = getProperty(name); if(value == null) return def; else { try { return Integer.parseInt(value.trim()); } catch(NumberFormatException nf) { return def; } } } //}}} //{{{ getDoubleProperty() method public static double getDoubleProperty(String name, double def) { String value = getProperty(name); if(value == null) return def; else { try { return Double.parseDouble(value.trim()); } catch(NumberFormatException nf) { return def; } } } //}}} //{{{ getFontProperty() method /** * Returns the value of a font property. The family is stored * in the <code><i>name</i></code> property, the font size is stored * in the <code><i>name</i>size</code> property, and the font style is * stored in <code><i>name</i>style</code>. For example, if * <code><i>name</i></code> is <code>view.gutter.font</code>, the * properties will be named <code>view.gutter.font</code>, * <code>view.gutter.fontsize</code>, and * <code>view.gutter.fontstyle</code>. * * @param name The property * @since jEdit 4.0pre1 */ public static Font getFontProperty(String name) { return getFontProperty(name,null); } //}}} //{{{ getFontProperty() method /** * Returns the value of a font property. The family is stored * in the <code><i>name</i></code> property, the font size is stored * in the <code><i>name</i>size</code> property, and the font style is * stored in <code><i>name</i>style</code>. For example, if * <code><i>name</i></code> is <code>view.gutter.font</code>, the * properties will be named <code>view.gutter.font</code>, * <code>view.gutter.fontsize</code>, and * <code>view.gutter.fontstyle</code>. * * @param name The property * @param def The default value * @since jEdit 4.0pre1 */ public static Font getFontProperty(String name, Font def) { String family = getProperty(name); String sizeString = getProperty(name + "size"); String styleString = getProperty(name + "style"); if(family == null || sizeString == null || styleString == null) return def; else { int size, style; try { size = Integer.parseInt(sizeString); } catch(NumberFormatException nf) { return def; } try { style = Integer.parseInt(styleString); } catch(NumberFormatException nf) { return def; } return new Font(family,style,size); } } //}}} //{{{ getColorProperty() method /** * Returns the value of a color property. * @param name The property name * @since jEdit 4.0pre1 */ public static Color getColorProperty(String name) { return getColorProperty(name,Color.black); } //}}} //{{{ getColorProperty() method /** * Returns the value of a color property. * @param name The property name * @param def The default value * @since jEdit 4.0pre1 */ public static Color getColorProperty(String name, Color def) { String value = getProperty(name); if(value == null) return def; else return SyntaxUtilities.parseColor(value, def); } //}}} //{{{ setColorProperty() method /** * Sets the value of a color property. * @param name The property name * @param value The value * @since jEdit 4.0pre1 */ public static void setColorProperty(String name, Color value) { setProperty(name, SyntaxUtilities.getColorHexString(value)); } //}}} //{{{ setProperty() method /** * Sets a property to a new value. * @param name The property * @param value The new value */ public static void setProperty(String name, String value) { propMgr.setProperty(name,value); } //}}} //{{{ setTemporaryProperty() method /** * Sets a property to a new value. Properties set using this * method are not saved to the user properties list. * @param name The property * @param value The new value * @since jEdit 2.3final */ public static void setTemporaryProperty(String name, String value) { propMgr.setTemporaryProperty(name,value); } //}}} //{{{ setBooleanProperty() method /** * Sets a boolean property. * @param name The property * @param value The value */ public static void setBooleanProperty(String name, boolean value) { setProperty(name,value ? "true" : "false"); } //}}} //{{{ setIntegerProperty() method /** * Sets the value of an integer property. * @param name The property * @param value The value * @since jEdit 4.0pre1 */ public static void setIntegerProperty(String name, int value) { setProperty(name,String.valueOf(value)); } //}}} //{{{ setDoubleProperty() method public static void setDoubleProperty(String name, double value) { setProperty(name,String.valueOf(value)); } //}}} //{{{ setFontProperty() method /** * Sets the value of a font property. The family is stored * in the <code><i>name</i></code> property, the font size is stored * in the <code><i>name</i>size</code> property, and the font style is * stored in <code><i>name</i>style</code>. For example, if * <code><i>name</i></code> is <code>view.gutter.font</code>, the * properties will be named <code>view.gutter.font</code>, * <code>view.gutter.fontsize</code>, and * <code>view.gutter.fontstyle</code>. * * @param name The property * @param value The value * @since jEdit 4.0pre1 */ public static void setFontProperty(String name, Font value) { setProperty(name,value.getFamily()); setIntegerProperty(name + "size",value.getSize()); setIntegerProperty(name + "style",value.getStyle()); } //}}} //{{{ unsetProperty() method /** * Unsets (clears) a property. * @param name The property */ public static void unsetProperty(String name) { propMgr.unsetProperty(name); } //}}} //{{{ resetProperty() method /** * Resets a property to its default value. * @param name The property * * @since jEdit 2.5pre3 */ public static void resetProperty(String name) { propMgr.resetProperty(name); } //}}} //{{{ propertiesChanged() method /** * Reloads various settings from the properties. */ public static void propertiesChanged() { initPLAF(); keymapManager.reload(); initKeyBindings(); Autosave.setInterval(getIntegerProperty("autosave",30)); saveCaret = getBooleanProperty("saveCaret"); UIDefaults defaults = UIManager.getDefaults(); defaults.put("SplitPane.continuousLayout", true); // give all text areas the same font Font font = getFontProperty("view.font"); //defaults.put("TextField.font",font); defaults.put("TextArea.font",font); defaults.put("TextPane.font",font); // Enable/Disable tooltips ToolTipManager.sharedInstance().setEnabled( jEdit.getBooleanProperty("showTooltips")); initProxy(); // we do this here instead of adding buffers to the bus. Buffer buffer = buffersFirst; while(buffer != null) { buffer.resetCachedProperties(); buffer.propertiesChanged(); buffer = buffer.next; } HistoryModel.setDefaultMax(getIntegerProperty("history",25)); HistoryModel.setDefaultMaxSize(getIntegerProperty("historyMaxSize", 5000000)); KillRing.getInstance().propertiesChanged(getIntegerProperty("history",25)); Chunk.propertiesChanged(propertyManager); Log.setBeepOnOutput(jEdit.getBooleanProperty("debug.beepOnOutput")); if (getBooleanProperty("systrayicon")) { EventQueue.invokeLater(new Runnable() { @Override public void run() { JTrayIconManager.addTrayIcon(); } }); } else { JTrayIconManager.removeTrayIcon(); } EditBus.send(new PropertiesChanged(null)); } //}}} //}}} Property methods fold end //{{{ Plugin management methods //{{{ getNotLoadedPluginJARs() method /** * Returns a list of plugin JARs pathnames that are not currently loaded * by examining the user and system plugin directories. * @since jEdit 3.2pre1 */ public static String[] getNotLoadedPluginJARs() { List<String> returnValue = new ArrayList<String>(); if(jEditHome != null) { String systemPluginDir = MiscUtilities .constructPath(jEditHome,"jars"); String[] list = new File(systemPluginDir).list(); if(list != null) getNotLoadedPluginJARs(returnValue,systemPluginDir,list); } if(settingsDirectory != null) { String userPluginDir = MiscUtilities .constructPath(settingsDirectory,"jars"); String[] list = new File(userPluginDir).list(); if(list != null) { getNotLoadedPluginJARs(returnValue, userPluginDir,list); } } String[] _returnValue = new String[returnValue.size()]; returnValue.toArray(_returnValue); return _returnValue; } //}}} //{{{ getPlugin() method /** * Returns the plugin with the specified class name. * Only works for plugins that were loaded. */ public static EditPlugin getPlugin(String name) { return getPlugin(name, false); } //}}} //{{{ getPlugin(String, boolean) method /** * Returns the plugin with the specified class name. * If * <code>loadIfNecessary</code> is true, the plugin will be searched for, * loaded, and activated in case it has not yet been loaded. * * @param name the classname of the main Plugin class. * @param loadIfNecessary - loads plugin + dependencies if it is not loaded yet. * @since jEdit 4.2pre4 */ public static EditPlugin getPlugin(String name, boolean loadIfNecessary) { if (name == null) { return null; } EditPlugin[] plugins = getPlugins(); EditPlugin plugin = null; for (EditPlugin ep : plugins) { if (ep.getClassName().equals(name)) { plugin = ep; break; } } if (!loadIfNecessary) { return plugin; } if (plugin instanceof EditPlugin.Deferred) { plugin.getPluginJAR().activatePlugin(); plugin = plugin.getPluginJAR().getPlugin(); } String jarPath = PluginJAR.findPlugin(name); PluginJAR pjar = PluginJAR.load(jarPath, true); return pjar.getPlugin(); } //}}} //{{{ getPlugins() method /** * Returns an array of installed plugins. */ public static EditPlugin[] getPlugins() { Collection<EditPlugin> pluginList = new ArrayList<EditPlugin>(); for(int i = 0; i < jars.size(); i++) { EditPlugin plugin = jars.elementAt(i).getPlugin(); if(plugin != null) pluginList.add(plugin); } EditPlugin[] array = new EditPlugin[pluginList.size()]; pluginList.toArray(array); return array; } //}}} //{{{ getPluginJARs() method /** * Returns an array of installed plugins. * @since jEdit 4.2pre1 */ public static PluginJAR[] getPluginJARs() { PluginJAR[] array = new PluginJAR[jars.size()]; jars.copyInto(array); return array; } //}}} //{{{ getPluginJAR() method /** * Returns the JAR with the specified path name. * @param path The path name * @since jEdit 4.2pre1 */ public static PluginJAR getPluginJAR(String path) { for(int i = 0; i < jars.size(); i++) { PluginJAR jar = jars.elementAt(i); if(jar.getPath().equals(path)) return jar; } return null; } //}}} //{{{ addPluginJAR() method /** * Loads the plugin JAR with the specified path. Some notes about this * method: * * <ul> * <li>Calling this at a time other than jEdit startup can have * unpredictable results if the plugin has not been updated for the * jEdit 4.2 plugin API. * <li>You must make sure yourself the plugin is not already loaded. * <li>After loading, you just make sure all the plugin's dependencies * are satisified before activating the plugin, using the * {@link PluginJAR#checkDependencies()} method. * </ul> * * @param path The JAR file path * @since jEdit 4.2pre1 */ public static void addPluginJAR(String path) { PluginJAR jar = new PluginJAR(new File(path)); jars.addElement(jar); if (jar.init()) { String jarName = MiscUtilities.getFileName(path); jEdit.unsetProperty("plugin-blacklist."+jarName); jEdit.unsetProperty("plugin." + jarName + ".disabled"); EditBus.send(new PluginUpdate(jar,PluginUpdate.LOADED,false)); if(!isMainThread()) { EditBus.send(new DynamicMenuChanged("plugins")); initKeyBindings(); } } else { jars.removeElement(jar); jar.uninit(false); } } //}}} //{{{ addPluginJARsFromDirectory() method /** * Loads all plugins in a directory. * @param directory The directory * @since jEdit 4.2pre1 */ private static void addPluginJARsFromDirectory(String directory) { Log.log(Log.NOTICE,jEdit.class,"Loading plugins from " + directory); File file = new File(directory); if(!(file.exists() && file.isDirectory())) return; String[] plugins = file.list(); if(plugins == null) return; for (String plugin : plugins) { if (!plugin.toLowerCase().endsWith(".jar")) continue; String path = MiscUtilities.constructPath(directory, plugin); if (jEdit.getBooleanProperty("plugin-blacklist." + plugin)) continue; addPluginJAR(path); } } //}}} //{{{ removePluginJAR() method /** * Unloads the given plugin JAR with the specified path. Note that * calling this at a time other than jEdit shutdown can have * unpredictable results if the plugin has not been updated for the * jEdit 4.2 plugin API. * * @param jar The <code>PluginJAR</code> instance * @param exit Set to true if jEdit is exiting; enables some * shortcuts so the editor can close faster. * @since jEdit 4.2pre1 */ public static void removePluginJAR(PluginJAR jar, boolean exit) { if(exit) { jar.uninit(true); } else { jar.uninit(false); jars.removeElement(jar); if (!isMainThread()) initKeyBindings(); } EditBus.send(new PluginUpdate(jar,PluginUpdate.UNLOADED,exit)); if(!isMainThread() && !exit) EditBus.send(new DynamicMenuChanged("plugins")); } //}}} //}}} //{{{ Action methods //{{{ getActionContext() method /** * Returns the action context used to store editor actions. * @since jEdit 4.2pre1 */ public static ActionContext getActionContext() { return actionContext; } //}}} //{{{ addActionSet() method /** * Adds a new action set to jEdit's list of ActionSets (viewable from the shortcuts * option pane). By default, each plugin has one ActionSet, * but some plugins may create dynamic action sets, such as ProjectViewer and Console. * These plugins must call removeActionSet() when the plugin is unloaded. * * @since jEdit 4.0pre1 * @see #removeActionSet(ActionSet) */ public static void addActionSet(ActionSet actionSet) { actionContext.addActionSet(actionSet); } //}}} //{{{ removeActionSet() method /** * Removes an action set from jEdit's list. * Plugins that add a dynamic action set must call this method at plugin * unload time. * @since jEdit 4.2pre1 */ public static void removeActionSet(ActionSet actionSet) { actionContext.removeActionSet(actionSet); } //}}} //{{{ getBuiltInActionSet() method /** * Returns the set of commands built into jEdit. * @since jEdit 4.2pre1 */ public static ActionSet getBuiltInActionSet() { return builtInActionSet; } //}}} // {{{ getActionSets() method /** * Returns all registered action sets. * * @return the ActionSet(s) * @since jEdit 4.0pre1 */ public static ActionSet[] getActionSets() { return actionContext.getActionSets(); } // }}} //{{{ getAction() method /** * Returns the specified action. * @param name The action name */ public static EditAction getAction(String name) { return actionContext.getAction(name); } //}}} //{{{ getActionSetForAction() method /** * Returns the action set that contains the specified action. * * @param action The action * @since jEdit 4.2pre1 */ public static ActionSet getActionSetForAction(String action) { return actionContext.getActionSetForAction(action); } //}}} //{{{ getActionNames() method /** * Returns all registered action names. */ public static String[] getActionNames() { return actionContext.getActionNames(); } //}}} //}}} //{{{ Edit mode methods //{{{ reloadModes() method /** * Reloads all edit modes. User defined edit modes are loaded after * global modes so that user modes supercede global modes. * @since jEdit 3.2pre2 */ public static void reloadModes() { ModeProvider.instance.removeAll(); //{{{ Load the global catalog first if(jEditHome == null) loadModeCatalog("/modes/catalog",true); else { loadModeCatalog(MiscUtilities.constructPath(jEditHome, "modes","catalog"),false); } //}}} //{{{ Load user catalog second so user modes override global modes. if(settingsDirectory != null) { File userModeDir = new File(MiscUtilities.constructPath( settingsDirectory,"modes")); if(!userModeDir.exists()) userModeDir.mkdirs(); File userCatalog = new File(MiscUtilities.constructPath( settingsDirectory,"modes","catalog")); if(!userCatalog.exists()) { // create dummy catalog BufferedWriter out = null; try { out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(userCatalog), "UTF-8")); out.write(jEdit.getProperty("defaultCatalog")); } catch(IOException io) { Log.log(Log.ERROR,jEdit.class,io); } finally { IOUtilities.closeQuietly((Closeable)out); } } loadModeCatalog(userCatalog.getPath(),false); } //}}} Buffer buffer = buffersFirst; while(buffer != null) { // This reloads the token marker and sends a message // which causes edit panes to repaint their text areas buffer.setMode(); buffer = buffer.next; } } //}}} //{{{ getMode() method /** * Returns the edit mode with the specified name. * @param name The edit mode */ public static Mode getMode(String name) { return ModeProvider.instance.getMode(name); } //}}} //{{{ getModes() method /** * Returns an array of installed edit modes. */ public static Mode[] getModes() { return ModeProvider.instance.getModes(); } //}}} //}}} //{{{ Buffer creation methods //{{{ openFiles() method /** * Opens the file names specified in the argument array. This * handles +line and +marker arguments just like the command * line parser. * @param parent The parent directory * @param args The file names to open * @since jEdit 3.2pre4 */ public static Buffer openFiles(View view, String parent, String[] args) { Buffer retVal = null; Buffer lastBuffer = null; for (String arg : args) { if (arg == null) continue; else if (arg.startsWith("+line:") || arg.startsWith("+marker:")) { if (lastBuffer != null) gotoMarker(view, lastBuffer, arg); continue; } lastBuffer = openFile((View) null, parent, arg, false, null); if (retVal == null && lastBuffer != null) retVal = lastBuffer; } if(view != null && retVal != null) view.setBuffer(retVal); return retVal; } //}}} //{{{ openFile() methods /** * Opens a file, either immediately if the application is finished starting up, * or after the first view has been created if not. * @param path The file path * * @return the buffer if succesfully loaded immediately, or null otherwise * * @since jEdit 4.5pre1 */ public static Buffer openFileAfterStartup(String path) { if (isStartupDone()) { return openFile(getActiveView(), path); } else { // These additional file names will be treated just as if they had // been passed on the command line additionalFiles.add(path); return null; } } /** * Opens a file. Note that as of jEdit 2.5pre1, this may return * null if the buffer could not be opened. * @param view The view to open the file in * @param path The file path * * @return the buffer, or null if jEdit was unable to load it * * @since jEdit 2.4pre1 */ public static Buffer openFile(View view, String path) { return openFile(view,null,path,false,new Hashtable<String,Object>()); } /** * Opens a file. This may return null if the buffer could not be * opened for some reason. * @param view The view to open the file in. If it is null, the file * will be opened and added to the bufferSet of the current edit pane, * but not selected * @param parent The parent directory of the file * @param path The path name of the file * @param newFile True if the file should not be loaded from disk * be prompted if it should be reloaded * @param props Buffer-local properties to set in the buffer * * @return the buffer, or null if jEdit was unable to load it * * @since jEdit 3.2pre10 */ public static Buffer openFile(View view, String parent, String path, boolean newFile, Hashtable<String,Object> props) { return openFile(view == null ? null : view.getEditPane(), parent, path, newFile, props); } /** * Opens a file. Note that as of jEdit 2.5pre1, this may return * null if the buffer could not be opened. * @param editPane the EditPane to open the file in. * @param path The file path * * @return the buffer, or null if jEdit was unable to load it * * @since jEdit 4.3pre17 */ public static Buffer openFile(EditPane editPane, String path) { return openFile(editPane,null,path,false,new Hashtable<String,Object>()); } /** * Opens a file. This may return null if the buffer could not be * opened for some reason. * @param editPane the EditPane to open the file in. * @param parent The parent directory of the file * @param path The path name of the file * @param newFile True if the file should not be loaded from disk * be prompted if it should be reloaded * @param props Buffer-local properties to set in the buffer * * @return the buffer, or null if jEdit was unable to load it * * @since jEdit 4.3pre17 */ public static Buffer openFile(EditPane editPane, String parent, String path, boolean newFile, Hashtable<String,Object> props) { PerspectiveManager.setPerspectiveDirty(true); if(editPane != null && parent == null && editPane.getBuffer() != null) parent = editPane.getBuffer().getDirectory(); try { URL u = new URL(path); if ("file".equals(u.getProtocol())) { path = URLDecoder.decode(u.getPath(), "UTF-8"); } } catch(UnsupportedEncodingException e) { path = MiscUtilities.constructPath(parent,path); } catch (MalformedURLException e) { path = MiscUtilities.constructPath(parent,path); } if(props == null) props = new Hashtable<String,Object>(); composeBufferPropsFromHistory(props, path); Buffer newBuffer; synchronized (editBusOrderingLock) { View view = editPane == null ? null : editPane.getView(); synchronized(bufferListLock) { Buffer buffer = getBuffer(path); if(buffer != null) { if(editPane != null) editPane.setBuffer(buffer,true); return buffer; } newBuffer = new Buffer(path,newFile,false,props); if(!newBuffer.load(view,false)) return null; addBufferToList(newBuffer); if (editPane != null) bufferSetManager.addBuffer(editPane, newBuffer); else bufferSetManager.addBuffer(jEdit.getActiveView(), newBuffer); } EditBus.send(new BufferUpdate(newBuffer,view,BufferUpdate.CREATED)); } if(editPane != null) editPane.setBuffer(newBuffer,true); return newBuffer; } //}}} //{{{ openTemporary() methods /** * Opens a temporary buffer. A temporary buffer is like a normal * buffer, except that an event is not fired and the buffer is * not added to the buffers list. * <p>If a buffer for the given <code>path</code> was * already opened in jEdit, then this instance is returned. * Otherwise jEdit will not store a reference * to the returned Buffer object. * <p>This method is thread-safe. * * @param view The view to open the file in * @param parent The parent directory of the file * @param path The path name of the file * @param newFile True if the file should not be loaded from disk * * @return the buffer, or null if jEdit was unable to load it * * @since jEdit 3.2pre10 */ public static Buffer openTemporary(View view, String parent, String path, boolean newFile) { return openTemporary(view, parent, path, newFile, null); } /** * Opens a temporary buffer. * Details: {@link #openTemporary(View, String, String, boolean)} * * @param view The view to open the file in * @param parent The parent directory of the file * @param path The path name of the file * @param newFile True if the file should not be loaded from disk * @param props Buffer-local properties to set in the buffer * * @return the buffer, or null if jEdit was unable to load it * * @since jEdit 4.3pre10 */ public static Buffer openTemporary(View view, String parent, String path, boolean newFile, Hashtable<String, Object> props) { if(view != null && parent == null) parent = view.getBuffer().getDirectory(); if(MiscUtilities.isURL(path)) { if("file".equals(MiscUtilities.getProtocolOfURL(path))) path = path.substring(5); } path = MiscUtilities.constructPath(parent,path); if(props == null) props = new Hashtable<String, Object>(); composeBufferPropsFromHistory(props, path); synchronized(bufferListLock) { Buffer buffer = getBuffer(path); if(buffer != null) return buffer; buffer = new Buffer(path,newFile,true,props); buffer.setBooleanProperty(Buffer.ENCODING_AUTODETECT, true); if(!buffer.load(view,false)) return null; else return buffer; } } //}}} //{{{ commitTemporary() method /** * Adds a temporary buffer to the buffer list. This must be done * before allowing the user to interact with the buffer in any * way. * @param buffer The buffer */ public static void commitTemporary(Buffer buffer) { if(!buffer.isTemporary()) return; PerspectiveManager.setPerspectiveDirty(true); addBufferToList(buffer); buffer.commitTemporary(); // send full range of events to avoid breaking plugins EditBus.send(new BufferUpdate(buffer,null,BufferUpdate.CREATED)); EditBus.send(new BufferUpdate(buffer,null,BufferUpdate.LOAD_STARTED)); EditBus.send(new BufferUpdate(buffer,null,BufferUpdate.LOADED)); } //}}} //{{{ newFile() methods /** * Creates a new `untitled' file. * * @param view The view to create the file in * * @return the new buffer */ public static Buffer newFile(View view) { return newFile(view == null ? null : view.getEditPane()); } /** * Creates a new `untitled' file. * @param view The view to create the file in * @param dir The directory to create the file in * * @return the new buffer * * @since jEdit 3.1pre2 */ public static Buffer newFile(View view, String dir) { EditPane editPane = null; if (view != null) { editPane = view.getEditPane(); } else { View v = getActiveView(); if (v != null) { editPane = v.getEditPane(); } } return newFile(editPane, dir); } /** * Creates a new `untitled' file. * * @param editPane The editPane to create the file in * * @return the new buffer * @since jEdit 4.3pre17 */ public static Buffer newFile(EditPane editPane) { String path; if(editPane != null && editPane.getBuffer() != null) { path = editPane.getBuffer().getDirectory(); VFS vfs = VFSManager.getVFSForPath(path); // don't want 'New File' to create a read only buffer // if current file is on SQL VFS or something if((vfs.getCapabilities() & VFS.WRITE_CAP) == 0) path = System.getProperty("user.home"); } else path = null; return newFile(editPane,path); } /** * Creates a new `untitled' file. * * @param editPane The editPane to create the file in * @param dir The directory to create the file in * * @return the new buffer * * @since jEdit 4.3pre17 */ public static Buffer newFile(EditPane editPane, String dir) { if (editPane != null) { BufferSet bufferSet = editPane.getBufferSet(); Buffer[] buffers = bufferSet.getAllBuffers(); for (Buffer buf:buffers) { if (buf.isUntitled() && !buf.isDirty()) { if (!MiscUtilities.getParentOfPath(buf.getPath()).equals(dir)) { // Find the highest Untitled-n file int untitledCount = getNextUntitledBufferId(); Buffer newBuffer = openFile(editPane,dir,"Untitled-" + untitledCount,true,null); jEdit.closeBuffer(editPane, buf); return newBuffer; } /* if "never mark untitled buffers dirty" * is selected, we might have contents in non-dirty * untitled buffers. We must clear those contents * if user requested new file. */ int l = buf.getLength(); if (l > 0) buf.remove(0, l); editPane.setBuffer(buf); return buf; } } } // Find the highest Untitled-n file int untitledCount = getNextUntitledBufferId(); return openFile(editPane,dir,"Untitled-" + untitledCount,true,null); } //}}} //}}} //{{{ Buffer management methods //{{{ closeBuffer() method /** * Closes a buffer. If there are unsaved changes, the user is * prompted if they should be saved first. * @param view The view * @param buffer The buffer * @return True if the buffer was really closed, false otherwise */ public static boolean closeBuffer(View view, Buffer buffer) { // Wait for pending I/O requests if(buffer.isPerformingIO()) { TaskManager.instance.waitForIoTasks(); if(VFSManager.errorOccurred()) return false; } if(buffer.isDirty()) { Object[] args = { buffer.getName() }; int result = GUIUtilities.confirm(view,"notsaved",args, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); if(result == JOptionPane.YES_OPTION) { if(!buffer.save(view,null,true)) return false; TaskManager.instance.waitForIoTasks(); if(buffer.getBooleanProperty(BufferIORequest .ERROR_OCCURRED)) { return false; } } else if(result != JOptionPane.NO_OPTION) return false; } _closeBuffer(view,buffer); return true; } //}}} //{{{ closeBuffer() method /** * Close a buffer. * The buffer is first removed from the EditPane's bufferSet. * If the buffer is not in any bufferSet after that, it is closed * @param editPane the edit pane (it cannot be null) * @param buffer the buffer (it cannot be null) * @since jEdit 4.3pre15 */ public static void closeBuffer(EditPane editPane, Buffer buffer) { switch (bufferSetManager.getScope()) { case global: closeBuffer(editPane.getView(), buffer); break; case view: View[] views = jEdit.getViews(); int viewOwner = 0; for (View view : views) { BufferSet bufferSet = view.getEditPane().getBufferSet(); // no need to check every bufferSet since it's view scope if (bufferSet.indexOf(buffer) != -1) { viewOwner++; if (viewOwner > 1) break; } } if (viewOwner > 1) { // the buffer is in several view, we can remove it from bufferSet bufferSetManager.removeBuffer(editPane, buffer); } else { closeBuffer(editPane.getView(), buffer); } break; case editpane: int bufferSetsCount = bufferSetManager.countBufferSets(buffer); if (bufferSetsCount < 2) { closeBuffer(editPane.getView(), buffer); } else { bufferSetManager.removeBuffer(editPane, buffer); } break; } } //}}} //{{{ _closeBuffer() method /** * Closes the buffer, even if it has unsaved changes. * @param view The view, may be null * @param buffer The buffer * * @exception NullPointerException if the buffer is null * * @since jEdit 2.2pre1 */ public static void _closeBuffer(View view, Buffer buffer) { if(buffer.isClosed()) { // can happen if the user presses C+w twice real // quick and the buffer has unsaved changes return; } // in case of a temporary buffer, just close it if(buffer.isTemporary()) { buffer.close(); return; } PerspectiveManager.setPerspectiveDirty(true); if(!buffer.isNewFile()) { if(view != null) view.getEditPane().saveCaretInfo(); Integer _caret = (Integer)buffer.getProperty(Buffer.CARET); int caret = _caret == null ? 0 : _caret.intValue(); BufferHistory.setEntry(buffer.getPath(),caret, (Selection[])buffer.getProperty(Buffer.SELECTION), buffer.getStringProperty(JEditBuffer.ENCODING), buffer.getMode().getName()); } EditBus.send(new BufferUpdate(buffer,view,BufferUpdate.CLOSING)); //FIXME: Duplicate code? Same is done in removeBufferFromList(buffer); String path = buffer.getSymlinkPath(); if((VFSManager.getVFSForPath(path).getCapabilities() & VFS.CASE_INSENSITIVE_CAP) != 0) { path = path.toLowerCase(); } bufferHash.remove(path); removeBufferFromList(buffer); buffer.close(); DisplayManager.bufferClosed(buffer); bufferSetManager.removeBuffer(buffer); EditBus.send(new BufferUpdate(buffer,view,BufferUpdate.CLOSED)); if(jEdit.getBooleanProperty("persistentMarkers")) buffer.updateMarkersFile(view); } //}}} //{{{ closeAllBuffers() methods /** * Closes all open buffers. * @param view The view * * @return true if all buffers were closed, false otherwise */ public static boolean closeAllBuffers(View view) { return closeAllBuffers(view,false); } /** * Closes all open buffers. * @param view The view * @param isExiting This must be false unless this method is * being called by the exit() method * * @return true if all buffers were closed, false otherwise */ public static boolean closeAllBuffers(View view, boolean isExiting) { if(view != null) view.getEditPane().saveCaretInfo(); boolean dirty = false; boolean saveRecent = !(isExiting && jEdit.getBooleanProperty("restore")); Buffer buffer = buffersFirst; while(buffer != null) { if(buffer.isDirty()) { dirty = true; break; } buffer = buffer.next; } if(dirty) { boolean ok = new CloseDialog(view).isOK(); if(!ok) return false; } // Wait for pending I/O requests TaskManager.instance.waitForIoTasks(); if(VFSManager.errorOccurred()) return false; // close remaining buffers (the close dialog only deals with // dirty ones) buffer = buffersFirst; // zero it here so that BufferTabs doesn't have any problems buffersFirst = buffersLast = null; bufferHash.clear(); bufferCount = 0; while(buffer != null) { if(!buffer.isNewFile() && saveRecent) { Integer _caret = (Integer)buffer.getProperty(Buffer.CARET); int caret = _caret == null ? 0 : _caret.intValue(); BufferHistory.setEntry(buffer.getPath(),caret, (Selection[])buffer.getProperty(Buffer.SELECTION), buffer.getStringProperty(JEditBuffer.ENCODING), buffer.getMode().getName()); } if(!isExiting) { EditBus.send(new BufferUpdate(buffer,view,BufferUpdate.CLOSING)); } buffer.close(); DisplayManager.bufferClosed(buffer); if(!isExiting) { bufferSetManager.removeBuffer(buffer); EditBus.send(new BufferUpdate(buffer,view, BufferUpdate.CLOSED)); } if(jEdit.getBooleanProperty("persistentMarkers")) buffer.updateMarkersFile(view); buffer = buffer.next; } PerspectiveManager.setPerspectiveDirty(true); return true; } //}}} //{{{ saveAllBuffers() method /** * Saves all open buffers. * @param view The view * @since jEdit 4.2pre1 */ public static void saveAllBuffers(View view) { saveAllBuffers(view,jEdit.getBooleanProperty("confirmSaveAll")); } //}}} //{{{ saveAllBuffers() method /** * Saves all open buffers. * @param view The view * @param confirm If true, a confirmation dialog will be shown first * @since jEdit 2.7pre2 */ public static void saveAllBuffers(View view, boolean confirm) { if(confirm) { int result = GUIUtilities.confirm(view,"saveall",null, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if(result != JOptionPane.YES_OPTION) return; } Buffer current = view.getBuffer(); Buffer buffer = buffersFirst; while(buffer != null) { if(buffer.isDirty()) { if(buffer.isNewFile()) view.setBuffer(buffer); buffer.save(view,null,true,true); } buffer = buffer.next; } view.setBuffer(current); } //}}} //{{{ reloadAllBuffers() method /** * Reloads all open buffers. * @param view The view * @param confirm If true, a confirmation dialog will be shown first * if any buffers are dirty * @since jEdit 2.7pre2 */ public static void reloadAllBuffers(View view, boolean confirm) { boolean hasDirty = false; Buffer[] buffers = jEdit.getBuffers(); for(int i = 0; i < buffers.length && !hasDirty; i++) hasDirty = !buffers[i].isUntitled() && buffers[i].isDirty(); if(confirm && hasDirty) { int result = GUIUtilities.confirm(view,"reload-all",null, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if(result != JOptionPane.YES_OPTION) return; } // save caret info. Buffer.load() will load it. visit(new SaveCaretInfoVisitor()); for (Buffer buffer : buffers) { if (buffer.isUntitled()) continue; buffer.load(view, true); } } //}}} //{{{ _getBuffer() method /** * Returns the buffer with the specified path name. The path name * must be an absolute, canonical, path. * * @param path The path name * * @return the searched buffer, or null if it is not already open * * @see MiscUtilities#constructPath(String,String) * @see MiscUtilities#resolveSymlinks(String) * @see #getBuffer(String) * * @since jEdit 4.2pre7 */ public static Buffer _getBuffer(String path) { // paths on case-insensitive filesystems are stored as lower // case in the hash. if((VFSManager.getVFSForPath(path).getCapabilities() & VFS.CASE_INSENSITIVE_CAP) != 0) { path = path.toLowerCase(); } synchronized(bufferListLock) { return bufferHash.get(path); } } //}}} //{{{ getBuffer() method /** * Returns the buffer with the specified path name. The path name * must be an absolute path. This method automatically resolves * symbolic links. If performance is critical, cache the canonical * path and call {@link #_getBuffer(String)} instead. * * @param path The path name * * @return the searched buffer, or null if it is not already open * * @see MiscUtilities#constructPath(String,String) * @see MiscUtilities#resolveSymlinks(String) */ public static Buffer getBuffer(String path) { return _getBuffer(MiscUtilities.resolveSymlinks(path)); } //}}} //{{{ getBuffers() method /** * Returns an array of all open buffers from any View. * @return an array of all open buffers * @see View#getBuffers() */ public static Buffer[] getBuffers() { synchronized(bufferListLock) { Buffer[] buffers = new Buffer[bufferCount]; Buffer buffer = buffersFirst; for(int i = 0; i < bufferCount; i++) { buffers[i] = buffer; buffer = buffer.next; } return buffers; } } //}}} //{{{ getBufferCount() method /** * Returns the number of open buffers. */ public static int getBufferCount() { return bufferCount; } //}}} //{{{ getFirstBuffer() method /** * Returns the first buffer. */ public static Buffer getFirstBuffer() { return buffersFirst; } //}}} //{{{ getLastBuffer() method /** * Returns the last buffer. * @return the last buffer */ public static Buffer getLastBuffer() { return buffersLast; } //}}} //{{{ moveBuffer() method /** * Moves a buffer from a old position to a new position in the * BufferSet used in an EditPane. * @param editPane The EditPane in which a buffer is moved * @param oldPosition The position before the move * @param newPosition The position after the move */ public static void moveBuffer(EditPane editPane, int oldPosition, int newPosition) { bufferSetManager.moveBuffer(editPane, oldPosition, newPosition); } //}}} //{{{ getBufferSetManager() method /** * Returns the bufferSet manager. * @return the bufferSetManager * @since jEdit 4.3pre15 */ public static BufferSetManager getBufferSetManager() { return bufferSetManager; } //}}} //{{{ getPropertyManager() method /** * @return the propertyManager * @since jEdit 4.3pre15 */ public static JEditPropertyManager getPropertyManager() { return propertyManager; } //}}} //{{{ checkBufferStatus() methods /** * Checks each buffer's status on disk and shows the dialog box * informing the user that buffers changed on disk, if necessary. * @param view The view * @since jEdit 4.2pre1 */ public static void checkBufferStatus(View view) { checkBufferStatus(view,false); } /** * Checks buffer status on disk and shows the dialog box * informing the user that buffers changed on disk, if necessary. * @param view The view * @param currentBuffer indicates whether to check only the current buffer * @since jEdit 4.2pre1 */ public static void checkBufferStatus(View view, boolean currentBuffer) { Log.log(Log.DEBUG, jEdit.class, "checkBufferStatus for " + (currentBuffer ? "current buffer: " + view.getBuffer() : "all buffers")); // still need to call the status check even if the option is // off, so that the write protection is updated if it changes // on disk // auto reload changed buffers? boolean autoReload = getBooleanProperty("autoReload"); // the problem with this is that if we have two edit panes // looking at the same buffer and the file is reloaded both // will jump to the same location visit(new SaveCaretInfoVisitor()); Buffer buffer = buffersFirst; int[] states = new int[bufferCount]; int i = 0; boolean notifyFileChanged = false; while(buffer != null) { if(currentBuffer && buffer != view.getBuffer()) { buffer = buffer.next; i++; continue; } states[i] = buffer.checkFileStatus(view); switch(states[i]) { case Buffer.FILE_CHANGED: if(buffer.getAutoReload()) { if(buffer.isDirty()) notifyFileChanged = true; else buffer.load(view,true); } else // no automatic reload even if general setting is true autoReload = false; // don't notify user if "do nothing" was chosen if(buffer.getAutoReloadDialog()) notifyFileChanged = true; break; case Buffer.FILE_DELETED: notifyFileChanged = true; break; } buffer = buffer.next; i++; } if(notifyFileChanged) new FilesChangedDialog(view,states,autoReload); } //}}} //}}} //{{{ View methods //{{{ getInputHandler() method /** * Returns the current input handler (key binding to action mapping) * @see org.gjt.sp.jedit.gui.InputHandler */ public static InputHandler getInputHandler() { return inputHandler; } //}}} /* public static void newViewTest() { long time = System.currentTimeMillis(); for(int i = 0; i < 30; i++) { Buffer b = newFile(null); b.insert(0,"x"); new View(b,null,false); } System.err.println(System.currentTimeMillis() - time); } */ //{{{ newView() methods /** * Creates a new view. * @param view An existing view * @since jEdit 3.2pre2 */ public static View newView(View view) { return newView(view,null,false); } /** * Creates a new view of a buffer. * @param view An existing view * @param buffer The buffer */ public static View newView(View view, Buffer buffer) { return newView(view,buffer,false); } /** * Creates a new view of a buffer. * @param view An existing view * @param buffer The buffer * @param plainView If true, the view will not have dockable windows or * tool bars. * * @since 4.1pre2 */ public static View newView(View view, Buffer buffer, boolean plainView) { View.ViewConfig config; if(view != null && (plainView == view.isPlainView())) { config = view.getViewConfig(); config.x -= 20; config.y += 20; } else { config = new View.ViewConfig(plainView); } return newView(view,buffer,config); } /** * Creates a new view. * @param view An existing view * @param buffer A buffer to display, or null * @param config Encapsulates the view geometry, split configuration * and if the view is a plain view * @since jEdit 4.2pre1 */ public static View newView(View view, Buffer buffer, View.ViewConfig config) { // Mark the perspective as dirty, unless the new view is created // during jEdit startup, by the loading of the perspective. if (isStartupDone()) PerspectiveManager.setPerspectiveDirty(true); try { if(view != null) { view.showWaitCursor(); view.getEditPane().saveCaretInfo(); } View newView = new View(buffer,config); addViewToList(newView); EditBus.send(new ViewUpdate(newView,ViewUpdate.CREATED)); newView.pack(); newView.adjust(view, config); newView.setVisible(true); if(!config.plainView) { int index; synchronized (startupDone) { index = startupDone.size(); startupDone.add(false); } EventQueue.invokeLater(new DockingLayoutSetter( newView, config, index)); } // show tip of the day if(newView == viewsFirst) { newView.getTextArea().requestFocus(); // Don't show the welcome message if jEdit was started // with the -nosettings switch if(settingsDirectory != null && getBooleanProperty("firstTime")) new HelpViewer("welcome.html"); else if(jEdit.getBooleanProperty("tip.show")) new TipOfTheDay(newView); setBooleanProperty("firstTime",false); } else GUIUtilities.requestFocus(newView,newView.getTextArea()); return newView; } finally { if(view != null) view.hideWaitCursor(); } } //}}} //{{{ closeView() method /** * Closes a view. * * jEdit will exit if this was the last open view. */ public static void closeView(View view) { closeView(view,true); } //}}} //{{{ getViews() method /** * Returns an array of all open views. */ public static View[] getViews() { View[] views = new View[viewCount]; View view = viewsFirst; for(int i = 0; i < viewCount; i++) { views[i] = view; view = view.next; } return views; } //}}} //{{{ getViewCount() method /** * Returns the number of open views. */ public static int getViewCount() { return viewCount; } //}}} //{{{ getFirstView() method /** * Returns the first view. */ public static View getFirstView() { return viewsFirst; } //}}} //{{{ getLastView() method /** * Returns the last view. */ public static View getLastView() { return viewsLast; } //}}} //{{{ getActiveView() method /** * Returns the currently focused view. * @since jEdit 4.1pre1 */ public static View getActiveView() { if(activeView == null) { // eg user just closed a view and didn't focus another return viewsFirst; } else return activeView; } //}}} //}}} //{{{ Miscellaneous methods //{{{ relocateSettings() method public static void relocateSettings() { String oldSettingsPath = MiscUtilities.constructPath( System.getProperty("user.home"), ".jedit"); File oldSettingsDir = new File(oldSettingsPath); File newSettingsDir = new File(settingsDirectory); if(oldSettingsDir.exists() && !newSettingsDir.exists()) { Log.log(Log.NOTICE,jEdit.class,"Old settings directory found (HOME/.jedit). Moving to new location ("+newSettingsDir+ ')'); try { oldSettingsDir.renameTo(newSettingsDir); } catch(SecurityException se) { Log.log(Log.ERROR,jEdit.class,se); } } } //}}} //{{{ isStartupDone() method /** * Whether jEdit startup is over. * @since jEdit 4.3pre17 */ public static boolean isStartupDone() { return (! startupDone.contains(false)); } //}}} //{{{ isMainThread() method /** * Returns true if the currently running thread is the main thread. * @since jEdit 4.2pre1 */ public static boolean isMainThread() { return Thread.currentThread() == mainThread; } //}}} //{{{ isBackgroundMode() method /** * Returns true if jEdit was started with the <code>-background</code> * command-line switch. * @since jEdit 4.0pre4 */ public static boolean isBackgroundModeEnabled() { return background; } //}}} //{{{ showMemoryDialog() method /** * Performs garbage collection and displays a dialog box showing * memory status. * @param view The view * @since jEdit 4.0pre1 */ public static void showMemoryDialog(View view) { Runtime rt = Runtime.getRuntime(); long usedBefore = rt.totalMemory() - rt.freeMemory(); System.gc(); long free = rt.freeMemory(); long total = rt.totalMemory(); long used = total - free; int totalKb = (int) (total / 1024); int usedKb = (int) (used / 1024); JProgressBar progress = new JProgressBar(0,totalKb); progress.setValue(usedKb); progress.setStringPainted(true); progress.setString(jEdit.getProperty("memory-status.use", new Object[] { usedKb, totalKb })); Object[] message = new Object[4]; message[0] = getProperty("memory-status.gc", new Object[] { (usedBefore - used) / 1024 }); message[1] = Box.createVerticalStrut(12); message[2] = progress; message[3] = Box.createVerticalStrut(6); JOptionPane.showMessageDialog(view,message, jEdit.getProperty("memory-status.title"), JOptionPane.INFORMATION_MESSAGE); } //}}} //{{{ getJEditHome() method /** * Returns the jEdit install directory. */ public static String getJEditHome() { return jEditHome; } //}}} //{{{ getSettingsDirectory() method /** * Returns the path of the directory where user-specific settings * are stored. This will be <code>null</code> if jEdit was * started with the <code>-nosettings</code> command-line switch; do not * blindly use this method without checking for a <code>null</code> * return value first. <p> * * <b>NOTE</b>: plugins should <b>not</b> use this directory as a base to * store their files. Instead, they should use EditPlugin.getPluginHome(). * @see EditPlugin#getPluginHome() */ public static String getSettingsDirectory() { return settingsDirectory; } //}}} //{{{ getJARCacheDirectory() method /** * Returns the directory where plugin cache files are stored. * @since jEdit 4.2pre1 */ public static String getJARCacheDirectory() { return jarCacheDirectory; } //}}} //{{{ backupSettingsFile() method /** * Backs up the specified file in the settings directory. * You should call this on any settings files your plugin * writes. * @param file The file * @since jEdit 4.0pre1 */ public static void backupSettingsFile(File file) { if(settingsDirectory == null || !file.exists()) return; String backupDir = MiscUtilities.constructPath( settingsDirectory,"settings-backup"); File dir = new File(backupDir); if(!dir.exists()) dir.mkdirs(); // ... sweet. saveBackup() will create backupDir if it // doesn't exist. MiscUtilities.saveBackup(file,5,null,"~",backupDir); } //}}} //{{{ saveSettings() method /** * Saves all user preferences to disk. */ public static void saveSettings() { if(settingsDirectory == null) return; Abbrevs.save(); keymapManager.getKeymap().save(); FavoritesVFS.saveFavorites(); HistoryModel.saveHistory(); Registers.saveRegisters(); SearchAndReplace.save(); BufferHistory.save(); KillRing.getInstance().save(); File file1 = new File(MiscUtilities.constructPath( settingsDirectory,"#properties#save#")); File file2 = new File(MiscUtilities.constructPath( settingsDirectory,"properties")); if(file2.exists() && file2.lastModified() != propsModTime) { Log.log(Log.WARNING,jEdit.class,file2 + " changed" + " on disk; will not save user properties"); } else { backupSettingsFile(file2); OutputStream out = null; try { out = new FileOutputStream(file1); propMgr.saveUserProps(out); } catch(IOException io) { Log.log(Log.ERROR,jEdit.class,io); } finally { IOUtilities.closeQuietly((Closeable)out); } file2.delete(); if (! file1.renameTo(file2)) { Log.log(Log.ERROR,jEdit.class,"Failed to rename \"" + file1 + "\" to the user properties file \"" + file2 + "\"."); } propsModTime = file2.lastModified(); } } //}}} //{{{ exit() method /** * Exits cleanly from jEdit, prompting the user if any unsaved files * should be saved first. * @param view The view from which this exit was called * @param reallyExit If background mode is enabled and this parameter * is true, then jEdit will close all open views instead of exiting * entirely. */ public static void exit(View view, boolean reallyExit) { // Close dialog, view.close() call need a view... if(view == null) view = activeView; // Wait for pending I/O requests TaskManager.instance.waitForIoTasks(); // Create a new EditorExitRequested EditorExitRequested eer = new EditorExitRequested(view); // Send EditorExitRequested EditBus.send(eer); // Check if the ExitRequest has been cancelled // if so, do not proceed anymore in the exiting if (eer.hasBeenExitCancelled()) { Log.log(Log.MESSAGE, jEdit.class, "Exit has been cancelled"); return; } // Even if reallyExit is false, we still exit properly // if background mode is off reallyExit |= !background; PerspectiveManager.savePerspective(false); try { PerspectiveManager.setPerspectiveEnabled(false); // Close all buffers if(!closeAllBuffers(view,reallyExit)) return; } finally { PerspectiveManager.setPerspectiveEnabled(true); } // If we are running in background mode and // reallyExit was not specified, then return here. if(!reallyExit) { // in this case, we can't directly call // view.close(); we have to call closeView() // for all open views view = viewsFirst; while(view != null) { closeView(view,false); view = view.next; } // Save settings in case user kills the backgrounded // jEdit process saveSettings(); } else { // Send EditorExiting EditBus.send(new EditorExiting(null)); // Save view properties here view = viewsFirst; while(view != null) { closeView(view,false); view = view.next; } // Stop autosave timer Autosave.stop(); // Stop server if(server != null) server.stopServer(); // Stop all plugins PluginJAR[] plugins = getPluginJARs(); for (PluginJAR plugin : plugins) removePluginJAR(plugin, true); // Save settings saveSettings(); // Close activity log stream Log.closeStream(); // Byebye... System.exit(0); } } //}}} //{{{ getEditServer() method /** * Returns the edit server instance. You can use this to find out the * port number jEdit is listening on. * @since jEdit 4.2pre10 */ public static EditServer getEditServer() { return server; } //}}} //{{{ visit() method /** * Visit the views, editpanes and textareas * @param visitor the visitor * @since jEdit 4.3pre13 */ public static void visit(JEditVisitor visitor) { View view = jEdit.getFirstView(); while (view != null) { visitor.visit(view); view.visit(visitor); view = view.getNext(); } } //}}} //{{{ getRegisterStatusPrompt() method /** * Returns the status prompt for the given register action. Only * intended to be called from <code>actions.xml</code>. * @since jEdit 4.3pre16 */ public static String getRegisterStatusPrompt(String action) { String registerNameString = Registers.getRegisterNameString(); return jEdit.getProperty("view.status." + action, new String[] {registerNameString == null ? jEdit.getProperty("view.status.no-registers") : registerNameString}); } //}}} //{{{ getKeyMapManager() method public static KeymapManager getKeymapManager() { return keymapManager; } //}}} //{{{ logTime(String) method /** Logs time since startup, for benchmarking */ private static void logTime(String label) { long currentTime = System.currentTimeMillis(); Log.log(Log.DEBUG, jEdit.class, label + ':' + (currentTime - startupTime) + " ms"); } //}}} //}}} Miscellaneous methods fold end //{{{ Package-private members //{{{ updatePosition() method /** * If buffer sorting is enabled, this repositions the buffer. */ static void updatePosition(String oldPath, Buffer buffer) { if((VFSManager.getVFSForPath(oldPath).getCapabilities() & VFS.CASE_INSENSITIVE_CAP) != 0) { oldPath = oldPath.toLowerCase(); } bufferHash.remove(oldPath); String path = buffer.getSymlinkPath(); if((VFSManager.getVFSForPath(path).getCapabilities() & VFS.CASE_INSENSITIVE_CAP) != 0) { path = path.toLowerCase(); } bufferHash.put(path,buffer); if(sortBuffers) { removeBufferFromList(buffer); addBufferToList(buffer); } } //}}} //{{{ loadMode() method /** * Loads an XML-defined edit mode from the specified reader. * @param mode The edit mode */ /* package-private */ static void loadMode(Mode mode) { final String fileName = (String)mode.getProperty("file"); XModeHandler xmh = new XModeHandler(mode.getName()) { @Override public void error(String what, Object subst) { String msg; Object line = "<unknown>"; if(subst == null) msg = jEdit.getProperty("xmode-error." + what); else { msg = jEdit.getProperty("xmode-error." + what, new String[] { subst.toString() }); if(subst instanceof Throwable) Log.log(Log.ERROR,this,subst); if (subst instanceof SAXParseException) { line = ((SAXParseException)subst).getLineNumber(); } } Object[] args = { fileName, line, null, msg }; GUIUtilities.error(null,"xmode-error",args); } @Override public TokenMarker getTokenMarker(String modeName) { Mode mode = getMode(modeName); if(mode == null) return null; else return mode.getTokenMarker(); } }; ModeProvider.instance.loadMode(mode, xmh); } //}}} //{{{ addPluginProps() method static void addPluginProps(Properties map) { propMgr.addPluginProps(map); } //}}} //{{{ removePluginProps() method static void removePluginProps(Properties map) { propMgr.removePluginProps(map); } //}}} //{{{ addPluginLocalizationProps() method static void addPluginLocalizationProps(Properties map) { propMgr.addPluginLocalizationProps(map); } //}}} //{{{ removePluginLocalizationProps() method static void removePluginLocalizationProps(Properties map) { propMgr.removePluginLocalizationProps(map); } //}}} //{{{ pluginError() method /** * @param path * @param messageProp - a property of a message to print * @param args a list of arguments which correspond to {0} and {1} in the string to print. */ static void pluginError(String path, String messageProp, Object[] args) { synchronized(pluginErrorLock) { if(pluginErrors == null) pluginErrors = new Vector<ErrorListDialog.ErrorEntry>(); ErrorListDialog.ErrorEntry newEntry = new ErrorListDialog.ErrorEntry( path,messageProp,args); for (ErrorListDialog.ErrorEntry pluginError : pluginErrors) { if (pluginError.equals(newEntry)) return; } pluginErrors.addElement(newEntry); if(isStartupDone()) { EventQueue.invokeLater(new Runnable() { @Override public void run() { showPluginErrorDialog(); } }); } } } //}}} //{{{ setActiveView() method static void setActiveView(View view) { jEdit.activeView = view; } //}}} //{{{ getActiveViewInternal() method /** * Returns the internal active view, which might be null. * * @since 4.3pre10 */ public static View getActiveViewInternal() { return activeView; } //}}} //}}} //{{{ Private members //{{{ Static variables private static String jEditHome; private static String settingsDirectory; private static String jarCacheDirectory; private static long propsModTime; private static PropertyManager propMgr; private static EditServer server; private static boolean background; private static ActionContext actionContext; private static ActionSet builtInActionSet; private static Vector<ErrorListDialog.ErrorEntry> pluginErrors; private static final Object pluginErrorLock = new Object(); private static Vector<PluginJAR> jars; private static final JEditPropertyManager propertyManager = new JEditPropertyManager(); private static long startupTime = System.currentTimeMillis(); private static boolean saveCaret; private static InputHandler inputHandler; private static KeymapManager keymapManager; private static BufferSetManager bufferSetManager; // buffer link list private static boolean sortBuffers; private static boolean sortByName; private static int bufferCount; private static Buffer buffersFirst; private static Buffer buffersLast; private static Map<String, Buffer> bufferHash; // makes openTemporary() thread-safe private static final Object bufferListLock = new Object(); private static final Object editBusOrderingLock = new Object(); // view link list private static int viewCount; private static View viewsFirst; private static View viewsLast; private static View activeView; private static final List<Boolean> startupDone = new Vector<Boolean>(); private static Vector<String> additionalFiles = new Vector<String>(); private static Thread mainThread; //}}} private jEdit() {} //{{{ usage() method private static void usage() { System.out.println("Usage: jedit [<options>] [<files>]"); System.out.println(" <file> +marker:<marker>: Positions caret" + " at marker <marker>"); System.out.println(" <file> +line:<line>: Positions caret" + " at line number <line>"); System.out.println(" <file> +line:<line>,<column>: Positions caret" + " at line number <line> and column number <column>"); System.out.println(" --: End of options"); System.out.println(" -background: Run in background mode"); System.out.println(" -nobackground: Disable background mode (default)"); System.out.println(" -gui: Only if running in background mode; open initial view (default)"); System.out.println(" -nogui: Only if running in background mode; don't open initial view"); System.out.println(" -log=<level>: Log messages with level equal to or higher than this to"); System.out.println(" standard error. <level> must be between 1 and 9. Default is 7."); System.out.println(" -newplainview: Client instance opens a new plain view"); System.out.println(" -newview: Client instance opens a new view (default)"); System.out.println(" -plugins: Load plugins (default)"); System.out.println(" -noplugins: Don't load any plugins"); System.out.println(" -restore: Restore previously open files (default)"); System.out.println(" -norestore: Don't restore previously open files"); System.out.println(" -reuseview: Client instance reuses existing view"); System.out.println(" -quit: Quit a running instance"); System.out.println(" -run=<script>: Run the specified BeanShell script"); System.out.println(" -server: Read/write server info from/to $HOME/.jedit/server (default)"); System.out.println(" -server=<name>: Read/write server info from/to $HOME/.jedit/<name>"); System.out.println(" -noserver: Don't start edit server"); System.out.println(" -settings=<path>: Load user-specific settings from <path>"); System.out.println(" -nosettings: Don't load user-specific settings"); System.out.println(" -nosplash: Don't show splash screen"); System.out.println(" -startupscripts: Run startup scripts (default)"); System.out.println(" -nostartupscripts: Don't run startup scripts"); System.out.println(" -usage: Print this message and exit"); System.out.println(" -version: Print jEdit version and exit"); System.out.println(" -wait: Wait until the user closes the specified buffer in the server"); System.out.println(" instance. Does nothing if passed to the initial jEdit instance."); System.out.println(); System.out.println("Report bugs to http://sourceforge.net/tracker/?group_id=588&atid=100588"); } //}}} //{{{ version() method private static void version() { System.out.println("jEdit " + getVersion()); } //}}} //{{{ makeServerScript() method /** * Creates a BeanShell script that can be sent to a running edit server. */ private static String makeServerScript(boolean wait, boolean restore, boolean newView, boolean newPlainView, String[] args, String scriptFile) { StringBuilder script = new StringBuilder(); String userDir = System.getProperty("user.dir"); script.append("parent = \""); script.append(StandardUtilities.charsToEscapes(userDir)); script.append("\";\n"); script.append("args = new String["); script.append(args.length); script.append("];\n"); for(int i = 0; i < args.length; i++) { script.append("args["); script.append(i); script.append("] = "); if(args[i] == null) script.append("null"); else { script.append('"'); script.append(StandardUtilities.charsToEscapes(args[i])); script.append('"'); } script.append(";\n"); } script.append("view = jEdit.getLastView();\n"); script.append("buffer = EditServer.handleClient("); script.append(restore).append(',').append(newView).append(',').append(newPlainView); script.append(",parent,args);\n"); script.append("if(buffer != null && ").append(wait).append(") {\n"); script.append("\tbuffer.setWaitSocket(socket);\n"); script.append("\tdoNotCloseSocket = true;\n"); script.append("}\n"); script.append("if(view != jEdit.getLastView() && ").append(wait).append(") {\n"); script.append("\tjEdit.getLastView().setWaitSocket(socket);\n"); script.append("\tdoNotCloseSocket = true;\n"); script.append("}\n"); script.append("if(doNotCloseSocket == void)\n"); script.append("\tsocket.close();\n"); if(scriptFile != null) { scriptFile = MiscUtilities.constructPath(userDir,scriptFile); script.append("BeanShell.runScript(view,\"") .append(StandardUtilities.charsToEscapes(scriptFile)) .append("\",null,this.namespace);\n"); } return script.toString(); } //}}} //{{{ initMisc() method /** * Initialise various objects, register protocol handlers. */ private static void initMisc() { ModeProvider.instance = new ModeProvider() { @Override protected void error(String fileName, Throwable e) { Log.log(Log.ERROR, this, e); if (e instanceof SAXParseException) { String message = e.getMessage(); int line = ((SAXParseException)e).getLineNumber(); int col = ((SAXParseException)e).getColumnNumber(); Object[] args = { fileName, line, col, message }; GUIUtilities.error(null,"xmode-error",args); } } }; jars = new Vector<PluginJAR>(); FoldHandler.foldHandlerProvider = new ServiceManager.ServiceFoldHandlerProvider(); actionContext = new ActionContext() { @Override public void invokeAction(EventObject evt, EditAction action) { View view = GUIUtilities.getView( (Component)evt.getSource()); boolean actionBarVisible; if(view.getActionBar() == null || !view.getActionBar().isShowing()) actionBarVisible = false; else { actionBarVisible = view.getActionBar() .isVisible(); } view.getInputHandler().invokeAction(action); if(actionBarVisible) { // XXX: action bar might not be 'temp' ActionBar actionBar = view .getActionBar(); if(actionBar != null) view.removeToolBar(actionBar); } } }; bufferHash = new HashMap<String, Buffer>(); File userKeymapFolder = null; if (settingsDirectory != null) { userKeymapFolder = new File(settingsDirectory, "keymaps"); } inputHandler = new DefaultInputHandler(null); // Add our protocols to java.net.URL's list System.getProperties().put("java.protocol.handler.pkgs", "org.gjt.sp.jedit.proto|" + System.getProperty("java.protocol.handler.pkgs","")); // Set the User-Agent string used by the java.net HTTP handler String userAgent = "jEdit/" + getVersion() + " (Java " + System.getProperty("java.version") + ". " + System.getProperty("java.vendor") + "; " + System.getProperty("os.arch") + ')'; System.getProperties().put("http.agent",userAgent); /* Determine installation directory. * If the jedit.home property is set, use that. * Then, look for jedit.jar in the classpath. * If that fails, assume this is the web start version. */ jEditHome = System.getProperty("jedit.home"); if(jEditHome == null) { String classpath = System .getProperty("java.class.path"); int index = classpath.toLowerCase() .indexOf("jedit.jar"); int start = classpath.lastIndexOf(File .pathSeparator,index) + 1; // if started with java -jar jedit.jar if(start == index) { jEditHome = System.getProperty("user.dir"); } else if(index > start) { jEditHome = classpath.substring(start, index - 1); } else { // check if web start /* if(jEdit.class.getResource("/modes/catalog") != null) { // modes bundled in; hence web start jEditHome = null; } else */ { // use user.dir as last resort jEditHome = System.getProperty("user.dir"); Log.log(Log.WARNING,jEdit.class,"jedit.jar not in class path!"); Log.log(Log.WARNING,jEdit.class,"Assuming jEdit is installed in " + jEditHome + '.'); Log.log(Log.WARNING,jEdit.class,"Override with jedit.home " + "system property."); } } } jEditHome = MiscUtilities.resolveSymlinks(jEditHome); Log.log(Log.MESSAGE,jEdit.class,"jEdit home directory is " + jEditHome); keymapManager = new KeymapManagerImpl(propertyManager, new File(jEditHome, "keymaps"), userKeymapFolder); if(settingsDirectory != null) { jarCacheDirectory = MiscUtilities.constructPath( settingsDirectory,"jars-cache"); new File(jarCacheDirectory).mkdirs(); } //if(jEditHome == null) // Log.log(Log.DEBUG,jEdit.class,"Web start mode"); // Add an EditBus component that will reload edit modes and // macros if they are changed from within the editor EditBus.addToBus(new SettingsReloader()); // Set the ContextClassLoader for the main jEdit thread. // This way, the ContextClassLoader will be a JARClassLoader // even at plugin activation and the EventQueue can also pick // up the JARClassLoader. That's why this call has to be done // before the usage of the EventQueue. Thread.currentThread().setContextClassLoader(new JARClassLoader()); // Perhaps if Xerces wasn't slightly brain-damaged, we would // not need this EventQueue.invokeLater(new Runnable() { @Override public void run() { Thread.currentThread().setContextClassLoader( new JARClassLoader()); } }); } //}}} //{{{ getResourceAsUTF8Text() method private static Reader getResourceAsUTF8Text(String name) throws IOException { InputStream bytes = jEdit.class.getResourceAsStream(name); if (bytes == null) { return null; } Reader text = null; try { // Using our CharsetEncoding to reliably detect // encoding errors. CharsetEncoding utf8 = new CharsetEncoding("UTF-8"); text = utf8.getTextReader(bytes); } finally { if (text == null) { bytes.close(); } } return text; } //}}} //{{{ initSystemProperties() method /** * Load system properties. */ private static void initSystemProperties() { propMgr = new PropertyManager(); try { propMgr.loadSystemProps(getResourceAsUTF8Text( "/org/gjt/sp/jedit/jedit.props")); propMgr.loadSystemProps(getResourceAsUTF8Text( "/org/gjt/sp/jedit/jedit_gui.props")); propMgr.loadSystemProps(getResourceAsUTF8Text( "/org/jedit/localization/jedit_en.props")); } catch(Exception e) { Log.log(Log.ERROR,jEdit.class, "Error while loading system properties!"); Log.log(Log.ERROR,jEdit.class, "One of the following property files could not be loaded:\n" + "- jedit.props\n" + "- jedit_gui.props\n" + "- jedit_en.props\n" + "jedit.jar is probably corrupt."); Log.log(Log.ERROR,jEdit.class,e); System.exit(1); } } //}}} //{{{ initSiteProperties() method /** * Load site properties. */ private static void initSiteProperties() { // site properties are loaded as default properties, overwriting // jEdit's system properties String siteSettingsDirectory = MiscUtilities.constructPath( jEditHome, "properties"); File siteSettings = new File(siteSettingsDirectory); if (!(siteSettings.exists() && siteSettings.isDirectory())) return; String[] snippets = siteSettings.list(); if (snippets == null) return; Arrays.sort(snippets, new StandardUtilities.StringCompare<String>(true)); for (String snippet : snippets) { if (!snippet.toLowerCase().endsWith(".props")) continue; try { String path = MiscUtilities.constructPath(siteSettingsDirectory, snippet); Log.log(Log.DEBUG, jEdit.class, "Loading site snippet: " + path); propMgr.loadSiteProps(new FileInputStream(new File(path))); } catch (FileNotFoundException fnf) { Log.log(Log.DEBUG, jEdit.class, fnf); } catch (IOException e) { Log.log(Log.ERROR, jEdit.class, "Cannot load site snippet " + snippet); Log.log(Log.ERROR, jEdit.class, e); } } } //}}} //{{{ initResources() method private static void initResources() { builtInActionSet = new ActionSet(null,null,null, jEdit.class.getResource("actions.xml")); builtInActionSet.setLabel(getProperty("action-set.jEdit")); builtInActionSet.load(); actionContext.addActionSet(builtInActionSet); DockableWindowFactory.getInstance() .loadDockableWindows(null, jEdit.class.getResource("dockables.xml"), null); ServiceManager.loadServices(null, jEdit.class.getResource("services.xml"), null); } //}}} //{{{ initPlugins() method /** * Loads plugins. */ private static void initPlugins() { if(jEditHome != null) { addPluginJARsFromDirectory(MiscUtilities.constructPath( jEditHome,"jars")); } if(settingsDirectory != null) { File jarsDirectory = new File(settingsDirectory,"jars"); if(!jarsDirectory.exists()) jarsDirectory.mkdir(); addPluginJARsFromDirectory(jarsDirectory.getPath()); } PluginJAR[] jars = getPluginJARs(); for (PluginJAR jar : jars) jar.checkDependencies(); } //}}} //{{{ initUserProperties() method /** * Loads user properties. */ private static void initUserProperties() { if(settingsDirectory != null) { File file = new File(MiscUtilities.constructPath( settingsDirectory,"properties")); propsModTime = file.lastModified(); try { propMgr.loadUserProps( new FileInputStream(file)); } catch(FileNotFoundException fnf) { //Log.log(Log.DEBUG,jEdit.class,fnf); } catch(Exception e) { Log.log(Log.ERROR,jEdit.class,e); } } } //}}} //{{{ initLocalizationProperties() method /** * Loads localization property file(s). */ private static void initLocalizationProperties() { String language = getCurrentLanguage(); if ("en".equals(language)) { // no need to load english as localization property as it always loaded as default language return; } Reader langResource = null; try { langResource = getResourceAsUTF8Text("/org/jedit/localization/jedit_" + language + ".props"); propMgr.loadLocalizationProps(langResource); } catch (IOException e) { if (getBooleanProperty("lang.usedefaultlocale")) { // if it is the default locale, it is not an error Log.log(Log.ERROR, jEdit.class, "Unable to load language", e); } } finally { IOUtilities.closeQuietly((Closeable)langResource); } } //}}} //{{{ fontStyleToString() method private static String fontStyleToString(int style) { if(style == 0) return "PLAIN"; else if(style == Font.BOLD) return "BOLD"; else if(style == Font.ITALIC) return "ITALIC"; else if(style == (Font.BOLD | Font.ITALIC)) return "BOLDITALIC"; else throw new RuntimeException("Invalid style: " + style); } //}}} //{{{ fontToString() method private static String fontToString(Font font) { return font.getFamily() + '-' + fontStyleToString(font.getStyle()) + '-' + font.getSize(); } //}}} //{{{ initPLAF() method /** * Sets the Swing look and feel. */ private static void initPLAF() { String lf = getProperty("lookAndFeel"); final String sLf = getPLAFClassName(lf); String sLfOld = null; String sLfNew = null; LookAndFeel lfOld = UIManager.getLookAndFeel(); if (lfOld != null) sLfOld = lfOld.getClass().getName(); // do not change anything if Look and Feel did not change if (isStartupDone() && sLf.equals(sLfOld)) { return; } Font primaryFont = jEdit.getFontProperty( "metal.primary.font"); if(primaryFont != null) { String primaryFontString = fontToString(primaryFont); System.getProperties().put( "swing.plaf.metal.controlFont", primaryFontString); System.getProperties().put( "swing.plaf.metal.menuFont", primaryFontString); } Font secondaryFont = jEdit.getFontProperty( "metal.secondary.font"); if(secondaryFont != null) { String secondaryFontString = fontToString(secondaryFont); System.getProperties().put( "swing.plaf.metal.systemFont", secondaryFontString); System.getProperties().put( "swing.plaf.metal.userFont", secondaryFontString); } // Though the cause is not known, this must precede // UIManager.setLookAndFeel(), so that menu bar // interaction by ALT key interacts with swing.JMenuBar // (which uses L&F) instead of awt.MenuBar which we // don't use (and doesn't use L&F). // The difference of the behavior was seen on Sun JRE // 6u16 on Windows XP and Windows L&F. KeyboardFocusManager.setCurrentKeyboardFocusManager( new MyFocusManager()); // A couple of issues here -- (these are fixed) // First, setLookAndFeel must be called on the EDT. On initial start // up this isn't a problem, but initPLAF is called on propertiesChanged, // which can happen a lot. // Second, this will fail to load the look and feel as set in the // LookAndFeel plugin on initial start up because the plugins haven't // been loaded yet. if (EventQueue.isDispatchThread()) { try { UIManager.setLookAndFeel(sLf); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { // ignored, there really isn't anything to do and this may be // bogus, the lnf may be from the Look And Feel plugin } } else { try { EventQueue.invokeAndWait( new Runnable() { public void run() { try { UIManager.setLookAndFeel(sLf); } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | UnsupportedLookAndFeelException e) { // same as above, there really isn't anything to do and this may be // bogus, the lnf may be from the Look And Feel plugin } } } ); } catch (InterruptedException | InvocationTargetException e) { // don't worry about this one either } } LookAndFeel lfNew = UIManager.getLookAndFeel(); if (lfNew != null) sLfNew = lfNew.getClass().getName(); Log.log(Log.DEBUG, jEdit.class, "initPLAF " + (EventQueue.isDispatchThread() ? "edt" : "non-edt") + " old=" + sLfOld + " requested=" + lf + " new=" + sLfNew ); if (!sLf.equals(sLfNew)) Log.log(Log.WARNING, jEdit.class, "initPLAF failed to set requested l&f " + lf); UIDefaults defaults = UIManager.getDefaults(); // give all Swing components our colors if(jEdit.getBooleanProperty("textColors")) { Color background = new javax.swing.plaf.ColorUIResource( jEdit.getColorProperty("view.bgColor")); Color foreground = new javax.swing.plaf.ColorUIResource( jEdit.getColorProperty("view.fgColor")); Color caretColor = new javax.swing.plaf.ColorUIResource( jEdit.getColorProperty("view.caretColor")); Color selectionColor = new javax.swing.plaf.ColorUIResource( jEdit.getColorProperty("view.selectionColor")); String[] prefixes = { "PasswordField", "TextField", "TextArea", "List", "Table" }; for (String prefix : prefixes) { defaults.put(prefix + ".foreground", foreground); defaults.put(prefix + ".background", background); defaults.put(prefix + ".disabledForeground", foreground); defaults.put(prefix + ".disabledBackground", background); defaults.put(prefix + ".caretForeground", caretColor); defaults.put(prefix + ".selectionForeground", foreground); defaults.put(prefix + ".selectionBackground", selectionColor); } defaults.put("ComboBox.foreground",foreground); defaults.put("ComboBox.background",background); defaults.put("ComboBox.disabledForeground",foreground); defaults.put("ComboBox.disabledBackground",background); defaults.put("ComboBox.selectedForeground",foreground); defaults.put("ComboBox.selectedBackground",selectionColor); defaults.put("Tree.background",background); defaults.put("Tree.foreground",foreground); defaults.put("Tree.textBackground",background); defaults.put("Tree.textForeground",foreground); defaults.put("Tree.selectionForeground",foreground); defaults.put("Tree.selectionBackground",selectionColor); } defaults.remove("SplitPane.border"); defaults.remove("SplitPaneDivider.border"); JFrame.setDefaultLookAndFeelDecorated( getBooleanProperty("decorate.frames")); JDialog.setDefaultLookAndFeelDecorated( getBooleanProperty("decorate.dialogs")); if (isStartupDone()) { int iWindow = 0; for (Window window : Window.getWindows()) { try { SwingUtilities.updateComponentTreeUI(window); } catch(Exception e) { Log.log(Log.ERROR, jEdit.class, "Window " + iWindow + ": " + window, e); break; } iWindow++; } } } //}}} @Nonnull private static String getPLAFClassName(@Nullable String lf) { if (lf != null && lf.length() != 0) { return lf; } else if(OperatingSystem.isMacOS()) { return UIManager.getSystemLookAndFeelClassName(); } else { return UIManager.getCrossPlatformLookAndFeelClassName(); } } //{{{ getNextUntitledBufferId() method public static int getNextUntitledBufferId() { int untitledCount = 0; Buffer buffer = buffersFirst; while(buffer != null) { if(buffer.getName().startsWith("Untitled-")) { try { untitledCount = Math.max(untitledCount, Integer.parseInt(buffer.getName() .substring(9))); } catch(NumberFormatException nf) { } } buffer = buffer.next; } return untitledCount + 1; } //}}} //{{{ runStartupScripts() method /** * Runs scripts in a directory. */ private static void runStartupScripts(File directory) { if (!directory.isDirectory()) return; File[] snippets = directory.listFiles(); if (snippets == null) return; Arrays.sort(snippets, new StandardUtilities.StringCompare<File>(true)); /* * Force the default encoding to UTF-8 temporarily. * The shipped scripts use that encoding, so we need * to make sure we can load them correctly. If users * want to write script with a different encoding, * they can use buffer-local properties on the * script to set it. */ String defaultEncoding = getProperty("buffer.encoding"); setProperty("buffer.encoding", "UTF-8"); for (File snippet : snippets) { Macros.Handler handler = Macros.getHandlerForPathName(snippet.getPath()); if (handler == null) continue; try { Macros.Macro newMacro = handler.createMacro(snippet.getName(), snippet.getPath()); handler.runMacro(null, newMacro, false); } catch (Exception e) { Log.log(Log.ERROR, jEdit.class, e); } } setProperty("buffer.encoding", defaultEncoding); } //}}} //{{{ initProxy() method private static void initProxy() { boolean socksEnabled = jEdit.getBooleanProperty("firewall.socks.enabled"); if(!socksEnabled) { Log.log(Log.DEBUG,jEdit.class,"SOCKS proxy disabled"); System.getProperties().remove("socksProxyHost"); System.getProperties().remove("socksProxyPort"); } else { String socksHost = jEdit.getProperty("firewall.socks.host"); if( socksHost != null ) { System.setProperty("socksProxyHost", socksHost); Log.log(Log.DEBUG, jEdit.class, "SOCKS proxy enabled: " + socksHost); } String socksPort = jEdit.getProperty("firewall.socks.port"); if(socksPort != null) System.setProperty("socksProxyPort", socksPort); } boolean httpEnabled = jEdit.getBooleanProperty("firewall.enabled"); if (!httpEnabled) { Log.log(Log.DEBUG, jEdit.class, "HTTP proxy disabled"); System.getProperties().remove("proxySet"); System.getProperties().remove("proxyHost"); System.getProperties().remove("proxyPort"); System.getProperties().remove("http.proxyHost"); System.getProperties().remove("http.proxyPort"); System.getProperties().remove("http.nonProxyHosts"); Authenticator.setDefault(null); } else { // set proxy host String host = jEdit.getProperty("firewall.host"); if (host == null) return; System.setProperty("http.proxyHost", host); Log.log(Log.DEBUG, jEdit.class, "HTTP proxy enabled: " + host); // set proxy port String port = jEdit.getProperty("firewall.port"); if (port != null) System.setProperty("http.proxyPort", port); // set non proxy hosts list String nonProxyHosts = jEdit.getProperty("firewall.nonProxyHosts"); if (nonProxyHosts != null) System.setProperty("http.nonProxyHosts", nonProxyHosts); // set proxy authentication String username = jEdit.getProperty("firewall.user"); String password = jEdit.getProperty("firewall.password"); // null not supported? if(password == null) password = ""; if(username == null || username.length()==0) { Log.log(Log.DEBUG, jEdit.class, "HTTP proxy without user"); Authenticator.setDefault(new FirewallAuthenticator(null)); } else { Log.log(Log.DEBUG, jEdit.class, "HTTP proxy user: " + username); PasswordAuthentication pw = new PasswordAuthentication( username,password.toCharArray() ); Authenticator.setDefault(new FirewallAuthenticator(pw)); } } } //}}} //{{{ FirewallAuthenticator class static class FirewallAuthenticator extends Authenticator { PasswordAuthentication pw; FirewallAuthenticator(PasswordAuthentication pw) { this.pw = pw; } @Override protected PasswordAuthentication getPasswordAuthentication() { return pw; } } //}}} //{{{ finishStartup() method private static void finishStartup(final boolean gui, final boolean restore, final boolean newPlainView, final String userDir, final String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { int count = getBufferCount(); boolean restoreFiles = restore && jEdit.getBooleanProperty("restore") && (count == 0 || jEdit.getBooleanProperty("restore.cli")); if(gui || count != 0) { View view; if (newPlainView) view = newView(null,null,true); else view = PerspectiveManager.loadPerspective(restoreFiles); if(view == null) view = newView(null,null); Buffer buffer; // Treat the elements of additionalFiles just like command-line arguments if (!additionalFiles.isEmpty()) { String[] newArgs = new String[additionalFiles.size() + args.length]; additionalFiles.copyInto(newArgs); System.arraycopy(args, 0, newArgs, additionalFiles.size(), args.length); buffer = openFiles(null,userDir,newArgs); } else { buffer = openFiles(null,userDir,args); } if(buffer != null) view.setBuffer(buffer); view.toFront(); } else { openFiles(null,userDir,args); } // Start I/O threads EditBus.send(new EditorStarted(null)); VFSManager.start(); // Start edit server if(server != null) server.start(); GUIUtilities.hideSplashScreen(); Log.log(Log.MESSAGE,jEdit.class,"Startup " + "complete: " + (System.currentTimeMillis() - startupTime) + " ms"); //{{{ Report any plugin errors if(pluginErrors != null) { showPluginErrorDialog(); } //}}} startupDone.set(0, true); // in one case not a single AWT class will // have been touched (splash screen off + // -nogui -nobackground switches on command // line) Toolkit.getDefaultToolkit(); } }); } //}}} //{{{ showPluginErrorDialog() method private static void showPluginErrorDialog() { if(pluginErrors == null) return; String caption = getProperty( "plugin-error.caption" + (pluginErrors.size() == 1 ? "-1" : "")); Frame frame = (PluginManager.getInstance() == null ? viewsFirst : PluginManager.getInstance()); new ErrorListDialog(frame, getProperty("plugin-error.title"), caption,pluginErrors,true); pluginErrors = null; } //}}} //{{{ getNotLoadedPluginJARs() method private static void getNotLoadedPluginJARs(Collection<String> returnValue, String dir, String[] list) { loop: for (String name : list) { if (!name.toLowerCase().endsWith(".jar")) continue loop; String path = MiscUtilities.constructPath(dir, name); for (int j = 0; j < jars.size(); j++) { PluginJAR jar = jars.elementAt(j); String jarPath = jar.getPath(); if (path.equals(jarPath) || name.equals(MiscUtilities.getFileName(jarPath)) && !new File(jarPath).exists()) continue loop; } returnValue.add(path); } } //}}} //{{{ gotoMarker() method private static void gotoMarker(final View view, final Buffer buffer, final String marker) { AwtRunnableQueue.INSTANCE.runAfterIoTasks(new Runnable() { @Override public void run() { int pos; // Handle line number if(marker.startsWith("+line:")) { try { String arg = marker.substring(6); String[] lineCol = arg.split(","); int line, col; if(lineCol.length > 1) { line = Integer.parseInt(lineCol[0]); col = Integer.parseInt(lineCol[1]); } else { line = Integer.parseInt(marker.substring(6)); col = 1; } pos = buffer.getLineStartOffset(line - 1) + (col - 1); } catch(Exception e) { return; } } // Handle marker else if(marker.startsWith("+marker:")) { if(marker.length() != 9) return; Marker m = buffer.getMarker(marker.charAt(8)); if(m == null) return; pos = m.getPosition(); } // Can't happen else throw new InternalError(); if(view != null && view.getBuffer() == buffer) { view.getTextArea().setCaretPosition(pos); buffer.setIntegerProperty(Buffer.CARET,pos); buffer.setBooleanProperty(Buffer.CARET_POSITIONED,true); } else { buffer.setIntegerProperty(Buffer.CARET,pos); buffer.setBooleanProperty(Buffer.CARET_POSITIONED,true); buffer.unsetProperty(Buffer.SCROLL_VERT); } } }); } //}}} //{{{ addBufferToList() method private static void addBufferToList(Buffer buffer) { synchronized(bufferListLock) { String symlinkPath = buffer.getSymlinkPath(); if((VFSManager.getVFSForPath(symlinkPath).getCapabilities() & VFS.CASE_INSENSITIVE_CAP) != 0) { symlinkPath = symlinkPath.toLowerCase(); } bufferCount++; bufferHash.put(symlinkPath,buffer); if(buffersFirst == null) { buffersFirst = buffersLast = buffer; return; } //{{{ Sort buffer list else if(sortBuffers) { String str11, str12; if(sortByName) { str11 = buffer.getName(); str12 = buffer.getDirectory(); } else { str11 = buffer.getDirectory(); str12 = buffer.getName(); } Buffer _buffer = buffersFirst; while(_buffer != null) { String str21, str22; if(sortByName) { str21 = _buffer.getName(); str22 = _buffer.getDirectory(); } else { str21 = _buffer.getDirectory(); str22 = _buffer.getName(); } int comp = StandardUtilities.compareStrings(str11,str21,true); if(comp < 0 || (comp == 0 && StandardUtilities.compareStrings(str12,str22,true) < 0)) { buffer.next = _buffer; buffer.prev = _buffer.prev; _buffer.prev = buffer; if(_buffer != buffersFirst) buffer.prev.next = buffer; else buffersFirst = buffer; return; } _buffer = _buffer.next; } } //}}} buffer.prev = buffersLast; // fixes the hang that can occur if we 'save as' to a // new filename which requires re-sorting buffer.next = null; buffersLast.next = buffer; buffersLast = buffer; } } //}}} //{{{ removeBufferFromList() method private static void removeBufferFromList(Buffer buffer) { synchronized(bufferListLock) { bufferCount--; String path = buffer.getPath(); if(OperatingSystem.isCaseInsensitiveFS()) path = path.toLowerCase(); bufferHash.remove(path); if(buffer == buffersFirst && buffer == buffersLast) { buffersFirst = buffersLast = null; return; } if(buffer == buffersFirst) { buffersFirst = buffer.next; buffer.next.prev = null; } else { if (buffer.prev != null) buffer.prev.next = buffer.next; } if(buffer == buffersLast) { buffersLast = buffersLast.prev; buffer.prev.next = null; } else { if (buffer.next != null) buffer.next.prev = buffer.prev; } // fixes the hang that can occur if we 'save as' to a new // filename which requires re-sorting buffer.next = buffer.prev = null; } } //}}} //{{{ addViewToList() method private static void addViewToList(View view) { viewCount++; if(viewsFirst == null) viewsFirst = viewsLast = view; else { view.prev = viewsLast; viewsLast.next = view; viewsLast = view; } } //}}} //{{{ removeViewFromList() method private static void removeViewFromList(View view) { viewCount--; if(viewsFirst == viewsLast) { viewsFirst = viewsLast = null; return; } if(view == viewsFirst) { viewsFirst = view.next; view.next.prev = null; } else { view.prev.next = view.next; } if(view == viewsLast) { viewsLast = viewsLast.prev; view.prev.next = null; } else { view.next.prev = view.prev; } } //}}} //{{{ closeView() method /** * closeView() used by exit(). */ private static boolean closeView(View view, boolean callExit) { PerspectiveManager.setPerspectiveDirty(true); if(viewsFirst == viewsLast && callExit) { exit(view,false); /* exit does editor event & save */ // Coming here means the request has been canceled. return false; } else { if (!view.confirmToCloseDirty()) return false; view.close(); view.dispose(); removeViewFromList(view); if(view == activeView) activeView = null; return true; } } //}}} //{{{ loadModeCatalog() method /** * Loads a mode catalog file. * @since jEdit 3.2pre2 */ private static void loadModeCatalog(String path, boolean resource) { Log.log(Log.MESSAGE,jEdit.class,"Loading mode catalog file " + path); ModeCatalogHandler handler = new ModeCatalogHandler( MiscUtilities.getParentOfPath(path),resource) { @Override protected Mode instantiateMode(String modeName) { return new JEditMode(modeName); } }; try { InputStream _in; if(resource) _in = jEdit.class.getResourceAsStream(path); else _in = new FileInputStream(path); XMLUtilities.parseXML(_in, handler); } catch(IOException e) { Log.log(Log.ERROR,jEdit.class,e); } } //}}} //{{{ initKeyBindings() method /** * Loads all key bindings from the properties. * * @since 3.1pre1 */ private static void initKeyBindings() { inputHandler.removeAllKeyBindings(); ActionSet[] actionSets = getActionSets(); for (ActionSet actionSet : actionSets) actionSet.initKeyBindings(); } //}}} //{{{ composeBufferPropsFromHistory() method /** * Compose buffer-local properties which can be got from history. * @since 4.3pre10 */ private static void composeBufferPropsFromHistory(Map<String, Object> props, String path) { BufferHistory.Entry entry = BufferHistory.getEntry(path); if(entry != null && saveCaret && props.get(Buffer.CARET) == null) { props.put(Buffer.CARET, entry.caret); if(entry.selection != null) { // getSelection() converts from string to // Selection[] props.put(Buffer.SELECTION,entry.getSelection()); } } if(entry != null && props.get(JEditBuffer.ENCODING) == null) { if(entry.encoding != null) props.put(JEditBuffer.ENCODING,entry.encoding); } if (entry != null && props.get("mode") == null) { if (entry.mode != null) props.put("mode", entry.mode); } } //}}} //}}} //{{{ MyFocusManager class private static class MyFocusManager extends DefaultKeyboardFocusManager { MyFocusManager() { setDefaultFocusTraversalPolicy(new LayoutFocusTraversalPolicy()); } @Override public boolean postProcessKeyEvent(KeyEvent evt) { if(!evt.isConsumed()) { Component comp = (Component)evt.getSource(); if(!comp.isShowing()) return true; for(;;) { if(comp instanceof View) { ((View)comp).getInputHandler().processKeyEvent(evt, View.VIEW, false); return true; } else if(comp == null || comp instanceof Window || comp instanceof JEditTextArea) { if (comp instanceof PluginManager) { evt.setSource(comp); ((PluginManager)comp).processKeyEvents(evt); } break; } else comp = comp.getParent(); } } return super.postProcessKeyEvent(evt); } } //}}} //{{{ JEditPropertyManager class public static class JEditPropertyManager implements IPropertyManager { @Override public String getProperty(String name) { return jEdit.getProperty(name); } } //}}} //{{{ DockingLayoutSetter class private static class DockingLayoutSetter implements Runnable { private final View view; private final ViewConfig config; private final int startupDoneIndex; DockingLayoutSetter(View view, ViewConfig config, int startupDoneIndex) { this.view = view; this.config = config; this.startupDoneIndex = startupDoneIndex; } @Override public void run() { DockableWindowManager wm = view.getDockableWindowManager(); wm.setDockingLayout(config.docking); startupDone.set(startupDoneIndex, true); } } //}}} }