PageRenderTime 357ms CodeModel.GetById 219ms app.highlight 122ms RepoModel.GetById 1ms app.codeStats 1ms

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

#
Java | 1900 lines | 1323 code | 216 blank | 361 comment | 194 complexity | 8d3d0fed43f813f4fee9bfb7a8240f1e MD5 | raw file
   1/*
   2 * DockableWindowManager.java - manages dockable windows
   3 * :tabSize=8:indentSize=8:noTabs=false:
   4 * :folding=explicit:collapseFolds=1:
   5 *
   6 * Copyright (C) 2000, 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 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  21 */
  22
  23package org.gjt.sp.jedit.gui;
  24
  25//{{{ Imports
  26 import bsh.*;
  27 import com.microstar.xml.*;
  28 import javax.swing.*;
  29 import java.awt.event.*;
  30 import java.awt.*;
  31 import java.io.*;
  32 import java.net.URL;
  33 import java.util.*;
  34 import org.gjt.sp.jedit.msg.*;
  35 import org.gjt.sp.jedit.*;
  36 import org.gjt.sp.util.Log;
  37//}}}
  38
  39/**
  40 * The <code>DockableWindowManager</code> keeps track of dockable windows.
  41 * Each {@link org.gjt.sp.jedit.View} has an instance of this class.<p>
  42 *
  43 * <b>dockables.xml:</b><p>
  44 *
  45 * Dockable window definitions are read from <code>dockables.xml</code> files
  46 * contained inside plugin JARs. A dockable definition file has the following
  47 * form:
  48 *
  49 * <pre>&lt;?xml version="1.0"?&gt;
  50 *&lt;!DOCTYPE DOCKABLES SYSTEM "dockables.dtd"&gt;
  51 *&lt;DOCKABLES&gt;
  52 *    &lt;DOCKABLE NAME="name"&gt;
  53 *        // Code to create the dockable
  54 *    &lt;/DOCKABLE&gt;
  55 *&lt;/DOCKABLES&gt;</pre>
  56 *
  57 * More than one <code>&lt;DOCKABLE&gt;<code> tag may be present. The code that
  58 * creates the dockable can reference any BeanShell built-in variable
  59 * (see {@link org.gjt.sp.jedit.BeanShell}), along with a variable
  60 * <code>position</code> whose value is one of
  61 * {@link #FLOATING}, {@link #TOP}, {@link #LEFT}, {@link #BOTTOM},
  62 * and {@link #RIGHT}.<p>
  63 *
  64 * The following properties must be defined for each dockable window:
  65 *
  66 * <ul>
  67 * <li><code><i>name</i>.title</code> - the string to show in the title bar
  68 * of the dockable.</li>
  69 * <li><code><i>name</i>.label</code> - the dockable's menu item label.</li>
  70 * </ul>
  71 *
  72 * A number of actions are automatically created for each dockable window:
  73 *
  74 * <ul>
  75 * <li><code><i>name</i></code> - opens the dockable window.</li>
  76 * <li><code><i>name</i>-toggle</code> - toggles the dockable window's visibility.</li>
  77 * <li><code><i>name</i>-float</code> - opens the dockable window in a new
  78 * floating window.</li>
  79 * </ul>
  80 *
  81 * Note that only the first action needs a <code>label</code> property, the
  82 * rest have automatically-generated labels.
  83 *
  84 * <b>Implementation details:</b><p>
  85 *
  86 * When an instance of this class is initialized by the {@link org.gjt.sp.jedit.View}
  87 * class, it
  88 * iterates through the list of registered dockable windows (from jEdit itself,
  89 * and any loaded plugins) and
  90 * examines options supplied by the user in the <b>Global
  91 * Options</b> dialog box. Any plugins designated for one of the
  92 * four docking positions are displayed.<p>
  93 *
  94 * To create an instance of a dockable window, the <code>DockableWindowManager</code>
  95 * finds and executes the BeanShell code extracted from the appropriate
  96 * <code>dockables.xml</code> file. This code will typically consist of a call
  97 * to the constructor of the dockable window component. The result of the
  98 * BeanShell expression, typically a newly constructed component, is placed
  99 * in a window managed by this class.
 100 *
 101 * @see org.gjt.sp.jedit.View#getDockableWindowManager()
 102 *
 103 * @author Slava Pestov
 104 * @author John Gellene (API documentation)
 105 * @version $Id: DockableWindowManager.java 5053 2004-05-29 01:55:26Z spestov $
 106 * @since jEdit 2.6pre3
 107 */
 108public class DockableWindowManager extends JPanel implements EBComponent
 109{
 110	//{{{ Static part of class
 111
 112	//{{{ Constants
 113	/**
 114	 * Floating position.
 115	 * @since jEdit 2.6pre3
 116	 */
 117	public static final String FLOATING = "floating";
 118
 119	/**
 120	 * Top position.
 121	 * @since jEdit 2.6pre3
 122	 */
 123	public static final String TOP = "top";
 124
 125	/**
 126	 * Left position.
 127	 * @since jEdit 2.6pre3
 128	 */
 129	public static final String LEFT = "left";
 130
 131	/**
 132	 * Bottom position.
 133	 * @since jEdit 2.6pre3
 134	 */
 135	public static final String BOTTOM = "bottom";
 136
 137	/**
 138	 * Right position.
 139	 * @since jEdit 2.6pre3
 140	 */
 141	public static final String RIGHT = "right";
 142	//}}}
 143
 144	//{{{ loadDockableWindows() method
 145	/**
 146	 * Plugins shouldn't need to call this method.
 147	 * @since jEdit 4.2pre1
 148	 */
 149	public static void loadDockableWindows(PluginJAR plugin, URL uri,
 150		PluginJAR.PluginCacheEntry cache)
 151	{
 152		Reader in = null;
 153
 154		try
 155		{
 156			Log.log(Log.DEBUG,DockableWindowManager.class,
 157				"Loading dockables from " + uri);
 158
 159			DockableListHandler dh = new DockableListHandler(plugin,uri);
 160			in = new BufferedReader(
 161				new InputStreamReader(
 162				uri.openStream()));
 163			XmlParser parser = new XmlParser();
 164			parser.setHandler(dh);
 165			parser.parse(null, null, in);
 166			if(cache != null)
 167			{
 168				cache.cachedDockableNames = dh.getCachedDockableNames();
 169				cache.cachedDockableActionFlags = dh.getCachedDockableActionFlags();
 170			}
 171		}
 172		catch(XmlException xe)
 173		{
 174			int line = xe.getLine();
 175			String message = xe.getMessage();
 176			Log.log(Log.ERROR,DockableWindowManager.class,uri + ":" + line
 177				+ ": " + message);
 178		}
 179		catch(Exception e)
 180		{
 181			Log.log(Log.ERROR,DockableWindowManager.class,e);
 182		}
 183		finally
 184		{
 185			try
 186			{
 187				if(in != null)
 188					in.close();
 189			}
 190			catch(IOException io)
 191			{
 192				Log.log(Log.ERROR,DockableWindowManager.class,io);
 193			}
 194		}
 195	} //}}}
 196
 197	//{{{ unloadDockableWindows() method
 198	/**
 199	 * Plugins shouldn't need to call this method.
 200	 * @since jEdit 4.2pre1
 201	 */
 202	public static void unloadDockableWindows(PluginJAR plugin)
 203	{
 204		Iterator entries = dockableWindowFactories.entrySet().iterator();
 205		while(entries.hasNext())
 206		{
 207			Map.Entry entry = (Map.Entry)entries.next();
 208			Factory factory = (Factory)entry.getValue();
 209			if(factory.plugin == plugin)
 210				entries.remove();
 211		}
 212	} //}}}
 213
 214	//{{{ cacheDockableWindows() method
 215	/**
 216	 * @since jEdit 4.2pre1
 217	 */
 218	public static void cacheDockableWindows(PluginJAR plugin,
 219		String[] name, boolean[] actions)
 220	{
 221		for(int i = 0; i < name.length; i++)
 222		{
 223			Factory factory = new Factory(plugin,
 224				name[i],null,actions[i]);
 225			dockableWindowFactories.put(name[i],factory);
 226		}
 227	} //}}}
 228
 229	//{{{ registerDockableWindow() method
 230	public static void registerDockableWindow(PluginJAR plugin,
 231		String name, String code, boolean actions)
 232	{
 233		Factory factory = (Factory)dockableWindowFactories.get(name);
 234		if(factory != null)
 235		{
 236			factory.code = code;
 237			factory.loaded = true;
 238		}
 239		else
 240		{
 241			factory = new Factory(plugin,name,code,actions);
 242			dockableWindowFactories.put(name,factory);
 243		}
 244	} //}}}
 245
 246	//{{{ getRegisteredDockableWindows() method
 247	public static String[] getRegisteredDockableWindows()
 248	{
 249		String[] retVal = new String[dockableWindowFactories.size()];
 250		Iterator entries = dockableWindowFactories.values().iterator();
 251		int i = 0;
 252		while(entries.hasNext())
 253		{
 254			Factory factory = (Factory)entries.next();
 255			retVal[i++] = factory.name;
 256		}
 257
 258		return retVal;
 259	} //}}}
 260
 261	//{{{ DockableListHandler class
 262	static class DockableListHandler extends HandlerBase
 263	{
 264		//{{{ DockableListHandler constructor
 265		DockableListHandler(PluginJAR plugin, URL uri)
 266		{
 267			this.plugin = plugin;
 268			this.uri = uri;
 269			stateStack = new Stack();
 270			actions = true;
 271
 272			cachedDockableNames = new LinkedList();
 273			cachedDockableActionFlags = new LinkedList();
 274		} //}}}
 275
 276		//{{{ resolveEntity() method
 277		public Object resolveEntity(String publicId, String systemId)
 278		{
 279			if("dockables.dtd".equals(systemId))
 280			{
 281				// this will result in a slight speed up, since we
 282				// don't need to read the DTD anyway, as AElfred is
 283				// non-validating
 284				return new StringReader("<!-- -->");
 285
 286				/* try
 287				{
 288					return new BufferedReader(new InputStreamReader(
 289						getClass().getResourceAsStream
 290						("/org/gjt/sp/jedit/dockables.dtd")));
 291				}
 292				catch(Exception e)
 293				{
 294					Log.log(Log.ERROR,this,"Error while opening"
 295						+ " dockables.dtd:");
 296					Log.log(Log.ERROR,this,e);
 297				} */
 298			}
 299
 300			return null;
 301		} //}}}
 302
 303		//{{{ attribute() method
 304		public void attribute(String aname, String value, boolean isSpecified)
 305		{
 306			aname = (aname == null) ? null : aname.intern();
 307			value = (value == null) ? null : value.intern();
 308
 309			if(aname == "NAME")
 310				dockableName = value;
 311			else if(aname == "NO_ACTIONS")
 312				actions = (value == "FALSE");
 313		} //}}}
 314
 315		//{{{ doctypeDecl() method
 316		public void doctypeDecl(String name, String publicId,
 317			String systemId) throws Exception
 318		{
 319			if("DOCKABLES".equals(name))
 320				return;
 321
 322			Log.log(Log.ERROR,this,uri + ": DOCTYPE must be DOCKABLES");
 323		} //}}}
 324
 325		//{{{ charData() method
 326		public void charData(char[] c, int off, int len)
 327		{
 328			String tag = peekElement();
 329			String text = new String(c, off, len);
 330
 331			if (tag == "DOCKABLE")
 332			{
 333				code = text;
 334			}
 335		} //}}}
 336
 337		//{{{ startElement() method
 338		public void startElement(String tag)
 339		{
 340			tag = pushElement(tag);
 341		} //}}}
 342
 343		//{{{ endElement() method
 344		public void endElement(String name)
 345		{
 346			if(name == null)
 347				return;
 348
 349			String tag = peekElement();
 350
 351			if(name.equals(tag))
 352			{
 353				if(tag == "DOCKABLE")
 354				{
 355					registerDockableWindow(plugin,
 356						dockableName,code,actions);
 357					cachedDockableNames.add(dockableName);
 358					cachedDockableActionFlags.add(
 359						Boolean.valueOf(actions));
 360					// make default be true for the next
 361					// action
 362					actions = true;
 363				}
 364
 365				popElement();
 366			}
 367			else
 368			{
 369				// can't happen
 370				throw new InternalError();
 371			}
 372		} //}}}
 373
 374		//{{{ startDocument() method
 375		public void startDocument()
 376		{
 377			try
 378			{
 379				pushElement(null);
 380			}
 381			catch (Exception e)
 382			{
 383				e.printStackTrace();
 384			}
 385		} //}}}
 386
 387		//{{{ getCachedDockableNames() method
 388		public String[] getCachedDockableNames()
 389		{
 390			return (String[])cachedDockableNames.toArray(new String[cachedDockableNames.size()]);
 391		} //}}}
 392
 393		//{{{ getCachedDockableActionFlags() method
 394		public boolean[] getCachedDockableActionFlags()
 395		{
 396			boolean[] returnValue = new boolean[
 397				cachedDockableActionFlags.size()];
 398			Iterator iter = cachedDockableActionFlags.iterator();
 399			int i = 0;
 400			while(iter.hasNext())
 401			{
 402				boolean flag = ((Boolean)iter.next())
 403					.booleanValue();
 404				returnValue[i++] = flag;
 405			}
 406
 407			return returnValue;
 408		} //}}}
 409
 410		//{{{ Private members
 411
 412		//{{{ Instance variables
 413		private PluginJAR plugin;
 414		private URL uri;
 415
 416		private java.util.List cachedDockableNames;
 417		private java.util.List cachedDockableActionFlags;
 418
 419		private String dockableName;
 420		private String code;
 421		private boolean actions;
 422
 423		private Stack stateStack;
 424		//}}}
 425
 426		//{{{ pushElement() method
 427		private String pushElement(String name)
 428		{
 429			name = (name == null) ? null : name.intern();
 430
 431			stateStack.push(name);
 432
 433			return name;
 434		} //}}}
 435
 436		//{{{ peekElement() method
 437		private String peekElement()
 438		{
 439			return (String) stateStack.peek();
 440		} //}}}
 441
 442		//{{{ popElement() method
 443		private String popElement()
 444		{
 445			return (String) stateStack.pop();
 446		} //}}}
 447
 448		//}}}
 449	} //}}}
 450
 451	//{{{ Factory class
 452	static class Factory
 453	{
 454		PluginJAR plugin;
 455		String name;
 456		String code;
 457		boolean loaded;
 458
 459		//{{{ Factory constructor
 460		Factory(PluginJAR plugin, String name, String code,
 461			boolean actions)
 462		{
 463			this.plugin = plugin;
 464			this.name = name;
 465			this.code = code;
 466
 467			if(code != null)
 468				loaded = true;
 469
 470			if(actions)
 471			{
 472				ActionSet actionSet = (plugin == null
 473					? jEdit.getBuiltInActionSet()
 474					: plugin.getActionSet());
 475				actionSet.addAction(new OpenAction(name));
 476				actionSet.addAction(new ToggleAction(name));
 477				actionSet.addAction(new FloatAction(name));
 478
 479				String label = jEdit.getProperty(name
 480					+ ".label");
 481				if(label == null)
 482					label = "NO LABEL PROPERTY: " + name;
 483
 484				String[] args = { label };
 485				jEdit.setTemporaryProperty(name + ".label",
 486					label);
 487				jEdit.setTemporaryProperty(name
 488					+ "-toggle.label",
 489					jEdit.getProperty(
 490					"view.docking.toggle.label",args));
 491				jEdit.setTemporaryProperty(name
 492					+ "-toggle.toggle","true");
 493				jEdit.setTemporaryProperty(name
 494					+ "-float.label",
 495					jEdit.getProperty(
 496					"view.docking.float.label",args));
 497			}
 498		} //}}}
 499
 500		//{{{ load() method
 501		void load()
 502		{
 503			if(loaded)
 504				return;
 505
 506			loadDockableWindows(plugin,plugin.getDockablesURI(),null);
 507		} //}}}
 508
 509		//{{{ createDockableWindow() method
 510		JComponent createDockableWindow(View view, String position)
 511		{
 512			load();
 513
 514			if(!loaded)
 515			{
 516				Log.log(Log.WARNING,this,"Outdated cache");
 517				return null;
 518			}
 519
 520			NameSpace nameSpace = new NameSpace(
 521				BeanShell.getNameSpace(),
 522				"DockableWindowManager.Factory"
 523				+ ".createDockableWindow()");
 524			try
 525			{
 526				nameSpace.setVariable(
 527					"position",position);
 528			}
 529			catch(UtilEvalError e)
 530			{
 531				Log.log(Log.ERROR,this,e);
 532			}
 533			JComponent win = (JComponent)BeanShell.eval(view,
 534				nameSpace,code);
 535			return win;
 536		} //}}}
 537
 538		//{{{ OpenAction class
 539		static class OpenAction extends EditAction
 540		{
 541			private String dockable;
 542
 543			//{{{ OpenAction constructor
 544			OpenAction(String name)
 545			{
 546				super(name);
 547				this.dockable = name;
 548			} //}}}
 549
 550			//{{{ invoke() method
 551			public void invoke(View view)
 552			{
 553				view.getDockableWindowManager()
 554					.showDockableWindow(dockable);
 555			} //}}}
 556
 557			//{{{ getCode() method
 558			public String getCode()
 559			{
 560				return "view.getDockableWindowManager()"
 561					+ ".showDockableWindow(\"" + dockable + "\");";
 562			} //}}}
 563		} //}}}
 564
 565		//{{{ ToggleAction class
 566		static class ToggleAction extends EditAction
 567		{
 568			private String dockable;
 569
 570			//{{{ ToggleAction constructor
 571			ToggleAction(String name)
 572			{
 573				super(name + "-toggle");
 574				this.dockable = name;
 575			} //}}}
 576
 577			//{{{ invoke() method
 578			public void invoke(View view)
 579			{
 580				view.getDockableWindowManager()
 581					.toggleDockableWindow(dockable);
 582			} //}}}
 583
 584			//{{{ isSelected() method
 585			public boolean isSelected(View view)
 586			{
 587				return view.getDockableWindowManager()
 588					.isDockableWindowVisible(dockable);
 589			} //}}}
 590
 591			//{{{ getCode() method
 592			public String getCode()
 593			{
 594				return "view.getDockableWindowManager()"
 595					+ ".toggleDockableWindow(\"" + dockable + "\");";
 596			} //}}}
 597		} //}}}
 598
 599		//{{{ FloatAction class
 600		static class FloatAction extends EditAction
 601		{
 602			private String dockable;
 603
 604			//{{{ FloatAction constructor
 605			FloatAction(String name)
 606			{
 607				super(name + "-float");
 608				this.dockable = name;
 609			} //}}}
 610
 611			//{{{ invoke() method
 612			public void invoke(View view)
 613			{
 614				view.getDockableWindowManager()
 615					.floatDockableWindow(dockable);
 616			} //}}}
 617
 618			//{{{ getCode() method
 619			public String getCode()
 620			{
 621				return "view.getDockableWindowManager()"
 622					+ ".floatDockableWindow(\"" + dockable + "\");";
 623			} //}}}
 624		} //}}}
 625	} //}}}
 626
 627	private static HashMap dockableWindowFactories;
 628
 629	//{{{ Static initializer
 630	static
 631	{
 632		dockableWindowFactories = new HashMap();
 633	} //}}}
 634
 635	//}}}
 636
 637	//{{{ Instance part of class
 638
 639	//{{{ DockableWindowManager constructor
 640	/**
 641	 * Creates a new dockable window manager.
 642	 * @param view The view
 643	 * @since jEdit 2.6pre3
 644	 */
 645	public DockableWindowManager(View view, View.ViewConfig config)
 646	{
 647		setLayout(new DockableLayout());
 648		this.view = view;
 649		windows = new Hashtable();
 650		clones = new ArrayList();
 651
 652		top = new PanelWindowContainer(this,TOP,config.topPos);
 653		left = new PanelWindowContainer(this,LEFT,config.leftPos);
 654		bottom = new PanelWindowContainer(this,BOTTOM,config.bottomPos);
 655		right = new PanelWindowContainer(this,RIGHT,config.rightPos);
 656
 657		add(DockableLayout.TOP_BUTTONS,top.buttonPanel);
 658		add(DockableLayout.LEFT_BUTTONS,left.buttonPanel);
 659		add(DockableLayout.BOTTOM_BUTTONS,bottom.buttonPanel);
 660		add(DockableLayout.RIGHT_BUTTONS,right.buttonPanel);
 661
 662		add(TOP,top.dockablePanel);
 663		add(LEFT,left.dockablePanel);
 664		add(BOTTOM,bottom.dockablePanel);
 665		add(RIGHT,right.dockablePanel);
 666	} //}}}
 667
 668	//{{{ init() method
 669	/**
 670	 * Initialises dockable window manager. Do not call this method directly.
 671	 */
 672	public void init()
 673	{
 674		EditBus.addToBus(this);
 675
 676		Iterator entries = dockableWindowFactories.values().iterator();
 677
 678		while(entries.hasNext())
 679			addEntry((Factory)entries.next());
 680
 681		propertiesChanged();
 682	} //}}}
 683
 684	//{{{ getView() method
 685	/**
 686	 * Returns this dockable window manager's view.
 687	 * @since jEdit 4.0pre2
 688	 */
 689	public View getView()
 690	{
 691		return view;
 692	} //}}}
 693
 694	//{{{ floatDockableWindow() method
 695	/**
 696	 * Opens a new instance of the specified dockable window in a floating
 697	 * container.
 698	 * @param name The dockable window name
 699	 * @return The new dockable window instance
 700	 * @since jEdit 4.1pre2
 701	 */
 702	public JComponent floatDockableWindow(String name)
 703	{
 704		Entry entry = (Entry)windows.get(name);
 705		if(entry == null)
 706		{
 707			Log.log(Log.ERROR,this,"Unknown dockable window: " + name);
 708			return null;
 709		}
 710
 711		// create a copy of this dockable window and float it
 712		Entry newEntry = new Entry(entry.factory,FLOATING);
 713		newEntry.win = newEntry.factory.createDockableWindow(view,FLOATING);
 714		if(newEntry.win != null)
 715		{
 716			newEntry.container = new FloatingWindowContainer(this,true);
 717			newEntry.container.register(newEntry);
 718			newEntry.container.show(newEntry);
 719		}
 720
 721		clones.add(newEntry);
 722		return newEntry.win;
 723	} //}}}
 724
 725	//{{{ showDockableWindow() method
 726	/**
 727	 * Opens the specified dockable window.
 728	 * @param name The dockable window name
 729	 * @since jEdit 2.6pre3
 730	 */
 731	public void showDockableWindow(String name)
 732	{
 733		Entry entry = (Entry)windows.get(name);
 734		if(entry == null)
 735		{
 736			Log.log(Log.ERROR,this,"Unknown dockable window: " + name);
 737			return;
 738		}
 739
 740		if(entry.win == null)
 741		{
 742			entry.win = entry.factory.createDockableWindow(
 743				view,entry.position);
 744		}
 745
 746		if(entry.win != null)
 747		{
 748			if(entry.position.equals(FLOATING)
 749				&& entry.container == null)
 750			{
 751				entry.container = new FloatingWindowContainer(
 752					this,view.isPlainView());
 753				entry.container.register(entry);
 754			}
 755
 756			entry.container.show(entry);
 757		}
 758		else
 759			/* an error occurred */;
 760	} //}}}
 761
 762	//{{{ addDockableWindow() method
 763	/**
 764	 * Opens the specified dockable window. As of jEdit 4.0pre1, has the
 765	 * same effect as calling showDockableWindow().
 766	 * @param name The dockable window name
 767	 * @since jEdit 2.6pre3
 768	 */
 769	public void addDockableWindow(String name)
 770	{
 771		showDockableWindow(name);
 772	} //}}}
 773
 774	//{{{ hideDockableWindow() method
 775	/**
 776	 * Hides the specified dockable window.
 777	 * @param name The dockable window name
 778	 * @since jEdit 2.6pre3
 779	 */
 780	public void hideDockableWindow(String name)
 781	{
 782		Entry entry = (Entry)windows.get(name);
 783		if(entry == null)
 784		{
 785			Log.log(Log.ERROR,this,"Unknown dockable window: " + name);
 786			return;
 787		}
 788
 789		if(entry.win == null)
 790			return;
 791
 792		entry.container.show(null);
 793	} //}}}
 794
 795	//{{{ removeDockableWindow() method
 796	/**
 797	 * Hides the specified dockable window. As of jEdit 4.2pre1, has the
 798	 * same effect as calling hideDockableWindow().
 799	 * @param name The dockable window name
 800	 * @since jEdit 4.2pre1
 801	 */
 802	public void removeDockableWindow(String name)
 803	{
 804		hideDockableWindow(name);
 805	} //}}}
 806
 807	//{{{ toggleDockableWindow() method
 808	/**
 809	 * Toggles the visibility of the specified dockable window.
 810	 * @param name The dockable window name
 811	 */
 812	public void toggleDockableWindow(String name)
 813	{
 814		if(isDockableWindowVisible(name))
 815			removeDockableWindow(name);
 816		else
 817			addDockableWindow(name);
 818	} //}}}
 819
 820	//{{{ getDockableWindow() method
 821	/**
 822	 * Returns the specified dockable window.
 823	 *
 824	 * Note that this method
 825	 * will return null if the dockable has not been added yet.
 826	 * Make sure you call {@link #addDockableWindow(String)} first.
 827	 *
 828	 * @param name The name of the dockable window
 829	 * @since jEdit 4.1pre2
 830	 */
 831	public JComponent getDockableWindow(String name)
 832	{
 833		return getDockable(name);
 834	} //}}}
 835
 836	//{{{ getDockable() method
 837	/**
 838	 * Returns the specified dockable window.
 839	 *
 840	 * Note that this method
 841	 * will return null if the dockable has not been added yet.
 842	 * Make sure you call {@link #addDockableWindow(String)} first.
 843	 *
 844	 * For historical reasons, this
 845	 * does the same thing as {@link #getDockableWindow(String)}.
 846	 *
 847	 * @param name The name of the dockable window
 848	 * @since jEdit 4.0pre1
 849	 */
 850	public JComponent getDockable(String name)
 851	{
 852		Entry entry = (Entry)windows.get(name);
 853		if(entry == null || entry.win == null)
 854			return null;
 855		else
 856			return entry.win;
 857	} //}}}
 858
 859	//{{{ getDockableTitle() method
 860	/**
 861	 * Returns the title of the specified dockable window.
 862	 * @param name The name of the dockable window.
 863	 * @since jEdit 4.1pre5
 864	 */
 865	public String getDockableTitle(String name)
 866	{
 867		String title = jEdit.getProperty(name + ".title");
 868		if(title == null)
 869			return "NO TITLE PROPERTY: " + name;
 870		else
 871			return title;
 872	} //}}}
 873
 874	//{{{ isDockableWindowVisible() method
 875	/**
 876	 * Returns if the specified dockable window is visible.
 877	 * @param name The dockable window name
 878	 */
 879	public boolean isDockableWindowVisible(String name)
 880	{
 881		Entry entry = (Entry)windows.get(name);
 882		if(entry == null || entry.win == null)
 883			return false;
 884		else
 885			return entry.container.isVisible(entry);
 886	} //}}}
 887
 888	//{{{ isDockableWindowDocked() method
 889	/**
 890	 * Returns if the specified dockable window is docked into the
 891	 * view.
 892	 * @param name The dockable's name
 893	 * @since jEdit 4.0pre2
 894	 */
 895	public boolean isDockableWindowDocked(String name)
 896	{
 897		Entry entry = (Entry)windows.get(name);
 898		if(entry == null)
 899			return false;
 900		else
 901			return !entry.position.equals(FLOATING);
 902	} //}}}
 903
 904	//{{{ closeCurrentArea() method
 905	/**
 906	 * Closes the currently focused docking area.
 907	 * @since jEdit 4.1pre3
 908	 */
 909	public void closeCurrentArea()
 910	{
 911		// I don't know of any other way to fix this, since invoking this
 912		// command from a menu results in the focus owner being the menu
 913		// until the menu goes away.
 914		SwingUtilities.invokeLater(new Runnable()
 915		{
 916			public void run()
 917			{
 918				Component comp = view.getFocusOwner();
 919				while(comp != null)
 920				{
 921					//System.err.println(comp.getClass());
 922					if(comp instanceof PanelWindowContainer
 923						.DockablePanel)
 924					{
 925						PanelWindowContainer container =
 926							((PanelWindowContainer.DockablePanel)
 927							comp).getWindowContainer();
 928						container.show(null);
 929						return;
 930					}
 931
 932					comp = comp.getParent();
 933				}
 934
 935				getToolkit().beep();
 936			}
 937		});
 938	} //}}}
 939
 940	//{{{ close() method
 941	/**
 942	 * Called when the view is being closed.
 943	 * @since jEdit 2.6pre3
 944	 */
 945	public void close()
 946	{
 947		EditBus.removeFromBus(this);
 948
 949		Iterator iter = windows.values().iterator();
 950		while(iter.hasNext())
 951		{
 952			Entry entry = (Entry)iter.next();
 953			if(entry.win != null)
 954			{
 955				entry.container.unregister(entry);
 956			}
 957		}
 958
 959		iter = clones.iterator();
 960		while(iter.hasNext())
 961		{
 962			Entry entry = (Entry)iter.next();
 963			if(entry.win != null)
 964			{
 965				entry.container.unregister(entry);
 966			}
 967		}
 968	} //}}}
 969
 970	//{{{ getTopDockingArea() method
 971	public PanelWindowContainer getTopDockingArea()
 972	{
 973		return top;
 974	} //}}}
 975
 976	//{{{ getLeftDockingArea() method
 977	public PanelWindowContainer getLeftDockingArea()
 978	{
 979		return left;
 980	} //}}}
 981
 982	//{{{ getBottomDockingArea() method
 983	public PanelWindowContainer getBottomDockingArea()
 984	{
 985		return bottom;
 986	} //}}}
 987
 988	//{{{ getRightDockingArea() method
 989	public PanelWindowContainer getRightDockingArea()
 990	{
 991		return right;
 992	} //}}}
 993
 994	//{{{ createPopupMenu() method
 995	public JPopupMenu createPopupMenu(
 996		final DockableWindowContainer container,
 997		final String dockable,
 998		final boolean clone)
 999	{
1000		JPopupMenu popup = new JPopupMenu();
1001		if(dockable == null && container instanceof PanelWindowContainer)
1002		{
1003			ActionListener listener = new ActionListener()
1004			{
1005				public void actionPerformed(ActionEvent evt)
1006				{
1007					showDockableWindow(evt.getActionCommand());
1008				}
1009			};
1010
1011			String[] dockables = ((PanelWindowContainer)
1012				container).getDockables();
1013			for(int i = 0; i < dockables.length; i++)
1014			{
1015				String name = dockables[i];
1016				JMenuItem item = new JMenuItem(getDockableTitle(name));
1017				item.setActionCommand(name);
1018				item.addActionListener(listener);
1019				popup.add(item);
1020			}
1021		}
1022		else
1023		{
1024			JMenuItem caption = new JMenuItem(getDockableTitle(dockable));
1025			caption.setEnabled(false);
1026			popup.add(caption);
1027			popup.addSeparator();
1028			String currentPos = jEdit.getProperty(dockable + ".dock-position",FLOATING);
1029			if(!clone)
1030			{
1031				String[] positions = { FLOATING, TOP, LEFT, BOTTOM, RIGHT };
1032				for(int i = 0; i < positions.length; i++)
1033				{
1034					final String pos = positions[i];
1035					if(pos.equals(currentPos))
1036						continue;
1037
1038					JMenuItem moveMenuItem = new JMenuItem(jEdit.getProperty("view.docking.menu-"
1039						+ pos));
1040
1041					moveMenuItem.addActionListener(new ActionListener()
1042					{
1043						public void actionPerformed(ActionEvent evt)
1044						{
1045							jEdit.setProperty(dockable + ".dock-position",pos);
1046							EditBus.send(new DockableWindowUpdate(
1047								DockableWindowManager.this,
1048								DockableWindowUpdate.PROPERTIES_CHANGED,
1049								null
1050							));
1051							showDockableWindow(dockable);
1052						}
1053					});
1054					popup.add(moveMenuItem);
1055				}
1056
1057				popup.addSeparator();
1058			}
1059
1060			JMenuItem cloneMenuItem = new JMenuItem(jEdit.getProperty("view.docking.menu-clone"));
1061
1062			cloneMenuItem.addActionListener(new ActionListener()
1063			{
1064				public void actionPerformed(ActionEvent evt)
1065				{
1066					floatDockableWindow(dockable);
1067				}
1068			});
1069			popup.add(cloneMenuItem);
1070
1071			popup.addSeparator();
1072
1073			JMenuItem closeMenuItem = new JMenuItem(jEdit.getProperty("view.docking.menu-close"));
1074
1075			closeMenuItem.addActionListener(new ActionListener()
1076			{
1077				public void actionPerformed(ActionEvent evt)
1078				{
1079					if(clone)
1080						((FloatingWindowContainer)container).dispose();
1081					else
1082						removeDockableWindow(dockable);
1083				}
1084			});
1085			popup.add(closeMenuItem);
1086
1087			if(!(clone || currentPos.equals(FLOATING)))
1088			{
1089				JMenuItem undockMenuItem = new JMenuItem(jEdit.getProperty("view.docking.menu-undock"));
1090
1091				undockMenuItem.addActionListener(new ActionListener()
1092				{
1093					public void actionPerformed(ActionEvent evt)
1094					{
1095						jEdit.setProperty(dockable + ".dock-position",FLOATING);
1096						EditBus.send(new DockableWindowUpdate(
1097							DockableWindowManager.this,
1098							DockableWindowUpdate.PROPERTIES_CHANGED,
1099							null
1100						));
1101					}
1102				});
1103				popup.add(undockMenuItem);
1104			}
1105		}
1106
1107		return popup;
1108	} //}}}
1109
1110	//{{{ paintChildren() method
1111	public void paintChildren(Graphics g)
1112	{
1113		super.paintChildren(g);
1114
1115		if(resizeRect != null)
1116		{
1117			g.setColor(Color.darkGray);
1118			g.fillRect(resizeRect.x,resizeRect.y,
1119				resizeRect.width,resizeRect.height);
1120		}
1121	} //}}}
1122
1123	//{{{ handleMessage() method
1124	public void handleMessage(EBMessage msg)
1125	{
1126		if(msg instanceof DockableWindowUpdate)
1127		{
1128			if(((DockableWindowUpdate)msg).getWhat()
1129				== DockableWindowUpdate.PROPERTIES_CHANGED)
1130				propertiesChanged();
1131		}
1132		else if(msg instanceof PropertiesChanged)
1133			propertiesChanged();
1134		else if(msg instanceof PluginUpdate)
1135		{
1136			PluginUpdate pmsg = (PluginUpdate)msg;
1137			if(pmsg.getWhat() == PluginUpdate.LOADED)
1138			{
1139				Iterator iter = dockableWindowFactories
1140					.values().iterator();
1141
1142				while(iter.hasNext())
1143				{
1144					Factory factory = (Factory)iter.next();
1145					if(factory.plugin == pmsg.getPluginJAR())
1146						addEntry(factory);
1147				}
1148
1149				propertiesChanged();
1150			}
1151			else if(pmsg.isExiting())
1152			{
1153				// we don't care
1154			}
1155			else if(pmsg.getWhat() == PluginUpdate.DEACTIVATED)
1156			{
1157				Iterator iter = getAllPluginEntries(
1158					pmsg.getPluginJAR(),false);
1159				while(iter.hasNext())
1160				{
1161					Entry entry = (Entry)iter.next();
1162					if(entry.container != null)
1163						entry.container.remove(entry);
1164				}
1165			}
1166			else if(pmsg.getWhat() == PluginUpdate.UNLOADED)
1167			{
1168				Iterator iter = getAllPluginEntries(
1169					pmsg.getPluginJAR(),true);
1170				while(iter.hasNext())
1171				{
1172					Entry entry = (Entry)iter.next();
1173					if(entry.container != null)
1174					{
1175						entry.container.unregister(entry);
1176						entry.win = null;
1177						entry.container = null;
1178					}
1179				}
1180			}
1181		}
1182	} //}}}
1183
1184	//{{{ Package-private members
1185	int resizePos;
1186	Rectangle resizeRect;
1187
1188	//{{{ setResizePos() method
1189	void setResizePos(int resizePos, PanelWindowContainer resizing)
1190	{
1191		this.resizePos = resizePos;
1192
1193		if(resizePos < 0)
1194			resizePos = 0;
1195
1196		Rectangle newResizeRect = new Rectangle(0,0,
1197			PanelWindowContainer.SPLITTER_WIDTH - 2,
1198			PanelWindowContainer.SPLITTER_WIDTH - 2);
1199		if(resizing == top)
1200		{
1201			resizePos = Math.min(resizePos,getHeight()
1202				- top.buttonPanel.getHeight()
1203				- bottom.dockablePanel.getHeight()
1204				- bottom.buttonPanel.getHeight()
1205				- PanelWindowContainer.SPLITTER_WIDTH);
1206			newResizeRect.x = top.dockablePanel.getX() + 1;
1207			newResizeRect.y = resizePos + top.buttonPanel.getHeight() + 1;
1208			newResizeRect.width = top.dockablePanel.getWidth() - 2;
1209		}
1210		else if(resizing == left)
1211		{
1212			resizePos = Math.min(resizePos,getWidth()
1213				- left.buttonPanel.getWidth()
1214				- right.dockablePanel.getWidth()
1215				- right.buttonPanel.getWidth()
1216				- PanelWindowContainer.SPLITTER_WIDTH);
1217			newResizeRect.x = resizePos + left.buttonPanel.getWidth() + 1;
1218			newResizeRect.y = left.dockablePanel.getY() + 1;
1219			newResizeRect.height = left.dockablePanel.getHeight() - 2;
1220		}
1221		else if(resizing == bottom)
1222		{
1223			resizePos = Math.min(resizePos,getHeight()
1224				- bottom.buttonPanel.getHeight()
1225				- top.dockablePanel.getHeight()
1226				- top.buttonPanel.getHeight()
1227				- PanelWindowContainer.SPLITTER_WIDTH);
1228			newResizeRect.x = bottom.dockablePanel.getX() + 1;
1229			newResizeRect.y = getHeight() - bottom.buttonPanel.getHeight() - resizePos
1230				- PanelWindowContainer.SPLITTER_WIDTH + 2;
1231			newResizeRect.width = bottom.dockablePanel.getWidth() - 2;
1232		}
1233		else if(resizing == right)
1234		{
1235			resizePos = Math.min(resizePos,getWidth()
1236				- right.buttonPanel.getWidth()
1237				- left.dockablePanel.getWidth()
1238				- left.buttonPanel.getWidth()
1239				- PanelWindowContainer.SPLITTER_WIDTH);
1240			newResizeRect.x = getWidth() - right.buttonPanel.getWidth() - resizePos
1241				- PanelWindowContainer.SPLITTER_WIDTH + 1;
1242			newResizeRect.y = right.dockablePanel.getY() + 1;
1243			newResizeRect.height = right.dockablePanel.getHeight() - 2;
1244		}
1245
1246		Rectangle toRepaint;
1247		if(resizeRect == null)
1248			toRepaint = newResizeRect;
1249		else
1250			toRepaint = resizeRect.union(newResizeRect);
1251		resizeRect = newResizeRect;
1252		repaint(toRepaint);
1253	} //}}}
1254
1255	//{{{ finishResizing() method
1256	void finishResizing()
1257	{
1258		resizeRect = null;
1259		repaint();
1260	} //}}}
1261
1262	//}}}
1263
1264	//{{{ Private members
1265	private View view;
1266	private Hashtable windows;
1267	private boolean alternateLayout;
1268	private PanelWindowContainer left;
1269	private PanelWindowContainer right;
1270	private PanelWindowContainer top;
1271	private PanelWindowContainer bottom;
1272	private ArrayList clones;
1273
1274	//{{{ propertiesChanged() method
1275	private void propertiesChanged()
1276	{
1277		if(view.isPlainView())
1278			return;
1279
1280		alternateLayout = jEdit.getBooleanProperty("view.docking.alternateLayout");
1281
1282		String[] windowList = getRegisteredDockableWindows();
1283
1284		for(int i = 0; i < windowList.length; i++)
1285		{
1286			String dockable = windowList[i];
1287			Entry entry = (Entry)windows.get(dockable);
1288
1289			String newPosition = jEdit.getProperty(dockable
1290				+ ".dock-position",FLOATING);
1291			if(newPosition.equals(entry.position))
1292			{
1293				continue;
1294			}
1295
1296			entry.position = newPosition;
1297			if(entry.container != null)
1298			{
1299				entry.container.unregister(entry);
1300				entry.container = null;
1301				entry.win = null;
1302			}
1303
1304			if(newPosition.equals(FLOATING))
1305				/* do nothing */;
1306			else
1307			{
1308				if(newPosition.equals(TOP))
1309					entry.container = top;
1310				else if(newPosition.equals(LEFT))
1311					entry.container = left;
1312				else if(newPosition.equals(BOTTOM))
1313					entry.container = bottom;
1314				else if(newPosition.equals(RIGHT))
1315					entry.container = right;
1316				else
1317				{
1318					Log.log(Log.WARNING,this,
1319						"Unknown position: "
1320						+ newPosition);
1321					continue;
1322				}
1323
1324				entry.container.register(entry);
1325			}
1326		}
1327
1328		top.sortDockables();
1329		left.sortDockables();
1330		bottom.sortDockables();
1331		right.sortDockables();
1332
1333		revalidate();
1334		repaint();
1335	} //}}}
1336
1337	//{{{ addEntry() method
1338	private void addEntry(Factory factory)
1339	{
1340		Entry e;
1341		if(view.isPlainView())
1342		{
1343			// don't show menu items to dock into a plain view
1344			e = new Entry(factory,FLOATING);
1345		}
1346		else
1347		{
1348			e = new Entry(factory);
1349			if(e.position.equals(FLOATING))
1350				/* nothing to do */;
1351			else if(e.position.equals(TOP))
1352				e.container = top;
1353			else if(e.position.equals(LEFT))
1354				e.container = left;
1355			else if(e.position.equals(BOTTOM))
1356				e.container = bottom;
1357			else if(e.position.equals(RIGHT))
1358				e.container = right;
1359			else
1360			{
1361				Log.log(Log.WARNING,this,
1362					"Unknown position: "
1363					+ e.position);
1364			}
1365
1366			if(e.container != null)
1367				e.container.register(e);
1368		}
1369		windows.put(factory.name,e);
1370	} //}}}
1371
1372	//{{{ getAllPluginEntries() method
1373	/**
1374	 * If remove is false, only remove from clones list, otherwise remove
1375	 * from both entries and clones.
1376	 */
1377	private Iterator getAllPluginEntries(PluginJAR plugin, boolean remove)
1378	{
1379		java.util.List returnValue = new LinkedList();
1380		Iterator iter = windows.values().iterator();
1381		while(iter.hasNext())
1382		{
1383			Entry entry = (Entry)iter.next();
1384			if(entry.factory.plugin == plugin)
1385			{
1386				returnValue.add(entry);
1387				if(remove)
1388					iter.remove();
1389			}
1390		}
1391
1392		iter = clones.iterator();
1393		while(iter.hasNext())
1394		{
1395			Entry entry = (Entry)iter.next();
1396			if(entry.factory.plugin == plugin)
1397			{
1398				returnValue.add(entry);
1399				iter.remove();
1400			}
1401		}
1402
1403		return returnValue.iterator();
1404	} //}}}
1405
1406	//}}}
1407
1408	//}}}
1409
1410	//{{{ DockableLayout class
1411	public class DockableLayout implements LayoutManager2
1412	{
1413		// for backwards compatibility with plugins that fiddle with
1414		// jEdit's UI layout
1415		static final String CENTER = BorderLayout.CENTER;
1416
1417		public static final String TOP_TOOLBARS = "top-toolbars";
1418		public static final String BOTTOM_TOOLBARS = "bottom-toolbars";
1419
1420		static final String TOP_BUTTONS = "top-buttons";
1421		static final String LEFT_BUTTONS = "left-buttons";
1422		static final String BOTTOM_BUTTONS = "bottom-buttons";
1423		static final String RIGHT_BUTTONS = "right-buttons";
1424
1425		Component topToolbars, bottomToolbars;
1426		Component center;
1427		Component top, left, bottom, right;
1428		Component topButtons, leftButtons, bottomButtons, rightButtons;
1429
1430		//{{{ addLayoutComponent() method
1431		public void addLayoutComponent(String name, Component comp)
1432		{
1433			addLayoutComponent(comp,name);
1434		} //}}}
1435
1436		//{{{ addLayoutComponent() method
1437		public void addLayoutComponent(Component comp, Object cons)
1438		{
1439			if(cons == null || CENTER.equals(cons))
1440				center = comp;
1441			else if(TOP_TOOLBARS.equals(cons))
1442				topToolbars = comp;
1443			else if(BOTTOM_TOOLBARS.equals(cons))
1444				bottomToolbars = comp;
1445			else if(TOP.equals(cons))
1446				top = comp;
1447			else if(LEFT.equals(cons))
1448				left = comp;
1449			else if(BOTTOM.equals(cons))
1450				bottom = comp;
1451			else if(RIGHT.equals(cons))
1452				right = comp;
1453			else if(TOP_BUTTONS.equals(cons))
1454				topButtons = comp;
1455			else if(LEFT_BUTTONS.equals(cons))
1456				leftButtons = comp;
1457			else if(BOTTOM_BUTTONS.equals(cons))
1458				bottomButtons = comp;
1459			else if(RIGHT_BUTTONS.equals(cons))
1460				rightButtons = comp;
1461		} //}}}
1462
1463		//{{{ removeLayoutComponent() method
1464		public void removeLayoutComponent(Component comp)
1465		{
1466			if(center == comp)
1467				center = null;
1468			if(comp == topToolbars)
1469				topToolbars = null;
1470			if(comp == bottomToolbars)
1471				bottomToolbars = null;
1472			{
1473				// none of the others are ever meant to be
1474				// removed. retarded, eh? this needs to be
1475				// fixed eventually, for plugins might
1476				// want to do weird stuff to jEdit's UI
1477			}
1478		} //}}}
1479
1480		//{{{ preferredLayoutSize() method
1481		public Dimension preferredLayoutSize(Container parent)
1482		{
1483			Dimension prefSize = new Dimension(0,0);
1484			Dimension _top = top.getPreferredSize();
1485			Dimension _left = left.getPreferredSize();
1486			Dimension _bottom = bottom.getPreferredSize();
1487			Dimension _right = right.getPreferredSize();
1488			Dimension _topButtons = topButtons.getPreferredSize();
1489			Dimension _leftButtons = leftButtons.getPreferredSize();
1490			Dimension _bottomButtons = bottomButtons.getPreferredSize();
1491			Dimension _rightButtons = rightButtons.getPreferredSize();
1492			Dimension _center = (center == null
1493				? new Dimension(0,0)
1494				: center.getPreferredSize());
1495			Dimension _topToolbars = (topToolbars == null
1496				? new Dimension(0,0)
1497				: topToolbars.getPreferredSize());
1498			Dimension _bottomToolbars = (bottomToolbars == null
1499				? new Dimension(0,0)
1500				: bottomToolbars.getPreferredSize());
1501
1502			prefSize.height = _top.height + _bottom.height + _center.height
1503				+ _topButtons.height + _bottomButtons.height
1504				+ _topToolbars.height + _bottomToolbars.height;
1505			prefSize.width = _left.width + _right.width
1506				+ Math.max(_center.width,
1507				Math.max(_topToolbars.width,_bottomToolbars.width))
1508				+ _leftButtons.width + _rightButtons.width;
1509
1510			return prefSize;
1511		} //}}}
1512
1513		//{{{ minimumLayoutSize() method
1514		public Dimension minimumLayoutSize(Container parent)
1515		{
1516			// I'm lazy
1517			return preferredLayoutSize(parent);
1518		} //}}}
1519
1520		//{{{ maximumLayoutSize() method
1521		public Dimension maximumLayoutSize(Container parent)
1522		{
1523			return new Dimension(Integer.MAX_VALUE,Integer.MAX_VALUE);
1524		} //}}}
1525
1526		//{{{ layoutContainer() method
1527		public void layoutContainer(Container parent)
1528		{
1529			Dimension size = parent.getSize();
1530
1531			Dimension _topToolbars = (topToolbars == null
1532				? new Dimension(0,0)
1533				: topToolbars.getPreferredSize());
1534			Dimension _bottomToolbars = (bottomToolbars == null
1535				? new Dimension(0,0)
1536				: bottomToolbars.getPreferredSize());
1537
1538			int topButtonHeight = -1;
1539			int bottomButtonHeight = -1;
1540			int leftButtonWidth = -1;
1541			int rightButtonWidth = -1;
1542
1543			Dimension _top = top.getPreferredSize();
1544			Dimension _left = left.getPreferredSize();
1545			Dimension _bottom = bottom.getPreferredSize();
1546			Dimension _right = right.getPreferredSize();
1547
1548			int topHeight = _top.height;
1549			int bottomHeight = _bottom.height;
1550			int leftWidth = _left.width;
1551			int rightWidth = _right.width;
1552
1553			boolean topEmpty = ((Container)topButtons)
1554				.getComponentCount() <= 2;
1555			boolean leftEmpty = ((Container)leftButtons)
1556				.getComponentCount() <= 2;
1557			boolean bottomEmpty = ((Container)bottomButtons)
1558				.getComponentCount() <= 2;
1559			boolean rightEmpty = ((Container)rightButtons)
1560				.getComponentCount() <= 2;
1561
1562			Dimension closeBoxSize;
1563			if(((Container)topButtons).getComponentCount() == 0)
1564				closeBoxSize = new Dimension(0,0);
1565			else
1566			{
1567				closeBoxSize = ((Container)topButtons)
1568					.getComponent(0).getPreferredSize();
1569			}
1570
1571			int closeBoxWidth = Math.max(closeBoxSize.width,
1572				closeBoxSize.height) + 1;
1573
1574			if(alternateLayout)
1575			{
1576				//{{{ Lay out independent buttons
1577				int _width = size.width;
1578
1579				int padding = (leftEmpty&&rightEmpty)
1580					? 0 : closeBoxWidth;
1581
1582				topButtonHeight = DockableWindowManager.this.
1583					top.getWrappedDimension(_width
1584					- closeBoxWidth * 2);
1585				topButtons.setBounds(
1586					padding,
1587					0,
1588					size.width - padding * 2,
1589					topButtonHeight);
1590
1591				bottomButtonHeight = DockableWindowManager.this.
1592					bottom.getWrappedDimension(_width);
1593				bottomButtons.setBounds(
1594					padding,
1595					size.height - bottomButtonHeight,
1596					size.width - padding * 2,
1597					bottomButtonHeight);
1598
1599				int _height = size.height
1600					- topButtonHeight
1601					- bottomButtonHeight;
1602				//}}}
1603
1604				//{{{ Lay out dependent buttons
1605				leftButtonWidth = DockableWindowManager.this.
1606					left.getWrappedDimension(_height);
1607				leftButtons.setBounds(
1608					0,
1609					topHeight + topButtonHeight,
1610					leftButtonWidth,
1611					_height - topHeight - bottomHeight);
1612
1613				rightButtonWidth = DockableWindowManager.this.
1614					right.getWrappedDimension(_height);
1615				rightButtons.setBounds(
1616					size.width - rightButtonWidth,
1617					topHeight + topButtonHeight,
1618					rightButtonWidth,
1619					_height - topHeight - bottomHeight);
1620				//}}}
1621
1622				int[] dimensions = adjustDockingAreasToFit(
1623					size,
1624					topHeight,
1625					leftWidth,
1626					bottomHeight,
1627					rightWidth,
1628					topButtonHeight,
1629					leftButtonWidth,
1630					bottomButtonHeight,
1631					rightButtonWidth,
1632					_topToolbars,
1633					_bottomToolbars);
1634
1635				topHeight = dimensions[0];
1636				leftWidth = dimensions[1];
1637				bottomHeight = dimensions[2];
1638				rightWidth = dimensions[3];
1639
1640				//{{{ Lay out docking areas
1641				top.setBounds(
1642					0,
1643					topButtonHeight,
1644					size.width,
1645					topHeight);
1646
1647				bottom.setBounds(
1648					0,
1649					size.height
1650					- bottomHeight
1651					- bottomButtonHeight,
1652					size.width,
1653					bottomHeight);
1654
1655				left.setBounds(
1656					leftButtonWidth,
1657					topButtonHeight + topHeight,
1658					leftWidth,
1659					_height - topHeight - bottomHeight);
1660
1661				right.setBounds(
1662					_width - rightButtonWidth - rightWidth,
1663					topButtonHeight + topHeight,
1664					rightWidth,
1665					_height - topHeight - bottomHeight); //}}}
1666			}
1667			else
1668			{
1669				//{{{ Lay out independent buttons
1670				int _height = size.height;
1671
1672				int padding = (topEmpty && bottomEmpty
1673					? 0 : closeBoxWidth);
1674
1675				leftButtonWidth = DockableWindowManager.this.
1676					left.getWrappedDimension(_height
1677					- closeBoxWidth * 2);
1678				leftButtons.setBounds(
1679					0,
1680					padding,
1681					leftButtonWidth,
1682					_height - padding * 2);
1683
1684				rightButtonWidth = DockableWindowManager.this.
1685					right.getWrappedDimension(_height);
1686				rightButtons.setBounds(
1687					size.width - rightButtonWidth,
1688					padding,
1689					rightButtonWidth,
1690					_height - padding * 2);
1691
1692				int _width = size.width
1693					- leftButtonWidth
1694					- rightButtonWidth;
1695				//}}}
1696
1697				//{{{ Lay out dependent buttons
1698				topButtonHeight = DockableWindowManager.this.
1699					top.getWrappedDimension(_width);
1700				topButtons.setBounds(
1701					leftButtonWidth + leftWidth,
1702					0,
1703					_width - leftWidth - rightWidth,
1704					topButtonHeight);
1705
1706				bottomButtonHeight = DockableWindowManager.this.
1707					bottom.getWrappedDimension(_width);
1708				bottomButtons.setBounds(
1709					leftButtonWidth + leftWidth,
1710					_height - bottomButtonHeight,
1711					_width - leftWidth - rightWidth,
1712					bottomButtonHeight); //}}}
1713
1714				int[] dimensions = adjustDockingAreasToFit(
1715					size,
1716					topHeight,
1717					leftWidth,
1718					bottomHeight,
1719					rightWidth,
1720					topButtonHeight,
1721					leftButtonWidth,
1722					bottomButtonHeight,
1723					rightButtonWidth,
1724					_topToolbars,
1725					_bottomToolbars);
1726
1727				topHeight = dimensions[0];
1728				leftWidth = dimensions[1];
1729				bottomHeight = dimensions[2];
1730				rightWidth = dimensions[3];
1731
1732				//{{{ Lay out docking areas
1733				top.setBounds(
1734					leftButtonWidth + leftWidth,
1735					topButtonHeight,
1736					_width - leftWidth - rightWidth,
1737					topHeight);
1738
1739				bottom.setBounds(
1740					leftButtonWidth + leftWidth,
1741					size.height - bottomHeight - bottomButtonHeight,
1742					_width - leftWidth - rightWidth,
1743					bottomHeight);
1744
1745				left.setBounds(
1746					leftButtonWidth,
1747					0,
1748					leftWidth,
1749					_height);
1750
1751				right.setBounds(
1752					size.width - rightWidth - rightButtonWidth,
1753					0,
1754					rightWidth,
1755					_height); //}}}
1756			}
1757
1758			//{{{ Position tool bars if they are managed by us
1759			if(topToolbars != null)
1760			{
1761				topToolbars.setBounds(
1762					leftButtonWidth + leftWidth,
1763					topButtonHeight + topHeight,
1764					size.width - leftWidth - rightWidth
1765					- leftButtonWidth - rightButtonWidth,
1766					_topToolbars.height);
1767			}
1768
1769			if(bottomToolbars != null)
1770			{
1771				bottomToolbars.setBounds(
1772					leftButtonWidth + leftWidth,
1773					size.height - bottomHeight
1774					- bottomButtonHeight
1775					- _bottomToolbars.height
1776					+ topButtonHeight
1777					+ topHeight,
1778					size.width - leftWidth - rightWidth
1779					- leftButtonWidth - rightButtonWidth,
1780					_bottomToolbars.height);
1781			} //}}}
1782
1783			//{{{ Position center (edit pane, or split pane)
1784			if(center != null)
1785			{
1786				center.setBounds(
1787					leftButtonWidth + leftWidth,
1788					topButtonHeight + topHeight
1789					+ _topToolbars.height,
1790					size.width
1791					- leftWidth
1792					- rightWidth
1793					- leftButtonWidth
1794					- rightButtonWidth,
1795					size.height
1796					- topHeight
1797					- topButtonHeight
1798					- bottomHeight
1799					- bottomButtonHeight
1800					- _topToolbars.height
1801					- _bottomToolbars.height);
1802			} //}}}
1803		} //}}}
1804
1805		//{{{ adjustDockingAreasToFit() method
1806		private int[] adjustDockingAreasToFit(
1807			Dimension size,
1808			int topHeight,
1809			int leftWidth,
1810			int bottomHeight,
1811			int rightWidth,
1812			int topButtonHeight,
1813			int leftButtonWidth,
1814			int bottomButtonHeight,
1815			int rightButtonWidth,
1816			Dimension _topToolbars,
1817			Dimension _bottomToolbars)
1818		{
1819			int maxTopHeight = size.height - bottomHeight
1820				- topButtonHeight - bottomButtonHeight
1821				- _topToolbars.height - _bottomToolbars.height;
1822			topHeight = Math.min(Math.max(0,maxTopHeight),
1823				topHeight);
1824			leftWidth = Math.min(Math.max(0,
1825				size.width - leftButtonWidth
1826				- rightButtonWidth - rightWidth),leftWidth);
1827			int maxBottomHeight = size.height - topHeight
1828				- topButtonHeight - bottomButtonHeight
1829				- _topToolbars.height - _bottomToolbars.height;
1830			bottomHeight = Math.min(Math.max(0,maxBottomHeight),
1831				bottomHeight);
1832			rightWidth = Math.min(Math.max(0,
1833				size.width - leftButtonWidth
1834				- rightButtonWidth - leftWidth),rightWidth);
1835
1836			DockableWindowManager.this.top.setDimension(topHeight);
1837			DockableWindowManager.this.left.setDimension(leftWidth);
1838			DockableWindowManager.this.bottom.setDimension(bottomHeight);
1839			DockableWindowManager.this.right.setDimension(rightWidth);
1840
1841			return new int[] {
1842				topHeight,
1843				leftWidth,
1844				bottomHeight,
1845				rightWidth
1846			};
1847		} //}}}
1848
1849		//{{{ getLayoutAlignmentX() method
1850		public float getLayoutAlignmentX(Container target)
1851		{
1852			return 0.5f;
1853		} //}}}
1854
1855		//{{{ getLayoutAlignmentY() method
1856		public float getLayoutAlignmentY(Container target)
1857		{
1858			return 0.5f;
1859		} //}}}
1860
1861		//{{{ invalidateLayout() method
1862		public void invalidateLayout(Container target) {}
1863		//}}}
1864	} //}}}
1865
1866	//{{{ Entry class
1867	class Entry
1868	{
1869		Factory factory;
1870
1871		String title;
1872		String position;
1873		DockableWindowContainer container;
1874
1875		// only set if open
1876		JComponent win;
1877
1878		// only for docked
1879		AbstractButton btn;
1880
1881		//{{{ Entry constructor
1882		Entry(Factory factory)
1883		{
1884			this(factory,jEdit.getProperty(factory.name
1885				+ ".dock-position",FLOATING));
1886		} //}}}
1887
1888		//{{{ Entry constructor
1889		Entry(Factory factory, String position)
1890		{
1891			this.factory = factory;
1892			this.position = position;
1893
1894			// get the title here, not in the factory constructor,
1895			// since the factory might be created before a plugin's
1896			// props are loaded
1897			title = getDockableTitle(factory.name);
1898		} //}}}
1899	} //}}}
1900}