aboutsummaryrefslogtreecommitdiffstats
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/Makefile.am2
-rw-r--r--shell/e-shell-migrate.c339
-rw-r--r--shell/e-shell-migrate.h45
-rw-r--r--shell/e-shell-module.c35
-rw-r--r--shell/e-shell-module.h14
-rw-r--r--shell/e-shell.c43
-rw-r--r--shell/e-shell.h1
-rw-r--r--shell/test/e-test-shell-module.c39
8 files changed, 505 insertions, 13 deletions
diff --git a/shell/Makefile.am b/shell/Makefile.am
index 0603f603fa..f8510f9357 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -61,6 +61,7 @@ eshellincludedir = $(privincludedir)/shell
eshellinclude_HEADERS = \
e-shell-common.h \
e-shell-content.h \
+ e-shell-migrate.h \
e-shell-module.h \
e-shell-sidebar.h \
e-shell-switcher.h \
@@ -73,6 +74,7 @@ eshellinclude_HEADERS = \
libeshell_la_SOURCES = \
$(IDL_GENERATED) \
e-shell-content.c \
+ e-shell-migrate.c \
e-shell-module.c \
e-shell-sidebar.c \
e-shell-switcher.c \
diff --git a/shell/e-shell-migrate.c b/shell/e-shell-migrate.c
new file mode 100644
index 0000000000..9979899563
--- /dev/null
+++ b/shell/e-shell-migrate.c
@@ -0,0 +1,339 @@
+/*
+ * e-shell-migrate.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-shell-migrate.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <libedataserver/e-xml-utils.h>
+
+#include "e-util/e-bconf-map.h"
+#include "e-util/e-error.h"
+#include "e-util/e-fsutils.h"
+#include "e-util/e-util.h"
+
+#define GCONF_VERSION_KEY "/apps/evolution/version"
+#define GCONF_LAST_VERSION_KEY "/apps/evolution/last_version"
+
+static const gchar *
+shell_migrate_get_old_data_dir (void)
+{
+ static gchar *old_data_dir = NULL;
+
+ if (G_UNLIKELY (old_data_dir == NULL))
+ old_data_dir = g_build_filename (
+ g_get_home_dir (), "evolution", NULL);
+
+ return old_data_dir;
+}
+
+static gboolean
+shell_migrate_attempt (EShell *shell,
+ gint major,
+ gint minor,
+ gint micro)
+{
+ GList *modules;
+ gboolean success = TRUE;
+
+ modules = e_shell_list_modules (shell);
+
+ while (success && modules != NULL) {
+ EShellModule *shell_module = modules->data;
+ GError *error = NULL;
+
+ success = e_shell_module_migrate (
+ shell_module, major, minor, micro, &error);
+
+ if (error != NULL) {
+ gint response;
+
+ response = e_error_run (
+ NULL, "shell:upgrade-failed",
+ error->message, NULL);
+
+ if (response == GTK_RESPONSE_CANCEL)
+ success = FALSE;
+
+ g_error_free (error);
+ }
+
+ modules = g_list_next (modules);
+ }
+
+ return success;
+}
+
+static void
+shell_migrate_get_version (gint *major,
+ gint *minor,
+ gint *micro)
+{
+ GConfClient *client;
+ const gchar *key;
+ const gchar *old_data_dir;
+ gchar *string;
+
+ old_data_dir = shell_migrate_get_old_data_dir ();
+
+ key = GCONF_VERSION_KEY;
+ client = gconf_client_get_default ();
+ string = gconf_client_get_string (client, key, NULL);
+ g_object_unref (client);
+
+ if (string != NULL) {
+ /* Since 1.4.0 we've kept the version key in GConf. */
+ sscanf (string, "%d.%d.%d", major, minor, micro);
+ g_free (string);
+
+ } else if (!g_file_test (old_data_dir, G_FILE_TEST_IS_DIR)) {
+ /* If the old data directory does not exist,
+ * it must be a new installation. */
+ *major = 0;
+ *minor = 0;
+ *micro = 0;
+
+ } else {
+ xmlDocPtr doc;
+ xmlNodePtr source;
+ gchar *filename;
+
+ filename = g_build_filename (
+ old_data_dir, "config.xmldb", NULL);
+ doc = e_xml_parse_file (filename);
+ g_free (filename);
+
+ if (doc == NULL)
+ return;
+
+ source = e_bconf_get_path (doc, "/Shell");
+ if (source != NULL) {
+ key = "upgrade_from_1_0_to_1_2_performed";
+ string = e_bconf_get_value (source, key);
+ }
+
+ if (string != NULL && *string == '1') {
+ *major = 1;
+ *minor = 2;
+ *micro = 0;
+ } else {
+ *major = 1;
+ *minor = 0;
+ *micro = 0;
+ }
+
+ g_free (string);
+
+ if (doc != NULL)
+ xmlFreeDoc (doc);
+ }
+}
+
+static gint
+shell_migrate_remove_dir (const gchar *root,
+ const gchar *path)
+{
+ GDir *dir;
+ const gchar *basename;
+ gchar *filename;
+ gint result = -1;
+
+ /* Recursively removes a directory and its contents. */
+
+ dir = g_dir_open (path, 0, NULL);
+ if (dir == NULL)
+ return -1;
+
+ while ((basename = g_dir_read_name (dir)) != NULL) {
+ filename = g_build_filename (path, basename, NULL);
+
+ /* Make sure we haven't strayed from the evolution dir. */
+ g_return_val_if_fail (strlen (path) >= strlen (root), -1);
+ g_return_val_if_fail (g_str_has_prefix (path, root), -1);
+
+ if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
+ if (shell_migrate_remove_dir (root, filename) < 0)
+ goto fail;
+ } else {
+ if (g_unlink (filename) < 0)
+ goto fail;
+ }
+
+ g_free (filename);
+ filename = NULL;
+ }
+
+ result = g_rmdir (path);
+
+fail:
+ g_free (filename);
+ g_dir_close (dir);
+
+ return result;
+}
+
+gboolean
+e_shell_migrate_attempt (EShell *shell)
+{
+ GConfClient *client;
+ const gchar *key;
+ const gchar *old_data_dir;
+ gint major, minor, micro;
+ gint last_major, last_minor, last_micro;
+ gint curr_major, curr_minor, curr_micro;
+ gboolean migrated = FALSE;
+ gchar *string;
+
+ g_return_val_if_fail (E_IS_SHELL (shell), FALSE);
+
+ old_data_dir = shell_migrate_get_old_data_dir ();
+
+ if (sscanf (BASE_VERSION, "%d.%d", &curr_major, &curr_minor) != 2) {
+ g_warning ("Could not parse BASE_VERSION (%s)", BASE_VERSION);
+ return TRUE;
+ }
+
+ curr_micro = atoi (UPGRADE_REVISION);
+
+ shell_migrate_get_version (&major, &minor, &micro);
+
+ if (!(curr_major > major ||
+ (curr_major == major && curr_minor > minor) ||
+ (curr_minor == minor && curr_micro > micro)))
+ goto check_old;
+
+ /* If upgrading from < 1.5, we need to copy most data from
+ * ~/evolution to ~/.evolution. Make sure we have the disk
+ * space for it before proceeding. */
+ if (major == 1 && minor < 5) {
+ glong avail;
+ glong usage;
+
+ usage = e_fsutils_usage (old_data_dir);
+ avail = e_fsutils_avail (g_get_home_dir ());
+ if (usage >= 0 && avail >= 0 && avail < usage) {
+ gchar *need;
+ gchar *have;
+
+ need = g_strdup_printf (_("%ld KB"), usage);
+ have = g_strdup_printf (_("%ld KB"), avail);
+
+ e_error_run (
+ NULL, "shell:upgrade-nospace",
+ need, have, NULL);
+
+ g_free (need);
+ g_free (have);
+
+ _exit (EXIT_SUCCESS);
+ }
+ }
+
+ if (!shell_migrate_attempt (shell, major, minor, micro))
+ _exit (EXIT_SUCCESS);
+
+ /* Record a successful migration. */
+ client = gconf_client_get_default ();
+ string = g_strdup_printf ("%d.%d.%d", major, minor, micro);
+ gconf_client_set_string (client, GCONF_VERSION_KEY, string, NULL);
+ g_object_unref (client);
+ g_free (string);
+
+ migrated = TRUE;
+
+check_old:
+
+ key = GCONF_LAST_VERSION_KEY;
+ client = gconf_client_get_default ();
+
+ /* Try to retrieve the last migrated version from GConf. */
+ string = gconf_client_get_string (client, key, NULL);
+ if (migrated || string == NULL || sscanf (string, "%d.%d.%d",
+ &last_major, &last_minor, &last_micro) != 3) {
+ last_major = major;
+ last_minor = minor;
+ last_micro = micro;
+ }
+ g_free (string);
+
+ /* If the last migrated version was old, check for stuff to remove. */
+ if (last_major == 1 && last_minor < 5 &&
+ g_file_test (old_data_dir, G_FILE_TEST_IS_DIR)) {
+
+ gint response;
+
+ string = g_strdup_printf (
+ "%d.%d.%d", last_major, last_minor, last_micro);
+ response = e_error_run (
+ NULL, "shel:upgrade-remove-1-4", string, NULL);
+ g_free (string);
+
+ switch (response) {
+ case GTK_RESPONSE_OK: /* delete */
+ response = e_error_run (
+ NULL,
+ "shell:upgrade-remove-1-4-confirm",
+ NULL);
+ if (response == GTK_RESPONSE_OK)
+ shell_migrate_remove_dir (
+ old_data_dir, old_data_dir);
+ else
+ break;
+ /* fall through */
+
+ case GTK_RESPONSE_ACCEPT: /* keep */
+ last_major = curr_major;
+ last_minor = curr_minor;
+ last_micro = curr_micro;
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ last_major = curr_major;
+ last_minor = curr_minor;
+ last_micro = curr_micro;
+ }
+
+ string = g_strdup_printf (
+ "%d.%d.%d", last_major, last_minor, last_micro);
+ gconf_client_set_string (client, key, string, NULL);
+ g_free (string);
+
+ g_object_unref (client);
+
+ return TRUE;
+}
+
+GQuark
+e_shell_migrate_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string (
+ "e-shell-migrate-error-quark");
+
+ return quark;
+}
diff --git a/shell/e-shell-migrate.h b/shell/e-shell-migrate.h
new file mode 100644
index 0000000000..315a2904c5
--- /dev/null
+++ b/shell/e-shell-migrate.h
@@ -0,0 +1,45 @@
+/*
+ * e-shell-migrate.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+/* This is an EShell extension that handles migrating from older versions. */
+
+#ifndef E_SHELL_MIGRATE_H
+#define E_SHELL_MIGRATE_H
+
+#include <shell/e-shell-common.h>
+#include <shell/e-shell.h>
+
+#define E_SHELL_MIGRATE_ERROR \
+ (e_shell_migrate_error_quark ())
+
+G_BEGIN_DECLS
+
+/* XXX Need more specific error codes? */
+typedef enum {
+ E_SHELL_MIGRATE_ERROR_FAILED
+} EShellMigrateError;
+
+gboolean e_shell_migrate_attempt (EShell *shell);
+GQuark e_shell_migrate_error_quark (void);
+
+G_END_DECLS
+
+#endif /* E_SHELL_MIGRATE_H */
diff --git a/shell/e-shell-module.c b/shell/e-shell-module.c
index 1eea8188b6..64d5efc41e 100644
--- a/shell/e-shell-module.c
+++ b/shell/e-shell-module.c
@@ -499,6 +499,40 @@ e_shell_module_shutdown (EShellModule *shell_module)
}
/**
+ * e_shell_migrate:
+ * @shell_module: an #EShellModule
+ * @major: major part of version to migrate from
+ * @minor: minor part of version to migrate from
+ * @micro: micro part of version to migrate from
+ * @error: return location for a #GError, or %NULL
+ *
+ * Attempts to migrate data and settings from version %major.%minor.%micro.
+ * Returns %TRUE if the migration was successful or if no action was
+ * necessary. Returns %FALSE and sets %error if the migration failed.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise
+ **/
+gboolean
+e_shell_module_migrate (EShellModule *shell_module,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error)
+{
+ EShellModuleInfo *module_info;
+
+ g_return_val_if_fail (E_IS_SHELL_MODULE (shell_module), TRUE);
+
+ module_info = &shell_module->priv->info;
+
+ if (module_info->migrate != NULL)
+ return module_info->migrate (
+ shell_module, major, minor, micro, error);
+
+ return TRUE;
+}
+
+/**
* e_shell_module_set_info:
* @shell_module: an #EShellModule
* @info: an #EShellModuleInfo
@@ -535,6 +569,7 @@ e_shell_module_set_info (EShellModule *shell_module,
module_info->is_busy = info->is_busy;
module_info->shutdown = info->shutdown;
+ module_info->migrate = info->migrate;
/* Determine the user data directory for this module. */
g_free (shell_module->priv->data_dir);
diff --git a/shell/e-shell-module.h b/shell/e-shell-module.h
index 8b00660e91..8dff5a1425 100644
--- a/shell/e-shell-module.h
+++ b/shell/e-shell-module.h
@@ -81,6 +81,10 @@ typedef struct _EShellModulePrivate EShellModulePrivate;
* shutting down. Returning %FALSE indicates there
* are still unfinished operations and the #EShell
* should check back shortly.
+ * @migrate: Callback for notifying the module to migrate data and
+ * settings from the given version. Returns %TRUE if the
+ * migration was successful or if no action was necessary.
+ * Returns %FALSE and sets a #GError if the migration failed.
**/
struct _EShellModuleInfo {
const gchar *name;
@@ -90,6 +94,11 @@ struct _EShellModuleInfo {
gboolean (*is_busy) (EShellModule *shell_module);
gboolean (*shutdown) (EShellModule *shell_module);
+ gboolean (*migrate) (EShellModule *shell_module,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error);
};
/**
@@ -120,6 +129,11 @@ void e_shell_module_add_activity (EShellModule *shell_module,
EActivity *activity);
gboolean e_shell_module_is_busy (EShellModule *shell_module);
gboolean e_shell_module_shutdown (EShellModule *shell_module);
+gboolean e_shell_module_migrate (EShellModule *shell_module,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error);
void e_shell_module_set_info (EShellModule *shell_module,
const EShellModuleInfo *info);
diff --git a/shell/e-shell.c b/shell/e-shell.c
index 625285839b..7461329e1d 100644
--- a/shell/e-shell.c
+++ b/shell/e-shell.c
@@ -26,6 +26,7 @@
#include <e-util/e-util.h>
#include <e-shell-module.h>
+#include <e-shell-upgrade.h>
#include <e-shell-window.h>
#define SHUTDOWN_TIMEOUT 500 /* milliseconds */
@@ -79,6 +80,31 @@ shell_window_delete_event_cb (EShell *shell,
return !e_shell_quit (shell);
}
+static gboolean
+shell_window_focus_in_event_cb (EShell *shell,
+ GdkEventFocus *event,
+ EShellWindow *shell_window)
+{
+ GList *list, *link;
+
+ /* Keep the active windows list sorted by most recently focused,
+ * so the first item in the list should always be the currently
+ * focused shell window. */
+
+ list = shell->priv->active_windows;
+ link = g_list_find (list, shell_window);
+ g_return_val_if_fail (link != NULL, FALSE);
+
+ if (link != list) {
+ list = g_list_remove_link (list, link);
+ list = g_list_concat (link, list);
+ }
+
+ shell->priv->active_windows = list;
+
+ return FALSE;
+}
+
static void
shell_window_weak_notify_cb (EShell *shell,
GObject *where_the_object_was)
@@ -300,6 +326,8 @@ shell_constructed (GObject *object)
}
g_dir_close (dir);
+
+ e_shell_upgrade_attempt (shell);
}
static void
@@ -493,6 +521,10 @@ e_shell_create_window (EShell *shell)
shell_window, "delete-event",
G_CALLBACK (shell_window_delete_event_cb), shell);
+ g_signal_connect_swapped (
+ shell_window, "focus-in-event",
+ G_CALLBACK (shell_window_focus_in_event_cb), shell);
+
g_object_weak_ref (
G_OBJECT (shell_window), (GWeakNotify)
shell_window_weak_notify_cb, shell);
@@ -504,6 +536,17 @@ e_shell_create_window (EShell *shell)
return shell_window;
}
+GtkWidget *
+e_shell_get_focused_window (EShell *shell)
+{
+ g_return_val_if_fail (E_IS_SHELL (shell), NULL);
+
+ if (shell->priv->active_windows == NULL)
+ return NULL;
+
+ return GTK_WIDGET (shell->priv->active_windows->data);
+}
+
gboolean
e_shell_handle_uri (EShell *shell,
const gchar *uri)
diff --git a/shell/e-shell.h b/shell/e-shell.h
index d3d5b18f77..183028948c 100644
--- a/shell/e-shell.h
+++ b/shell/e-shell.h
@@ -90,6 +90,7 @@ EShellModule * e_shell_get_module_by_name (EShell *shell,
EShellModule * e_shell_get_module_by_scheme (EShell *shell,
const gchar *scheme);
GtkWidget * e_shell_create_window (EShell *shell);
+GtkWidget * e_shell_get_focused_window (EShell *shell);
gboolean e_shell_handle_uri (EShell *shell,
const gchar *uri);
void e_shell_send_receive (EShell *shell,
diff --git a/shell/test/e-test-shell-module.c b/shell/test/e-test-shell-module.c
index 32de51a650..19b5d3bd96 100644
--- a/shell/test/e-test-shell-module.c
+++ b/shell/test/e-test-shell-module.c
@@ -86,8 +86,20 @@ test_module_shutdown (EShellModule *shell_module)
}
static gboolean
-test_module_handle_uri (EShellModule *shell_module,
- const gchar *uri)
+test_module_migrate (EShellModule *shell_module,
+ gint major,
+ gint minor,
+ gint micro,
+ GError **error)
+{
+ g_debug ("%s (from %d.%d.%d)", G_STRFUNC, major, minor, micro);
+
+ return TRUE;
+}
+
+static gboolean
+test_module_handle_uri_cb (EShellModule *shell_module,
+ const gchar *uri)
{
g_debug ("%s (uri=%s)", G_STRFUNC, uri);
@@ -95,15 +107,15 @@ test_module_handle_uri (EShellModule *shell_module,
}
static void
-test_module_send_receive (EShellModule *shell_module,
- GtkWindow *parent_window)
+test_module_send_receive_cb (EShellModule *shell_module,
+ GtkWindow *parent_window)
{
g_debug ("%s (window=%p)", G_STRFUNC, parent_window);
}
static void
-test_module_window_created (EShellModule *shell_module,
- EShellWindow *shell_window)
+test_module_window_created_cb (EShellModule *shell_module,
+ EShellWindow *shell_window)
{
const gchar *module_name;
@@ -121,8 +133,8 @@ test_module_window_created (EShellModule *shell_module,
}
static void
-test_module_window_destroyed (EShellModule *shell_module,
- gboolean last_window)
+test_module_window_destroyed_cb (EShellModule *shell_module,
+ gboolean last_window)
{
g_debug ("%s (last=%d)", G_STRFUNC, last_window);
}
@@ -136,7 +148,8 @@ static EShellModuleInfo module_info = {
/* Methods */
test_module_is_busy,
- test_module_shutdown
+ test_module_shutdown,
+ test_module_migrate
};
void
@@ -153,17 +166,17 @@ e_shell_module_init (GTypeModule *type_module)
g_signal_connect_swapped (
shell, "handle-uri",
- G_CALLBACK (test_module_handle_uri), shell_module);
+ G_CALLBACK (test_module_handle_uri_cb), shell_module);
g_signal_connect_swapped (
shell, "send-receive",
- G_CALLBACK (test_module_send_receive), shell_module);
+ G_CALLBACK (test_module_send_receive_cb), shell_module);
g_signal_connect_swapped (
shell, "window-created",
- G_CALLBACK (test_module_window_created), shell_module);
+ G_CALLBACK (test_module_window_created_cb), shell_module);
g_signal_connect_swapped (
shell, "window-destroyed",
- G_CALLBACK (test_module_window_destroyed), shell_module);
+ G_CALLBACK (test_module_window_destroyed_cb), shell_module);
}