PageRenderTime 72ms CodeModel.GetById 53ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 1ms

/bundles/plugins-trunk/Sessions/sessions/SessionManager.java

#
Java | 589 lines | 305 code | 88 blank | 196 comment | 47 complexity | 98879bdf1a28f707dcff71d808cb48f4 MD5 | raw file
  1/*
  2 * SessionManager.java
  3 * Copyright (c) 2001 Dirk Moebius, Sergey V. Udaltsov
  4 * Copyright (c) 2007, 2008 Steve Jakob
  5 *
  6 * :tabSize=4:indentSize=4:noTabs=false:maxLineLen=0:
  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
 23
 24package sessions;
 25
 26
 27import java.awt.Component;
 28import java.io.File;
 29import java.io.FilenameFilter;
 30import java.io.FileNotFoundException;
 31import java.io.IOException;
 32import java.util.Arrays;
 33import java.util.Vector;
 34import javax.swing.JOptionPane;
 35import org.gjt.sp.jedit.*;
 36import org.gjt.sp.jedit.browser.VFSBrowser;
 37import org.gjt.sp.jedit.io.VFSManager;
 38import org.gjt.sp.jedit.msg.PropertiesChanged;
 39import org.gjt.sp.util.Log;
 40import org.gjt.sp.util.StandardUtilities;
 41import org.gjt.sp.jedit.msg.BufferUpdate;
 42
 43
 44/**
 45 * A singleton class that holds a current session and acts as a controller for handling 
 46 * session management tasks.
 47 *
 48 * <h1>Saving Sessions</code>
 49 * <p>There are two different types of situations that may result in a session being
 50 * saved, each of which is handled differently. These are:</p>
 51 * <ul>
 52 * <li><b>User-requested save</b>: occurs when the user manually requests that a session be 
 53 * saved, either from the session switcher component or the session manager dialog. In 
 54 * this cases there is no need to confirm the user's desire to save the session, but the 
 55 * user will be notified following a successful save. This type of save is handled by 
 56 * the #saveCurrentSession(View) method.</li>
 57 * <li><b>Autosave</b>: occurs when the user initiates an action that results in the 
 58 * current session being closed. Such actions include switching to a different session, 
 59 * renaming a session, or shutting down the plugin. In this cases, the user might be 
 60 * shown a dialog to confirm his/her desire to save (unless the users' preferences indicate
 61 * that no confirmation is desired), but no confirmation of save sucess is given. This type 
 62 * of save is handled by the #autosaveCurrentSession method.</li>
 63 * </ul>
 64 */
 65public class SessionManager implements EBComponent
 66{
 67
 68	public final static String SESSION_PROPERTY = "sessions.currentSession";
 69
 70
 71	/** The current session instance. */
 72	private Session currentSession;
 73
 74
 75	/** The singleton SessionManager instance. */
 76	private static SessionManager instance;
 77
 78
 79	/** The string that will contain the new temp jEdit view.title property.*/	
 80	private String titleBarSessionName;
 81
 82
 83	/** The default jEdit view.title property. */
 84	private String defaultViewTitle;
 85	
 86	/** Returns the singleton SessionManager instance */
 87	public static SessionManager getInstance()
 88	{
 89		if(instance == null)
 90			instance = new SessionManager();
 91		return instance;
 92	}
 93
 94
 95	/**
 96	 * Initialization
 97	 */
 98	private SessionManager()
 99	{
100		currentSession = new Session(jEdit.getProperty(SESSION_PROPERTY, "default"));
101
102		// create directory <jedithome>/sessions if it not yet exists
103		File dir = new File(getSessionsDir());
104		if(!dir.exists())
105			dir.mkdirs();
106
107		// convert old format session files, if necessary
108		new SessionFileConverter().run();
109
110		// create default session file if it not yet exists
111		File defaultSessionFile = new File(createSessionFileName("default"));
112		if(!defaultSessionFile.exists())
113			new Session("default").save(null);
114		
115		
116		// initialize the variables for the jEdit title bar
117		defaultViewTitle = jEdit.getProperty("view.title", 
118								jEdit.getProperty("sessions.titlebar.default"));
119		titleBarSessionName = new String();
120	}
121
122
123	/**
124	 * Save the current session (subject to user preferences) and switch to a new session.
125	 * This sends out a SessionChanged message on EditBus, if the
126	 * session could be changed successfully.
127	 *
128	 * @param view  view for displaying error messages
129	 * @param newSession  the new session name
130	 * @return false, if the new session could not be set.
131	 */
132	public void setCurrentSession(final View view, final String newSessionName)
133	{
134		if(newSessionName.equals(currentSession.getName()))
135			return;
136
137		Log.log(Log.DEBUG, this, "setCurrentSession:"
138			+ " currentSession=" + currentSession.getName()
139			+ " newSessionName=" + newSessionName);
140
141		File currentSessionFile = new File(currentSession.getFilename());
142		if(currentSessionFile.exists())
143		{
144			// Auto-save the current session, subject to user preferences.
145			if (!autosaveCurrentSession(view))
146			{
147				// User doesn't want to switch the session
148				return;
149			}
150		}
151		else
152		{
153			// The current session file has been deleted, probably by the SessionManagerDialog.
154			// Do nothing, because save would recreate it.
155		}
156
157		// close all open buffers, if closeAll option is set:
158		if (jEdit.getBooleanProperty("sessions.switcher.closeAll", true))
159			if (!jEdit.closeAllBuffers(view))
160				return;  // jEdit should have shown an error
161
162		final String oldSessionName = currentSession.getName();
163		EditBus.send(new SessionChanging(this, oldSessionName, newSessionName));
164
165		// load new session
166		// make sure this is not done from the AWT thread
167
168		// {{{ This section changed by Steve Jakob
169		//     Opening the new session in a separate thread would occasionally
170		//     cause jEdit to freeze. I've left the code here in case we wish to try 
171		//     this sort of thing in the future.
172		/*
173		new Thread()
174		{
175			public void run()
176			{
177				currentSession = new Session(newSessionName);
178				saveCurrentSessionProperty();
179				currentSession.open(view);
180				EditBus.send(new SessionChanged(
181					SessionManager.this, oldSessionName, newSessionName, currentSession));
182			}
183		}.start();
184		*/
185		
186		currentSession = new Session(newSessionName);
187		saveCurrentSessionProperty();
188		currentSession.open(view);
189		EditBus.send(new SessionChanged(
190			SessionManager.this, oldSessionName, newSessionName, currentSession));
191		// }}} End of section changed by Steve Jakob
192
193		// Change FSB directory if appropriate
194		changeFSBToBaseDirectory(view);
195		
196		// update the jEdit title bar with the session name
197		setSessionNameInTitleBar();
198	}
199
200
201	/** Return the current session name. */
202	public String getCurrentSession()
203	{
204		return currentSession.getName();
205	}
206
207
208	/** Return the current session. */
209	public Session getCurrentSessionInstance()
210	{
211		return currentSession;
212	}
213
214
215	/**
216	 * Save current session and show a dialog that it has been saved. This is the method 
217	 * that is called for a "user-requested" save operation.
218	 *
219	 * @param view  view for displaying error messages
220	 */
221	public void saveCurrentSession(View view)
222	{
223		saveCurrentSession(view, false);
224	}
225
226
227	/**
228	 * Save current session without showing the save confirmation dialog, 
229	 * unless the "display confirmation dialog" flag has been set in the 
230	 * plugin properties pane. This is the method that is called for "autosave" 
231	 * functionality (ie. switching sessions, renaming a session, or shutting 
232	 * down the plugin).
233	 *
234	 * @param view  view for displaying error messages
235	 * @return <code>false</code> if the autosave process has been cancelled
236	 */
237	public boolean autosaveCurrentSession(View view)
238	{
239		if (currentSession.hasFileListChanged())
240		{
241			// If autosave sessions is on, save current session silently.
242			if (jEdit.getBooleanProperty("sessions.switcher.autoSave", true))
243			{
244				// If the "askSave" property is set to "true" ...
245				if (jEdit.getBooleanProperty("sessions.switcher.askSave", false))
246				{
247					// ... confirm whether the session should be saved
248					boolean ok = new SaveDialog(view).isOK();
249					if (!ok)
250					{
251						// User doesn't want to save the session
252						return false;
253					}
254				}
255				else
256				{
257					// Save the session.
258					Log.log(Log.DEBUG, this, "autosaving current session...");
259					saveCurrentSession(view, true);
260				}
261			}
262		}
263		return true;
264	}
265
266
267	/**
268	 * Save current session. NOTE: developers should not call this method directly. Instead, 
269	 * either the #saveCurrentSession(View) or #autosaveCurrentSession method should be 
270	 * used, depending on the nature of the save operation.
271	 *
272	 * @param view  view for displaying error messages
273	 * @param silently  if false, show a dialog that the current session has been saved.
274	 */
275	public void saveCurrentSession(View view, boolean silently)
276	{
277		currentSession.save(view);
278		saveCurrentSessionProperty();
279		if (!silently)
280		{
281			GUIUtilities.message(view, "sessions.switcher.save.saved", 
282						new Object[] { currentSession });
283		}
284		Log.log(Log.DEBUG, this, "session saved: " + currentSession.getName());
285	}
286
287
288	/**
289	 * Save current session under a different name and switch to it.
290	 * This sends out SessionListChanged and SessionChanged messages
291	 * on EditBus, if the session could be changed successfully, in this
292	 * order.
293	 *
294	 * @param view  view for displaying error messages
295	 */
296	public void saveCurrentSessionAs(View view)
297	{
298		String newName = inputSessionName(view, currentSession.getName());
299
300		if (newName == null)
301			return;
302
303		File file = new File(createSessionFileName(newName));
304		if (file.exists())
305		{
306			int answer = GUIUtilities.confirm(view,
307				"sessions.switcher.saveAs.exists",
308				new Object[] { newName },
309				JOptionPane.YES_NO_OPTION,
310				JOptionPane.WARNING_MESSAGE
311			);
312			if (answer != JOptionPane.YES_OPTION)
313				return;
314		}
315
316		// create new session:
317		Session newSession = currentSession.getClone();
318		newSession.setName(newName);
319		newSession.save(view);
320		EditBus.send(new SessionChanging(this, currentSession.getName(), newName));
321
322		// set new session:
323		String oldSessionName = currentSession.getName();
324		currentSession = newSession;
325		saveCurrentSessionProperty();
326		EditBus.send(new SessionListChanged(this));
327		EditBus.send(new SessionChanged(this, oldSessionName, newName, currentSession));
328		// update the jEdit title bar with the session name
329		setSessionNameInTitleBar();
330	}
331
332
333	/**
334	 * Reload current session.
335	 *
336	 * @param view  view for displaying error messages
337	 */
338	public void reloadCurrentSession(final View view)
339	{
340		Log.log(Log.DEBUG, this, "reloadCurrentSession: currentSession=" + currentSession);
341
342		// close all open buffers
343		if(!jEdit.closeAllBuffers(view))
344			return; // user cancelled
345
346		// FIXME: do we need to make sure this is not the AWT thread?!?
347		currentSession.open(view); // ignore any errors and return value
348	}
349
350
351	/**
352	 * Change FSB directory if relevant option selected, "basedir" property
353	 * set, and FSB is visible.
354	 *
355	 * @param view look for FSB in this View
356	 */
357	public void changeFSBToBaseDirectory(View view)
358	{
359		if ((jEdit.getBooleanProperty("sessions.switcher.changeFSBDirectory", false)) &&
360			!("".equals(currentSession.getProperty(Session.BASE_DIRECTORY))) &&
361			(view.getDockableWindowManager().isDockableWindowVisible(VFSBrowser.NAME)))
362		{
363			VFSBrowser.browseDirectory(view,
364				currentSession.getProperty(Session.BASE_DIRECTORY));
365		}
366	}
367
368
369	/**
370	 * Show the Session Manager dialog.
371	 * If the user changes the current session or modifies the
372	 * list of sessions, SessionChanged and SessionListChanged
373	 * messages are sent on EditBus.
374	 *
375	 * @param  view  center dialog on this View.
376	 */
377	public void showSessionManagerDialog(View view)
378	{
379		SessionManagerDialog dlg = new SessionManagerDialog(view, currentSession.getName());
380		String newSession = dlg.getSelectedSession();
381
382		if(dlg.isListModified())
383		{
384			EditBus.send(new SessionListChanged(this));
385			if(newSession == null)
386			{
387				// Session list has been modified, but dialog has been cancelled.
388				// Send out session changed events, just in case...
389				String name = currentSession.getName();
390				EditBus.send(new SessionChanging(this, name, name));
391				EditBus.send(new SessionChanged(this, name, name, currentSession));
392			}
393		}
394
395		if(newSession != null)
396			setCurrentSession(view, newSession);
397	}
398
399
400	public void showSessionPropertiesDialog(View view)
401	{
402		SessionPropertiesShowing message = new SessionPropertiesShowing(this, currentSession);
403		EditBus.send(message);
404		new SessionPropertiesDialog(view, currentSession.getName(), message.getRootGroup());
405	}
406
407
408	public static String[] getSessionNames()
409	{
410		String[] files = new File(getSessionsDir()).list(new FilenameFilter()
411		{
412			public boolean accept(File dir, String name)
413			{
414				return name.toLowerCase().endsWith(".xml");
415			}
416		});
417
418		Arrays.sort(files, new StandardUtilities.StringCompare(false));
419
420		Vector v = new Vector();
421		boolean foundDefault = false;
422		for (int i=0; i < files.length; i++)
423		{
424			String name = files[i].substring(0, files[i].length() - 4); // cut off ".xml"
425			if (name.equalsIgnoreCase("default"))
426			{
427				// default session always first
428				v.insertElementAt(name, 0);
429				foundDefault = true;
430			}
431			else
432				v.addElement(name);
433		}
434
435		if (!foundDefault)
436			v.insertElementAt("default", 0);
437
438		String[] result = new String[v.size()];
439		v.copyInto(result);
440		return result;
441	}
442
443
444	/** EBComponent implementation; does nothing */
445	public void handleMessage(EBMessage msg) {}
446	
447
448
449	/**
450	 * Converts a session name (eg, "default") to a full path name
451	 * (eg, "/home/slava/.jedit/sessions/default.xml").
452	 */
453	public static String createSessionFileName(String session)
454	{
455		String filename = MiscUtilities.constructPath(getSessionsDir(), session);
456		if (!filename.toLowerCase().endsWith(".xml"))
457			filename = filename + ".xml";
458		return filename;
459	}
460
461
462	/**
463	 * Return the directory where the session files are stored,
464	 * usually $HOME/.jedit/sessions.
465	 */
466	public static String getSessionsDir()
467	{
468		return MiscUtilities.constructPath(jEdit.getSettingsDirectory(), "sessions");
469	}
470
471
472	/**
473	 * Shows an input dialog asking for a session name as long as a valid
474	 * session name is entered or the dialog is cancelled.
475	 * A session name is valid if it doesn't contains the following characters:
476	 * File.separatorChar, File.pathSeparatorChar and ':'.
477	 *
478	 * @param relativeTo  the component where the dialog is centered on.
479	 * @param defaultName  a default session name to display in the input dialog; may be null.
480	 * @return the new session name, or null if the dialog was cancelled.
481	 */
482	public static String inputSessionName(Component relativeTo, String defaultName)
483	{
484		String name = defaultName;
485
486		do
487		{
488			name = GUIUtilities.input(relativeTo, "sessions.switcher.saveAs.input", name);
489			if (name != null)
490			{
491				name = name.trim();
492				if (name.length() == 0)
493					GUIUtilities.error(relativeTo, "sessions.switcher.saveAs.error.empty", null);
494				if (name.indexOf('/') >= 0 || name.indexOf('\\') >= 0
495					|| name.indexOf(';') >= 0 || name.indexOf(':') >= 0)
496				{
497					GUIUtilities.error(relativeTo, "sessions.switcher.saveAs.error.illegalChars", new Object[] { "/  \\  ;  :" });
498					name = "";
499				}
500			}
501		} while (name != null && name.length() == 0);
502
503		return name;
504	}
505
506
507	/**
508	 * Show an error message dialog (using GUIUtilities.error())
509	 * after the GUI has been updated, in the AWT thread.
510	 */
511	public static final void showErrorLater(final View view, final String messageProperty, final Object[] args)
512	{
513		VFSManager.runInAWTThread(new Runnable()
514		{
515			public void run()
516			{
517				GUIUtilities.error(view, messageProperty, args);
518			}
519		});
520	}
521
522	// {{{ jEdit title bar controls
523	// added by Paul Russell 2004-09-26
524	/**
525	 * Record the name of the current session in a jEdit property (SESSION_PROPERTY). This 
526	 * property is used to restore the last used session the next time the plugin is started.
527	 */
528	void saveCurrentSessionProperty()
529	{
530		Log.log(Log.DEBUG, this, "saveCurrentSessionProperty: currentSession=" + currentSession.getName());
531		jEdit.setProperty(SESSION_PROPERTY, currentSession.getName());
532		jEdit.saveSettings();
533	}
534	
535	/**
536	 * Put the session name in the jEdit title bar
537	 */
538	public void setSessionNameInTitleBar()
539	{
540		if ( currentSession != null )
541		{
542			if ( jEdit.getBooleanProperty("sessions.switcher.showSessionNameInTitleBar", true) )
543			{
544				if (jEdit.getBooleanProperty("sessions.switcher.showSessionPrefixInTitleBar", true))
545				{
546					titleBarSessionName = defaultViewTitle + 
547						jEdit.getProperty("sessions.titlebar.startbracket") +
548						jEdit.getProperty("sessions.titlebar.prefix") +
549						currentSession.getName() +
550						jEdit.getProperty("sessions.titlebar.endbracket");	
551				}
552				else
553				{
554					titleBarSessionName = defaultViewTitle + 
555						jEdit.getProperty("sessions.titlebar.startbracket") +
556						currentSession.getName() +
557						jEdit.getProperty("sessions.titlebar.endbracket");
558				}
559						
560				jEdit.setTemporaryProperty("view.title", titleBarSessionName);
561				refreshTitleBar();
562			}
563		}
564	}
565	
566	/**
567	 * Restore the original jEdit title bar text
568	 */
569	public void restoreTitleBarText()
570	{
571		jEdit.setTemporaryProperty("view.title", defaultViewTitle);	
572	}
573
574	/**
575	 * refreshes the jEdit Title Bar. This only needs to be called
576	 * when the plugin stops or restarts.
577	 */
578	public void refreshTitleBar()
579	{
580		View[] views = jEdit.getViews();
581		
582		for( int i = 0; i < views.length; i++ )
583		{
584			views[i].updateTitle();
585		}	
586	}
587	
588	// }}}
589}