/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2007 The GNOME Foundation * Written by Thomas Wood * Jens Granseuer * All Rights Reserved * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "appearance.h" #include #include #include #include #include #include #include "capplet-util.h" #include "file-transfer-dialog.h" #include "theme-installer.h" #include "theme-util.h" enum { THEME_INVALID, THEME_ICON, THEME_MATE, THEME_GTK, THEME_ENGINE, THEME_MARCO, THEME_CURSOR, THEME_ICON_CURSOR }; enum { TARGZ, TARBZ, TARXZ, DIRECTORY }; static gboolean cleanup_tmp_dir (GIOSchedulerJob *job, GCancellable *cancellable, const gchar *tmp_dir) { GFile *directory; directory = g_file_new_for_path (tmp_dir); capplet_file_delete_recursive (directory, NULL); g_object_unref (directory); return FALSE; } static int file_theme_type (const gchar *dir) { gchar *filename = NULL; gboolean exists; if (!dir) return THEME_INVALID; filename = g_build_filename (dir, "index.theme", NULL); exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR); if (exists) { GPatternSpec *pattern; gchar *file_contents = NULL; gsize file_size; gboolean match; g_file_get_contents (filename, &file_contents, &file_size, NULL); g_free (filename); pattern = g_pattern_spec_new ("*[Icon Theme]*"); match = g_pattern_match_string (pattern, file_contents); g_pattern_spec_free (pattern); if (match) { pattern = g_pattern_spec_new ("*Directories=*"); match = g_pattern_match_string (pattern, file_contents); g_pattern_spec_free (pattern); g_free (file_contents); if (match) { /* check if we have a cursor, too */ filename = g_build_filename (dir, "cursors", NULL); exists = g_file_test (filename, G_FILE_TEST_IS_DIR); g_free (filename); if (exists) return THEME_ICON_CURSOR; else return THEME_ICON; } return THEME_CURSOR; } pattern = g_pattern_spec_new ("*[X-GNOME-Metatheme]*"); match = g_pattern_match_string (pattern, file_contents); g_pattern_spec_free (pattern); g_free (file_contents); if (match) return THEME_MATE; } else { g_free (filename); } filename = g_build_filename (dir, "gtk-2.0", "gtkrc", NULL); exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR); g_free (filename); if (exists) return THEME_GTK; filename = g_build_filename (dir, "metacity-1", "metacity-theme-2.xml", NULL); exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR); g_free (filename); if (exists) return THEME_MARCO; filename = g_build_filename (dir, "metacity-1", "metacity-theme-1.xml", NULL); exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR); g_free (filename); if (exists) return THEME_MARCO; /* cursor themes don't necessarily have an index.theme */ filename = g_build_filename (dir, "cursors", NULL); exists = g_file_test (filename, G_FILE_TEST_IS_DIR); g_free (filename); if (exists) return THEME_CURSOR; filename = g_build_filename (dir, "configure", NULL); exists = g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE); g_free (filename); if (exists) return THEME_ENGINE; return THEME_INVALID; } static void transfer_cancel_cb (GtkWidget *dialog, gchar *path) { GFile *todelete; todelete = g_file_new_for_path (path); capplet_file_delete_recursive (todelete, NULL); g_object_unref (todelete); g_free (path); gtk_widget_destroy (dialog); } static void missing_utility_message_dialog (GtkWindow *parent, const gchar *utility) { GtkWidget *dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Cannot install theme")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("The %s utility is not installed."), utility); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } /* this works around problems when doing fork/exec in a threaded app * with some locks being held/waited on in different threads. * * we do the idle callback so that the async xfer has finished and * cleaned up its vfs job. otherwise it seems the slave thread gets * woken up and it removes itself from the job queue before it is * supposed to. very strange. * * see bugzilla.gnome.org #86141 for details */ static gboolean process_local_theme_tgz_tbz (GtkWindow *parent, const gchar *util, const gchar *tmp_dir, const gchar *archive) { gboolean rc; int status; gchar *command, *filename, *zip, *tar; if (!(zip = g_find_program_in_path (util))) { missing_utility_message_dialog (parent, util); return FALSE; } if (!(tar = g_find_program_in_path ("tar"))) { missing_utility_message_dialog (parent, "tar"); g_free (zip); return FALSE; } filename = g_shell_quote (archive); /* this should be something more clever and nonblocking */ command = g_strdup_printf ("sh -c 'cd \"%s\"; %s -d -c < \"%s\" | %s xf - '", tmp_dir, zip, filename, tar); g_free (zip); g_free (tar); g_free (filename); rc = (g_spawn_command_line_sync (command, NULL, NULL, &status, NULL) && status == 0); g_free (command); if (rc == FALSE) { GtkWidget *dialog; dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Cannot install theme")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("There was a problem while extracting the theme.")); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } return rc; } static gboolean process_local_theme_archive (GtkWindow *parent, gint filetype, const gchar *tmp_dir, const gchar *archive) { if (filetype == TARGZ) return process_local_theme_tgz_tbz (parent, "gzip", tmp_dir, archive); else if (filetype == TARBZ) return process_local_theme_tgz_tbz (parent, "bzip2", tmp_dir, archive); else if (filetype == TARXZ) return process_local_theme_tgz_tbz (parent, "xz", tmp_dir, archive); else return FALSE; } static void invalid_theme_dialog (GtkWindow *parent, const gchar *filename, gboolean maybe_theme_engine) { GtkWidget *dialog; const gchar *primary = _("There was an error installing the selected file"); const gchar *secondary = _("\"%s\" does not appear to be a valid theme."); const gchar *engine = _("\"%s\" does not appear to be a valid theme. It may be a theme engine which you need to compile."); dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", primary); if (maybe_theme_engine) gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), engine, filename); else gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), secondary, filename); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } static gboolean mate_theme_install_real (GtkWindow *parent, const gchar *tmp_dir, const gchar *theme_name, gboolean ask_user) { gboolean success = TRUE; GtkWidget *dialog, *apply_button; GFile *theme_source_dir, *theme_dest_dir; GError *error = NULL; gint theme_type; gchar *target_dir = NULL; /* What type of theme is it? */ theme_type = file_theme_type (tmp_dir); switch (theme_type) { case THEME_ICON: case THEME_CURSOR: case THEME_ICON_CURSOR: target_dir = g_build_path (G_DIR_SEPARATOR_S, g_get_home_dir (), ".icons", theme_name, NULL); break; case THEME_MATE: case THEME_MARCO: case THEME_GTK: target_dir = g_build_path (G_DIR_SEPARATOR_S, g_get_home_dir (), ".themes", theme_name, NULL); break; case THEME_ENGINE: invalid_theme_dialog (parent, theme_name, TRUE); return FALSE; default: invalid_theme_dialog (parent, theme_name, FALSE); return FALSE; } /* see if there is an icon theme lurking in this package */ if (theme_type == THEME_MATE) { gchar *path; path = g_build_path (G_DIR_SEPARATOR_S, tmp_dir, "icons", NULL); if (g_file_test (path, G_FILE_TEST_IS_DIR) && (file_theme_type (path) == THEME_ICON)) { gchar *new_path, *update_icon_cache; GFile *new_file; GFile *src_file; src_file = g_file_new_for_path (path); new_path = g_build_path (G_DIR_SEPARATOR_S, g_get_home_dir (), ".icons", theme_name, NULL); new_file = g_file_new_for_path (new_path); if (!g_file_move (src_file, new_file, G_FILE_COPY_NONE, NULL, NULL, NULL, &error)) { g_warning ("Error while moving from `%s' to `%s': %s", path, new_path, error->message); g_error_free (error); error = NULL; } g_object_unref (new_file); g_object_unref (src_file); /* update icon cache - shouldn't really matter if this fails */ update_icon_cache = g_strdup_printf ("gtk-update-icon-cache %s", new_path); g_spawn_command_line_async (update_icon_cache, NULL); g_free (update_icon_cache); g_free (new_path); } g_free (path); } /* Move the dir to the target dir */ theme_source_dir = g_file_new_for_path (tmp_dir); theme_dest_dir = g_file_new_for_path (target_dir); if (g_file_test (target_dir, G_FILE_TEST_EXISTS)) { gchar *str; str = g_strdup_printf (_("The theme \"%s\" already exists."), theme_name); dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_NONE, "%s", str); g_free (str); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("Do you want to install it again?")); gtk_dialog_add_button (GTK_DIALOG (dialog), _("Cancel"), GTK_RESPONSE_CLOSE); apply_button = gtk_button_new_with_label (_("Install")); gtk_button_set_image (GTK_BUTTON (apply_button), gtk_image_new_from_icon_name ("gtk-apply", GTK_ICON_SIZE_BUTTON)); gtk_dialog_add_action_widget (GTK_DIALOG (dialog), apply_button, GTK_RESPONSE_APPLY); gtk_widget_set_can_default (apply_button, TRUE); gtk_widget_show (apply_button); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_APPLY); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_APPLY) { gtk_widget_destroy (dialog); if (!capplet_file_delete_recursive (theme_dest_dir, NULL)) { GtkWidget *info_dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Theme cannot be deleted")); gtk_dialog_run (GTK_DIALOG (info_dialog)); gtk_widget_destroy (info_dialog); success = FALSE; goto end; } }else{ gtk_widget_destroy (dialog); success = FALSE; goto end; } } if (!g_file_move (theme_source_dir, theme_dest_dir, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error)) { gchar *str; str = g_strdup_printf (_("Installation for theme \"%s\" failed."), theme_name); dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", str); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message); g_free (str); g_error_free (error); error = NULL; gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); success = FALSE; } else { if (theme_type == THEME_ICON || theme_type == THEME_ICON_CURSOR) { gchar *update_icon_cache; /* update icon cache - shouldn't really matter if this fails */ update_icon_cache = g_strdup_printf ("gtk-update-icon-cache %s", target_dir); g_spawn_command_line_async (update_icon_cache, NULL); g_free (update_icon_cache); } if (ask_user) { /* Ask to apply theme (if we can) */ if (theme_type == THEME_GTK || theme_type == THEME_MARCO || theme_type == THEME_ICON || theme_type == THEME_CURSOR || theme_type == THEME_ICON_CURSOR) { /* TODO: currently cannot apply "mate themes" */ gchar *str; str = g_strdup_printf (_("The theme \"%s\" has been installed."), theme_name); dialog = gtk_message_dialog_new_with_markup (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_NONE, "%s", str); g_free (str); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("Would you like to apply it now, or keep your current theme?")); gtk_dialog_add_button (GTK_DIALOG (dialog), _("Keep Current Theme"), GTK_RESPONSE_CLOSE); apply_button = gtk_button_new_with_label (_("Apply New Theme")); gtk_button_set_image (GTK_BUTTON (apply_button), gtk_image_new_from_icon_name ("gtk-apply", GTK_ICON_SIZE_BUTTON)); gtk_dialog_add_action_widget (GTK_DIALOG (dialog), apply_button, GTK_RESPONSE_APPLY); gtk_widget_set_can_default (apply_button, TRUE); gtk_widget_show (apply_button); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_APPLY); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_APPLY) { /* apply theme here! */ GSettings *settings; switch (theme_type) { case THEME_GTK: settings = g_settings_new (INTERFACE_SCHEMA); g_settings_set_string (settings, GTK_THEME_KEY, theme_name); g_object_unref (settings); break; case THEME_MARCO: settings = g_settings_new (MARCO_SCHEMA); g_settings_set_string (settings, MARCO_THEME_KEY, theme_name); g_object_unref (settings); break; case THEME_ICON: settings = g_settings_new (INTERFACE_SCHEMA); g_settings_set_string (settings, ICON_THEME_KEY, theme_name); g_object_unref (settings); break; case THEME_CURSOR: settings = g_settings_new (MOUSE_SCHEMA); g_settings_set_string (settings, CURSOR_THEME_KEY, theme_name); g_object_unref (settings); break; case THEME_ICON_CURSOR: settings = g_settings_new (INTERFACE_SCHEMA); g_settings_set_string (settings, ICON_THEME_KEY, theme_name); g_object_unref (settings); settings = g_settings_new (MOUSE_SCHEMA); g_settings_set_string (settings, CURSOR_THEME_KEY, theme_name); g_object_unref (settings); break; default: break; } } } else { dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, _("MATE Theme %s correctly installed"), theme_name); gtk_dialog_run (GTK_DIALOG (dialog)); } gtk_widget_destroy (dialog); } } end: g_free (target_dir); g_object_unref (theme_source_dir); g_object_unref (theme_dest_dir); return success; } static void process_local_theme (GtkWindow *parent, const char *path) { GtkWidget *dialog; gint filetype; if (g_str_has_suffix (path, ".tar.gz") || g_str_has_suffix (path, ".tgz") || g_str_has_suffix(path, ".gtp")) { filetype = TARGZ; } else if (g_str_has_suffix (path, ".tar.bz2")) { filetype = TARBZ; } else if (g_str_has_suffix (path, ".tar.xz")) { filetype = TARXZ; } else if (g_file_test (path, G_FILE_TEST_IS_DIR)) { filetype = DIRECTORY; } else { gchar *filename; filename = g_path_get_basename (path); invalid_theme_dialog (parent, filename, FALSE); g_free (filename); return; } if (filetype == DIRECTORY) { gchar *name = g_path_get_basename (path); mate_theme_install_real (parent, path, name, TRUE); g_free (name); } else { /* Create a temp directory and uncompress file there */ GDir *dir; const gchar *name; gchar *tmp_dir; gboolean ok; gint n_themes; GFile *todelete; tmp_dir = g_strdup_printf ("%s/.themes/.theme-%u", g_get_home_dir (), g_random_int ()); if ((g_mkdir (tmp_dir, 0700)) != 0) { dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Failed to create temporary directory")); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); g_free (tmp_dir); return; } if (!process_local_theme_archive (parent, filetype, tmp_dir, path) || ((dir = g_dir_open (tmp_dir, 0, NULL)) == NULL)) { g_io_scheduler_push_job ((GIOSchedulerJobFunc) cleanup_tmp_dir, g_strdup (tmp_dir), g_free, G_PRIORITY_DEFAULT, NULL); g_free (tmp_dir); return; } todelete = g_file_new_for_path (path); g_file_delete (todelete, NULL, NULL); g_object_unref (todelete); /* See whether we have multiple themes to install. If so, * we won't ask the user whether to apply the new theme * after installation. */ n_themes = 0; for (name = g_dir_read_name (dir); name && n_themes <= 1; name = g_dir_read_name (dir)) { gchar *theme_dir; theme_dir = g_build_filename (tmp_dir, name, NULL); if (g_file_test (theme_dir, G_FILE_TEST_IS_DIR)) ++n_themes; g_free (theme_dir); } g_dir_rewind (dir); ok = TRUE; for (name = g_dir_read_name (dir); name && ok; name = g_dir_read_name (dir)) { gchar *theme_dir; theme_dir = g_build_filename (tmp_dir, name, NULL); if (g_file_test (theme_dir, G_FILE_TEST_IS_DIR)) ok = mate_theme_install_real (parent, theme_dir, name, n_themes == 1); g_free (theme_dir); } g_dir_close (dir); if (ok && n_themes > 1) { dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, _("New themes have been successfully installed.")); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } g_io_scheduler_push_job ((GIOSchedulerJobFunc) cleanup_tmp_dir, tmp_dir, g_free, G_PRIORITY_DEFAULT, NULL); } } typedef struct { GtkWindow *parent; char *path; } TransferData; static void transfer_done_cb (GtkWidget *dialog, TransferData *tdata) { /* XXX: path should be on the local filesystem by now? */ if (dialog != NULL) { gtk_widget_destroy (dialog); } process_local_theme (tdata->parent, tdata->path); g_free (tdata->path); g_free (tdata); } void mate_theme_install (GFile *file, GtkWindow *parent) { GtkWidget *dialog; gchar *path, *base; GList *src, *target; const gchar *template; TransferData *tdata; if (file == NULL) { dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("No theme file location specified to install")); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); return; } /* see if someone dropped a local directory */ if (g_file_is_native (file) && g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL) == G_FILE_TYPE_DIRECTORY) { path = g_file_get_path (file); process_local_theme (parent, path); g_free (path); return; } /* we can't tell if this is an icon theme yet, so just make a * temporary copy in .themes */ path = g_build_filename (g_get_home_dir (), ".themes", NULL); if (access (path, X_OK | W_OK) != 0) { dialog = gtk_message_dialog_new (parent, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Insufficient permissions to install the theme in:\n%s"), path); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); g_free (path); return; } g_free (path); base = g_file_get_basename (file); if (g_str_has_suffix (base, ".tar.gz") || g_str_has_suffix (base, ".tgz") || g_str_has_suffix (base, ".gtp")) template = "mate-theme-%d.gtp"; else if (g_str_has_suffix (base, ".tar.bz2")) template = "mate-theme-%d.tar.bz2"; else if (g_str_has_suffix (base, ".tar.xz")) template = "mate-theme-%d.tar.xz"; else { invalid_theme_dialog (parent, base, FALSE); g_free (base); return; } g_free (base); src = g_list_append (NULL, g_object_ref (file)); path = NULL; do { gchar *file_tmp; g_free (path); file_tmp = g_strdup_printf (template, g_random_int ()); path = g_build_filename (g_get_home_dir (), ".themes", file_tmp, NULL); g_free (file_tmp); } while (g_file_test (path, G_FILE_TEST_EXISTS)); tdata = g_new0 (TransferData, 1); tdata->parent = parent; tdata->path = path; dialog = file_transfer_dialog_new_with_parent (parent); g_signal_connect (dialog, "cancel", (GCallback) transfer_cancel_cb, path); g_signal_connect (dialog, "done", (GCallback) transfer_done_cb, tdata); target = g_list_append (NULL, g_file_new_for_path (path)); file_transfer_dialog_copy_async (FILE_TRANSFER_DIALOG (dialog), src, target, FILE_TRANSFER_DIALOG_DEFAULT, G_PRIORITY_DEFAULT); gtk_widget_show (dialog); /* don't free the path since we're using that for the signals */ g_list_foreach (src, (GFunc) g_object_unref, NULL); g_list_free (src); g_list_foreach (target, (GFunc) g_object_unref, NULL); g_list_free (target); } void mate_theme_installer_run (GtkWindow *parent) { static gboolean running_theme_install = FALSE; static gchar old_folder[512] = ""; GtkWidget *dialog; GtkFileFilter *filter; if (running_theme_install) return; running_theme_install = TRUE; dialog = gtk_file_chooser_dialog_new (_("Select Theme"), parent, GTK_FILE_CHOOSER_ACTION_OPEN, "gtk-cancel", GTK_RESPONSE_REJECT, "gtk-open", GTK_RESPONSE_ACCEPT, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); filter = gtk_file_filter_new (); gtk_file_filter_set_name (filter, _("Theme Packages")); gtk_file_filter_add_mime_type (filter, "application/x-bzip-compressed-tar"); gtk_file_filter_add_mime_type (filter, "application/x-xz-compressed-tar"); gtk_file_filter_add_mime_type (filter, "application/x-compressed-tar"); gtk_file_filter_add_mime_type (filter, "application/x-mate-theme-package"); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); filter = gtk_file_filter_new (); gtk_file_filter_set_name (filter, _("All Files")); gtk_file_filter_add_pattern(filter, "*"); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); if (strcmp (old_folder, "")) gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), old_folder); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { gchar *uri_selected, *folder; uri_selected = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog)); folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog)); g_strlcpy (old_folder, folder, 255); g_free (folder); gtk_widget_destroy (dialog); if (uri_selected != NULL) { GFile *file = g_file_new_for_uri (uri_selected); g_free (uri_selected); mate_theme_install (file, parent); g_object_unref (file); } } else { gtk_widget_destroy (dialog); } /* * we're relying on the mate theme info module to pick up changes * to the themes so we don't need to update the model here */ running_theme_install = FALSE; }