diff options
author | Matthew Barnes <mbarnes@src.gnome.org> | 2008-10-05 12:12:09 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@src.gnome.org> | 2008-10-05 12:12:09 +0800 |
commit | 6d1aea1b231c120441061c2046157b40e34f8e3a (patch) | |
tree | e28bb0e3ea779b19b6d838a3a989ba57aba6324a /shell | |
parent | 2c71859895d091f51dea23f9ed9552a0962b7ba4 (diff) | |
download | gsoc2013-evolution-6d1aea1b231c120441061c2046157b40e34f8e3a.tar.gz gsoc2013-evolution-6d1aea1b231c120441061c2046157b40e34f8e3a.tar.zst gsoc2013-evolution-6d1aea1b231c120441061c2046157b40e34f8e3a.zip |
Support migration in the new shell design.
Some code got duplicated for calendars and tasks. Made a note to revisit.
svn path=/branches/kill-bonobo/; revision=36560
Diffstat (limited to 'shell')
-rw-r--r-- | shell/Makefile.am | 2 | ||||
-rw-r--r-- | shell/e-shell-migrate.c | 339 | ||||
-rw-r--r-- | shell/e-shell-migrate.h | 45 | ||||
-rw-r--r-- | shell/e-shell-module.c | 35 | ||||
-rw-r--r-- | shell/e-shell-module.h | 14 | ||||
-rw-r--r-- | shell/e-shell.c | 43 | ||||
-rw-r--r-- | shell/e-shell.h | 1 | ||||
-rw-r--r-- | shell/test/e-test-shell-module.c | 39 |
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, µ); + + 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); } |