/* * PerspectiveManager.java - Saves view configuration * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2003 Slava Pestov * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit; import java.io.Closeable; import java.io.StreamTokenizer; import java.io.StringReader; import java.io.IOException; import java.util.Collection; import java.util.LinkedList; import java.util.Stack; import org.gjt.sp.util.IOUtilities; import org.gjt.sp.util.Log; import org.gjt.sp.util.XMLUtilities; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.helpers.DefaultHandler; /** Manages persistence of open buffers and views across jEdit sessions. * @since jEdit 4.2pre1 * @author Slava Pestov * @version $Id: PerspectiveManager.java 22357 2012-10-13 04:58:01Z ezust $ */ public class PerspectiveManager { private static final String PERSPECTIVE_FILENAME = "perspective"; //{{{ isPerspectiveDirty() method /** * We only autosave the perspective if it has changed, to avoid spinning * up the disk on laptops. * @since jEdit 4.2pre13 */ public static boolean isPerspectiveDirty() { return dirty; } //}}} //{{{ setPerspectiveDirty() method /** * We only autosave the perspective if it has changed, to avoid spinning * up the disk on laptops. * @since jEdit 4.2pre13 */ public static void setPerspectiveDirty(boolean dirty) { PerspectiveManager.dirty = dirty; } //}}} //{{{ isPerspectiveEnabled() method /** * We disable saving of the perspective while the 'close all' dialog is * showing. * @since jEdit 4.3pre2 */ public static boolean isPerspectiveEnabled() { return enabled; } //}}} //{{{ setPerspectiveEnabled() method /** * We disable saving of the perspective while the 'close all' dialog is * showing. * @since jEdit 4.3pre2 */ public static void setPerspectiveEnabled(boolean enabled) { PerspectiveManager.enabled = enabled; } //}}} //{{{ loadPerspective() method public static View loadPerspective(boolean restoreFiles) { if(perspectiveXML == null) return null; if(!perspectiveXML.fileExists()) return null; Log.log(Log.MESSAGE,PerspectiveManager.class,"Loading " + perspectiveXML); PerspectiveHandler handler = new PerspectiveHandler(restoreFiles); try { perspectiveXML.load(handler); } catch(IOException e) { Log.log(Log.ERROR,PerspectiveManager.class,e); } return handler.view; } //}}} //{{{ savePerspective() method public static void savePerspective(boolean autosave) { if(!isPerspectiveEnabled() || !jEdit.isStartupDone()) return; if(perspectiveXML == null) return; // backgrounded if(jEdit.getBufferCount() == 0) return; Buffer[] buffers = jEdit.getBuffers(); Collection<Buffer> savedBuffers = new LinkedList<Buffer>(); for (Buffer buffer: buffers) { if (!buffer.isNewFile()) { savedBuffers.add(buffer); } } if(!autosave) Log.log(Log.MESSAGE,PerspectiveManager.class,"Saving " + perspectiveXML); String lineSep = System.getProperty("line.separator"); SettingsXML.Saver out = null; try { out = perspectiveXML.openSaver(); out.writeXMLDeclaration(); out.write("<!DOCTYPE PERSPECTIVE SYSTEM \"perspective.dtd\">"); out.write(lineSep); out.write("<PERSPECTIVE>"); out.write(lineSep); for (Buffer buffer: savedBuffers) { out.write("<BUFFER AUTORELOAD=\""); out.write(buffer.getAutoReload() ? "TRUE" : "FALSE"); out.write("\" AUTORELOAD_DIALOG=\""); out.write(buffer.getAutoReloadDialog() ? "TRUE" : "FALSE"); out.write("\">"); out.write(XMLUtilities.charsToEntities(buffer.getPath(), false)); out.write("</BUFFER>"); out.write(lineSep); } View[] views = jEdit.getViews(); for(int i = 0; i < views.length; i++) { View view = views[i]; // ensures that active view is saved last, // ie created last on next load, ie in front // on next load if(view == jEdit.getActiveView() && i != views.length - 1) { View last = views[views.length - 1]; views[i] = last; views[views.length - 1] = view; view = last; } View.ViewConfig config = views[i].getViewConfig(); out.write("<VIEW PLAIN=\""); out.write(config.plainView ? "TRUE" : "FALSE"); out.write("\">"); out.write(lineSep); if (config.title != null) { out.write(lineSep); out.write("<TITLE>"); out.write(XMLUtilities.charsToEntities(config.title,false)); out.write("</TITLE>"); out.write(lineSep); } out.write("<PANES>"); out.write(lineSep); out.write(XMLUtilities.charsToEntities( config.splitConfig,false)); out.write(lineSep); out.write("</PANES>"); out.write(lineSep); out.write("<GEOMETRY X=\""); out.write(String.valueOf(config.x)); out.write("\" Y=\""); out.write(String.valueOf(config.y)); out.write("\" WIDTH=\""); out.write(String.valueOf(config.width)); out.write("\" HEIGHT=\""); out.write(String.valueOf(config.height)); out.write("\" EXT_STATE=\""); out.write(String.valueOf(config.extState)); out.write("\" />"); out.write(lineSep); if (config.docking != null) config.docking.saveLayout(PERSPECTIVE_FILENAME, i); out.write("</VIEW>"); out.write(lineSep); } out.write("</PERSPECTIVE>"); out.write(lineSep); out.finish(); } catch(IOException io) { Log.log(Log.ERROR,PerspectiveManager.class,"Error saving " + perspectiveXML); Log.log(Log.ERROR,PerspectiveManager.class,io); } finally { IOUtilities.closeQuietly((Closeable)out); } } //}}} //{{{ Private members private static boolean dirty, enabled = true; private static SettingsXML perspectiveXML; //}}} //{{{ Class initializer static { String settingsDirectory = jEdit.getSettingsDirectory(); if(settingsDirectory != null) { perspectiveXML = new SettingsXML(settingsDirectory, PERSPECTIVE_FILENAME); } } //}}} //{{{ PerspectiveHandler class private static class PerspectiveHandler extends DefaultHandler { View view; private StringBuilder charData; View.ViewConfig config; boolean restoreFiles; boolean restoreSplits; String autoReload, autoReloadDialog; PerspectiveHandler(boolean restoreFiles) { this.restoreFiles = restoreFiles; restoreSplits = jEdit.getBooleanProperty("restore.splits", true); config = new View.ViewConfig(); charData = new StringBuilder(); config.docking = View.getDockingFrameworkProvider().createDockingLayout(); } @Override public InputSource resolveEntity(String publicId, String systemId) { return XMLUtilities.findEntity(systemId, "perspective.dtd", getClass()); } @Override public void startElement(String uri, String localName, String qName, Attributes attrs) { charData.setLength(0); for (int i = 0; i < attrs.getLength(); i++) { String name = attrs.getQName(i); String value = attrs.getValue(i); attribute(name, value); } } private void attribute(String aname, String value) { if(aname.equals("X")) config.x = Integer.parseInt(value); else if(aname.equals("Y")) config.y = Integer.parseInt(value); else if(aname.equals("WIDTH")) config.width = Integer.parseInt(value); else if(aname.equals("HEIGHT")) config.height = Integer.parseInt(value); else if(aname.equals("EXT_STATE")) config.extState = Integer.parseInt(value); else if(aname.equals("PLAIN")) config.plainView = ("TRUE".equals(value)); else if(aname.equals("AUTORELOAD")) autoReload = value; else if(aname.equals("AUTORELOAD_DIALOG")) autoReloadDialog = value; } /** * @return true if the uri points to a remote file */ public static boolean skipRemote(String uri) { if (jEdit.getBooleanProperty("restore.remote")) return false; if(MiscUtilities.isURL(uri)) { String protocol = MiscUtilities.getProtocolOfURL(uri); if (!protocol.equals("file")) return true; } return false; } @Override public void endElement(String uri, String localName, String name) { if(name.equals("BUFFER")) { if (restoreFiles && !skipRemote(charData.toString())) { Buffer restored = jEdit.openTemporary(null,null, charData.toString(), false); // if the autoReload attributes are not present, don't set anything // it's sufficient to check whether they are present on the first BUFFER element if (restored != null) { if(autoReload != null) restored.setAutoReload("TRUE".equals(autoReload)); if(autoReloadDialog != null) restored.setAutoReloadDialog("TRUE".equals(autoReloadDialog)); jEdit.commitTemporary(restored); } } } else if(name.equals("PANES")) { SplitConfigParser parser = new SplitConfigParser(charData.toString()); parser.setIncludeSplits(restoreSplits); parser.setIncludeFiles(restoreFiles); parser.setIncludeRemoteFiles(jEdit.getBooleanProperty("restore.remote")); config.splitConfig = parser.parse(); } else if(name.equals("VIEW")) { if (config.docking != null) config.docking.loadLayout(PERSPECTIVE_FILENAME, jEdit.getViewCount()); view = jEdit.newView(view,null,config); config = new View.ViewConfig(); config.docking = View.getDockingFrameworkProvider().createDockingLayout(); } else if(name.equals("TITLE")) config.title = charData.toString(); } @Override public void characters(char[] ch, int start, int length) { charData.append(ch,start,length); } } //}}} }