PageRenderTime 123ms CodeModel.GetById 16ms app.highlight 89ms RepoModel.GetById 1ms app.codeStats 1ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/jEdit.java

#
Java | 2748 lines | 1624 code | 333 blank | 791 comment | 377 complexity | b9734d3831fe104fde059ae39542be5a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 * jEdit.java - Main class of the jEdit editor
   3 * :tabSize=8:indentSize=8:noTabs=false:
   4 * :folding=explicit:collapseFolds=1:
   5 *
   6 * Copyright (C) 1998, 2004 Slava Pestov
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License
  10 * as published by the Free Software Foundation; either version 2
  11 * of the License, or any later version.
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  20 */
  21
  22package org.gjt.sp.jedit;
  23
  24//{{{ Imports
  25import bsh.UtilEvalError;
  26import com.microstar.xml.*;
  27import javax.swing.plaf.metal.*;
  28import javax.swing.*;
  29import java.awt.*;
  30import java.io.*;
  31import java.net.*;
  32import java.text.MessageFormat;
  33import java.util.*;
  34import org.gjt.sp.jedit.buffer.BufferIORequest;
  35import org.gjt.sp.jedit.buffer.KillRing;
  36import org.gjt.sp.jedit.msg.*;
  37import org.gjt.sp.jedit.gui.*;
  38import org.gjt.sp.jedit.help.HelpViewer;
  39import org.gjt.sp.jedit.io.*;
  40import org.gjt.sp.jedit.pluginmgr.PluginManager;
  41import org.gjt.sp.jedit.search.SearchAndReplace;
  42import org.gjt.sp.jedit.syntax.*;
  43import org.gjt.sp.jedit.textarea.*;
  44import org.gjt.sp.util.Log;
  45//}}}
  46
  47/**
  48 * The main class of the jEdit text editor.
  49 * @author Slava Pestov
  50 * @version $Id: jEdit.java 5053 2004-05-29 01:55:26Z spestov $
  51 */
  52public class jEdit
  53{
  54	//{{{ getVersion() method
  55	/**
  56	 * Returns the jEdit version as a human-readable string.
  57	 */
  58	public static String getVersion()
  59	{
  60		return MiscUtilities.buildToVersion(getBuild());
  61	} //}}}
  62
  63	//{{{ getBuild() method
  64	/**
  65	 * Returns the internal version. MiscUtilities.compareStrings() can be used
  66	 * to compare different internal versions.
  67	 */
  68	public static String getBuild()
  69	{
  70		// (major).(minor).(<99 = preX, 99 = final).(bug fix)
  71		return "04.02.14.00";
  72	} //}}}
  73
  74	//{{{ main() method
  75	/**
  76	 * The main method of the jEdit application.
  77	 * This should never be invoked directly.
  78	 * @param args The command line arguments
  79	 */
  80	public static void main(String[] args)
  81	{
  82		//{{{ Check for Java 1.3 or later
  83		String javaVersion = System.getProperty("java.version");
  84		if(javaVersion.compareTo("1.3") < 0)
  85		{
  86			System.err.println("You are running Java version "
  87				+ javaVersion + ".");
  88			System.err.println("jEdit requires Java 1.3 or later.");
  89			System.exit(1);
  90		} //}}}
  91
  92		// later on we need to know if certain code is called from
  93		// the main thread
  94		mainThread = Thread.currentThread();
  95
  96		settingsDirectory = ".jedit";
  97
  98		// MacOS users expect the app to keep running after all windows
  99		// are closed
 100		background = OperatingSystem.isMacOS();
 101
 102		//{{{ Parse command line
 103		boolean endOpts = false;
 104		int level = Log.WARNING;
 105		String portFile = "server";
 106		boolean restore = true;
 107		boolean newView = true;
 108		boolean newPlainView = false;
 109		boolean gui = true; // open initial view?
 110		boolean loadPlugins = true;
 111		boolean runStartupScripts = true;
 112		boolean quit = false;
 113		boolean wait = false;
 114		String userDir = System.getProperty("user.dir");
 115
 116		// script to run
 117		String scriptFile = null;
 118
 119		for(int i = 0; i < args.length; i++)
 120		{
 121			String arg = args[i];
 122			if(arg == null)
 123				continue;
 124			else if(arg.length() == 0)
 125				args[i] = null;
 126			else if(arg.startsWith("-") && !endOpts)
 127			{
 128				if(arg.equals("--"))
 129					endOpts = true;
 130				else if(arg.equals("-usage"))
 131				{
 132					version();
 133					System.err.println();
 134					usage();
 135					System.exit(1);
 136				}
 137				else if(arg.equals("-version"))
 138				{
 139					version();
 140					System.exit(1);
 141				}
 142				else if(arg.startsWith("-log="))
 143				{
 144					try
 145					{
 146						level = Integer.parseInt(arg.substring("-log=".length()));
 147					}
 148					catch(NumberFormatException nf)
 149					{
 150						System.err.println("Malformed option: " + arg);
 151					}
 152				}
 153				else if(arg.equals("-nosettings"))
 154					settingsDirectory = null;
 155				else if(arg.startsWith("-settings="))
 156					settingsDirectory = arg.substring(10);
 157				else if(arg.startsWith("-noserver"))
 158					portFile = null;
 159				else if(arg.equals("-server"))
 160					portFile = "server";
 161				else if(arg.startsWith("-server="))
 162					portFile = arg.substring(8);
 163				else if(arg.startsWith("-background"))
 164					background = true;
 165				else if(arg.startsWith("-nobackground"))
 166					background = false;
 167				else if(arg.equals("-gui"))
 168					gui = true;
 169				else if(arg.equals("-nogui"))
 170					gui = false;
 171				else if(arg.equals("-newview"))
 172					newView = true;
 173				else if(arg.equals("-newplainview"))
 174					newPlainView = true;
 175				else if(arg.equals("-reuseview"))
 176					newPlainView = newView = false;
 177				else if(arg.equals("-restore"))
 178					restore = true;
 179				else if(arg.equals("-norestore"))
 180					restore = false;
 181				else if(arg.equals("-plugins"))
 182					loadPlugins = true;
 183				else if(arg.equals("-noplugins"))
 184					loadPlugins = false;
 185				else if(arg.equals("-startupscripts"))
 186					runStartupScripts = true;
 187				else if(arg.equals("-nostartupscripts"))
 188					runStartupScripts = false;
 189				else if(arg.startsWith("-run="))
 190					scriptFile = arg.substring(5);
 191				else if(arg.equals("-wait"))
 192					wait = true;
 193				else if(arg.equals("-quit"))
 194					quit = true;
 195				else
 196				{
 197					System.err.println("Unknown option: "
 198						+ arg);
 199					usage();
 200					System.exit(1);
 201				}
 202				args[i] = null;
 203			}
 204		} //}}}
 205
 206		//{{{ We need these initializations very early on
 207		if(settingsDirectory != null)
 208		{
 209			settingsDirectory = MiscUtilities.constructPath(
 210				System.getProperty("user.home"),
 211				settingsDirectory);
 212			settingsDirectory = MiscUtilities.resolveSymlinks(
 213				settingsDirectory);
 214		}
 215
 216		if(settingsDirectory != null && portFile != null)
 217			portFile = MiscUtilities.constructPath(settingsDirectory,portFile);
 218		else
 219			portFile = null;
 220
 221		Log.init(true,level);
 222		//}}}
 223
 224		//{{{ Try connecting to another running jEdit instance
 225		if(portFile != null && new File(portFile).exists())
 226		{
 227			int port, key;
 228			try
 229			{
 230				BufferedReader in = new BufferedReader(new FileReader(portFile));
 231				String check = in.readLine();
 232				if(!check.equals("b"))
 233					throw new Exception("Wrong port file format");
 234
 235				port = Integer.parseInt(in.readLine());
 236				key = Integer.parseInt(in.readLine());
 237
 238				Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),port);
 239				DataOutputStream out = new DataOutputStream(
 240					socket.getOutputStream());
 241				out.writeInt(key);
 242
 243				String script;
 244				if(quit)
 245				{
 246					script = "socket.close();\n"
 247						+ "jEdit.exit(null,true);\n";
 248				}
 249				else
 250				{
 251					script = makeServerScript(wait,restore,
 252						newView,newPlainView,args,
 253						scriptFile);
 254				}
 255
 256				out.writeUTF(script);
 257
 258				Log.log(Log.DEBUG,jEdit.class,"Waiting for server");
 259				// block until its closed
 260				try
 261				{
 262					socket.getInputStream().read();
 263				}
 264				catch(Exception e)
 265				{
 266				}
 267
 268				in.close();
 269				out.close();
 270
 271				System.exit(0);
 272			}
 273			catch(Exception e)
 274			{
 275				// ok, this one seems to confuse newbies
 276				// endlessly, so log it as NOTICE, not
 277				// ERROR
 278				Log.log(Log.NOTICE,jEdit.class,"An error occurred"
 279					+ " while connecting to the jEdit server instance.");
 280				Log.log(Log.NOTICE,jEdit.class,"This probably means that"
 281					+ " jEdit crashed and/or exited abnormally");
 282				Log.log(Log.NOTICE,jEdit.class,"the last time it was run.");
 283				Log.log(Log.NOTICE,jEdit.class,"If you don't"
 284					+ " know what this means, don't worry.");
 285				Log.log(Log.NOTICE,jEdit.class,e);
 286			}
 287		}
 288
 289		if(quit)
 290		{
 291			// if no server running and user runs jedit -quit,
 292			// just exit
 293			System.exit(0);
 294		} //}}}
 295
 296		// don't show splash screen if there is a file named
 297		// 'nosplash' in the settings directory
 298		if(!new File(settingsDirectory,"nosplash").exists())
 299			GUIUtilities.showSplashScreen();
 300
 301		//{{{ Initialize settings directory
 302		Writer stream;
 303		if(settingsDirectory != null)
 304		{
 305			File _settingsDirectory = new File(settingsDirectory);
 306			if(!_settingsDirectory.exists())
 307				_settingsDirectory.mkdirs();
 308			File _macrosDirectory = new File(settingsDirectory,"macros");
 309			if(!_macrosDirectory.exists())
 310				_macrosDirectory.mkdir();
 311
 312			String logPath = MiscUtilities.constructPath(
 313				settingsDirectory,"activity.log");
 314
 315			backupSettingsFile(new File(logPath));
 316
 317			try
 318			{
 319				stream = new BufferedWriter(new FileWriter(logPath));
 320
 321				// Write a warning message:
 322				String lineSep = System.getProperty("line.separator");
 323				stream.write("Log file created on " + new Date());
 324				stream.write(lineSep);
 325				stream.write("IMPORTANT:");
 326				stream.write(lineSep);
 327				stream.write("Because updating this file after "
 328					+ "every log message would kill");
 329				stream.write(lineSep);
 330				stream.write("performance, it will be *incomplete* "
 331					+ "unless you invoke the");
 332				stream.write(lineSep);
 333				stream.write("Utilities->Troubleshooting->Update "
 334					+ "Activity Log on Disk command!");
 335				stream.write(lineSep);
 336			}
 337			catch(Exception e)
 338			{
 339				e.printStackTrace();
 340				stream = null;
 341			}
 342		}
 343		else
 344		{
 345			stream = null;
 346		} //}}}
 347
 348		Log.setLogWriter(stream);
 349
 350		Log.log(Log.NOTICE,jEdit.class,"jEdit version " + getVersion());
 351		Log.log(Log.MESSAGE,jEdit.class,"Settings directory is "
 352			+ settingsDirectory);
 353
 354		//{{{ Get things rolling
 355		initMisc();
 356		initSystemProperties();
 357
 358		GUIUtilities.advanceSplashProgress();
 359
 360		GUIUtilities.init();
 361		BeanShell.init();
 362
 363		if(jEditHome != null)
 364			initSiteProperties();
 365
 366		initUserProperties();
 367		//}}}
 368
 369		//{{{ Initialize server
 370		if(portFile != null)
 371		{
 372			server = new EditServer(portFile);
 373			if(!server.isOK())
 374				server = null;
 375		}
 376		else
 377		{
 378			if(background)
 379			{
 380				background = false;
 381				Log.log(Log.WARNING,jEdit.class,"You cannot specify both the"
 382					+ " -background and -noserver switches");
 383			}
 384		} //}}}
 385
 386		//{{{ Do more stuff
 387		initPLAF();
 388
 389		VFSManager.init();
 390		initResources();
 391		SearchAndReplace.load();
 392
 393		GUIUtilities.advanceSplashProgress();
 394
 395		if(loadPlugins)
 396			initPlugins();
 397
 398		HistoryModel.loadHistory();
 399		BufferHistory.load();
 400		KillRing.load();
 401		propertiesChanged();
 402
 403		GUIUtilities.advanceSplashProgress();
 404
 405		// Buffer sort
 406		sortBuffers = getBooleanProperty("sortBuffers");
 407		sortByName = getBooleanProperty("sortByName");
 408
 409		reloadModes();
 410
 411		GUIUtilities.advanceSplashProgress();
 412		//}}}
 413
 414		//{{{ Initialize Java 1.4-specific code
 415		if(OperatingSystem.hasJava14())
 416		{
 417			try
 418			{
 419				ClassLoader loader = jEdit.class.getClassLoader();
 420				Class clazz;
 421				if(loader != null)
 422					clazz = loader.loadClass("org.gjt.sp.jedit.Java14");
 423				else
 424					clazz = Class.forName("org.gjt.sp.jedit.Java14");
 425				java.lang.reflect.Method meth = clazz
 426					.getMethod("init",new Class[0]);
 427				meth.invoke(null,new Object[0]);
 428			}
 429			catch(Exception e)
 430			{
 431				Log.log(Log.ERROR,jEdit.class,e);
 432				System.exit(1);
 433			}
 434		} //}}}
 435
 436		//{{{ Activate plugins that must be activated at startup
 437		for(int i = 0; i < jars.size(); i++)
 438		{
 439			((PluginJAR)jars.elementAt(i)).activatePluginIfNecessary();
 440		} //}}}
 441
 442		//{{{ Load macros and run startup scripts, after plugins and settings are loaded
 443		Macros.loadMacros();
 444		Macros.getMacroActionSet().initKeyBindings();
 445
 446		if(runStartupScripts && jEditHome != null)
 447		{
 448			String path = MiscUtilities.constructPath(jEditHome,"startup");
 449			File file = new File(path);
 450			if(file.exists())
 451				runStartupScripts(file);
 452		}
 453
 454		if(runStartupScripts && settingsDirectory != null)
 455		{
 456			String path = MiscUtilities.constructPath(settingsDirectory,"startup");
 457			File file = new File(path);
 458			if(!file.exists())
 459				file.mkdirs();
 460			else
 461				runStartupScripts(file);
 462		} //}}}
 463
 464		//{{{ Run script specified with -run= parameter
 465		if(scriptFile != null)
 466		{
 467			scriptFile = MiscUtilities.constructPath(userDir,scriptFile);
 468			try
 469			{
 470				BeanShell.getNameSpace().setVariable("args",args);
 471			}
 472			catch(UtilEvalError e)
 473			{
 474				Log.log(Log.ERROR,jEdit.class,e);
 475			}
 476			BeanShell.runScript(null,scriptFile,null,false);
 477		} //}}}
 478
 479		GUIUtilities.advanceSplashProgress();
 480
 481		// Open files, create the view and hide the splash screen.
 482		finishStartup(gui,restore,userDir,args);
 483	} //}}}
 484
 485	//{{{ Property methods
 486
 487	//{{{ getProperties() method
 488	/**
 489	 * Returns the properties object which contains all known
 490	 * jEdit properties. Note that as of jEdit 4.2pre10, this returns a
 491	 * new collection, not the existing properties instance.
 492	 * @since jEdit 3.1pre4
 493	 */
 494	public static final Properties getProperties()
 495	{
 496		return propMgr.getProperties();
 497	} //}}}
 498
 499	//{{{ getProperty() method
 500	/**
 501	 * Fetches a property, returning null if it's not defined.
 502	 * @param name The property
 503	 */
 504	public static final String getProperty(String name)
 505	{
 506		return propMgr.getProperty(name);
 507	} //}}}
 508
 509	//{{{ getProperty() method
 510	/**
 511	 * Fetches a property, returning the default value if it's not
 512	 * defined.
 513	 * @param name The property
 514	 * @param def The default value
 515	 */
 516	public static final String getProperty(String name, String def)
 517	{
 518		String value = propMgr.getProperty(name);
 519		if(value == null)
 520			return def;
 521		else
 522			return value;
 523	} //}}}
 524
 525	//{{{ getProperty() method
 526	/**
 527	 * Returns the property with the specified name.<p>
 528	 *
 529	 * The elements of the <code>args</code> array are substituted
 530	 * into the value of the property in place of strings of the
 531	 * form <code>{<i>n</i>}</code>, where <code><i>n</i></code> is an index
 532	 * in the array.<p>
 533	 *
 534	 * You can find out more about this feature by reading the
 535	 * documentation for the <code>format</code> method of the
 536	 * <code>java.text.MessageFormat</code> class.
 537	 *
 538	 * @param name The property
 539	 * @param args The positional parameters
 540	 */
 541	public static final String getProperty(String name, Object[] args)
 542	{
 543		if(name == null)
 544			return null;
 545		if(args == null)
 546			return getProperty(name);
 547		else
 548		{
 549			String value = getProperty(name);
 550			if(value == null)
 551				return null;
 552			else
 553				return MessageFormat.format(value,args);
 554		}
 555	} //}}}
 556
 557	//{{{ getBooleanProperty() method
 558	/**
 559	 * Returns the value of a boolean property.
 560	 * @param name The property
 561	 */
 562	public static final boolean getBooleanProperty(String name)
 563	{
 564		return getBooleanProperty(name,false);
 565	} //}}}
 566
 567	//{{{ getBooleanProperty() method
 568	/**
 569	 * Returns the value of a boolean property.
 570	 * @param name The property
 571	 * @param def The default value
 572	 */
 573	public static final boolean getBooleanProperty(String name, boolean def)
 574	{
 575		String value = getProperty(name);
 576		if(value == null)
 577			return def;
 578		else if(value.equals("true") || value.equals("yes")
 579			|| value.equals("on"))
 580			return true;
 581		else if(value.equals("false") || value.equals("no")
 582			|| value.equals("off"))
 583			return false;
 584		else
 585			return def;
 586	} //}}}
 587
 588	//{{{ getIntegerProperty() method
 589	/**
 590	 * Returns the value of an integer property.
 591	 * @param name The property
 592	 * @param def The default value
 593	 * @since jEdit 4.0pre1
 594	 */
 595	public static final int getIntegerProperty(String name, int def)
 596	{
 597		String value = getProperty(name);
 598		if(value == null)
 599			return def;
 600		else
 601		{
 602			try
 603			{
 604				return Integer.parseInt(value.trim());
 605			}
 606			catch(NumberFormatException nf)
 607			{
 608				return def;
 609			}
 610		}
 611	} //}}}
 612
 613	//{{{ getDoubleProperty() method
 614	public static double getDoubleProperty(String name, double def)
 615	{
 616		String value = getProperty(name);
 617		if(value == null)
 618			return def;
 619		else
 620		{
 621			try
 622			{
 623				return Double.parseDouble(value.trim());
 624			}
 625			catch(NumberFormatException nf)
 626			{
 627				return def;
 628			}
 629		}
 630	}
 631	//}}}
 632
 633	//{{{ getFontProperty() method
 634	/**
 635	 * Returns the value of a font property. The family is stored
 636	 * in the <code><i>name</i></code> property, the font size is stored
 637	 * in the <code><i>name</i>size</code> property, and the font style is
 638	 * stored in <code><i>name</i>style</code>. For example, if
 639	 * <code><i>name</i></code> is <code>view.gutter.font</code>, the
 640	 * properties will be named <code>view.gutter.font</code>,
 641	 * <code>view.gutter.fontsize</code>, and
 642	 * <code>view.gutter.fontstyle</code>.
 643	 *
 644	 * @param name The property
 645	 * @since jEdit 4.0pre1
 646	 */
 647	public static final Font getFontProperty(String name)
 648	{
 649		return getFontProperty(name,null);
 650	} //}}}
 651
 652	//{{{ getFontProperty() method
 653	/**
 654	 * Returns the value of a font property. The family is stored
 655	 * in the <code><i>name</i></code> property, the font size is stored
 656	 * in the <code><i>name</i>size</code> property, and the font style is
 657	 * stored in <code><i>name</i>style</code>. For example, if
 658	 * <code><i>name</i></code> is <code>view.gutter.font</code>, the
 659	 * properties will be named <code>view.gutter.font</code>,
 660	 * <code>view.gutter.fontsize</code>, and
 661	 * <code>view.gutter.fontstyle</code>.
 662	 *
 663	 * @param name The property
 664	 * @param def The default value
 665	 * @since jEdit 4.0pre1
 666	 */
 667	public static final Font getFontProperty(String name, Font def)
 668	{
 669		String family = getProperty(name);
 670		String sizeString = getProperty(name + "size");
 671		String styleString = getProperty(name + "style");
 672
 673		if(family == null || sizeString == null || styleString == null)
 674			return def;
 675		else
 676		{
 677			int size, style;
 678
 679			try
 680			{
 681				size = Integer.parseInt(sizeString);
 682			}
 683			catch(NumberFormatException nf)
 684			{
 685				return def;
 686			}
 687
 688			try
 689			{
 690				style = Integer.parseInt(styleString);
 691			}
 692			catch(NumberFormatException nf)
 693			{
 694				return def;
 695			}
 696
 697			return new Font(family,style,size);
 698		}
 699	} //}}}
 700
 701	//{{{ getColorProperty() method
 702	/**
 703	 * Returns the value of a color property.
 704	 * @param name The property name
 705	 * @since jEdit 4.0pre1
 706	 */
 707	public static Color getColorProperty(String name)
 708	{
 709		return getColorProperty(name,Color.black);
 710	} //}}}
 711
 712	//{{{ getColorProperty() method
 713	/**
 714	 * Returns the value of a color property.
 715	 * @param name The property name
 716	 * @param def The default value
 717	 * @since jEdit 4.0pre1
 718	 */
 719	public static Color getColorProperty(String name, Color def)
 720	{
 721		String value = getProperty(name);
 722		if(value == null)
 723			return def;
 724		else
 725			return GUIUtilities.parseColor(value,def);
 726	} //}}}
 727
 728	//{{{ setColorProperty() method
 729	/**
 730	 * Sets the value of a color property.
 731	 * @param name The property name
 732	 * @param value The value
 733	 * @since jEdit 4.0pre1
 734	 */
 735	public static void setColorProperty(String name, Color value)
 736	{
 737		setProperty(name,GUIUtilities.getColorHexString(value));
 738	} //}}}
 739
 740	//{{{ setProperty() method
 741	/**
 742	 * Sets a property to a new value.
 743	 * @param name The property
 744	 * @param value The new value
 745	 */
 746	public static final void setProperty(String name, String value)
 747	{
 748		propMgr.setProperty(name,value);
 749	} //}}}
 750
 751	//{{{ setTemporaryProperty() method
 752	/**
 753	 * Sets a property to a new value. Properties set using this
 754	 * method are not saved to the user properties list.
 755	 * @param name The property
 756	 * @param value The new value
 757	 * @since jEdit 2.3final
 758	 */
 759	public static final void setTemporaryProperty(String name, String value)
 760	{
 761		propMgr.setTemporaryProperty(name,value);
 762	} //}}}
 763
 764	//{{{ setBooleanProperty() method
 765	/**
 766	 * Sets a boolean property.
 767	 * @param name The property
 768	 * @param value The value
 769	 */
 770	public static final void setBooleanProperty(String name, boolean value)
 771	{
 772		setProperty(name,value ? "true" : "false");
 773	} //}}}
 774
 775	//{{{ setIntegerProperty() method
 776	/**
 777	 * Sets the value of an integer property.
 778	 * @param name The property
 779	 * @param value The value
 780	 * @since jEdit 4.0pre1
 781	 */
 782	public static final void setIntegerProperty(String name, int value)
 783	{
 784		setProperty(name,String.valueOf(value));
 785	} //}}}
 786
 787	//{{{ setDoubleProperty() method
 788	public static final void setDoubleProperty(String name, double value)
 789	{
 790		setProperty(name,String.valueOf(value));
 791	}
 792	//}}}
 793
 794	//{{{ setFontProperty() method
 795	/**
 796	 * Sets the value of a font property. The family is stored
 797	 * in the <code><i>name</i></code> property, the font size is stored
 798	 * in the <code><i>name</i>size</code> property, and the font style is
 799	 * stored in <code><i>name</i>style</code>. For example, if
 800	 * <code><i>name</i></code> is <code>view.gutter.font</code>, the
 801	 * properties will be named <code>view.gutter.font</code>,
 802	 * <code>view.gutter.fontsize</code>, and
 803	 * <code>view.gutter.fontstyle</code>.
 804	 *
 805	 * @param name The property
 806	 * @param value The value
 807	 * @since jEdit 4.0pre1
 808	 */
 809	public static final void setFontProperty(String name, Font value)
 810	{
 811		setProperty(name,value.getFamily());
 812		setIntegerProperty(name + "size",value.getSize());
 813		setIntegerProperty(name + "style",value.getStyle());
 814	} //}}}
 815
 816	//{{{ unsetProperty() method
 817	/**
 818	 * Unsets (clears) a property.
 819	 * @param name The property
 820	 */
 821	public static final void unsetProperty(String name)
 822	{
 823		propMgr.unsetProperty(name);
 824	} //}}}
 825
 826	//{{{ resetProperty() method
 827	/**
 828	 * Resets a property to its default value.
 829	 * @param name The property
 830	 *
 831	 * @since jEdit 2.5pre3
 832	 */
 833	public static final void resetProperty(String name)
 834	{
 835		propMgr.resetProperty(name);
 836	} //}}}
 837
 838	//{{{ propertiesChanged() method
 839	/**
 840	 * Reloads various settings from the properties.
 841	 */
 842	public static void propertiesChanged()
 843	{
 844		initKeyBindings();
 845
 846		Autosave.setInterval(getIntegerProperty("autosave",30));
 847
 848		saveCaret = getBooleanProperty("saveCaret");
 849
 850		//theme = new JEditMetalTheme();
 851		//theme.propertiesChanged();
 852		//MetalLookAndFeel.setCurrentTheme(theme);
 853
 854		UIDefaults defaults = UIManager.getDefaults();
 855
 856		// give all text areas the same font
 857		Font font = getFontProperty("view.font");
 858
 859		//defaults.put("TextField.font",font);
 860		defaults.put("TextArea.font",font);
 861		defaults.put("TextPane.font",font);
 862
 863		// Enable/Disable tooltips
 864		ToolTipManager.sharedInstance().setEnabled(
 865			jEdit.getBooleanProperty("showTooltips"));
 866
 867		initProxy();
 868
 869		// we do this here instead of adding buffers to the bus.
 870		Buffer buffer = buffersFirst;
 871		while(buffer != null)
 872		{
 873			buffer.resetCachedProperties();
 874			buffer.propertiesChanged();
 875			buffer = buffer.next;
 876		}
 877
 878		HistoryModel.propertiesChanged();
 879		KillRing.propertiesChanged();
 880
 881		EditBus.send(new PropertiesChanged(null));
 882	} //}}}
 883
 884	//}}}
 885
 886	//{{{ Plugin management methods
 887
 888	//{{{ getNotLoadedPluginJARs() method
 889	/**
 890	 * Returns a list of plugin JARs that are not currently loaded
 891	 * by examining the user and system plugin directories.
 892	 * @since jEdit 3.2pre1
 893	 */
 894	public static String[] getNotLoadedPluginJARs()
 895	{
 896		Vector returnValue = new Vector();
 897
 898		if(jEditHome != null)
 899		{
 900			String systemPluginDir = MiscUtilities
 901				.constructPath(jEditHome,"jars");
 902
 903			String[] list = new File(systemPluginDir).list();
 904			if(list != null)
 905				getNotLoadedPluginJARs(returnValue,systemPluginDir,list);
 906		}
 907
 908		if(settingsDirectory != null)
 909		{
 910			String userPluginDir = MiscUtilities
 911				.constructPath(settingsDirectory,"jars");
 912			String[] list = new File(userPluginDir).list();
 913			if(list != null)
 914			{
 915				getNotLoadedPluginJARs(returnValue,
 916					userPluginDir,list);
 917			}
 918		}
 919
 920		String[] _returnValue = new String[returnValue.size()];
 921		returnValue.copyInto(_returnValue);
 922		return _returnValue;
 923	} //}}}
 924
 925	//{{{ getPlugin() method
 926	/**
 927	 * Returns the plugin with the specified class name.
 928	 */
 929	public static EditPlugin getPlugin(String name)
 930	{
 931		return getPlugin(name, false);
 932	} //}}}
 933
 934	//{{{ getPlugin(String, boolean) method
 935	/**
 936	 * Returns the plugin with the specified class name. If
 937	 * <code>loadIfNecessary</code> is true, the plugin will be activated in
 938	 * case it has not yet been started.
 939	 * @since jEdit 4.2pre4
 940	 */
 941	public static EditPlugin getPlugin(String name, boolean loadIfNecessary)
 942	{
 943		EditPlugin[] plugins = getPlugins();
 944		EditPlugin plugin = null;
 945		for(int i = 0; i < plugins.length; i++)
 946		{
 947			if(plugins[i].getClassName().equals(name))
 948				plugin = plugins[i];
 949			if(loadIfNecessary)
 950			{
 951				if(plugin instanceof EditPlugin.Deferred)
 952				{
 953					plugin.getPluginJAR().activatePlugin();
 954					plugin = plugin.getPluginJAR().getPlugin();
 955					break;
 956				}
 957			}
 958		}
 959
 960		return plugin;
 961	} //}}}
 962
 963	//{{{ getPlugins() method
 964	/**
 965	 * Returns an array of installed plugins.
 966	 */
 967	public static EditPlugin[] getPlugins()
 968	{
 969		Vector vector = new Vector();
 970		for(int i = 0; i < jars.size(); i++)
 971		{
 972			EditPlugin plugin = ((PluginJAR)jars.elementAt(i))
 973				.getPlugin();
 974			if(plugin != null)
 975				vector.add(plugin);
 976		}
 977
 978		EditPlugin[] array = new EditPlugin[vector.size()];
 979		vector.copyInto(array);
 980		return array;
 981	} //}}}
 982
 983	//{{{ getPluginJARs() method
 984	/**
 985	 * Returns an array of installed plugins.
 986	 * @since jEdit 4.2pre1
 987	 */
 988	public static PluginJAR[] getPluginJARs()
 989	{
 990		PluginJAR[] array = new PluginJAR[jars.size()];
 991		jars.copyInto(array);
 992		return array;
 993	} //}}}
 994
 995	//{{{ getPluginJAR() method
 996	/**
 997	 * Returns the JAR with the specified path name.
 998	 * @param path The path name
 999	 * @since jEdit 4.2pre1
1000	 */
1001	public static PluginJAR getPluginJAR(String path)
1002	{
1003		for(int i = 0; i < jars.size(); i++)
1004		{
1005			PluginJAR jar = (PluginJAR)jars.elementAt(i);
1006			if(jar.getPath().equals(path))
1007				return jar;
1008		}
1009
1010		return null;
1011	} //}}}
1012
1013	//{{{ addPluginJAR() method
1014	/**
1015	 * Loads the plugin JAR with the specified path. Some notes about this
1016	 * method:
1017	 *
1018	 * <ul>
1019	 * <li>Calling this at a time other than jEdit startup can have
1020	 * unpredictable results if the plugin has not been updated for the
1021	 * jEdit 4.2 plugin API.
1022	 * <li>You must make sure yourself the plugin is not already loaded.
1023	 * <li>After loading, you just make sure all the plugin's dependencies
1024	 * are satisified before activating the plugin, using the
1025	 * {@link PluginJAR#checkDependencies()} method.
1026	 * </ul>
1027	 *
1028	 * @param path The JAR file path
1029	 * @since jEdit 4.2pre1
1030	 */
1031	public static void addPluginJAR(String path)
1032	{
1033		// backwards compatibility...
1034		PluginJAR jar = new EditPlugin.JAR(new File(path));
1035		jars.addElement(jar);
1036		jar.init();
1037
1038		EditBus.send(new PluginUpdate(jar,PluginUpdate.LOADED,false));
1039		if(!isMainThread())
1040		{
1041			EditBus.send(new DynamicMenuChanged("plugins"));
1042			initKeyBindings();
1043		}
1044	} //}}}
1045
1046	//{{{ addPluginJARsFromDirectory() method
1047	/**
1048	 * Loads all plugins in a directory.
1049	 * @param directory The directory
1050	 * @since jEdit 4.2pre1
1051	 */
1052	private static void addPluginJARsFromDirectory(String directory)
1053	{
1054		Log.log(Log.NOTICE,jEdit.class,"Loading plugins from "
1055			+ directory);
1056
1057		File file = new File(directory);
1058		if(!(file.exists() && file.isDirectory()))
1059			return;
1060		String[] plugins = file.list();
1061		if(plugins == null)
1062			return;
1063
1064		for(int i = 0; i < plugins.length; i++)
1065		{
1066			String plugin = plugins[i];
1067			if(!plugin.toLowerCase().endsWith(".jar"))
1068				continue;
1069
1070			String path = MiscUtilities.constructPath(directory,plugin);
1071
1072			// remove this when 4.1 plugin API is deprecated
1073			if(plugin.equals("EditBuddy.jar")
1074				|| plugin.equals("PluginManager.jar")
1075				|| plugin.equals("Firewall.jar")
1076				|| plugin.equals("Tidy.jar")
1077				|| plugin.equals("DragAndDrop.jar"))
1078			{
1079				pluginError(path,"plugin-error.obsolete",null);
1080				continue;
1081			}
1082
1083			addPluginJAR(path);
1084		}
1085	} //}}}
1086
1087	//{{{ removePluginJAR() method
1088	/**
1089	 * Unloads the given plugin JAR with the specified path. Note that
1090	 * calling this at a time other than jEdit shutdown can have
1091	 * unpredictable results if the plugin has not been updated for the
1092	 * jEdit 4.2 plugin API.
1093	 *
1094	 * @param jar The <code>PluginJAR</code> instance
1095	 * @param exit Set to true if jEdit is exiting; enables some
1096	 * shortcuts so the editor can close faster.
1097	 * @since jEdit 4.2pre1
1098	 */
1099	public static void removePluginJAR(PluginJAR jar, boolean exit)
1100	{
1101		if(exit)
1102		{
1103			jar.uninit(true);
1104		}
1105		else
1106		{
1107			jar.uninit(false);
1108			jars.removeElement(jar);
1109			initKeyBindings();
1110		}
1111
1112		EditBus.send(new PluginUpdate(jar,PluginUpdate.UNLOADED,exit));
1113		if(!isMainThread() && !exit)
1114			EditBus.send(new DynamicMenuChanged("plugins"));
1115	} //}}}
1116
1117	//}}}
1118
1119	//{{{ Action methods
1120
1121	//{{{ getActionContext() method
1122	/**
1123	 * Returns the action context used to store editor actions.
1124	 * @since jEdit 4.2pre1
1125	 */
1126	public static ActionContext getActionContext()
1127	{
1128		return actionContext;
1129	} //}}}
1130
1131	//{{{ addActionSet() method
1132	/**
1133	 * Adds a new action set to jEdit's list. Plugins probably won't
1134	 * need to call this method.
1135	 * @since jEdit 4.0pre1
1136	 */
1137	public static void addActionSet(ActionSet actionSet)
1138	{
1139		actionContext.addActionSet(actionSet);
1140	} //}}}
1141
1142	//{{{ removeActionSet() method
1143	/**
1144	 * Removes an action set from jEdit's list. Plugins probably won't
1145	 * need to call this method.
1146	 * @since jEdit 4.2pre1
1147	 */
1148	public static void removeActionSet(ActionSet actionSet)
1149	{
1150		actionContext.removeActionSet(actionSet);
1151	} //}}}
1152
1153	//{{{ getBuiltInActionSet() method
1154	/**
1155	 * Returns the set of commands built into jEdit.
1156	 * @since jEdit 4.2pre1
1157	 */
1158	public static ActionSet getBuiltInActionSet()
1159	{
1160		return builtInActionSet;
1161	} //}}}
1162
1163	//{{{ getActionSets() method
1164	/**
1165	 * Returns all registered action sets.
1166	 * @since jEdit 4.0pre1
1167	 */
1168	public static ActionSet[] getActionSets()
1169	{
1170		return actionContext.getActionSets();
1171	} //}}}
1172
1173	//{{{ getAction() method
1174	/**
1175	 * Returns the specified action.
1176	 * @param name The action name
1177	 */
1178	public static EditAction getAction(String name)
1179	{
1180		return actionContext.getAction(name);
1181	} //}}}
1182
1183	//{{{ getActionSetForAction() method
1184	/**
1185	 * Returns the action set that contains the specified action.
1186	 *
1187	 * @param action The action
1188	 * @since jEdit 4.2pre1
1189	 */
1190	public static ActionSet getActionSetForAction(String action)
1191	{
1192		return actionContext.getActionSetForAction(action);
1193	} //}}}
1194
1195	//{{{ getActionSetForAction() method
1196	/**
1197	 * @deprecated Use the form that takes a String instead
1198	 */
1199	public static ActionSet getActionSetForAction(EditAction action)
1200	{
1201		return actionContext.getActionSetForAction(action.getName());
1202	} //}}}
1203
1204	//{{{ getActions() method
1205	/**
1206	 * @deprecated Call getActionNames() instead
1207	 */
1208	public static EditAction[] getActions()
1209	{
1210		String[] names = actionContext.getActionNames();
1211		EditAction[] actions = new EditAction[names.length];
1212		for(int i = 0; i < actions.length; i++)
1213		{
1214			actions[i] = actionContext.getAction(names[i]);
1215			if(actions[i] == null)
1216				Log.log(Log.ERROR,jEdit.class,"wtf: " + names[i]);
1217		}
1218		return actions;
1219	} //}}}
1220
1221	//{{{ getActionNames() method
1222	/**
1223	 * Returns all registered action names.
1224	 */
1225	public static String[] getActionNames()
1226	{
1227		return actionContext.getActionNames();
1228	} //}}}
1229
1230	//}}}
1231
1232	//{{{ Edit mode methods
1233
1234	//{{{ reloadModes() method
1235	/**
1236	 * Reloads all edit modes.
1237	 * @since jEdit 3.2pre2
1238	 */
1239	public static void reloadModes()
1240	{
1241		/* Try to guess the eventual size to avoid unnecessary
1242		 * copying */
1243		modes = new Vector(50);
1244
1245		//{{{ Load the global catalog
1246		if(jEditHome == null)
1247			loadModeCatalog("/modes/catalog",true);
1248		else
1249		{
1250			loadModeCatalog(MiscUtilities.constructPath(jEditHome,
1251				"modes","catalog"),false);
1252		} //}}}
1253
1254		//{{{ Load user catalog
1255		if(settingsDirectory != null)
1256		{
1257			File userModeDir = new File(MiscUtilities.constructPath(
1258				settingsDirectory,"modes"));
1259			if(!userModeDir.exists())
1260				userModeDir.mkdirs();
1261
1262			File userCatalog = new File(MiscUtilities.constructPath(
1263				settingsDirectory,"modes","catalog"));
1264			if(!userCatalog.exists())
1265			{
1266				// create dummy catalog
1267				FileWriter out = null;
1268				try
1269				{
1270					out = new FileWriter(userCatalog);
1271					out.write(jEdit.getProperty("defaultCatalog"));
1272					out.close();
1273				}
1274				catch(IOException io)
1275				{
1276					Log.log(Log.ERROR,jEdit.class,io);
1277				}
1278				finally
1279				{
1280					try
1281					{
1282						if(out != null)
1283							out.close();
1284					}
1285					catch(IOException e)
1286					{
1287					}
1288				}
1289			}
1290
1291			loadModeCatalog(userCatalog.getPath(),false);
1292		} //}}}
1293
1294		Buffer buffer = buffersFirst;
1295		while(buffer != null)
1296		{
1297			// This reloads the token marker and sends a message
1298			// which causes edit panes to repaint their text areas
1299			buffer.setMode();
1300
1301			buffer = buffer.next;
1302		}
1303	} //}}}
1304
1305	//{{{ getMode() method
1306	/**
1307	 * Returns the edit mode with the specified name.
1308	 * @param name The edit mode
1309	 */
1310	public static Mode getMode(String name)
1311	{
1312		for(int i = 0; i < modes.size(); i++)
1313		{
1314			Mode mode = (Mode)modes.elementAt(i);
1315			if(mode.getName().equals(name))
1316				return mode;
1317		}
1318		return null;
1319	} //}}}
1320
1321	//{{{ getModes() method
1322	/**
1323	 * Returns an array of installed edit modes.
1324	 */
1325	public static Mode[] getModes()
1326	{
1327		Mode[] array = new Mode[modes.size()];
1328		modes.copyInto(array);
1329		return array;
1330	} //}}}
1331
1332	//}}}
1333
1334	//{{{ Buffer creation methods
1335
1336	//{{{ openFiles() method
1337	/**
1338	 * Opens the file names specified in the argument array. This
1339	 * handles +line and +marker arguments just like the command
1340	 * line parser.
1341	 * @param parent The parent directory
1342	 * @param args The file names to open
1343	 * @since jEdit 3.2pre4
1344	 */
1345	public static Buffer openFiles(View view, String parent, String[] args)
1346	{
1347		Buffer retVal = null;
1348		Buffer lastBuffer = null;
1349
1350		for(int i = 0; i < args.length; i++)
1351		{
1352			String arg = args[i];
1353			if(arg == null)
1354				continue;
1355			else if(arg.startsWith("+line:") || arg.startsWith("+marker:"))
1356			{
1357				if(lastBuffer != null)
1358					gotoMarker(view,lastBuffer,arg);
1359				continue;
1360			}
1361
1362			lastBuffer = openFile(null,parent,arg,false,null);
1363
1364			if(retVal == null && lastBuffer != null)
1365				retVal = lastBuffer;
1366		}
1367
1368		if(view != null && retVal != null)
1369			view.setBuffer(retVal);
1370
1371		return retVal;
1372	} //}}}
1373
1374	//{{{ openFile() method
1375	/**
1376	 * Opens a file. Note that as of jEdit 2.5pre1, this may return
1377	 * null if the buffer could not be opened.
1378	 * @param view The view to open the file in
1379	 * @param path The file path
1380	 *
1381	 * @since jEdit 2.4pre1
1382	 */
1383	public static Buffer openFile(View view, String path)
1384	{
1385		return openFile(view,null,path,false,new Hashtable());
1386	} //}}}
1387
1388	//{{{ openFile() method
1389	/**
1390	 * @deprecated The openFile() forms with the readOnly parameter
1391	 * should not be used. The readOnly prameter is no longer supported.
1392	 */
1393	public static Buffer openFile(View view, String parent,
1394		String path, boolean readOnly, boolean newFile)
1395	{
1396		return openFile(view,parent,path,newFile,new Hashtable());
1397	} //}}}
1398
1399	//{{{ openFile() method
1400	/**
1401	 * @deprecated The openFile() forms with the readOnly parameter
1402	 * should not be used. The readOnly prameter is no longer supported.
1403	 */
1404	public static Buffer openFile(View view, String parent,
1405		String path, boolean readOnly, boolean newFile,
1406		Hashtable props)
1407	{
1408		return openFile(view,parent,path,newFile,props);
1409	} //}}}
1410
1411	//{{{ openFile() method
1412	/**
1413	 * Opens a file. This may return null if the buffer could not be
1414	 * opened for some reason.
1415	 * @param view The view to open the file in
1416	 * @param parent The parent directory of the file
1417	 * @param path The path name of the file
1418	 * @param newFile True if the file should not be loaded from disk
1419	 * be prompted if it should be reloaded
1420	 * @param props Buffer-local properties to set in the buffer
1421	 *
1422	 * @since jEdit 3.2pre10
1423	 */
1424	public static Buffer openFile(View view, String parent,
1425		String path, boolean newFile, Hashtable props)
1426	{
1427		PerspectiveManager.setPerspectiveDirty(true);
1428
1429		if(view != null && parent == null)
1430			parent = view.getBuffer().getDirectory();
1431
1432		if(MiscUtilities.isURL(path))
1433		{
1434			if(MiscUtilities.getProtocolOfURL(path).equals("file"))
1435				path = path.substring(5);
1436		}
1437
1438		path = MiscUtilities.constructPath(parent,path);
1439
1440		Buffer newBuffer;
1441
1442		synchronized(bufferListLock)
1443		{
1444			Buffer buffer = getBuffer(path);
1445			if(buffer != null)
1446			{
1447				if(view != null)
1448					view.setBuffer(buffer);
1449
1450				return buffer;
1451			}
1452
1453			if(props == null)
1454				props = new Hashtable();
1455
1456			BufferHistory.Entry entry = BufferHistory.getEntry(path);
1457
1458			if(entry != null && saveCaret && props.get(Buffer.CARET) == null)
1459			{
1460				props.put(Buffer.CARET,new Integer(entry.caret));
1461				/* if(entry.selection != null)
1462				{
1463					// getSelection() converts from string to
1464					// Selection[]
1465					props.put(Buffer.SELECTION,entry.getSelection());
1466				} */
1467			}
1468
1469			if(entry != null && props.get(Buffer.ENCODING) == null)
1470			{
1471				if(entry.encoding != null)
1472					props.put(Buffer.ENCODING,entry.encoding);
1473			}
1474
1475			newBuffer = new Buffer(path,newFile,false,props);
1476
1477			if(!newBuffer.load(view,false))
1478				return null;
1479
1480			addBufferToList(newBuffer);
1481		}
1482
1483		EditBus.send(new BufferUpdate(newBuffer,view,BufferUpdate.CREATED));
1484
1485		if(view != null)
1486			view.setBuffer(newBuffer);
1487
1488		return newBuffer;
1489	} //}}}
1490
1491	//{{{ openTemporary() method
1492	/**
1493	 * Opens a temporary buffer. A temporary buffer is like a normal
1494	 * buffer, except that an event is not fired, the the buffer is
1495	 * not added to the buffers list.
1496	 *
1497	 * @param view The view to open the file in
1498	 * @param parent The parent directory of the file
1499	 * @param path The path name of the file
1500	 * @param newFile True if the file should not be loaded from disk
1501	 *
1502	 * @since jEdit 3.2pre10
1503	 */
1504	public static Buffer openTemporary(View view, String parent,
1505		String path, boolean newFile)
1506	{
1507		if(view != null && parent == null)
1508			parent = view.getBuffer().getDirectory();
1509
1510		if(MiscUtilities.isURL(path))
1511		{
1512			if(MiscUtilities.getProtocolOfURL(path).equals("file"))
1513				path = path.substring(5);
1514		}
1515
1516		path = MiscUtilities.constructPath(parent,path);
1517
1518		synchronized(bufferListLock)
1519		{
1520			Buffer buffer = getBuffer(path);
1521			if(buffer != null)
1522				return buffer;
1523
1524			buffer = new Buffer(path,newFile,true,new Hashtable());
1525			if(!buffer.load(view,false))
1526				return null;
1527			else
1528				return buffer;
1529		}
1530	} //}}}
1531
1532	//{{{ commitTemporary() method
1533	/**
1534	 * Adds a temporary buffer to the buffer list. This must be done
1535	 * before allowing the user to interact with the buffer in any
1536	 * way.
1537	 * @param buffer The buffer
1538	 */
1539	public static void commitTemporary(Buffer buffer)
1540	{
1541		if(!buffer.isTemporary())
1542			return;
1543
1544		PerspectiveManager.setPerspectiveDirty(true);
1545
1546		addBufferToList(buffer);
1547		buffer.commitTemporary();
1548
1549		// send full range of events to avoid breaking plugins
1550		EditBus.send(new BufferUpdate(buffer,null,BufferUpdate.CREATED));
1551		EditBus.send(new BufferUpdate(buffer,null,BufferUpdate.LOAD_STARTED));
1552		EditBus.send(new BufferUpdate(buffer,null,BufferUpdate.LOADED));
1553	} //}}}
1554
1555	//{{{ newFile() method
1556	/**
1557	 * Creates a new `untitled' file.
1558	 * @param view The view to create the file in
1559	 */
1560	public static Buffer newFile(View view)
1561	{
1562		String path;
1563
1564		if(view != null && view.getBuffer() != null)
1565		{
1566			path = view.getBuffer().getDirectory();
1567			VFS vfs = VFSManager.getVFSForPath(path);
1568			// don't want 'New File' to create a read only buffer
1569			// if current file is on SQL VFS or something
1570			if((vfs.getCapabilities() & VFS.WRITE_CAP) == 0)
1571				path = System.getProperty("user.home");
1572		}
1573		else
1574			path = null;
1575
1576		return newFile(view,path);
1577	} //}}}
1578
1579	//{{{ newFile() method
1580	/**
1581	 * Creates a new `untitled' file.
1582	 * @param view The view to create the file in
1583	 * @param dir The directory to create the file in
1584	 * @since jEdit 3.1pre2
1585	 */
1586	public static Buffer newFile(View view, String dir)
1587	{
1588		// If only one new file is open which is clean, just close
1589		// it, which will create an 'Untitled-1'
1590		if(dir != null
1591			&& buffersFirst != null
1592			&& buffersFirst == buffersLast
1593			&& buffersFirst.isUntitled()
1594			&& !buffersFirst.isDirty())
1595		{
1596			closeBuffer(view,buffersFirst);
1597			// return the newly created 'untitled-1'
1598			return buffersFirst;
1599		}
1600
1601		// Find the highest Untitled-n file
1602		int untitledCount = 0;
1603		Buffer buffer = buffersFirst;
1604		while(buffer != null)
1605		{
1606			if(buffer.getName().startsWith("Untitled-"))
1607			{
1608				try
1609				{
1610					untitledCount = Math.max(untitledCount,
1611						Integer.parseInt(buffer.getName()
1612						.substring(9)));
1613				}
1614				catch(NumberFormatException nf)
1615				{
1616				}
1617			}
1618			buffer = buffer.next;
1619		}
1620
1621		return openFile(view,dir,"Untitled-" + (untitledCount+1),true,null);
1622	} //}}}
1623
1624	//}}}
1625
1626	//{{{ Buffer management methods
1627
1628	//{{{ closeBuffer() method
1629	/**
1630	 * Closes a buffer. If there are unsaved changes, the user is
1631	 * prompted if they should be saved first.
1632	 * @param view The view
1633	 * @param buffer The buffer
1634	 * @return True if the buffer was really closed, false otherwise
1635	 */
1636	public static boolean closeBuffer(View view, Buffer buffer)
1637	{
1638		// Wait for pending I/O requests
1639		if(buffer.isPerformingIO())
1640		{
1641			VFSManager.waitForRequests();
1642			if(VFSManager.errorOccurred())
1643				return false;
1644		}
1645
1646		if(buffer.isDirty())
1647		{
1648			Object[] args = { buffer.getName() };
1649			int result = GUIUtilities.confirm(view,"notsaved",args,
1650				JOptionPane.YES_NO_CANCEL_OPTION,
1651				JOptionPane.WARNING_MESSAGE);
1652			if(result == JOptionPane.YES_OPTION)
1653			{
1654				if(!buffer.save(view,null,true))
1655					return false;
1656
1657				VFSManager.waitForRequests();
1658				if(buffer.getBooleanProperty(BufferIORequest
1659					.ERROR_OCCURRED))
1660				{
1661					return false;
1662				}
1663			}
1664			else if(result != JOptionPane.NO_OPTION)
1665				return false;
1666		}
1667
1668		_closeBuffer(view,buffer);
1669
1670		return true;
1671	} //}}}
1672
1673	//{{{ _closeBuffer() method
1674	/**
1675	 * Closes the buffer, even if it has unsaved changes.
1676	 * @param view The view
1677	 * @param buffer The buffer
1678	 *
1679	 * @since jEdit 2.2pre1
1680	 */
1681	public static void _closeBuffer(View view, Buffer buffer)
1682	{
1683		if(buffer.isClosed())
1684		{
1685			// can happen if the user presses C+w twice real
1686			// quick and the buffer has unsaved changes
1687			return;
1688		}
1689
1690		PerspectiveManager.setPerspectiveDirty(true);
1691
1692		if(!buffer.isNewFile())
1693		{
1694			view.getEditPane().saveCaretInfo();
1695			Integer _caret = (Integer)buffer.getProperty(Buffer.CARET);
1696			int caret = (_caret == null ? 0 : _caret.intValue());
1697
1698			BufferHistory.setEntry(buffer.getPath(),caret,
1699				(Selection[])buffer.getProperty(Buffer.SELECTION),
1700				buffer.getStringProperty(Buffer.ENCODING));
1701		}
1702
1703		String path = buffer.getSymlinkPath();
1704		if((VFSManager.getVFSForPath(path).getCapabilities()
1705			& VFS.CASE_INSENSITIVE_CAP) != 0)
1706		{
1707			path = path.toLowerCase();
1708		}
1709		bufferHash.remove(path);
1710		removeBufferFromList(buffer);
1711		buffer.close();
1712		DisplayManager.bufferClosed(buffer);
1713
1714		EditBus.send(new BufferUpdate(buffer,view,BufferUpdate.CLOSED));
1715
1716		// Create a new file when the last is closed
1717		if(buffersFirst == null && buffersLast == null)
1718			newFile(view);
1719	} //}}}
1720
1721	//{{{ closeAllBuffers() method
1722	/**
1723	 * Closes all open buffers.
1724	 * @param view The view
1725	 */
1726	public static boolean closeAllBuffers(View view)
1727	{
1728		return closeAllBuffers(view,false);
1729	} //}}}
1730
1731	//{{{ closeAllBuffers() method
1732	/**
1733	 * Closes all open buffers.
1734	 * @param view The view
1735	 * @param isExiting This must be false unless this method is
1736	 * being called by the exit() method
1737	 */
1738	public static boolean closeAllBuffers(View view, boolean isExiting)
1739	{
1740		boolean dirty = false;
1741
1742		Buffer buffer = buffersFirst;
1743		while(buffer != null)
1744		{
1745			if(buffer.isDirty())
1746			{
1747				dirty = true;
1748				break;
1749			}
1750			buffer = buffer.next;
1751		}
1752
1753		if(dirty)
1754		{
1755			boolean ok = new CloseDialog(view).isOK();
1756			if(!ok)
1757				return false;
1758		}
1759
1760		// Wait for pending I/O requests
1761		VFSManager.waitForRequests();
1762		if(VFSManager.errorOccurred())
1763			return false;
1764
1765		// close remaining buffers (the close dialog only deals with
1766		// dirty ones)
1767
1768		buffer = buffersFirst;
1769
1770		// zero it here so that BufferTabs doesn't have any problems
1771		buffersFirst = buffersLast = null;
1772		bufferHash.clear();
1773		bufferCount = 0;
1774
1775		while(buffer != null)
1776		{
1777			if(!buffer.isNewFile())
1778			{
1779				Integer _caret = (Integer)buffer.getProperty(Buffer.CARET);
1780				int caret = (_caret == null ? 0 : _caret.intValue());
1781				BufferHistory.setEntry(buffer.getPath(),caret,
1782					(Selection[])buffer.getProperty(Buffer.SELECTION),
1783					buffer.getStringProperty(Buffer.ENCODING));
1784			}
1785
1786			buffer.close();
1787			DisplayManager.bufferClosed(buffer);
1788			if(!isExiting)
1789			{
1790				EditBus.send(new BufferUpdate(buffer,view,
1791					BufferUpdate.CLOSED));
1792			}
1793			buffer = buffer.next;
1794		}
1795
1796		if(!isExiting)
1797			newFile(view);
1798
1799		PerspectiveManager.setPerspectiveDirty(true);
1800
1801		return true;
1802	} //}}}
1803
1804	//{{{ saveAllBuffers() method
1805	/**
1806	 * Saves all open buffers.
1807	 * @param view The view
1808	 * @since jEdit 4.2pre1
1809	 */
1810	public static void saveAllBuffers(View view)
1811	{
1812		saveAllBuffers(view,jEdit.getBooleanProperty("confirmSaveAll"));
1813	} //}}}
1814
1815	//{{{ saveAllBuffers() method
1816	/**
1817	 * Saves all open buffers.
1818	 * @param view The view
1819	 * @param confirm If true, a confirmation dialog will be shown first
1820	 * @since jEdit 2.7pre2
1821	 */
1822	public static void saveAllBuffers(View view, boolean confirm)
1823	{
1824		if(confirm)
1825		{
1826			int result = GUIUtilities.confirm(view,"saveall",null,
1827				JOptionPane.YES_NO_OPTION,
1828				JOptionPane.QUESTION_MESSAGE);
1829			if(result != JOptionPane.YES_OPTION)
1830				return;
1831		}
1832
1833		Buffer current = view.getBuffer();
1834
1835		Buffer buffer = buffersFirst;
1836		while(buffer != null)
1837		{
1838			if(buffer.isDirty())
1839			{
1840				if(buffer.isNewFile())
1841					view.setBuffer(buffer);
1842				buffer.save(view,null,true);
1843			}
1844
1845			buffer = buffer.next;
1846		}
1847
1848		view.setBuffer(current);
1849	} //}}}
1850
1851	//{{{ reloadAllBuffers() method
1852	/**
1853	 * Reloads all open buffers.
1854	 * @param view The view
1855	 * @param confirm If true, a confirmation dialog will be shown first
1856	 *	if any buffers are dirty
1857	 * @since jEdit 2.7pre2
1858	 */
1859	public static void reloadAllBuffers(final View view, boolean confirm)
1860	{
1861		boolean hasDirty = false;
1862		Buffer[] buffers = jEdit.getBuffers();
1863
1864		for(int i = 0; i < buffers.length && hasDirty == false; i++)
1865			hasDirty = buffers[i].isDirty();
1866
1867		if(confirm && hasDirty)
1868		{
1869			int result = GUIUtilities.confirm(view,"reload-all",null,
1870				JOptionPane.YES_NO_OPTION,
1871				JOptionPane.QUESTION_MESSAGE);
1872			if(result != JOptionPane.YES_OPTION)
1873				return;
1874		}
1875
1876		// save caret info. Buffer.load() will load it.
1877		View _view = viewsFirst;
1878		while(_view != null)
1879		{
1880			EditPane[] panes = _view.getEditPanes();
1881			for(int i = 0; i < panes.length; i++)
1882			{
1883				panes[i].saveCaretInfo();
1884			}
1885
1886			_view = _view.next;
1887		}
1888
1889		for(int i = 0; i < buffers.length; i++)
1890		{
1891			Buffer buffer = buffers[i];
1892			buffer.load(view,true);
1893		}
1894	} //}}}
1895
1896	//{{{ _getBuffer() method
1897	/**
1898	 * Returns the buffer with the specified path name. The path name
1899	 * must be an absolute, canonical, path.
1900	 *
1901	 * @param path The path name
1902	 * @see MiscUtilities#constructPath(String,String)
1903	 * @see MiscUtilities#resolveSymlinks(String)
1904	 * @see #getBuffer(String)
1905	 *
1906	 * @since jEdit 4.2pre7
1907	 */
1908	public static Buffer _getBuffer(String path)
1909	{
1910		// paths on case-insensitive filesystems are stored as lower
1911		// case in the hash.
1912		if((VFSManager.getVFSForPath(path).getCapabilities()
1913			& VFS.CASE_INSENSITIVE_CAP) != 0)
1914		{
1915			path = path.toLowerCase();
1916		}
1917
1918		synchronized(bufferListLock)
1919		{
1920			return (Buffer)bufferHash.get(path);
1921		}
1922	} //}}}
1923
1924	//{{{ getBuffer() method
1925	/**
1926	 * Returns the buffer with the specified path name. The path name
1927	 * must be an absolute path. This method automatically resolves
1928	 * symbolic links. If performance is critical, cache the canonical
1929	 * path and call {@link #_getBuffer(String)} instead.
1930	 *
1931	 * @param path The path name
1932	 * @see MiscUtilities#constructPath(String,String)
1933	 * @see MiscUtilities#resolveSymlinks(String)
1934	 */
1935	public static Buffer getBuffer(String path)
1936	{
1937		if(MiscUtilities.isURL(path))
1938			return _getBuffer(path);
1939		else
1940			return _getBuffer(MiscUtilities.resolveSymlinks(path));
1941	} //}}}
1942
1943	//{{{ getBuffers() method
1944	/**
1945	 * Returns an array of open buffers.
1946	 */
1947	public static Buffer[] getBuffers()
1948	{
1949		synchronized(bufferListLock)
1950		{
1951			Buffer[] buffers = new Buffer[bufferCount];
1952			Buffer buffer = buffersFirst;
1953			for(int i = 0; i < bufferCount; i++)
1954			{
1955				buffers[i] = buffer;
1956				buffer = buffer.next;
1957			}
1958			return buffers;
1959		}
1960	} //}}}
1961
1962	//{{{ getBufferCount() method
1963	/**
1964	 * Returns the number of open buffers.
1965	 */
1966	public static int getBufferCount()
1967	{
1968		return bufferCount;
1969	} //}}}
1970
1971	//{{{ getFirstBuffer() method
1972	/**
1973	 * Returns the first buffer.
1974	 */
1975	public static Buffer getFirstBuffer()
1976	{
1977		return buffersFirst;
1978	} //}}}
1979
1980	//{{{ getLastBuffer() method
1981	/**
1982	 * Returns the last buffer.
1983	 */
1984	public static Buffer getLastBuffer()
1985	{
1986		return buffersLast;
1987	} //}}}
1988
1989	//{{{ checkBufferStatus() method
1990	/**
1991	 * Checks each buffer's status on disk and shows the dialog box
1992	 * informing the user that buffers changed on disk, if necessary.
1993	 * @param view The view
1994	 * @since jEdit 4.2pre1
1995	 */
1996	public static void checkBufferStatus(View view)
1997	{
1998		// still need to call the status check even if the option is
1999		// off, so that the write protection is updated if it changes
2000		// on disk
2001		boolean showDialogSetting = getBooleanProperty(
2002			"autoReloadDialog");
2003
2004		// auto reload changed buffers?
2005		boolean autoReloadSetting = getBooleanProperty(
2006			"autoReload");
2007
2008		// the problem with this is that if we have two edit panes
2009		// looking at the same buffer and the file is reloaded both
2010		// will jump to the same location
2011		View _view = viewsFirst;
2012		while(_view != null)
2013		{
2014			EditPane[] editPanes = _view.getEditPanes();
2015			for(int i = 0; i < editPanes.length; i++)
2016			{
2017				editPanes[i].saveCaretInfo();
2018			}
2019			_view = _view.next;
2020		}
2021
2022		Buffer buffer = buffersFirst;
2023		int[] states = new int[bufferCount];
2024		int i = 0;
2025		boolean show = false;
2026		while(buffer != null)
2027		{
2028			states[i] = buffer.checkFileStatus(view);
2029
2030			switch(states[i])
2031			{
2032			case Buffer.FILE_CHANGED:
2033				if(autoReloadSetting
2034					&& showDialogSetting
2035					&& !buffer.isDirty())
2036				{
2037					buffer.load(view,true);
2038				}
2039				/* fall through */
2040			case Buffer.FILE_DELETED:
2041				show = true;
2042				break;
2043			}
2044
2045			buffer = buffer.next;
2046			i++;
2047		}
2048
2049		if(show && showDialogSetting)
2050			new FilesChangedDialog(view,states,autoReloadSetting);
2051	} //}}}
2052
2053	//}}}
2054
2055	//{{{ View methods
2056
2057	//{{{ getInputHandler() method
2058	/**
2059	 * Returns the current input handler

Large files files are truncated, but you can click here to view the full file