summaryrefslogtreecommitdiff
path: root/gedit/gedit-session.c
diff options
context:
space:
mode:
Diffstat (limited to 'gedit/gedit-session.c')
-rwxr-xr-xgedit/gedit-session.c601
1 files changed, 601 insertions, 0 deletions
diff --git a/gedit/gedit-session.c b/gedit/gedit-session.c
new file mode 100755
index 00000000..3dc9f126
--- /dev/null
+++ b/gedit/gedit-session.c
@@ -0,0 +1,601 @@
+/*
+ * gedit-session.c - Basic session management for gedit
+ * This file is part of gedit
+ *
+ * Copyright (C) 2002 Ximian, Inc.
+ * Copyright (C) 2005 - Paolo Maggi
+ *
+ * Author: Federico Mena-Quintero <[email protected]>
+ *
+ * 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
+ * (at your option) 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.
+ */
+
+/*
+ * Modified by the gedit Team, 2002-2005. See the AUTHORS file for a
+ * list of people on the gedit Team.
+ * See the ChangeLog files for a list of changes.
+ *
+ * $Id$
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+
+#include <libxml/tree.h>
+#include <libxml/xmlwriter.h>
+
+#include "gedit-session.h"
+
+#include "gedit-debug.h"
+#include "gedit-plugins-engine.h"
+#include "gedit-prefs-manager-app.h"
+#include "gedit-metadata-manager.h"
+#include "gedit-window.h"
+#include "gedit-app.h"
+#include "gedit-commands.h"
+#include "dialogs/gedit-close-confirmation-dialog.h"
+#include "smclient/eggsmclient.h"
+
+/* The master client we use for SM */
+static EggSMClient *master_client = NULL;
+
+/* global var used during quit_requested */
+static GSList *window_dirty_list;
+
+static void ask_next_confirmation (void);
+
+#define GEDIT_SESSION_LIST_OF_DOCS_TO_SAVE "gedit-session-list-of-docs-to-save-key"
+
+static void
+save_window_session (GKeyFile *state_file,
+ const gchar *group_name,
+ GeditWindow *window)
+{
+ const gchar *role;
+ int width, height;
+ GeditPanel *panel;
+ GList *docs, *l;
+ GPtrArray *doc_array;
+ GeditDocument *active_document;
+ gchar *uri;
+
+ gedit_debug (DEBUG_SESSION);
+
+ role = gtk_window_get_role (GTK_WINDOW (window));
+ g_key_file_set_string (state_file, group_name, "role", role);
+ gtk_window_get_size (GTK_WINDOW (window), &width, &height);
+ g_key_file_set_integer (state_file, group_name, "width", width);
+ g_key_file_set_integer (state_file, group_name, "height", height);
+
+ panel = gedit_window_get_side_panel (window);
+ g_key_file_set_boolean (state_file, group_name, "side-panel-visible",
+ GTK_WIDGET_VISIBLE (panel));
+
+ panel = gedit_window_get_bottom_panel (window);
+ g_key_file_set_boolean (state_file, group_name, "bottom-panel-visible",
+ GTK_WIDGET_VISIBLE (panel));
+
+ active_document = gedit_window_get_active_document (window);
+ if (active_document)
+ {
+ uri = gedit_document_get_uri (active_document);
+ g_key_file_set_string (state_file, group_name,
+ "active-document", uri);
+ }
+
+ docs = gedit_window_get_documents (window);
+
+ doc_array = g_ptr_array_new ();
+ for (l = docs; l != NULL; l = g_list_next (l))
+ {
+ uri = gedit_document_get_uri (GEDIT_DOCUMENT (l->data));
+
+ if (uri != NULL)
+ g_ptr_array_add (doc_array, uri);
+
+ }
+ g_list_free (docs);
+
+ if (doc_array->len)
+ {
+ guint i;
+
+ g_key_file_set_string_list (state_file, group_name,
+ "documents",
+ (const char **)doc_array->pdata,
+ doc_array->len);
+ for (i = 0; i < doc_array->len; i++)
+ g_free (doc_array->pdata[i]);
+ }
+ g_ptr_array_free (doc_array, TRUE);
+}
+
+static void
+client_save_state_cb (EggSMClient *client,
+ GKeyFile *state_file,
+ gpointer user_data)
+{
+ const GList *windows;
+ gchar *group_name;
+ int n;
+
+ windows = gedit_app_get_windows (gedit_app_get_default ());
+ n = 1;
+
+ while (windows != NULL)
+ {
+ group_name = g_strdup_printf ("gedit window %d", n);
+ save_window_session (state_file,
+ group_name,
+ GEDIT_WINDOW (windows->data));
+ g_free (group_name);
+
+ windows = g_list_next (windows);
+ n++;
+ }
+}
+
+static void
+window_handled (GeditWindow *window)
+{
+ window_dirty_list = g_slist_remove (window_dirty_list, window);
+
+ /* whee... we made it! */
+ if (window_dirty_list == NULL)
+ egg_sm_client_will_quit (master_client, TRUE);
+ else
+ ask_next_confirmation ();
+}
+
+static void
+window_state_change (GeditWindow *window,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ GeditWindowState state;
+ GList *unsaved_docs;
+ GList *docs_to_save;
+ GList *l;
+ gboolean done = TRUE;
+
+ state = gedit_window_get_state (window);
+
+ /* we are still saving */
+ if (state & GEDIT_WINDOW_STATE_SAVING)
+ return;
+
+ unsaved_docs = gedit_window_get_unsaved_documents (window);
+
+ docs_to_save = g_object_get_data (G_OBJECT (window),
+ GEDIT_SESSION_LIST_OF_DOCS_TO_SAVE);
+
+
+ for (l = docs_to_save; l != NULL; l = l->next)
+ {
+ if (g_list_find (unsaved_docs, l->data))
+ {
+ done = FALSE;
+ break;
+ }
+ }
+
+ if (done)
+ {
+ g_signal_handlers_disconnect_by_func (window, window_state_change, data);
+ g_list_free (docs_to_save);
+ g_object_set_data (G_OBJECT (window),
+ GEDIT_SESSION_LIST_OF_DOCS_TO_SAVE,
+ NULL);
+
+ window_handled (window);
+ }
+
+ g_list_free (unsaved_docs);
+}
+
+static void
+close_confirmation_dialog_response_handler (GeditCloseConfirmationDialog *dlg,
+ gint response_id,
+ GeditWindow *window)
+{
+ GList *selected_documents;
+ GSList *l;
+
+ gedit_debug (DEBUG_COMMANDS);
+
+ switch (response_id)
+ {
+ case GTK_RESPONSE_YES:
+ /* save selected docs */
+
+ g_signal_connect (window,
+ "notify::state",
+ G_CALLBACK (window_state_change),
+ NULL);
+
+ selected_documents = gedit_close_confirmation_dialog_get_selected_documents (dlg);
+
+ g_return_if_fail (g_object_get_data (G_OBJECT (window),
+ GEDIT_SESSION_LIST_OF_DOCS_TO_SAVE) == NULL);
+
+ g_object_set_data (G_OBJECT (window),
+ GEDIT_SESSION_LIST_OF_DOCS_TO_SAVE,
+ selected_documents);
+
+ _gedit_cmd_file_save_documents_list (window, selected_documents);
+
+ /* FIXME: also need to lock the window to prevent further changes... */
+
+ break;
+
+ case GTK_RESPONSE_NO:
+ /* dont save */
+ window_handled (window);
+ break;
+
+ default:
+ /* disconnect window_state_changed where needed */
+ for (l = window_dirty_list; l != NULL; l = l->next)
+ g_signal_handlers_disconnect_by_func (window,
+ window_state_change, NULL);
+ g_slist_free (window_dirty_list);
+ window_dirty_list = NULL;
+
+ /* cancel shutdown */
+ egg_sm_client_will_quit (master_client, FALSE);
+
+ break;
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dlg));
+}
+
+static void
+show_confirmation_dialog (GeditWindow *window)
+{
+ GList *unsaved_docs;
+ GtkWidget *dlg;
+
+ gedit_debug (DEBUG_SESSION);
+
+ unsaved_docs = gedit_window_get_unsaved_documents (window);
+
+ g_return_if_fail (unsaved_docs != NULL);
+
+ if (unsaved_docs->next == NULL)
+ {
+ /* There is only one unsaved document */
+ GeditTab *tab;
+ GeditDocument *doc;
+
+ doc = GEDIT_DOCUMENT (unsaved_docs->data);
+
+ tab = gedit_tab_get_from_document (doc);
+ g_return_if_fail (tab != NULL);
+
+ gedit_window_set_active_tab (window, tab);
+
+ dlg = gedit_close_confirmation_dialog_new_single (
+ GTK_WINDOW (window),
+ doc,
+ TRUE);
+ }
+ else
+ {
+ dlg = gedit_close_confirmation_dialog_new (GTK_WINDOW (window),
+ unsaved_docs,
+ TRUE);
+ }
+
+ g_list_free (unsaved_docs);
+
+ g_signal_connect (dlg,
+ "response",
+ G_CALLBACK (close_confirmation_dialog_response_handler),
+ window);
+
+ gtk_widget_show (dlg);
+}
+
+static void
+ask_next_confirmation (void)
+{
+ g_return_if_fail (window_dirty_list != NULL);
+
+ /* pop up the confirmation dialog for the first window
+ * in the dirty list. The next confirmation is asked once
+ * this one has been handled.
+ */
+ show_confirmation_dialog (GEDIT_WINDOW (window_dirty_list->data));
+}
+
+/* quit_requested handler for the master client */
+static void
+client_quit_requested_cb (EggSMClient *client, gpointer data)
+{
+ GeditApp *app;
+ const GList *l;
+
+ gedit_debug (DEBUG_SESSION);
+
+ app = gedit_app_get_default ();
+
+ if (window_dirty_list != NULL)
+ {
+ g_critical ("global variable window_dirty_list not NULL");
+ window_dirty_list = NULL;
+ }
+
+ for (l = gedit_app_get_windows (app); l != NULL; l = l->next)
+ {
+ if (gedit_window_get_unsaved_documents (GEDIT_WINDOW (l->data)) != NULL)
+ {
+ window_dirty_list = g_slist_prepend (window_dirty_list, l->data);
+ }
+ }
+
+ /* no modified docs */
+ if (window_dirty_list == NULL)
+ {
+ egg_sm_client_will_quit (client, TRUE);
+
+ return;
+ }
+
+ ask_next_confirmation ();
+
+ gedit_debug_message (DEBUG_SESSION, "END");
+}
+
+/* quit handler for the master client */
+static void
+client_quit_cb (EggSMClient *client, gpointer data)
+{
+#if 0
+ gedit_debug (DEBUG_SESSION);
+
+ if (!client->save_yourself_emitted)
+ gedit_file_close_all ();
+
+ gedit_debug_message (DEBUG_FILE, "All files closed.");
+
+ matecomponent_mdi_destroy (MATECOMPONENT_MDI (gedit_mdi));
+
+ gedit_debug_message (DEBUG_FILE, "Unref gedit_mdi.");
+
+ g_object_unref (G_OBJECT (gedit_mdi));
+
+ gedit_debug_message (DEBUG_FILE, "Unref gedit_mdi: DONE");
+
+ gedit_debug_message (DEBUG_FILE, "Unref gedit_app_server.");
+
+ matecomponent_object_unref (gedit_app_server);
+
+ gedit_debug_message (DEBUG_FILE, "Unref gedit_app_server: DONE");
+#endif
+
+ gtk_main_quit ();
+}
+
+/**
+ * gedit_session_init:
+ *
+ * Initializes session management support. This function should be called near
+ * the beginning of the program.
+ **/
+void
+gedit_session_init (void)
+{
+ gedit_debug (DEBUG_SESSION);
+
+ if (master_client)
+ return;
+
+ master_client = egg_sm_client_get ();
+ g_signal_connect (master_client,
+ "save_state",
+ G_CALLBACK (client_save_state_cb),
+ NULL);
+ g_signal_connect (master_client,
+ "quit_requested",
+ G_CALLBACK (client_quit_requested_cb),
+ NULL);
+ g_signal_connect (master_client,
+ "quit",
+ G_CALLBACK (client_quit_cb),
+ NULL);
+}
+
+/**
+ * gedit_session_is_restored:
+ *
+ * Returns whether this gedit is running from a restarted session.
+ *
+ * Return value: TRUE if the session manager restarted us, FALSE otherwise.
+ * This should be used to determine whether to pay attention to command line
+ * arguments in case the session was not restored.
+ **/
+gboolean
+gedit_session_is_restored (void)
+{
+ gboolean restored;
+
+ gedit_debug (DEBUG_SESSION);
+
+ if (!master_client)
+ return FALSE;
+
+ restored = egg_sm_client_is_resumed (master_client);
+
+ gedit_debug_message (DEBUG_SESSION, restored ? "RESTORED" : "NOT RESTORED");
+
+ return restored;
+}
+
+static void
+parse_window (GKeyFile *state_file, const char *group_name)
+{
+ GeditWindow *window;
+ gchar *role, *active_document, **documents;
+ int width, height;
+ gboolean visible;
+ GeditPanel *panel;
+ GError *error = NULL;
+
+ role = g_key_file_get_string (state_file, group_name, "role", NULL);
+
+ gedit_debug_message (DEBUG_SESSION, "Window role: %s", role);
+
+ window = _gedit_app_restore_window (gedit_app_get_default (), (gchar *) role);
+ g_free (role);
+
+ if (window == NULL)
+ {
+ g_warning ("Couldn't restore window");
+ return;
+ }
+
+ width = g_key_file_get_integer (state_file, group_name,
+ "width", &error);
+ if (error)
+ {
+ g_clear_error (&error);
+ width = -1;
+ }
+ height = g_key_file_get_integer (state_file, group_name,
+ "height", &error);
+ if (error)
+ {
+ g_clear_error (&error);
+ height = -1;
+ }
+ gtk_window_set_default_size (GTK_WINDOW (window), width, height);
+
+
+ visible = g_key_file_get_boolean (state_file, group_name,
+ "side-panel-visible", &error);
+ if (error)
+ {
+ g_clear_error (&error);
+ visible = FALSE;
+ }
+
+ panel = gedit_window_get_side_panel (window);
+
+ if (visible)
+ {
+ gedit_debug_message (DEBUG_SESSION, "Side panel visible");
+ gtk_widget_show (GTK_WIDGET (panel));
+ }
+ else
+ {
+ gedit_debug_message (DEBUG_SESSION, "Side panel _NOT_ visible");
+ gtk_widget_hide (GTK_WIDGET (panel));
+ }
+
+ visible = g_key_file_get_boolean (state_file, group_name,
+ "bottom-panel-visible", &error);
+ if (error)
+ {
+ g_clear_error (&error);
+ visible = FALSE;
+ }
+
+ panel = gedit_window_get_bottom_panel (window);
+ if (visible)
+ {
+ gedit_debug_message (DEBUG_SESSION, "Bottom panel visible");
+ gtk_widget_show (GTK_WIDGET (panel));
+ }
+ else
+ {
+ gedit_debug_message (DEBUG_SESSION, "Bottom panel _NOT_ visible");
+ gtk_widget_hide (GTK_WIDGET (panel));
+ }
+
+ active_document = g_key_file_get_string (state_file, group_name,
+ "active-document", NULL);
+ documents = g_key_file_get_string_list (state_file, group_name,
+ "documents", NULL, NULL);
+ if (documents)
+ {
+ int i;
+ gboolean jump_to = FALSE;
+
+ for (i = 0; documents[i]; i++)
+ {
+ if (active_document != NULL)
+ jump_to = strcmp (active_document,
+ documents[i]) == 0;
+
+ gedit_debug_message (DEBUG_SESSION,
+ "URI: %s (%s)",
+ documents[i],
+ jump_to ? "active" : "not active");
+ gedit_window_create_tab_from_uri (window,
+ documents[i],
+ NULL,
+ 0,
+ FALSE,
+ jump_to);
+ }
+ g_strfreev (documents);
+ }
+
+ g_free (active_document);
+
+ gtk_widget_show (GTK_WIDGET (window));
+}
+
+/**
+ * gedit_session_load:
+ *
+ * Loads the session by fetching the necessary information from the session
+ * manager and opening files.
+ *
+ * Return value: TRUE if the session was loaded successfully, FALSE otherwise.
+ **/
+gboolean
+gedit_session_load (void)
+{
+ GKeyFile *state_file;
+ gchar **groups;
+ int i;
+
+ gedit_debug (DEBUG_SESSION);
+
+ state_file = egg_sm_client_get_state_file (master_client);
+ if (state_file == NULL)
+ return FALSE;
+
+ groups = g_key_file_get_groups (state_file, NULL);
+
+ for (i = 0; groups[i] != NULL; i++)
+ {
+ if (g_str_has_prefix (groups[i], "gedit window "))
+ parse_window (state_file, groups[i]);
+ }
+
+ g_strfreev (groups);
+ g_key_file_free (state_file);
+
+ return TRUE;
+}