PageRenderTime 65ms CodeModel.GetById 40ms app.highlight 19ms RepoModel.GetById 2ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/gui/DockableWindowManager.java

#
Java | 754 lines | 435 code | 67 blank | 252 comment | 81 complexity | 6a1b3b94746843f931509ad040649cea MD5 | raw file
  1package org.gjt.sp.jedit.gui;
  2
  3// {{{ imports
  4import java.awt.event.KeyAdapter;
  5import java.awt.event.KeyEvent;
  6import java.awt.event.KeyListener;
  7import java.io.File;
  8import java.io.FilenameFilter;
  9import java.util.*;
 10import java.util.Map.Entry;
 11
 12import javax.swing.JComponent;
 13import javax.swing.JPanel;
 14
 15import org.gjt.sp.jedit.EditBus;
 16import org.gjt.sp.jedit.PluginJAR;
 17import org.gjt.sp.jedit.View;
 18import org.gjt.sp.jedit.jEdit;
 19import org.gjt.sp.jedit.EditBus.EBHandler;
 20import org.gjt.sp.jedit.View.ViewConfig;
 21import org.gjt.sp.jedit.gui.KeyEventTranslator.Key;
 22import org.gjt.sp.jedit.msg.DockableWindowUpdate;
 23import org.gjt.sp.jedit.msg.PluginUpdate;
 24import org.gjt.sp.jedit.msg.PropertiesChanged;
 25import org.gjt.sp.util.Log;
 26// }}}
 27
 28@SuppressWarnings("serial")
 29// {{{ abstract class DockableWindowManager
 30/**
 31 * <p>Keeps track of all dockable windows for a single View, and provides
 32 * an API for getting/showing/hiding them. </p>
 33 *
 34 * <p>Each {@link org.gjt.sp.jedit.View} has an instance of this class.</p>
 35 *
 36 * <p><b>dockables.xml:</b></p>
 37 *
 38 * <p>Dockable window definitions are read from <code>dockables.xml</code> files
 39 * contained inside plugin JARs. A dockable definition file has the following
 40 * form: </p>
 41 *
 42 * <pre>&lt;?xml version="1.0"?&gt;
 43 *&lt;!DOCTYPE DOCKABLES SYSTEM "dockables.dtd"&gt;
 44 *&lt;DOCKABLES&gt;
 45 *    &lt;DOCKABLE NAME="<i>dockableName</i>" MOVABLE="TRUE|FALSE"&gt;
 46 *        // Code to create the dockable
 47 *    &lt;/DOCKABLE&gt;
 48 *&lt;/DOCKABLES&gt;</pre>
 49 *
 50 * <p>The MOVABLE attribute specifies the behavior when the docking position of
 51 * the dockable window is changed. If MOVABLE is TRUE, the existing instance of
 52 * the dockable window is moved to the new docking position, and if the dockable
 53 * window implements the DockableWindow interface (see {@link DockableWindow}),
 54 * it is also notified about the change in docking position before it is moved.
 55 * If MOVABLE is FALSE, the BeanShell code is invoked to get the instance of
 56 * the dockable window to put in the new docking position. Typically, the
 57 * BeanShell code returns a new instance of the dockable window, and the state
 58 * of the existing instance is not preserved after the change. It is therefore
 59 * recommended to set MOVABLE to TRUE for all dockables in order to make them
 60 * preserve their state when they are moved. For backward compatibility reasons,
 61 * this attribute is set to FALSE by default.</p>
 62 * <p>More than one <code>&lt;DOCKABLE&gt;</code> tag may be present. The code that
 63 * creates the dockable can reference any BeanShell built-in variable
 64 * (see {@link org.gjt.sp.jedit.BeanShell}), along with a variable
 65 * <code>position</code> whose value is one of
 66 * {@link #FLOATING}, {@link #TOP}, {@link #LEFT}, {@link #BOTTOM},
 67 * and {@link #RIGHT}. </p>
 68 *
 69 * <p>The following properties must be defined for each dockable window: </p>
 70 *
 71 * <ul>
 72 * <li><code><i>dockableName</i>.title</code> - the string to show on the dockable
 73 * button. </li>
 74 * <li><code><i>dockableName</i>.label</code> - The string to use for generating
 75 *    menu items and action names. </li>
 76 * <li><code><i>dockableName</i>.longtitle</code> - (optional) the string to use
 77 *      in the dockable's floating window title (when it is floating).
 78 *       If not specified, the <code><i>dockableName</i>.title</code> property is used. </li>
 79 * </ul>
 80 *
 81 * A number of actions are automatically created for each dockable window:
 82 *
 83 * <ul>
 84 * <li><code><i>dockableName</i></code> - opens the dockable window.</li>
 85 * <li><code><i>dockableName</i>-toggle</code> - toggles the dockable window's visibility.</li>
 86 * <li><code><i>dockableName</i>-float</code> - opens the dockable window in a new
 87 * floating window.</li>
 88 * </ul>
 89 *
 90 * Note that only the first action needs a <code>label</code> property, the
 91 * rest have automatically-generated labels.
 92 *
 93 * <p> <b>Implementation details:</b></p>
 94 *
 95 * <p> When an instance of this class is initialized by the {@link org.gjt.sp.jedit.View}
 96 * class, it
 97 * iterates through the list of registered dockable windows (from jEdit itself,
 98 * and any loaded plugins) and
 99 * examines options supplied by the user in the <b>Global
100 * Options</b> dialog box. Any plugins designated for one of the
101 * four docking positions are displayed.</p>
102 *
103 * <p> To create an instance of a dockable window, the <code>DockableWindowManager</code>
104 * finds and executes the BeanShell code extracted from the appropriate
105 * <code>dockables.xml</code> file. This code will typically consist of a call
106 * to the constructor of the dockable window component. The result of the
107 * BeanShell expression, typically a newly constructed component, is placed
108 * in a window managed by this class. </p>
109 *
110 * @see org.gjt.sp.jedit.View#getDockableWindowManager()
111 *
112 * @author Slava Pestov
113 * @author John Gellene (API documentation)
114 * @author Shlomy Reinstein (refactoring into a base and an impl)
115 * @version $Id: DockableWindowManager.java 20108 2011-10-18 12:16:38Z evanpw $
116 * @since jEdit 2.6pre3
117 *
118 */
119 public abstract class DockableWindowManager extends JPanel
120{
121
122	//{{{ Constants
123	/**
124	 * Floating position.
125	 * @since jEdit 2.6pre3
126	 */
127	public static final String FLOATING = "floating";
128
129	/**
130	 * Top position.
131	 * @since jEdit 2.6pre3
132	 */
133	public static final String TOP = "top";
134
135	/**
136	 * Left position.
137	 * @since jEdit 2.6pre3
138	 */
139	public static final String LEFT = "left";
140
141	/**
142	 * Bottom position.
143	 * @since jEdit 2.6pre3
144	 */
145	public static final String BOTTOM = "bottom";
146
147	/**
148	 * Right position.
149	 * @since jEdit 2.6pre3
150	 */
151	public static final String RIGHT = "right";
152	//}}}
153
154	// {{{ data members
155	private final Map<PluginJAR, Set<String>> plugins = new HashMap<PluginJAR, Set<String>>();
156	private final Map<String, String> positions = new HashMap<String, String>();
157	protected View view;
158	protected DockableWindowFactory factory;
159	protected Map<String, JComponent> windows = new HashMap<String, JComponent>();
160
161	// variables for toggling all dock areas
162	private boolean tBottom, tTop, tLeft, tRight;
163	private boolean closeToggle = true;
164
165	private static final String ALTERNATE_LAYOUT_PROP = "view.docking.alternateLayout";
166	private boolean alternateLayout;
167	// }}}
168
169	// {{{ DockableWindowManager constructor
170	public DockableWindowManager(View view, DockableWindowFactory instance,
171			ViewConfig config)
172	{
173		this.view = view;
174		this.factory = instance;
175		alternateLayout = jEdit.getBooleanProperty(ALTERNATE_LAYOUT_PROP);
176	} // }}}
177
178	// {{{ Abstract methods
179	public abstract void setMainPanel(JPanel panel);
180	public abstract void showDockableWindow(String name);
181	public abstract void hideDockableWindow(String name);
182
183	/** Completely dispose of a dockable - called when a plugin is
184	    unloaded, to remove all references to the its dockables. */
185	public abstract void disposeDockableWindow(String name);
186	public abstract JComponent floatDockableWindow(String name);
187	public abstract boolean isDockableWindowDocked(String name);
188	public abstract boolean isDockableWindowVisible(String name);
189	public abstract void closeCurrentArea();
190	public abstract DockingLayout getDockingLayout(ViewConfig config);
191	public abstract DockingArea getLeftDockingArea();
192	public abstract DockingArea getRightDockingArea();
193	public abstract DockingArea getTopDockingArea();
194	public abstract DockingArea getBottomDockingArea();
195	// }}}
196
197	// {{{ public methods
198	// {{{ init()
199	public void init()
200	{
201		EditBus.addToBus(this);
202
203		Iterator<DockableWindowFactory.Window> entries = factory.getDockableWindowIterator();
204		while(entries.hasNext())
205		{
206			DockableWindowFactory.Window window = entries.next();
207			String dockable = window.name;
208			positions.put(dockable, getDockablePosition(dockable));
209			addPluginDockable(window.plugin, dockable);
210		}
211	} // }}}
212
213	// {{{ close()
214	public void close()
215	{
216		EditBus.removeFromBus(this);
217	} // }}}
218
219	// {{{ applyDockingLayout
220	public void applyDockingLayout(DockingLayout docking)
221	{
222		// By default, use the docking positions specified by the jEdit properties
223		Iterator<Entry<String, String>> iterator = positions.entrySet().iterator();
224		while (iterator.hasNext())
225		{
226			Entry<String, String> entry = iterator.next();
227			String dockable = entry.getKey();
228			String position = entry.getValue();
229			if (! position.equals(FLOATING))
230				showDockableWindow(dockable);
231		}
232	} //}}}
233
234	//{{{ addDockableWindow() method
235	/**
236	 * Opens the specified dockable window. As of jEdit 4.0pre1, has the
237	 * same effect as calling showDockableWindow().
238	 * @param name The dockable window name
239	 * @since jEdit 2.6pre3
240	 */
241	public void addDockableWindow(String name)
242	{
243		showDockableWindow(name);
244	} //}}}
245
246	//{{{ removeDockableWindow() method
247	/**
248	 * Hides the specified dockable window. As of jEdit 4.2pre1, has the
249	 * same effect as calling hideDockableWindow().
250	 * @param name The dockable window name
251	 * @since jEdit 4.2pre1
252	 */
253	public void removeDockableWindow(String name)
254	{
255		hideDockableWindow(name);
256	} //}}}
257
258	//{{{ toggleDockableWindow() method
259	/**
260	 * Toggles the visibility of the specified dockable window.
261	 * @param name The dockable window name
262	 */
263	public void toggleDockableWindow(String name)
264	{
265		if(isDockableWindowVisible(name))
266			removeDockableWindow(name);
267		else
268			addDockableWindow(name);
269	} //}}}
270
271	//{{{ getDockableWindow() method
272	/**
273	 * Returns the specified dockable window.
274	 *
275	 * Note that this method
276	 * will return null if the dockable has not been added yet.
277	 * Make sure you call {@link #addDockableWindow(String)} first.
278	 *
279	 * @param name The name of the dockable window
280	 * @since jEdit 4.1pre2
281	 */
282	public JComponent getDockableWindow(String name)
283	{
284		return getDockable(name);
285	} //}}}
286
287	// {{{ toggleDockAreas()
288	/**
289	 * Hides all visible dock areas, or shows them again,
290	 * if the last time it was a hide.
291	 * @since jEdit 4.3pre16
292	 *
293	 */
294	public void toggleDockAreas()
295	{
296		if (closeToggle)
297		{
298			tTop = getTopDockingArea().getCurrent() != null;
299			tLeft = getLeftDockingArea().getCurrent() != null;
300			tRight = getRightDockingArea().getCurrent() != null;
301			tBottom = getBottomDockingArea().getCurrent() != null;
302			getBottomDockingArea().show(null);
303			getTopDockingArea().show(null);
304			getRightDockingArea().show(null);
305			getLeftDockingArea().show(null);
306		}
307		else
308		{
309			if (tBottom) getBottomDockingArea().showMostRecent();
310			if (tLeft) getLeftDockingArea().showMostRecent();
311			if (tRight) getRightDockingArea().showMostRecent();
312			if (tTop) getTopDockingArea().showMostRecent();
313		}
314        view.closeAllMenus();
315		closeToggle = !closeToggle;
316		view.getTextArea().requestFocus();
317	} // }}}
318
319	// {{{ dockableTitleChanged
320	public void dockableTitleChanged(String dockable, String newTitle)
321	{
322	} // }}}
323
324	// {{{ closeListener() method
325	/**
326	 * The actionEvent "close-docking-area" by default only works on
327	 * dockable windows that have no special keyboard handling.
328
329	 * If you have dockable widgets with input widgets and/or other fancy
330	 * keyboard handling, those components may not respond to close docking area.
331
332	 * You can add key listeners to each keyboard-handling component
333	 * in your dockable that usually has keyboard focus.
334	 *
335	 * This function creates and returns a key listener which does exactly that.
336	 * It is also used by FloatingWindowContainer when creating new floating windows.
337	 *
338	 * @param dockableName the name of your dockable
339	 * @return a KeyListener you can add to that plugin's component.
340	 * @since jEdit 4.3pre6
341	 *
342	 */
343	public KeyListener closeListener(String dockableName)
344	{
345		return new KeyHandler(dockableName);
346	}
347	// }}}
348
349	//{{{ getView() method
350	/**
351	 * Returns this dockable window manager's view.
352	 * @since jEdit 4.0pre2
353	 */
354	public View getView()
355	{
356		return view;
357	} //}}}
358
359	//{{{ getDockable method
360	/**
361	 * @since jEdit 4.3pre2
362	 */
363	public JComponent getDockable(String name)
364	{
365		return windows.get(name);
366	} // }}}
367
368	//{{{ getDockableTitle() method
369	/**
370	 * Returns the title of the specified dockable window.
371	 * @param name The name of the dockable window.
372	 * @since jEdit 4.1pre5
373	 */
374	public String getDockableTitle(String name)
375	{
376		return longTitle(name);
377	}//}}}
378
379	//{{{ setDockableTitle() method
380	/**
381	 * Changes the .longtitle property of a dockable window, which corresponds to the
382	 * title shown when it is floating (not docked). Fires a change event that makes sure
383	 * all floating dockables change their title.
384	 *
385	 * @param dockable the name of the dockable, as specified in the dockables.xml
386	 * @param title the new .longtitle you want to see above it.
387	 * @since 4.3pre5
388	 *
389	 */
390	public void setDockableTitle(String dockable, String title)
391	{
392		String propName = getLongTitlePropertyName(dockable);
393		String oldTitle = jEdit.getProperty(propName);
394		jEdit.setProperty(propName, title);
395		firePropertyChange(propName, oldTitle, title);
396		dockableTitleChanged(dockable, title);
397	}
398	// }}}
399
400	//{{{ getRegisteredDockableWindows() method
401	public static String[] getRegisteredDockableWindows()
402	{
403		return DockableWindowFactory.getInstance()
404			.getRegisteredDockableWindows();
405	} //}}}
406
407	//{{{ getDockableWindowPluginClassName() method
408	public static String getDockableWindowPluginName(String name)
409	{
410		String pluginClass =
411			DockableWindowFactory.getInstance().getDockableWindowPluginClass(name);
412		if (pluginClass == null)
413			return null;
414		return jEdit.getProperty("plugin." + pluginClass + ".name");
415	} //}}}
416
417	// {{{ setDockingLayout method
418	public void setDockingLayout(DockingLayout docking)
419	{
420		applyDockingLayout(docking);
421		applyAlternateLayout(alternateLayout);
422	} // }}}
423
424	// {{{ addPluginDockable
425	private void addPluginDockable(PluginJAR plugin, String name)
426	{
427		Set<String> dockables = plugins.get(plugin);
428		if (dockables == null)
429		{
430			dockables = new HashSet<String>();
431			plugins.put(plugin, dockables);
432		}
433		dockables.add(name);
434	}
435	// }}}
436
437	// {{{ handleDockableWindowUpdate() method
438	@EBHandler
439	public void handleDockableWindowUpdate(DockableWindowUpdate msg)
440	{
441		if(msg.getWhat() == DockableWindowUpdate.PROPERTIES_CHANGED)
442			propertiesChanged();
443	} // }}}
444
445	// {{{ handlePropertiesChanged() method
446	@EBHandler
447	public void handlePropertiesChanged(PropertiesChanged msg)
448	{
449		propertiesChanged();
450	} // }}}
451
452	// {{{ handlePluginUpdate() method
453	@EBHandler
454	public void handlePluginUpdate(PluginUpdate pmsg)
455	{
456		if (pmsg.getWhat() == PluginUpdate.LOADED)
457		{
458			Iterator<DockableWindowFactory.Window> iter = factory.getDockableWindowIterator();
459			while (iter.hasNext())
460			{
461				DockableWindowFactory.Window w = iter.next();
462				if (w.plugin == pmsg.getPluginJAR())
463				{
464					String position = getDockablePosition(w.name);
465					positions.put(w.name, position);
466					addPluginDockable(w.plugin, w.name);
467					dockableLoaded(w.name, position);
468				}
469			}
470			propertiesChanged();
471		}
472		else if(pmsg.isExiting())
473		{
474			// we don't care
475		}
476		else if(pmsg.getWhat() == PluginUpdate.DEACTIVATED ||
477				pmsg.getWhat() == PluginUpdate.UNLOADED)
478		{
479			Set<String> dockables = plugins.remove(pmsg.getPluginJAR());
480			if (dockables != null)
481			{
482				for (String dockable: dockables)
483				{
484					disposeDockableWindow(dockable);
485					windows.remove(dockable);
486				}
487			}
488		}
489	} // }}}
490
491	// {{{ longTitle() method
492	public String longTitle(String name)
493	{
494		String title = jEdit.getProperty(getLongTitlePropertyName(name));
495		if (title == null)
496			return shortTitle(name);
497		return title;
498	} // }}}
499
500	// {{{ shortTitle() method
501	public String shortTitle(String name)
502	{
503		String title = jEdit.getProperty(name + ".title");
504		if(title == null)
505			return "NO TITLE PROPERTY: " + name;
506		return title;
507	} // }}}
508
509	// }}}
510
511	// {{{ protected methods
512	// {{{ applyAlternateLayout
513	protected void applyAlternateLayout(boolean alternateLayout)
514	{
515	} //}}}
516
517	// {{{
518	protected void dockableLoaded(String dockableName, String position)
519	{
520	}
521	// }}}
522
523	// {{{
524	protected void dockingPositionChanged(String dockableName,
525		String oldPosition, String newPosition)
526	{
527	} //}}}
528
529	// {{{ getAlternateLayoutProp()
530	protected boolean getAlternateLayoutProp()
531	{
532		return alternateLayout;
533	} // }}}
534
535	// {{{ propertiesChanged
536	protected void propertiesChanged()
537	{
538		if(view.isPlainView())
539			return;
540
541		boolean newAlternateLayout = jEdit.getBooleanProperty(ALTERNATE_LAYOUT_PROP);
542		if (newAlternateLayout != alternateLayout)
543		{
544			alternateLayout = newAlternateLayout;
545			applyAlternateLayout(newAlternateLayout);
546		}
547
548		String[] dockables = factory.getRegisteredDockableWindows();
549		for(int i = 0; i < dockables.length; i++)
550		{
551			String dockable = dockables[i];
552			String oldPosition = positions.get(dockable);
553			String newPosition = getDockablePosition(dockable);
554			if (oldPosition == null || !newPosition.equals(oldPosition))
555			{
556				positions.put(dockable, newPosition);
557				dockingPositionChanged(dockable, oldPosition, newPosition);
558			}
559		}
560
561	} // }}}
562
563	// {{{ createDockable()
564	protected JComponent createDockable(String name)
565	{
566		DockableWindowFactory.Window wf = factory.getDockableWindowFactory(name);
567		if (wf == null)
568		{
569			Log.log(Log.ERROR,this,"Unknown dockable window: " + name);
570			return null;
571		}
572		String position = getDockablePosition(name);
573		JComponent window = wf.createDockableWindow(view, position);
574		if (window != null)
575			windows.put(name, window);
576		return window;
577	} // }}}
578
579	// {{{ getDockablePosition()
580	protected String getDockablePosition(String name)
581	{
582		return jEdit.getProperty(name + ".dock-position", FLOATING);
583	} // }}}
584
585	// {{{ focusDockable
586	protected void focusDockable(String name)
587	{
588		JComponent c = getDockable(name);
589		if (c == null)
590			return;
591		if (c instanceof DefaultFocusComponent)
592			((DefaultFocusComponent)c).focusOnDefaultComponent();
593		else
594			c.requestFocus();
595	} // }}}
596
597	// {{{ getLongTitlePropertyName()
598	protected String getLongTitlePropertyName(String dockableName)
599	{
600		return dockableName + ".longtitle";
601	} //}}}
602	// }}}
603
604
605	// {{{ Inner classes
606	// {{{ DockingArea interface
607	public interface DockingArea
608	{
609		void showMostRecent();
610		String getCurrent();
611		void show(String name);
612		String [] getDockables();
613	}
614	// }}}
615
616	//{{{ KeyHandler class
617	/**
618	 * This keyhandler responds to only two key events - those corresponding to
619	 * the close-docking-area action event.
620	 *
621	 * @author ezust
622	 */
623	class KeyHandler extends KeyAdapter
624	{
625		static final String action = "close-docking-area";
626		private List<Key> b1;
627		private List<Key> b2;
628		private final String name;
629		private int match1;
630		private int match2;
631
632		KeyHandler(String dockableName)
633		{
634			String shortcut1=jEdit.getProperty(action + ".shortcut");
635			String shortcut2=jEdit.getProperty(action + ".shortcut2");
636			if (shortcut1 != null)
637				b1 = parseShortcut(shortcut1);
638			if (shortcut2 != null)
639				b2 = parseShortcut(shortcut2);
640			name = dockableName;
641			match1 = match2 = 0;
642		}
643
644		@Override
645		public void keyTyped(KeyEvent e)
646		{
647			if (b1 != null)
648				match1 = match(e, b1, match1);
649			if (b2 != null)
650				match2 = match(e, b2, match2);
651			if ((match1 > 0 && match1 == b1.size()) ||
652				(match2 > 0 && match2 == b2.size()))
653			{
654				hideDockableWindow(name);
655				match1 = match2 = 0;
656			}
657		}
658
659		private int match(KeyEvent e, List<Key> shortcut, int index)
660		{
661			char c = e.getKeyChar();
662			if (shortcut != null && c == shortcut.get(index).key)
663				return index + 1;
664			return 0;
665		}
666
667		private List<Key> parseShortcut(String shortcut)
668		{
669			String [] parts = shortcut.split("\\s+");
670			List<Key> keys = new ArrayList<Key>(parts.length);
671			for (String part: parts)
672			{
673				if (part.length() > 0)
674					keys.add(KeyEventTranslator.parseKey(part));
675			}
676			return keys;
677		}
678	} //}}}
679
680	// {{{ DockingLayout class
681	/**
682	 * Objects of DockingLayout class describe which dockables are docked where,
683	 * which ones are floating, and their sizes/positions for saving/loading perspectives.
684	 */
685	public abstract static class DockingLayout
686	{
687		public static final int NO_VIEW_INDEX = -1;
688		public abstract boolean loadLayout(String baseName, int viewIndex);
689		public abstract boolean saveLayout(String baseName, int viewIndex);
690		public abstract String getName();
691
692		public void setPlainView(boolean plain)
693		{
694		}
695
696		public String [] getSavedLayouts()
697		{
698			String layoutDir = getLayoutDirectory();
699			if (layoutDir == null)
700				return null;
701			File dir = new File(layoutDir);
702			File[] files = dir.listFiles(new FilenameFilter()
703			{
704				public boolean accept(File dir, String name)
705				{
706					return name.endsWith(".xml");
707				}
708			});
709			String[] layouts = new String[files.length];
710			for (int i = 0; i < files.length; i++)
711				layouts[i] = fileToLayout(files[i].getName());
712			return layouts;
713		}
714
715		private static String fileToLayout(String filename)
716		{
717			return filename.replaceFirst(".xml", "");
718		}
719
720		private static String layoutToFile(String baseName, int viewIndex)
721		{
722			StringBuilder name = new StringBuilder(baseName);
723			if (viewIndex != NO_VIEW_INDEX)
724				name.append("-view").append(viewIndex);
725			name.append(".xml");
726			return name.toString();
727		}
728
729		public String getLayoutFilename(String baseName, int viewIndex)
730		{
731			String dir = getLayoutDirectory();
732			if (dir == null)
733				return null;
734			return dir + File.separator + layoutToFile(baseName, viewIndex);
735		}
736
737		private String getLayoutDirectory()
738		{
739			String name = getName();
740			if (name == null)
741				return null;
742			String dir = jEdit.getSettingsDirectory();
743			if (dir == null)
744				return null;
745			dir = dir + File.separator + name;
746			File d = new File(dir);
747			if (!d.exists())
748				d.mkdir();
749			return dir;
750		}
751	} // }}}
752
753	//}}}
754} // }}}