1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3
* Copyright (C) 2007 The GNOME Foundation
4
* Written by Thomas Wood <thos@gnome.org>
5
* Jens Granseuer <jensgr@gmx.net>
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License along
19
* with this program; if not, write to the Free Software Foundation, Inc.,
20
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
#include "appearance.h"
27
#include <glib/gi18n.h>
29
#include <glib/gstdio.h>
32
#include "capplet-util.h"
33
#include "file-transfer-dialog.h"
34
#include "theme-installer.h"
35
#include "theme-util.h"
55
cleanup_tmp_dir (GIOSchedulerJob *job,
56
GCancellable *cancellable,
61
directory = g_file_new_for_path (tmp_dir);
62
capplet_file_delete_recursive (directory, NULL);
63
g_object_unref (directory);
69
file_theme_type (const gchar *dir)
71
gchar *filename = NULL;
77
filename = g_build_filename (dir, "index.theme", NULL);
78
exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR);
81
GPatternSpec *pattern;
82
gchar *file_contents = NULL;
86
g_file_get_contents (filename, &file_contents, &file_size, NULL);
89
pattern = g_pattern_spec_new ("*[Icon Theme]*");
90
match = g_pattern_match_string (pattern, file_contents);
91
g_pattern_spec_free (pattern);
94
pattern = g_pattern_spec_new ("*Directories=*");
95
match = g_pattern_match_string (pattern, file_contents);
96
g_pattern_spec_free (pattern);
97
g_free (file_contents);
100
/* check if we have a cursor, too */
101
filename = g_build_filename (dir, "cursors", NULL);
102
exists = g_file_test (filename, G_FILE_TEST_IS_DIR);
106
return THEME_ICON_CURSOR;
113
pattern = g_pattern_spec_new ("*[X-GNOME-Metatheme]*");
114
match = g_pattern_match_string (pattern, file_contents);
115
g_pattern_spec_free (pattern);
116
g_free (file_contents);
124
filename = g_build_filename (dir, "gtk-2.0", "gtkrc", NULL);
125
exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR);
131
filename = g_build_filename (dir, "metacity-1", "metacity-theme-1.xml", NULL);
132
exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR);
138
/* cursor themes don't necessarily have an index.theme */
139
filename = g_build_filename (dir, "cursors", NULL);
140
exists = g_file_test (filename, G_FILE_TEST_IS_DIR);
146
filename = g_build_filename (dir, "configure", NULL);
147
exists = g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE);
153
return THEME_INVALID;
157
transfer_cancel_cb (GtkWidget *dialog,
162
todelete = g_file_new_for_path (path);
163
capplet_file_delete_recursive (todelete, NULL);
165
g_object_unref (todelete);
167
gtk_widget_destroy (dialog);
171
missing_utility_message_dialog (GtkWindow *parent,
172
const gchar *utility)
174
GtkWidget *dialog = gtk_message_dialog_new (parent,
178
_("Cannot install theme"));
179
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
180
_("The %s utility is not installed."), utility);
181
gtk_dialog_run (GTK_DIALOG (dialog));
182
gtk_widget_destroy (dialog);
185
/* this works around problems when doing fork/exec in a threaded app
186
* with some locks being held/waited on in different threads.
188
* we do the idle callback so that the async xfer has finished and
189
* cleaned up its vfs job. otherwise it seems the slave thread gets
190
* woken up and it removes itself from the job queue before it is
191
* supposed to. very strange.
193
* see bugzilla.gnome.org #86141 for details
196
process_local_theme_tgz_tbz (GtkWindow *parent,
198
const gchar *tmp_dir,
199
const gchar *archive)
203
gchar *command, *filename, *zip, *tar;
205
if (!(zip = g_find_program_in_path (util))) {
206
missing_utility_message_dialog (parent, util);
209
if (!(tar = g_find_program_in_path ("tar"))) {
210
missing_utility_message_dialog (parent, "tar");
215
filename = g_shell_quote (archive);
217
/* this should be something more clever and nonblocking */
218
command = g_strdup_printf ("sh -c 'cd \"%s\"; %s -d -c < \"%s\" | %s xf - '",
219
tmp_dir, zip, filename, tar);
224
rc = (g_spawn_command_line_sync (command, NULL, NULL, &status, NULL) && status == 0);
230
dialog = gtk_message_dialog_new (parent,
234
_("Cannot install theme"));
235
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
236
_("There was a problem while extracting the theme."));
237
gtk_dialog_run (GTK_DIALOG (dialog));
238
gtk_widget_destroy (dialog);
245
process_local_theme_archive (GtkWindow *parent,
247
const gchar *tmp_dir,
248
const gchar *archive)
250
if (filetype == TARGZ)
251
return process_local_theme_tgz_tbz (parent, "gzip", tmp_dir, archive);
252
else if (filetype == TARBZ)
253
return process_local_theme_tgz_tbz (parent, "bzip2", tmp_dir, archive);
259
invalid_theme_dialog (GtkWindow *parent,
260
const gchar *filename,
261
gboolean maybe_theme_engine)
264
const gchar *primary = _("There was an error installing the selected file");
265
const gchar *secondary = _("\"%s\" does not appear to be a valid theme.");
266
const gchar *engine = _("\"%s\" does not appear to be a valid theme. It may be a theme engine which you need to compile.");
268
dialog = gtk_message_dialog_new (parent,
273
if (maybe_theme_engine)
274
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), engine, filename);
276
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), secondary, filename);
277
gtk_dialog_run (GTK_DIALOG (dialog));
278
gtk_widget_destroy (dialog);
282
mate_theme_install_real (GtkWindow *parent,
284
const gchar *tmp_dir,
285
const gchar *theme_name,
288
gboolean success = TRUE;
289
GtkWidget *dialog, *apply_button;
290
GFile *theme_source_dir, *theme_dest_dir;
291
GError *error = NULL;
293
gchar *target_dir = NULL;
295
/* What type of theme is it? */
296
theme_type = file_theme_type (tmp_dir);
297
switch (theme_type) {
300
case THEME_ICON_CURSOR:
301
target_dir = g_build_path (G_DIR_SEPARATOR_S,
302
g_get_home_dir (), ".icons",
306
target_dir = g_build_path (G_DIR_SEPARATOR_S,
307
g_get_home_dir (), ".themes",
312
target_dir = g_build_path (G_DIR_SEPARATOR_S,
313
g_get_home_dir (), ".themes",
317
invalid_theme_dialog (parent, theme_name, TRUE);
320
invalid_theme_dialog (parent, theme_name, FALSE);
324
/* see if there is an icon theme lurking in this package */
325
if (theme_type == THEME_MATE) {
328
path = g_build_path (G_DIR_SEPARATOR_S,
329
tmp_dir, "icons", NULL);
330
if (g_file_test (path, G_FILE_TEST_IS_DIR)
331
&& (file_theme_type (path) == THEME_ICON)) {
332
gchar *new_path, *update_icon_cache;
336
src_file = g_file_new_for_path (path);
337
new_path = g_build_path (G_DIR_SEPARATOR_S,
341
new_file = g_file_new_for_path (new_path);
343
if (!g_file_move (src_file, new_file, G_FILE_COPY_NONE,
344
NULL, NULL, NULL, &error)) {
345
g_warning ("Error while moving from `%s' to `%s': %s",
346
path, new_path, error->message);
347
g_error_free (error);
350
g_object_unref (new_file);
351
g_object_unref (src_file);
353
/* update icon cache - shouldn't really matter if this fails */
354
update_icon_cache = g_strdup_printf ("gtk-update-icon-cache %s", new_path);
355
g_spawn_command_line_async (update_icon_cache, NULL);
356
g_free (update_icon_cache);
363
/* Move the dir to the target dir */
364
theme_source_dir = g_file_new_for_path (tmp_dir);
365
theme_dest_dir = g_file_new_for_path (target_dir);
367
if (!g_file_move (theme_source_dir, theme_dest_dir,
368
G_FILE_COPY_OVERWRITE, NULL, NULL,
372
str = g_strdup_printf (_("Installation for theme \"%s\" failed."), theme_name);
373
dialog = gtk_message_dialog_new (parent,
379
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
380
"%s", error->message);
383
g_error_free (error);
386
gtk_dialog_run (GTK_DIALOG (dialog));
387
gtk_widget_destroy (dialog);
390
if (theme_type == THEME_ICON || theme_type == THEME_ICON_CURSOR)
392
gchar *update_icon_cache;
394
/* update icon cache - shouldn't really matter if this fails */
395
update_icon_cache = g_strdup_printf ("gtk-update-icon-cache %s", target_dir);
396
g_spawn_command_line_async (update_icon_cache, NULL);
398
g_free (update_icon_cache);
402
/* Ask to apply theme (if we can) */
403
if (theme_type == THEME_GTK
404
|| theme_type == THEME_MARCO
405
|| theme_type == THEME_ICON
406
|| theme_type == THEME_CURSOR
407
|| theme_type == THEME_ICON_CURSOR) {
408
/* TODO: currently cannot apply "mate themes" */
411
str = g_strdup_printf (_("The theme \"%s\" has been installed."), theme_name);
412
dialog = gtk_message_dialog_new_with_markup (parent,
416
"<span weight=\"bold\" size=\"larger\">%s</span>",
420
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
421
_("Would you like to apply it now, or keep your current theme?"));
423
gtk_dialog_add_button (GTK_DIALOG (dialog),
424
_("Keep Current Theme"),
427
apply_button = gtk_button_new_with_label (_("Apply New Theme"));
428
gtk_button_set_image (GTK_BUTTON (apply_button),
429
gtk_image_new_from_stock (GTK_STOCK_APPLY,
430
GTK_ICON_SIZE_BUTTON));
431
gtk_dialog_add_action_widget (GTK_DIALOG (dialog), apply_button, GTK_RESPONSE_APPLY);
432
gtk_widget_set_can_default (apply_button, TRUE);
433
gtk_widget_show (apply_button);
435
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_APPLY);
437
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_APPLY) {
438
/* apply theme here! */
441
switch (theme_type) {
443
settings = g_settings_new (INTERFACE_SCHEMA);
444
g_settings_set_string (settings, GTK_THEME_KEY, theme_name);
445
g_object_unref (settings);
448
settings = g_settings_new (MARCO_SCHEMA);
449
g_settings_set_string (settings, MARCO_THEME_KEY, theme_name);
450
g_object_unref (settings);
453
settings = g_settings_new (INTERFACE_SCHEMA);
454
g_settings_set_string (settings, ICON_THEME_KEY, theme_name);
455
g_object_unref (settings);
458
settings = g_settings_new (MOUSE_SCHEMA);
459
g_settings_set_string (settings, CURSOR_THEME_KEY, theme_name);
460
g_object_unref (settings);
462
case THEME_ICON_CURSOR:
463
settings = g_settings_new (INTERFACE_SCHEMA);
464
g_settings_set_string (settings, ICON_THEME_KEY, theme_name);
465
g_object_unref (settings);
466
settings = g_settings_new (MOUSE_SCHEMA);
467
g_settings_set_string (settings, CURSOR_THEME_KEY, theme_name);
468
g_object_unref (settings);
475
dialog = gtk_message_dialog_new (parent,
479
_("MATE Theme %s correctly installed"),
481
gtk_dialog_run (GTK_DIALOG (dialog));
483
gtk_widget_destroy (dialog);
493
process_local_theme (GtkWindow *parent,
499
if (g_str_has_suffix (path, ".tar.gz")
500
|| g_str_has_suffix (path, ".tgz")
501
|| g_str_has_suffix(path, ".gtp")) {
503
} else if (g_str_has_suffix (path, ".tar.bz2")) {
505
} else if (g_file_test (path, G_FILE_TEST_IS_DIR)) {
506
filetype = DIRECTORY;
509
filename = g_path_get_basename (path);
510
invalid_theme_dialog (parent, filename, FALSE);
515
if (filetype == DIRECTORY) {
516
gchar *name = g_path_get_basename (path);
517
mate_theme_install_real (parent,
524
/* Create a temp directory and uncompress file there */
532
tmp_dir = g_strdup_printf ("%s/.themes/.theme-%u",
536
if ((g_mkdir (tmp_dir, 0700)) != 0) {
537
dialog = gtk_message_dialog_new (parent,
541
_("Failed to create temporary directory"));
542
gtk_dialog_run (GTK_DIALOG (dialog));
543
gtk_widget_destroy (dialog);
548
if (!process_local_theme_archive (parent, filetype, tmp_dir, path)
549
|| ((dir = g_dir_open (tmp_dir, 0, NULL)) == NULL)) {
550
g_io_scheduler_push_job ((GIOSchedulerJobFunc) cleanup_tmp_dir,
559
todelete = g_file_new_for_path (path);
560
g_file_delete (todelete, NULL, NULL);
561
g_object_unref (todelete);
563
/* See whether we have multiple themes to install. If so,
564
* we won't ask the user whether to apply the new theme
565
* after installation. */
567
for (name = g_dir_read_name (dir);
568
name && n_themes <= 1;
569
name = g_dir_read_name (dir)) {
572
theme_dir = g_build_filename (tmp_dir, name, NULL);
574
if (g_file_test (theme_dir, G_FILE_TEST_IS_DIR))
582
for (name = g_dir_read_name (dir); name && ok;
583
name = g_dir_read_name (dir)) {
586
theme_dir = g_build_filename (tmp_dir, name, NULL);
588
if (g_file_test (theme_dir, G_FILE_TEST_IS_DIR))
589
ok = mate_theme_install_real (parent,
599
if (ok && n_themes > 1) {
600
dialog = gtk_message_dialog_new (parent,
604
_("New themes have been successfully installed."));
605
gtk_dialog_run (GTK_DIALOG (dialog));
606
gtk_widget_destroy (dialog);
608
g_io_scheduler_push_job ((GIOSchedulerJobFunc) cleanup_tmp_dir,
610
G_PRIORITY_DEFAULT, NULL);
621
transfer_done_cb (GtkWidget *dialog,
624
gdk_threads_enter ();
625
/* XXX: path should be on the local filesystem by now? */
627
if (dialog != NULL) {
628
gtk_widget_destroy (dialog);
631
process_local_theme (tdata->parent, tdata->path);
633
g_free (tdata->path);
636
gdk_threads_leave ();
640
mate_theme_install (GFile *file,
646
const gchar *template;
650
dialog = gtk_message_dialog_new (parent,
654
_("No theme file location specified to install"));
655
gtk_dialog_run (GTK_DIALOG (dialog));
656
gtk_widget_destroy (dialog);
660
/* see if someone dropped a local directory */
661
if (g_file_is_native (file)
662
&& g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL) == G_FILE_TYPE_DIRECTORY) {
663
path = g_file_get_path (file);
664
process_local_theme (parent, path);
669
/* we can't tell if this is an icon theme yet, so just make a
670
* temporary copy in .themes */
671
path = g_build_filename (g_get_home_dir (), ".themes", NULL);
673
if (access (path, X_OK | W_OK) != 0) {
674
dialog = gtk_message_dialog_new (parent,
678
_("Insufficient permissions to install the theme in:\n%s"), path);
679
gtk_dialog_run (GTK_DIALOG (dialog));
680
gtk_widget_destroy (dialog);
685
base = g_file_get_basename (file);
687
if (g_str_has_suffix (base, ".tar.gz")
688
|| g_str_has_suffix (base, ".tgz")
689
|| g_str_has_suffix (base, ".gtp"))
690
template = "mate-theme-%d.gtp";
691
else if (g_str_has_suffix (base, ".tar.bz2"))
692
template = "mate-theme-%d.tar.bz2";
694
invalid_theme_dialog (parent, base, FALSE);
700
src = g_list_append (NULL, g_object_ref (file));
707
file_tmp = g_strdup_printf (template, g_random_int ());
708
path = g_build_filename (g_get_home_dir (), ".themes", file_tmp, NULL);
710
} while (g_file_test (path, G_FILE_TEST_EXISTS));
712
tdata = g_new0 (TransferData, 1);
713
tdata->parent = parent;
716
dialog = file_transfer_dialog_new_with_parent (parent);
717
g_signal_connect (dialog,
719
(GCallback) transfer_cancel_cb, path);
720
g_signal_connect (dialog,
722
(GCallback) transfer_done_cb, tdata);
724
target = g_list_append (NULL, g_file_new_for_path (path));
725
file_transfer_dialog_copy_async (FILE_TRANSFER_DIALOG (dialog),
728
FILE_TRANSFER_DIALOG_DEFAULT,
730
gtk_widget_show (dialog);
732
/* don't free the path since we're using that for the signals */
733
g_list_foreach (src, (GFunc) g_object_unref, NULL);
735
g_list_foreach (target, (GFunc) g_object_unref, NULL);
736
g_list_free (target);
740
mate_theme_installer_run (GtkWindow *parent,
741
const gchar *filename)
743
static gboolean running_theme_install = FALSE;
744
static gchar old_folder[512] = "";
746
GtkFileFilter *filter;
748
if (running_theme_install)
751
running_theme_install = TRUE;
753
if (filename == NULL)
754
filename = old_folder;
756
dialog = gtk_file_chooser_dialog_new (_("Select Theme"),
758
GTK_FILE_CHOOSER_ACTION_OPEN,
764
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
766
filter = gtk_file_filter_new ();
767
gtk_file_filter_set_name (filter, _("Theme Packages"));
768
gtk_file_filter_add_mime_type (filter, "application/x-bzip-compressed-tar");
769
gtk_file_filter_add_mime_type (filter, "application/x-compressed-tar");
770
gtk_file_filter_add_mime_type (filter, "application/x-mate-theme-package");
771
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
773
filter = gtk_file_filter_new ();
774
gtk_file_filter_set_name (filter, _("All Files"));
775
gtk_file_filter_add_pattern(filter, "*");
776
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
778
if (strcmp (old_folder, ""))
779
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), old_folder);
781
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
782
gchar *uri_selected, *folder;
784
uri_selected = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
786
folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
787
g_strlcpy (old_folder, folder, 255);
790
gtk_widget_destroy (dialog);
792
if (uri_selected != NULL) {
793
GFile *file = g_file_new_for_uri (uri_selected);
794
g_free (uri_selected);
796
mate_theme_install (file, parent);
797
g_object_unref (file);
800
gtk_widget_destroy (dialog);
804
* we're relying on the mate theme info module to pick up changes
805
* to the themes so we don't need to update the model here
808
running_theme_install = FALSE;