PageRenderTime 176ms CodeModel.GetById 95ms app.highlight 73ms RepoModel.GetById 1ms app.codeStats 0ms

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

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