[gedit-plugins/sessionsaver] Upload files



commit 30e8e1e8505f70cac7ed60e8f3241082594640c5
Author: Jordi Mas <jmas softcatala org>
Date:   Mon Jun 24 09:10:16 2019 +0200

    Upload files

 meson.build                                        |   1 +
 meson_options.txt                                  |   1 +
 plugins/sessionsaver/meson.build                   |  28 +++
 .../sessionsaver/sessionsaver.plugin.desktop.in.in |  10 +
 plugins/sessionsaver/sessionsaver/__init__.py      | 169 ++++++++++++++++
 plugins/sessionsaver/sessionsaver/dialogs.py       | 167 ++++++++++++++++
 plugins/sessionsaver/sessionsaver/gpdefs.py        |   5 +
 plugins/sessionsaver/sessionsaver/sessionsaver.ui  | 213 +++++++++++++++++++++
 plugins/sessionsaver/sessionsaver/store.py         | 161 ++++++++++++++++
 9 files changed, 755 insertions(+)
---
diff --git a/meson.build b/meson.build
index b1c295e..6f07951 100644
--- a/meson.build
+++ b/meson.build
@@ -91,6 +91,7 @@ all_plugins = {
   'git': {'language': 'python'},
   'joinlines': {'language': 'python'},
   'multiedit': {'language': 'python'},
+  'sessionsaver': {'language': 'python'},
   'smartspaces': {'language': 'python'},
   'terminal': {'language': 'python'},
   'textsize': {'language': 'python'},
diff --git a/meson_options.txt b/meson_options.txt
index d2770b7..a7a578f 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -10,6 +10,7 @@ option('plugin_findinfiles', type: 'boolean', value: false)
 option('plugin_git', type: 'boolean')
 option('plugin_joinlines', type: 'boolean')
 option('plugin_multiedit', type: 'boolean')
+option('plugin_sessionsaver', type: 'boolean')
 option('plugin_smartspaces', type: 'boolean')
 option('plugin_terminal', type: 'boolean')
 option('plugin_textsize', type: 'boolean')
diff --git a/plugins/sessionsaver/meson.build b/plugins/sessionsaver/meson.build
new file mode 100644
index 0000000..3f4ef18
--- /dev/null
+++ b/plugins/sessionsaver/meson.build
@@ -0,0 +1,28 @@
+install_subdir(
+  'sessionsaver',
+  install_dir: join_paths(
+    pkglibdir,
+    'plugins',
+  )
+)
+
+sessionsaver_plugin_in = configure_file(
+  input: 'sessionsaver.plugin.desktop.in.in',
+  output: 'sessionsaver.plugin.desktop.in',
+  configuration: plugin_in,
+  install: false,
+)
+
+sessionsaver_plugin = custom_target(
+  'sessionsaver.plugin',
+  input: sessionsaver_plugin_in,
+  output: 'sessionsaver.plugin',
+  command: msgfmt_plugin_cmd,
+  install: true,
+  install_dir: join_paths(
+    pkglibdir,
+    'plugins',
+  )
+)
+
+
diff --git a/plugins/sessionsaver/sessionsaver.plugin.desktop.in.in 
b/plugins/sessionsaver/sessionsaver.plugin.desktop.in.in
new file mode 100644
index 0000000..aee31e3
--- /dev/null
+++ b/plugins/sessionsaver/sessionsaver.plugin.desktop.in.in
@@ -0,0 +1,10 @@
+[Plugin]
+Loader=python3
+Module=sessionsaver
+IAge=3
+Name=Session Saver
+Description=Save and restore your working sessions
+Authors=Steve Frécinaux
+Copyright=Copyright © 2006 Steve Frécinaux
+Website=http://www.gedit.org
+Version=@VERSION@
diff --git a/plugins/sessionsaver/sessionsaver/__init__.py b/plugins/sessionsaver/sessionsaver/__init__.py
new file mode 100644
index 0000000..b1f9ac5
--- /dev/null
+++ b/plugins/sessionsaver/sessionsaver/__init__.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+# __init__.py
+# This file is part of gedit Session Saver Plugin
+#
+# Copyright (C) 2006-2007 - Steve Frécinaux <code istique net>
+# Copyright (C) 2010 - Kenny Meyer <knny myer gmail com>
+#
+# gedit Session Saver Plugin 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
+# (at your option) any later version.
+#
+# gedit Session Saver Plugin 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 gedit Session Saver Plugin; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA  02110-1301  USA
+
+from gi.repository import GObject, Gtk, Gedit
+import gettext
+from .store import XMLSessionStore
+from .dialogs import SaveSessionDialog, SessionManagerDialog
+from .gpdefs import *
+
+try:
+    gettext.bindtextdomain(GETTEXT_PACKAGE, GP_LOCALEDIR)
+    _ = lambda s: gettext.dgettext(GETTEXT_PACKAGE, s);
+except:
+    _ = lambda s: s
+
+ui_string = """
+<ui>
+  <menubar name="MenuBar">
+    <menu name="FileMenu" action="File">
+      <placeholder name="FileOps_2">
+        <separator/>
+        <menu name="FileSessionMenu" action="FileSession">
+          <placeholder name="SessionPluginPlaceHolder"/>
+          <separator/>
+          <menuitem name="FileSessionSaveMenu" action="FileSessionSave"/>
+          <menuitem name="FileSessionManageMenu" action="FileSessionManage"/>
+        </menu>
+      </placeholder>
+    </menu>
+  </menubar>
+</ui>
+"""
+
+class SessionSaverPlugin(GObject.Object, Gedit.WindowActivatable):
+    __gtype_name__ = "SessionSaverPlugin"
+
+    window = GObject.property(type=Gedit.Window)
+
+    SESSION_MENU_PATH = '/MenuBar/FileMenu/FileOps_2/FileSessionMenu/SessionPluginPlaceHolder'
+
+    def __init__(self):
+        GObject.Object.__init__(self)
+        self.sessions = XMLSessionStore()
+
+    def do_activate(self):
+        self._insert_menu()
+
+    def do_deactivate(self):
+        self._remove_menu()
+
+    def do_update_state(self):
+        self._action_group.get_action("FileSessionSave").set_sensitive(self.window.get_active_document() != 
None)
+
+    def _insert_menu(self):
+        ui_manager = self.window.get_ui_manager()
+
+        self._action_group = Gtk.ActionGroup("SessionSaverPluginActions")
+        self._action_group.add_actions(
+            [("FileSession", None, _("Sa_ved sessions"), None, None),
+             ("FileSessionSave", Gtk.STOCK_SAVE_AS,
+              _("_Save current session"), None,
+              _("Save the current document list as a new session"),
+              self.on_save_session_action),
+             ("FileSessionManage", None,
+              _("_Manage saved sessions..."), None,
+              _("Open the saved session manager"),
+              self.on_manage_sessions_action)
+            ])
+        ui_manager.insert_action_group(self._action_group)
+
+        self._ui_id = ui_manager.add_ui_from_string(ui_string)
+
+        self._insert_session_menu()
+
+        ui_manager.ensure_update()
+
+    def _remove_menu(self):
+        ui_manager = self.window.get_ui_manager()
+
+        self._remove_session_menu()
+
+        ui_manager.remove_ui(self._ui_id)
+        ui_manager.remove_action_group(self._action_group)
+
+        ui_manager.ensure_update()
+
+    def _insert_session_menu(self):
+        ui_manager = self.window.get_ui_manager()
+
+        self._merge_id = ui_manager.new_merge_id()
+
+        self._session_action_group = Gtk.ActionGroup(name="SessionSaverPluginSessionActions")
+        ui_manager.insert_action_group(self._session_action_group)
+
+        for i, session in enumerate(self.sessions):
+            action_name = 'SessionSaver%X' % i
+            action = Gtk.Action(action_name,
+                                session.name,
+                                _("Recover '%s' session") % session.name,
+                                None)
+            handler = action.connect("activate", self.session_menu_action, session)
+
+            self._session_action_group.add_action(action)
+
+            ui_manager.add_ui(self._merge_id,
+                              self.SESSION_MENU_PATH,
+                              action_name,
+                              action_name,
+                              Gtk.UIManagerItemType.MENUITEM,
+                              False)
+
+    def _remove_session_menu(self):
+        ui_manager = self.window.get_ui_manager()
+
+        for action in self._session_action_group.list_actions():
+            action.disconnect_by_func(self.session_menu_action)
+
+        ui_manager.remove_ui(self._merge_id)
+        ui_manager.remove_action_group(self._session_action_group)
+
+        ui_manager.ensure_update()
+
+    def _update_session_menu(self):
+        self._remove_session_menu()
+        self._insert_session_menu()
+
+    def _load_session(self, session):
+        # Note: a session has to stand on its own window.
+        tab = self.window.get_active_tab()
+        if tab is not None and \
+           not (tab.get_document().is_untouched() and \
+                tab.get_state() == Gedit.TabState.STATE_NORMAL):
+            # Create a new gedit window
+            window = Gedit.App.get_default().create_window(None)
+            window.show()
+        else:
+            window = self.window
+
+        Gedit.commands_load_locations(window, session.files, None, 0, 0)
+
+    def on_save_session_action(self, action):
+        SaveSessionDialog(self.window, self, self.sessions).run()
+
+    def on_manage_sessions_action(self, action):
+        SessionManagerDialog(self, self.sessions).run()
+
+    def session_menu_action(self, action, session):
+        self._load_session(session)
+
+# ex:ts=4:et:
diff --git a/plugins/sessionsaver/sessionsaver/dialogs.py b/plugins/sessionsaver/sessionsaver/dialogs.py
new file mode 100644
index 0000000..b45dade
--- /dev/null
+++ b/plugins/sessionsaver/sessionsaver/dialogs.py
@@ -0,0 +1,167 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2007 - Steve Frécinaux <code istique net>
+# Copyright (c) 2010 - Kenny Meyer <knny myer gmail com>
+# Licence: GPL2 or later
+
+from gi.repository import GObject, Gtk, Gedit
+import os.path
+import gettext
+
+from .store import Session
+
+try:
+    from .gpdefs import *
+    gettext.bindtextdomain(GETTEXT_PACKAGE, GP_LOCALEDIR)
+    _ = lambda s: gettext.dgettext(GETTEXT_PACKAGE, s);
+except:
+    _ = lambda s: s
+
+class SessionModel(Gtk.ListStore):
+    OBJECT_COLUMN = 0
+    NAME_COLUMN = 1
+    N_COLUMNS = 2
+
+    def __init__(self, store):
+        super(SessionModel, self).__init__(GObject.TYPE_PYOBJECT, str)
+        self.store = store
+        for session in store:
+            row = { self.OBJECT_COLUMN : session,
+                    self.NAME_COLUMN: session.name }
+            self.append(row.values())
+        self.store.connect_after('session-added', self.on_session_added)
+        self.store.connect('session-removed', self.on_session_removed)
+
+    def on_session_added(self, store, session):
+        row = { self.OBJECT_COLUMN : session,
+                self.NAME_COLUMN: session.name }
+        self.append(row.values())
+
+    def on_session_removed(self, store, session):
+        it = self.get_iter_first()
+        if it is not None:
+            while True:
+                stored_session = self.get_value(it, self.OBJECT_COLUMN)
+                if stored_session == session:
+                    self.remove(it)
+                    break
+                it = self.iter_next(it)
+                if not it:
+                    break
+
+class Dialog(object):
+    UI_FILE = "sessionsaver.ui"
+
+    def __new__(cls):
+        if not ('_instance' in cls.__dict__) or cls._instance is None:
+            cls._instance = object.__new__(cls)
+        return cls._instance
+
+    def __init__(self, main_widget, datadir, parent_window = None):
+        super(Dialog, self).__init__()
+
+        if parent_window is None:
+            parent_window = Gedit.App.get_default().get_active_window()
+        self.parent = parent_window
+
+        self.ui = Gtk.Builder()
+        self.ui.set_translation_domain(GETTEXT_PACKAGE)
+        self.ui.add_from_file(os.path.join(datadir, self.UI_FILE))
+        self.dialog = self.ui.get_object(main_widget)
+        self.dialog.connect('delete-event', self.on_delete_event)
+
+    def __getitem__(self, item):
+        return self.ui.get_object(item)
+
+    def on_delete_event(self, dialog, event):
+        dialog.hide()
+        return True
+
+    def __del__(self):
+        self.__class__._instance = None
+
+    def run(self):
+        self.dialog.set_transient_for(self.parent)
+        self.dialog.show()
+
+    def destroy(self):
+        self.dialog.destroy()
+        self.__del__()
+
+class SaveSessionDialog(Dialog):
+    def __init__(self, window, plugin, sessions):
+        super(SaveSessionDialog, self).__init__('save-session-dialog',
+                                                plugin.plugin_info.get_data_dir(),
+                                                window)
+        self.plugin = plugin
+        self.sessions = sessions
+        self.sessionsaver = plugin
+
+        model = SessionModel(sessions)
+
+        combobox = self['session-name']
+        combobox.set_model(model)
+        combobox.set_entry_text_column(1)
+
+        self.dialog.connect('response', self.on_response)
+
+    def on_response(self, dialog, response_id):
+        if response_id == Gtk.ResponseType.OK:
+            files = [doc.get_location()
+                        for doc in self.parent.get_documents()
+                        if doc.get_location() is not None]
+            name = self['session-name'].get_child().get_text()
+            self.sessions.add(Session(name, files))
+            self.sessions.save()
+            self.sessionsaver.sessions = self.sessions
+            self.sessionsaver._update_session_menu()
+        self.destroy()
+
+class SessionManagerDialog(Dialog):
+    def __init__(self, plugin, sessions):
+        super(SessionManagerDialog, self).__init__('session-manager-dialog',
+                                                   plugin.plugin_info.get_data_dir())
+        self.plugin = plugin
+        self.sessions = sessions
+
+        model = SessionModel(sessions)
+
+        self.view = self['session-view']
+        self.view.set_model(model)
+
+        renderer = Gtk.CellRendererText()
+        column = Gtk.TreeViewColumn(_("Session Name"), renderer, text = model.NAME_COLUMN)
+        self.view.append_column(column)
+
+        handlers = {
+            'on_close_button_clicked': self.on_close_button_clicked,
+            'on_open_button_clicked': self.on_open_button_clicked,
+            'on_delete_button_clicked': self.on_delete_button_clicked
+        }
+        self.ui.connect_signals(handlers)
+
+    def on_delete_event(self, dialog, event):
+        dialog.hide()
+        self.sessions.save()
+        return True
+
+    def get_current_session(self):
+        (model, selected) = self.view.get_selection().get_selected()
+        if selected is None:
+            return None
+        return model.get_value(selected, SessionModel.OBJECT_COLUMN)
+
+    def on_open_button_clicked(self, button):
+        session = self.get_current_session()
+        if session is not None:
+            self.plugin._load_session(session)
+
+    def on_delete_button_clicked(self, button):
+        session = self.get_current_session()
+        self.sessions.remove(session)
+        self.plugin._update_session_menu()
+
+    def on_close_button_clicked(self, button):
+        self.sessions.save()
+        self.destroy()
+
+# ex:ts=4:et:
diff --git a/plugins/sessionsaver/sessionsaver/gpdefs.py b/plugins/sessionsaver/sessionsaver/gpdefs.py
new file mode 100644
index 0000000..d63bfd3
--- /dev/null
+++ b/plugins/sessionsaver/sessionsaver/gpdefs.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+
+PACKAGE = "gedit-plugins"
+GETTEXT_PACKAGE = "gedit-plugins"
+GP_LOCALEDIR = "/usr/share/locale"
diff --git a/plugins/sessionsaver/sessionsaver/sessionsaver.ui 
b/plugins/sessionsaver/sessionsaver/sessionsaver.ui
new file mode 100644
index 0000000..7e333df
--- /dev/null
+++ b/plugins/sessionsaver/sessionsaver/sessionsaver.ui
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 2.12 -->
+  <object class="GtkDialog" id="save-session-dialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">6</property>
+    <property name="title" translatable="yes">Save session</property>
+    <property name="resizable">False</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | 
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="button2">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="receives_default">False</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button1">
+                <property name="label">gtk-save</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="can_default">True</property>
+                <property name="has_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="border_width">6</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Session name:</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="session-name">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="has_entry">True</property>
+                <child internal-child="entry">
+                  <object class="GtkEntry" id="combobox-entry2">
+                    <property name="can_focus">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">button2</action-widget>
+      <action-widget response="-5">button1</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkWindow" id="session-manager-dialog">
+    <property name="width_request">400</property>
+    <property name="height_request">200</property>
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">Saved Sessions</property>
+    <property name="type_hint">dialog</property>
+    <child>
+      <object class="GtkBox" id="hbox1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="border_width">6</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkScrolledWindow" id="scrolledwindow1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="hexpand">True</property>
+            <property name="hscrollbar_policy">never</property>
+            <property name="shadow_type">in</property>
+            <child>
+              <object class="GtkTreeView" id="session-view">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="headers_visible">False</property>
+                <child internal-child="selection">
+                  <object class="GtkTreeSelection" id="treeview-selection1"/>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVButtonBox" id="vbuttonbox1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="spacing">6</property>
+            <property name="layout_style">start</property>
+            <child>
+              <object class="GtkButton" id="open-button">
+                <property name="label">gtk-open</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="receives_default">False</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="on_open_button_clicked" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="delete-button">
+                <property name="label">gtk-delete</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="receives_default">False</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="on_delete_button_clicked" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="close-button">
+                <property name="label">gtk-close</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="receives_default">False</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="on_close_button_clicked" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/plugins/sessionsaver/sessionsaver/store.py b/plugins/sessionsaver/sessionsaver/store.py
new file mode 100644
index 0000000..3348441
--- /dev/null
+++ b/plugins/sessionsaver/sessionsaver/store.py
@@ -0,0 +1,161 @@
+# -*- coding: utf-8 -*-
+# store.py
+# This file is part of gedit Session Saver Plugin
+#
+# Copyright (C) 2006-2007 - Steve Frécinaux <code istique net>
+# Copyright (C) 2010 - Kenny Meyer <knny myer gmail com>
+#
+# gedit Session Saver Plugin 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
+# (at your option) any later version.
+#
+# gedit Session Saver Plugin 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 gedit Session Saver Plugin; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA  02110-1301  USA
+
+import os.path
+from xml.parsers import expat
+from gi.repository import GObject, GLib, Gio
+
+class Session(object):
+    def __init__(self, name, files = None):
+        super(Session, self).__init__()
+        self.name = name
+        if files is None:
+            files = []
+        self.files = files
+
+    def __lt__(self, session):
+        return (self.name.lower() < session.name.lower())
+
+    def add_file(self, filename):
+        self.files.append(Gio.file_new_for_uri(filename))
+
+class SessionStore(GObject.Object):
+    __gsignals__ = {
+        "session-added":    (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
+                             (GObject.TYPE_PYOBJECT,)),
+        "session-changed":  (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
+                             (GObject.TYPE_PYOBJECT,)),
+        "session-removed":  (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
+                            (GObject.TYPE_PYOBJECT,))
+    }
+
+    _instance = None
+    def __new__(cls):
+        if cls._instance is None:
+            cls._instance = GObject.Object.__new__(cls)
+        return cls._instance
+
+    def __init__(self):
+        super(SessionStore, self).__init__()
+        self._sessions = []
+
+    def __iter__(self):
+        return iter(self._sessions)
+
+    def __getitem__(self, index):
+        return self._sessions[index]
+
+    def __getslice__(self, i, j):
+        return self._sessions[i:j]
+
+    def __len__(self):
+        return len(self._sessions)
+
+    def do_session_added(self, session):
+        self._sessions.append(session)
+        self._sessions.sort()
+
+    def do_session_changed(self, session):
+        index = self._sessions.index(session)
+        self._sessions[index] = session
+
+    def add(self, session):
+        assert isinstance(session, Session)
+
+        if session in self:
+            self.emit('session-changed', session)
+        else:
+            self.emit('session-added', session)
+
+    def do_session_removed(self, session):
+        self._sessions.remove(session)
+
+    def remove(self, session):
+        assert isinstance(session, Session)
+        if session in self:
+            self.emit('session-removed', session)
+
+    def index(self, session):
+        return self._sessions.index(session)
+
+class XMLSessionStore(SessionStore):
+    def __init__(self):
+        super(XMLSessionStore, self).__init__()
+        self.filename = os.path.join(GLib.get_user_config_dir(), 'gedit/saved-sessions.xml')
+        self.load()
+
+    def _escape(self, string):
+        return string.replace('&', '&amp;') \
+                     .replace('<', '&lt;')  \
+                     .replace('>', '&gt;')  \
+                     .replace('"', '&quot;')
+
+    def _dump_session(self, session):
+        files = ''.join(['  <file path="%s"/>\n' % self._escape(location.get_uri())
+                            for location in session.files])
+        session_name = self._escape(str(session.name))
+        return '<session name="%s">\n%s</session>\n' % (session_name, files)
+
+    def dump(self):
+        dump = [self._dump_session(session) for session in self]
+        return '<saved-sessions>\n%s</saved-sessions>\n' % ''.join(dump)
+
+    def save(self):
+        dirname = os.path.dirname(self.filename)
+        if not os.path.isdir(dirname):
+            os.makedirs(dirname)
+
+        fp = open(self.filename, "wb")
+        fp.write(bytes('<?xml version="1.0" encoding="UTF-8"?>\n','UTF-8'))
+        fp.write(bytes(self.dump(),'UTF-8'))
+        fp.close()
+
+    def load(self):
+        if not os.path.isfile(self.filename):
+            return
+
+        parser = expat.ParserCreate('UTF-8')
+        parser.buffer_text = True
+        parser.StartElementHandler = self._expat_start_handler
+        parser.EndElementHandler = self._expat_end_handler
+
+        self._current_session = None
+        try:
+            parser.ParseFile(open(self.filename, 'rb'))
+        except:
+            return
+        del self._current_session
+
+    def _expat_start_handler(self, tag, attr):
+        if tag == 'file':
+            assert self._current_session is not None
+            self._current_session.add_file(str(attr['path']))
+        elif tag == 'session':
+            assert self._current_session is None
+            self._current_session = Session(attr['name'])
+
+    def _expat_end_handler(self, tag):
+        if tag == 'session':
+            self.add(self._current_session)
+            self._current_session = None
+
+# ex:ts=4:et:


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]