Logo Search packages:      
Sourcecode: galeon version File versions

session.c

/*
 *  Copyright (C) 2002 Jorn Baayen
 *
 *  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, 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.
 */

#include "session.h"
#include "galeon-shell.h"
#include "galeon-dialog.h"
#include "galeon-tab.h"
#include "galeon-window.h"
#include "galeon-config.h"
#include "gul-general.h"
#include "prefs-strings.h"
#include "eel-gconf-extensions.h"
#include "gul-x11.h"

#include <string.h>
#include <time.h>

#include <glib/gi18n.h>
#include <gtk/gtkdialog.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlmemory.h>
#include <libgnomevfs/gnome-vfs-ops.h>
#include <libgnomeui/gnome-client.h>

#define CONF_STATE_RECOVERY_METHOD "/apps/galeon/State/recovery_method"

enum
{
      RESTORE_TYPE_PROP
};

enum
{
      RESTORE_SESSION,
      RESTORE_AS_BOOKMARKS,
      DISCARD_SESSION
};

static const
GaleonDialogProperty crashed_dialog_props [] =
{
        { RESTORE_TYPE_PROP, "restore_radio", CONF_STATE_RECOVERY_METHOD, PT_NORMAL, NULL },
        { -1, NULL, NULL }
};

static void session_class_init (SessionClass *klass);
static void session_init (Session *t);
static void session_finalize (GObject *object);
static void session_dispose (GObject *object);
static void session_create_bookmarks (Session *session, const char *filename);

static GObjectClass *parent_class = NULL;

#define SESSION_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), \
                               TYPE_SESSION, SessionPrivate))


struct SessionPrivate
{
      GaleonDialog *crash_dialog;
      GList *windows;
      gboolean dont_remove_crashed;
      gboolean session_saved;
};

enum
{
        ADD_RECENT_SESSION,
      NEW_WINDOW,
      CLOSE_WINDOW,
        LAST_SIGNAL
};

static guint session_signals[LAST_SIGNAL] = { 0 };

GType 
session_get_type (void)
{
        static GType session_type = 0;

        if (session_type == 0)
        {
                static const GTypeInfo our_info =
                {
                        sizeof (SessionClass),
                        NULL, /* base_init */
                        NULL, /* base_finalize */
                        (GClassInitFunc) session_class_init,
                        NULL,
                        NULL, /* class_data */
                        sizeof (Session),
                        0, /* n_preallocs */
                        (GInstanceInitFunc) session_init
                };

                session_type = g_type_register_static (G_TYPE_OBJECT,
                                             "Session",
                                             &our_info, 0);
        }

        return session_type;

}

static void
session_class_init (SessionClass *klass)
{
        GObjectClass *object_class = G_OBJECT_CLASS (klass);

        parent_class = g_type_class_peek_parent (klass);

        object_class->finalize = session_finalize;
      object_class->dispose = session_dispose;

      session_signals[ADD_RECENT_SESSION] =
                g_signal_new ("add_recent_session",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (SessionClass, add_recent_session),
                              NULL, NULL,
                              g_cclosure_marshal_VOID__STRING,
                              G_TYPE_NONE,
                              1,
                        G_TYPE_STRING);
      
      session_signals[NEW_WINDOW] =
                g_signal_new ("new_window",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (SessionClass, new_window),
                              NULL, NULL,
                              g_cclosure_marshal_VOID__OBJECT,
                              G_TYPE_NONE,
                              1,
                        G_TYPE_OBJECT);

      session_signals[CLOSE_WINDOW] =
            g_signal_new ("close_window",
                        G_OBJECT_CLASS_TYPE (object_class),
                        G_SIGNAL_RUN_FIRST,
                        G_STRUCT_OFFSET (SessionClass, close_window),
                        NULL, NULL,
                        g_cclosure_marshal_VOID__VOID,
                        G_TYPE_NONE,
                        0);

      g_type_class_add_private (klass, sizeof (SessionPrivate));
}

static char *
get_session_filename (const char *filename)
{
      char *save_to;
      
      g_return_val_if_fail (filename != NULL, NULL);

      if (strcmp (filename, SESSION_SAVED) == 0)
      {
            save_to = g_build_filename (g_get_home_dir (),
                                  GALEON_DIR,
                                  "session_saved.xml",
                                  NULL);
      }
      else if (strcmp (filename, SESSION_CRASHED) == 0)
      {
            save_to = g_build_filename (g_get_home_dir (),
                                  GALEON_DIR,
                                  "session_crashed.xml",
                                  NULL);
      }
      else if (strcmp (filename, SESSION_GNOME) == 0)
      {
            char *tmp;
            
            save_to = g_build_filename (g_get_home_dir (),
                                  GALEON_DIR,
                                  "session_gnome-XXXXXX",
                                  NULL);
            tmp = gul_general_tmp_filename (save_to, "xml");
            g_free (save_to);
            save_to = tmp;
      }
      else
      {
            save_to = g_strdup (filename);
      }

      return save_to;
}

static void
do_session_resume (Session *session, int type)
{
      char *crashed_session;

      crashed_session = get_session_filename (SESSION_CRASHED);   
      switch (type)
      {
      case RESTORE_SESSION:
            session_load (session, crashed_session, 0u);
            break;
      case RESTORE_AS_BOOKMARKS:
            session_create_bookmarks (session, crashed_session);
            break;
      case DISCARD_SESSION:
            break;
      }
      g_free (crashed_session);
}

static gboolean
crashed_resume_dialog (Session *session)
{
      GaleonDialog *dialog;
      int i;
      GValue resume_type = { 0, };
            
      dialog = galeon_dialog_new ();
      session->priv->crash_dialog = dialog;
      galeon_dialog_construct (dialog,
                         crashed_dialog_props,
                         "galeon.glade", 
                         "crash_recovery_dialog");
      i = galeon_dialog_run (dialog);

      galeon_dialog_get_value (session->priv->crash_dialog,
                         RESTORE_TYPE_PROP, 
                         &resume_type);
      
      g_object_unref (dialog);
      
      if (i == GTK_RESPONSE_OK)
      {
            do_session_resume (session, 
                                 g_value_get_int 
                           (&resume_type));
            return TRUE;
      }

      return FALSE;
}

/**
 * session_autoresume:
 * @session: a #Session
 *
 * Resume a crashed session when necessary (interactive)
 * 
 * Return value: returns SESSION_ABORT   if the user wants to abort starting galeon
 *                       SESSION_RESUMED if a session has been resumed
 *                       SESSION_OK      if no session was resumed
 **/
SessionResumeType
session_autoresume (Session *session)
{
      char *saved_session;
      gboolean loaded = FALSE;
      
      saved_session = get_session_filename (SESSION_CRASHED);

      if (g_file_test (saved_session, G_FILE_TEST_EXISTS))
      {
            /* we want to exit now, so tell our caller that the
             * user wants to abort, but dont remove the crashed
             * session. */
            if (!crashed_resume_dialog (session))
            {
                  session->priv->dont_remove_crashed = TRUE;
                  return SESSION_ABORT;
            }
                  
            loaded = TRUE;
      }

      g_free (saved_session);

      saved_session = get_session_filename (SESSION_SAVED);

      if (g_file_test (saved_session, G_FILE_TEST_EXISTS) 
          && !loaded
          && eel_gconf_get_boolean (CONF_GENERAL_ALWAYS_SAVE_SESSION))
      {
            session_load (session, saved_session, 0u);
      }
      
      g_free (saved_session);

      /* return SESSION_RESUMED, if a window was opened, else SESSION_OK */
      return (session->priv->windows != NULL) ? SESSION_RESUMED : SESSION_OK;
}

static gboolean 
save_yourself_cb  (GnomeClient *client,
                   gint phase,
                   GnomeSaveStyle save_style,
                   gboolean shutdown,
                   GnomeInteractStyle interact_style,
                   gboolean fast,
               Session *session)
{
      char *argv[4] = { "galeon", NULL, NULL, NULL };
      char *discard_argv[] = { "rm", "-r", NULL };
      char *session_file = NULL;
      int count = 1;

      if (galeon_shell_get_server_mode (galeon_shell))
      {
            argv[count++] = "--server";
      }

      if (session_get_windows (session))
      {
            session_file = get_session_filename (SESSION_GNOME);
            discard_argv[2] = session_file;
            gnome_client_set_discard_command (client, 3, discard_argv);

            argv[count++] = "--load-session";
            argv[count++] = session_file;

            session_save (session, session_file);
      }

      gnome_client_set_restart_command (client, count, argv);

      g_free (session_file);
     
      return TRUE;
}

static void
session_die_cb (GnomeClient* client, 
            Session *session)
{
      g_object_ref (galeon_shell);

      session_close (session);

      /* Turn off server mode, so that we exit nicely */
      galeon_shell_set_server_mode (galeon_shell, FALSE);

      g_object_unref (galeon_shell);
}

static void
gnome_session_init (Session *session)
{
      GnomeClient *client;
            
      client = gnome_master_client ();
      
      g_signal_connect (G_OBJECT (client), 
                    "save_yourself",
                          G_CALLBACK (save_yourself_cb), 
                    session);
      
      g_signal_connect (G_OBJECT (client), 
                    "die",
                          G_CALLBACK (session_die_cb), 
                    session);
}

static void
session_init (Session *session)
{
        session->priv = SESSION_GET_PRIVATE (session);
      session->priv->windows = NULL;
      session->priv->dont_remove_crashed = FALSE;
      session->priv->session_saved = FALSE;
      
      gnome_session_init (session);
}

/*
 * session_close:
 * @session: a #Session
 * 
 * Close the session and all the owned windows
 **/
void
session_close (Session *session)
{
      GList *windows, *l;

      /* save the session */
      session_save (session, SESSION_SAVED);
      session->priv->session_saved = TRUE;
      
      /* close all windows */
      windows = g_list_copy (session->priv->windows);
      for (l = windows; l != NULL; l = l->next)
      {     
            GaleonWindow *window = GALEON_WINDOW(l->data);
            gtk_widget_destroy (GTK_WIDGET(window));
      }
      g_list_free(windows);
}

static void 
session_delete (Session *session,
            const char *filename)
{
      char *save_to;
      
      save_to = get_session_filename (filename);

      gnome_vfs_unlink (save_to);

      g_free (save_to);
}

static void
session_dispose (GObject *object)
{
      Session *session = SESSION(object);

      if (!session->priv->dont_remove_crashed)
      {
            session_delete (session, SESSION_CRASHED);
      }

      if (!session->priv->session_saved)
      {
            session_delete (session, SESSION_SAVED);
      }
}

static void
session_finalize (GObject *object)
{
      Session *t;

        g_return_if_fail (object != NULL);
        g_return_if_fail (IS_SESSION (object));

      t = SESSION (object);
      
        g_return_if_fail (t->priv != NULL);

      g_list_free (t->priv->windows);
      

        G_OBJECT_CLASS (parent_class)->finalize (object);
}

/**
 * session_new:
 * 
 * Create a #Session. A session hold the information
 * about the windows currently opened and is able to persist
 * and restore his status.
 **/
Session *
session_new (void)
{
      Session *t;

      t = SESSION (g_object_new (TYPE_SESSION, NULL));

      g_return_val_if_fail (t->priv != NULL, NULL);

      return t;
}

static void
save_tab (GaleonWindow *window,
        GaleonTab *tab, 
        xmlDocPtr doc,
        xmlNodePtr window_node)
{
      EmbedChromeMask chrome;
      const char *location;   
      const char *title;
        xmlNodePtr embed_node;
      
      chrome = galeon_window_get_chrome (window);
                  
      /* skip if it's a XUL dialog */
        if (chrome & EMBED_CHROME_OPENASCHROME) return;
                        
      /* make a new XML node */
        embed_node = xmlNewDocNode (doc, NULL,
                                    "embed", NULL);

        /* store title in the node */
      title = galeon_tab_get_title (tab);
      xmlSetProp (embed_node, "title", title);
      
        /* otherwise, use the actual location. */
      location = galeon_tab_get_location (tab);
        xmlSetProp (embed_node, "url", location);
      
      /* insert node into the tree */
      xmlAddChild (window_node, embed_node);    
}

/*
 * session_save:
 * @session: a #Session
 * @filename: path of the xml file where the session is saved.
 *
 * Save the session on disk. Keep information about window size,
 * opened urls ...
 **/
void
session_save (Session *session,
            const char *filename)
{
      const GList *w;
        xmlNodePtr root_node;
        xmlNodePtr window_node;
        xmlDocPtr doc;
        gchar buffer[32];
      char *save_to;
      
      g_return_if_fail (IS_SESSION (session));

      /* Dont save empty sessions, just delete the session file */
      if (!session->priv->windows)
      {
            session_delete (session, filename);
            return;
      }

      save_to = get_session_filename (filename);
            
        doc = xmlNewDoc ("1.0");

        /* create and set the root node for the session */
        root_node = xmlNewDocNode (doc, NULL, "session", NULL);
        xmlDocSetRootElement (doc, root_node);

        /* iterate through all the windows */
        for (w = session->priv->windows; w != NULL; w = w->next)
        {
            GList *tabs, *l;
            int x = 0, y = 0, width = 0, height = 0;
            GaleonWindow *window = GALEON_WINDOW(w->data);
            GtkWidget *wmain;
            
            tabs = galeon_window_get_tabs (window);
            g_return_if_fail (tabs != NULL);
            
                /* make a new XML node */
                window_node = xmlNewDocNode (doc, NULL, "window", NULL);

                /* get window geometry */
            wmain = GTK_WIDGET (window);
                gtk_window_get_size (GTK_WINDOW(wmain), &width, &height);
                gtk_window_get_position (GTK_WINDOW(wmain), &x, &y);

                /* set window properties */
                g_snprintf(buffer, 32, "%d", x);
                xmlSetProp (window_node, "x", buffer);
                g_snprintf(buffer, 32, "%d", y);

            xmlSetProp (window_node, "y", buffer);
                g_snprintf(buffer, 32, "%d", width);
                xmlSetProp (window_node, "width", buffer);
                g_snprintf(buffer, 32, "%d", height);
                xmlSetProp (window_node, "height", buffer);

            for (l = tabs; l != NULL; l = l->next)
              {
                  GaleonTab *tab = GALEON_TAB(l->data);
                  save_tab (window, tab, doc, window_node);
            }
            g_list_free (tabs);

            xmlAddChild (root_node, window_node);
        }

        /* save it all out to disk */
      gul_general_safe_xml_save (save_to, doc);
        xmlFreeDoc (doc);

      g_free (save_to);
}

typedef void (*WindowCb) (xmlNodePtr child, 
                    const int x, const int y, 
                    const int width, const int height, 
                    gpointer data);
typedef void (*TabCb) (const char *url, const char *title, 
                   gpointer data);


static void
parse_embed (xmlNodePtr child, TabCb tab_cb, gpointer data)
{     
      while (child != NULL)
      {
            if (strcmp (child->name, "embed") == 0)
            {
                  char *url;
                  char *title;
                  
                  url = xmlGetProp (child, "url");
                  title = xmlGetProp (child, "title");
                  tab_cb (url, title, data);
                  xmlFree (url);
                  xmlFree (title);
            }

            child = child->next;
      }
}

static void 
parse_xml (Session *session,
         const char *filename, 
         WindowCb window_cb, gpointer data)
{
      xmlDocPtr doc;
        xmlNodePtr child;
      char *save_to;
      
      save_to = get_session_filename (filename);

      doc = xmlParseFile (save_to);
      if (doc == NULL)
      {
            return;
      }

      child = xmlDocGetRootElement (doc); 
      g_return_if_fail (child!= NULL);

      /* skip the session node */
      child = child->children;
      
      while (child != NULL)
      {
            if (strcmp (child->name, "window") == 0)
            {
                  gint x = 0, y = 0, width = 0, height = 0;
                  
                  x = xmlGetIntProp (child, "x");
                        y = xmlGetIntProp (child, "y");
                        width = xmlGetIntProp (child, "width");
                        height = xmlGetIntProp (child, "height");
                  window_cb (child, x, y, width, height, data);
            }

            child = child->next;
      }

      xmlFreeDoc (doc);

      g_free (save_to);
}


static void 
create_new_tab (const char *url, const char *title, GaleonWindow *window)
{
      GaleonTab *tab;
      GaleonEmbed *embed;

      tab = galeon_tab_new ();      
      embed = galeon_tab_get_embed (tab);
      
      gtk_widget_show (GTK_WIDGET(tab));
      
      galeon_window_add_tab (window, tab,
                         GALEON_WINDOW_ADD_TAB_LAST, FALSE);
      
      galeon_embed_load_url (embed, url);
}

static void
create_new_window (xmlNodePtr child,
               const int x, const int y, 
               const int width, const int height, gpointer data)
{
      GtkWidget *wmain;
      GaleonWindow *window;

      window = galeon_window_new ();
      wmain = GTK_WIDGET (window);

      gul_x11_window_update_user_time (wmain, GPOINTER_TO_INT (data));
      gtk_widget_show (GTK_WIDGET(window));
      
      gtk_window_move (GTK_WINDOW(wmain), x, y);
      gtk_window_set_default_size (GTK_WINDOW (wmain),
                             width, height);

      parse_embed (child->children, (TabCb)create_new_tab, window);
}

/*
 * The following 2 functions are used to create bookmarks from a session
 * files (used when galeon crashed)
 */

static void 
bookmark_from_tab (const char *url, const char *title, GbFolder *folder)
{
      GbSite *new_site;
      GbBookmarkSet *set;

      g_return_if_fail (GB_IS_FOLDER (folder));

      set = galeon_shell_get_bookmark_set (galeon_shell);
      new_site = gb_site_new (set, title, url);
      gb_folder_add_child (folder, GB_BOOKMARK(new_site), -1);
      gb_bookmark_set_time_added_now (GB_BOOKMARK (new_site));
}

static void
bookmarks_from_window (xmlNodePtr child,
                   const int x, const int y, 
                   const int width, const int height,
                   GbFolder *folder)
{
      g_return_if_fail (GB_IS_FOLDER (folder));
      parse_embed (child->children, (TabCb)bookmark_from_tab, folder);
}

/*
 * session_load:
 * @session: a #Session
 * @filename: the path of the source file
 *
 * Load a session from disk, restoring the windows and their state
 **/

void 
session_load (Session *session, const char *filename, guint32 user_time)
{
      parse_xml (session, filename, (WindowCb)create_new_window,
               GINT_TO_POINTER(user_time));
}

GList *
session_get_windows (Session *session)
{
      g_return_val_if_fail (IS_SESSION (session), NULL);
      
      return session->priv->windows;
}

/*
 * Create bookmarks from a crashed session. Store the bookmarks 
 * in a subfolder of the root folder called "Sites opened before
 * crash on <date> at <time>"
 */
static
void session_create_bookmarks (Session *session, const char *filename)
{
      GbFolder *root_folder;
      GbFolder *new_folder;
      GbBookmarkSet *set;
      char date[64];
      const char *date_hack="%c"; /* This is necessary to avoid a warning */
      struct tm *tm;
      time_t timep;
      gchar *folder_name;

      g_return_if_fail (IS_SESSION (session));

      /* Create a new folder under the root bookmarks which will be used
       * to add the recovered bookmarks
       */
      set = galeon_shell_get_bookmark_set (galeon_shell);
      root_folder = set->root;

      time (&timep);
      tm = localtime (&timep);
      if (strftime (date, sizeof(date), date_hack, tm) != 0) {
            gchar *date_utf8 = g_locale_to_utf8 (date, -1,
                                         NULL, NULL, NULL);
            
            folder_name = g_strdup_printf (_("Sites opened before crash on %s"), date_utf8);
            g_free (date_utf8);
      } else {
            folder_name = g_strdup_printf ( _("Sites opened before crash"));
      }
      new_folder =  gb_folder_new (set, folder_name);
      g_free (folder_name);
      gb_folder_add_child (root_folder, GB_BOOKMARK (new_folder), -1);
      gb_bookmark_set_time_added_now (GB_BOOKMARK (new_folder));

      /* Add bookmarks corresponding to the previous session to this folder
       */
      parse_xml (session, filename, (WindowCb)bookmarks_from_window, 
               new_folder);
}

/**
 * session_add_window:
 * @session: a #Session
 * @window: a #GaleonWindow
 *
 * Add a window to the session. #GaleonWindow take care of adding
 * itself to session.
 **/
void 
session_add_window (Session *session,
                GaleonWindow *window)
{
      session->priv->windows = g_list_append (session->priv->windows, window);

      g_signal_emit (G_OBJECT (session), 
                   session_signals[NEW_WINDOW], 
                   0, window);
}

/**
 * session_remove_window:
 * @session: a #Session
 * @window: a #GaleonWindow
 *
 * Remove a window from the session. #GaleonWindow take care of removing
 * itself to session.
 **/
void  
session_remove_window (Session *session,
                   GaleonWindow *window)
{
      g_signal_emit (G_OBJECT (session), 
                   session_signals[CLOSE_WINDOW],
                   0);

      session->priv->windows = g_list_remove (session->priv->windows, window);

      session_save (session, SESSION_CRASHED);

      /* autodestroy of the session, necessay to avoid
       * conflicts with the nautilus view */
      if (session->priv->windows == NULL && 
          galeon_shell_get_server_mode (galeon_shell) == FALSE)
      {
            g_object_unref (session);
      }
}


Generated by  Doxygen 1.6.0   Back to index