89f286865dea67a52c181e6ac68e3ab74b8a4884
[lxde/lxappearance.git] / src / utils.c
1 /*
2 * utils.c
3 *
4 * Copyright 2010 PCMan <pcman.tw@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #include "utils.h"
23 #include "lxappearance2.h"
24 #include <glib/gi18n.h>
25 #include <sys/types.h>
26 #include <signal.h>
27 #include <sys/wait.h>
28 #include <stdlib.h>
29 #include <glib/gstdio.h>
30
31 #include "icon-theme.h"
32
33 static void on_pid_exit(GPid pid, gint status, gpointer user_data)
34 {
35 GtkDialog* dlg = GTK_DIALOG(user_data);
36 gtk_dialog_response(dlg, GTK_RESPONSE_OK);
37 g_debug("pid exit");
38 }
39
40 static void on_progress_dlg_response(GtkDialog* dlg, int res, gpointer user_data)
41 {
42 if(res != GTK_RESPONSE_OK)
43 {
44 GPid* ppid = (GPid*)user_data;
45 int status;
46 kill(*ppid, SIGTERM);
47 waitpid(*ppid, &status, WNOHANG);
48 }
49 }
50
51 static gboolean on_progress_timeout(GtkProgressBar* progress)
52 {
53 gtk_progress_bar_pulse(progress);
54 return TRUE;
55 }
56
57 gboolean show_progress_for_pid(GtkWindow* parent, const char* title, const char* msg, GPid pid)
58 {
59 gint res;
60 GtkWidget* dlg = gtk_dialog_new_with_buttons(title, parent,
61 GTK_DIALOG_NO_SEPARATOR|GTK_DIALOG_MODAL,
62 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
63 GtkWidget* progress = gtk_progress_bar_new();
64 GtkWidget* vbox = gtk_dialog_get_content_area(dlg);
65 GtkWidget* label = gtk_label_new(msg);
66
67 guint child_watch = g_child_watch_add(pid, on_pid_exit, dlg);
68 guint timeout = g_timeout_add(300, on_progress_timeout, progress);
69
70 gtk_widget_show(label);
71 gtk_box_pack_start(vbox, label, FALSE, TRUE, 0);
72 gtk_widget_show(progress);
73 gtk_box_pack_start(vbox, progress, FALSE, TRUE, 0);
74 gtk_progress_set_activity_mode(progress, TRUE);
75 g_signal_connect(dlg, "response", G_CALLBACK(on_progress_dlg_response), &pid);
76
77 res = gtk_dialog_run(dlg);
78
79 g_source_remove(child_watch);
80 g_source_remove(timeout);
81 gtk_widget_destroy(dlg);
82
83 return (res == GTK_RESPONSE_OK);
84 }
85
86 static void insert_theme_to_models(IconTheme* theme)
87 {
88 int icon_theme_pos = 0;
89 int cursor_theme_pos = 0;
90 GSList* l;
91 GtkTreeIter it;
92
93 for(l = app.icon_themes; l; l=l->next)
94 {
95 IconTheme* theme2 = (IconTheme*)l->data;
96 if(l->data == theme)
97 break;
98 if(theme2->has_icon)
99 ++icon_theme_pos;
100 if(theme2->has_cursor)
101 ++cursor_theme_pos;
102 }
103 if(theme->has_icon)
104 gtk_list_store_insert_with_values(app.icon_theme_store, &it, icon_theme_pos, 0, theme->disp_name, 1, theme, -1);
105
106 if(theme->has_cursor)
107 gtk_list_store_insert_with_values(app.cursor_theme_store, &it, cursor_theme_pos, 0, theme->disp_name, 1, theme, -1);
108 }
109
110 static gboolean install_icon_theme_package(const char* package_path)
111 {
112 GPid pid = -1;
113 char* user_icons_dir = g_build_filename(g_get_home_dir(), ".icons", NULL);
114 char* tmp_dir = g_build_filename(user_icons_dir, "tmp.XXXXXX", NULL);
115 char* argv[]= {
116 "tar",
117 NULL,
118 "-C",
119 tmp_dir,
120 "-xf",
121 package_path,
122 NULL
123 };
124
125 if(g_mkdir_with_parents(user_icons_dir, 0700) == -1)
126 return FALSE;
127
128 if(!mkdtemp(tmp_dir))
129 return FALSE;
130
131 if(g_str_has_suffix(package_path, ".tar.gz"))
132 argv[1] = "--gzip";
133 else if(g_str_has_suffix(package_path, ".tar.bz2"))
134 argv[1] = "--bzip2";
135 else /* the file format is not supported */
136 goto _out;
137
138 char* cmd = g_strjoinv(" ", argv);
139 g_debug("extract: %s", cmd);
140 g_free(cmd);
141
142 if(g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL))
143 {
144 g_debug("pid = %d", pid);
145 /* show progress UI for this pid */
146 if(show_progress_for_pid(app.dlg, "Install themes", "Installing...", pid))
147 {
148 /* move files in tmp_dir to user_icons_dir */
149 GDir* dir;
150 GKeyFile* kf = g_key_file_new();
151
152 /* convert the themes in the dir to IconTheme structs and add them to app.icon_themes list */
153 load_icon_themes_from_dir(tmp_dir, kf);
154 g_key_file_free(kf);
155
156 /* now really move this themes to ~/.icons dir and also update the GUI */
157 dir = g_dir_open(tmp_dir, 0, NULL);
158 if(dir)
159 {
160 char* name;
161 while(name = g_dir_read_name(dir))
162 {
163 char* index_theme = g_build_filename(tmp_dir, name, "index.theme", NULL);
164 gboolean is_theme = g_file_test(index_theme, G_FILE_TEST_EXISTS);
165 g_free(index_theme);
166 if(is_theme)
167 {
168 char* theme_tmp = g_build_filename(tmp_dir, name, NULL);
169 char* theme_target = g_build_filename(user_icons_dir, name, NULL);
170 if(g_rename(theme_tmp, theme_target) == 0)
171 {
172 /* the theme is already installed to ~/.icons */
173 GSList* l= g_slist_find_custom(app.icon_themes, name, (GCompareFunc)icon_theme_cmp_name);
174 if(l)
175 {
176 IconTheme* theme = (IconTheme*)l->data;
177 g_debug("installed theme: %p, %s", theme, theme->name);
178 /* update UI */
179 insert_theme_to_models(theme);
180 }
181 }
182 else
183 {
184 /* errors happened */
185 }
186 g_free(theme_target);
187 g_free(theme_tmp);
188 }
189 }
190 g_dir_close(dir);
191
192 /* remove remaining files. FIXME: will this cause problems? */
193 name = g_strdup_printf("rm -rf '%s'", tmp_dir);
194 g_spawn_command_line_sync(name, NULL, NULL, NULL, NULL);
195 g_free(name);
196 }
197 }
198 }
199
200 _out:
201 g_free(tmp_dir);
202 g_free(user_icons_dir);
203 return (pid != -1);
204 }
205
206 gboolean install_icon_theme(GtkWindow* parent)
207 {
208 GtkFileFilter* filter = gtk_file_filter_new();
209 char* file = NULL;
210 int res;
211 GtkWidget* fc = gtk_file_chooser_dialog_new( _("Select an icon theme"), NULL,
212 GTK_FILE_CHOOSER_ACTION_OPEN,
213 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
214 GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL );
215 gtk_window_set_transient_for(fc, app.dlg);
216 gtk_file_filter_add_pattern( filter, "*.tar.gz" );
217 gtk_file_filter_add_pattern( filter, "*.tar.bz2" );
218 gtk_file_filter_set_name( filter, _("*.tar.gz, *.tar.bz2 (Icon Theme)") );
219
220 gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(fc), filter );
221 gtk_file_chooser_set_filter( GTK_FILE_CHOOSER(fc), filter );
222
223 res = gtk_dialog_run( (GtkDialog*)fc );
224 file = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(fc) );
225 gtk_widget_destroy( fc );
226
227 if( res == GTK_RESPONSE_OK )
228 install_icon_theme_package(file);
229
230 g_free(file);
231 return TRUE;
232 }
233