Add a Keywords line into lxappearance.desktop file.
[lxde/lxappearance.git] / src / utils.c
CommitLineData
0cd84ed6
HJYP
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"
3a7d812e 23#include "lxappearance.h"
2800b8a3
HJYP
24#include <glib/gi18n.h>
25#include <sys/types.h>
26#include <signal.h>
27#include <sys/wait.h>
28#include <stdlib.h>
90adc88c 29#include <glib/gstdio.h>
2800b8a3 30
9f90caed
HJYP
31#include "icon-theme.h"
32
2800b8a3
HJYP
33static void on_pid_exit(GPid pid, gint status, gpointer user_data)
34{
35 GtkDialog* dlg = GTK_DIALOG(user_data);
90adc88c 36 gtk_dialog_response(dlg, GTK_RESPONSE_OK);
2800b8a3
HJYP
37 g_debug("pid exit");
38}
39
40static void on_progress_dlg_response(GtkDialog* dlg, int res, gpointer user_data)
41{
90adc88c
HJYP
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
51static gboolean on_progress_timeout(GtkProgressBar* progress)
52{
53 gtk_progress_bar_pulse(progress);
54 return TRUE;
2800b8a3
HJYP
55}
56
90adc88c 57gboolean show_progress_for_pid(GtkWindow* parent, const char* title, const char* msg, GPid pid)
2800b8a3 58{
90adc88c 59 gint res;
2800b8a3 60 GtkWidget* dlg = gtk_dialog_new_with_buttons(title, parent,
89574cc5
JL
61#if GTK_CHECK_VERSION(3, 0, 0)
62 GTK_DIALOG_MODAL,
63#else
2800b8a3 64 GTK_DIALOG_NO_SEPARATOR|GTK_DIALOG_MODAL,
89574cc5 65#endif
2800b8a3 66 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
90adc88c 67 GtkWidget* progress = gtk_progress_bar_new();
df6716ee 68 GtkWidget* vbox = gtk_dialog_get_content_area(GTK_DIALOG(dlg));
2800b8a3 69 GtkWidget* label = gtk_label_new(msg);
90adc88c
HJYP
70
71 guint child_watch = g_child_watch_add(pid, on_pid_exit, dlg);
df6716ee 72 guint timeout = g_timeout_add(300, (GSourceFunc)on_progress_timeout, progress);
90adc88c 73
70ea103e 74 gtk_window_set_default_size(GTK_WINDOW(dlg), 240, -1);
df6716ee 75 gtk_box_set_spacing(GTK_BOX(vbox), 6);
2800b8a3 76 gtk_widget_show(label);
df6716ee 77 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
90adc88c 78 gtk_widget_show(progress);
df6716ee 79 gtk_box_pack_start(GTK_BOX(vbox), progress, FALSE, TRUE, 0);
073c0729 80 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progress));
2800b8a3 81 g_signal_connect(dlg, "response", G_CALLBACK(on_progress_dlg_response), &pid);
90adc88c 82
df6716ee 83 res = gtk_dialog_run(GTK_DIALOG(dlg));
90adc88c 84
2800b8a3 85 g_source_remove(child_watch);
90adc88c 86 g_source_remove(timeout);
2800b8a3 87 gtk_widget_destroy(dlg);
90adc88c
HJYP
88
89 return (res == GTK_RESPONSE_OK);
90}
91
9f90caed
HJYP
92static void insert_theme_to_models(IconTheme* theme)
93{
94 int icon_theme_pos = 0;
95 int cursor_theme_pos = 0;
96 GSList* l;
97 GtkTreeIter it;
98
99 for(l = app.icon_themes; l; l=l->next)
100 {
101 IconTheme* theme2 = (IconTheme*)l->data;
102 if(l->data == theme)
103 break;
104 if(theme2->has_icon)
105 ++icon_theme_pos;
106 if(theme2->has_cursor)
107 ++cursor_theme_pos;
108 }
109 if(theme->has_icon)
110 gtk_list_store_insert_with_values(app.icon_theme_store, &it, icon_theme_pos, 0, theme->disp_name, 1, theme, -1);
111
112 if(theme->has_cursor)
113 gtk_list_store_insert_with_values(app.cursor_theme_store, &it, cursor_theme_pos, 0, theme->disp_name, 1, theme, -1);
114}
115
90adc88c
HJYP
116static gboolean install_icon_theme_package(const char* package_path)
117{
118 GPid pid = -1;
2471c16d 119 const char* user_icons_dir = icon_theme_dirs[0];
90adc88c 120 char* tmp_dir = g_build_filename(user_icons_dir, "tmp.XXXXXX", NULL);
5412361d 121 const char* argv[]= {
90adc88c
HJYP
122 "tar",
123 NULL,
124 "-C",
125 tmp_dir,
126 "-xf",
df6716ee 127 (char*)package_path,
90adc88c
HJYP
128 NULL
129 };
130
9f90caed
HJYP
131 if(g_mkdir_with_parents(user_icons_dir, 0700) == -1)
132 return FALSE;
133
90adc88c
HJYP
134 if(!mkdtemp(tmp_dir))
135 return FALSE;
136
137 if(g_str_has_suffix(package_path, ".tar.gz"))
138 argv[1] = "--gzip";
139 else if(g_str_has_suffix(package_path, ".tar.bz2"))
140 argv[1] = "--bzip2";
141 else /* the file format is not supported */
142 goto _out;
143
5412361d 144 char* cmd = g_strjoinv(" ", (char**)argv);
90adc88c
HJYP
145 g_debug("extract: %s", cmd);
146 g_free(cmd);
147
5412361d 148 if(g_spawn_async(NULL, (char**)argv, NULL, G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL))
90adc88c
HJYP
149 {
150 g_debug("pid = %d", pid);
151 /* show progress UI for this pid */
df6716ee 152 if(show_progress_for_pid(GTK_WINDOW(app.dlg), "Install themes", "Installing...", pid))
90adc88c
HJYP
153 {
154 /* move files in tmp_dir to user_icons_dir */
9f90caed
HJYP
155 GDir* dir;
156 GKeyFile* kf = g_key_file_new();
157
158 /* convert the themes in the dir to IconTheme structs and add them to app.icon_themes list */
2471c16d 159 load_icon_themes_from_dir(user_icons_dir, tmp_dir, kf);
9f90caed
HJYP
160 g_key_file_free(kf);
161
162 /* now really move this themes to ~/.icons dir and also update the GUI */
163 dir = g_dir_open(tmp_dir, 0, NULL);
90adc88c
HJYP
164 if(dir)
165 {
6fde7f51
AG
166 const char* name;
167 while((name = g_dir_read_name(dir)) != NULL)
90adc88c 168 {
90adc88c
HJYP
169 char* index_theme = g_build_filename(tmp_dir, name, "index.theme", NULL);
170 gboolean is_theme = g_file_test(index_theme, G_FILE_TEST_EXISTS);
171 g_free(index_theme);
172 if(is_theme)
173 {
174 char* theme_tmp = g_build_filename(tmp_dir, name, NULL);
9f90caed 175 char* theme_target = g_build_filename(user_icons_dir, name, NULL);
90adc88c
HJYP
176 if(g_rename(theme_tmp, theme_target) == 0)
177 {
178 /* the theme is already installed to ~/.icons */
9f90caed
HJYP
179 GSList* l= g_slist_find_custom(app.icon_themes, name, (GCompareFunc)icon_theme_cmp_name);
180 if(l)
181 {
182 IconTheme* theme = (IconTheme*)l->data;
183 g_debug("installed theme: %p, %s", theme, theme->name);
184 /* update UI */
185 insert_theme_to_models(theme);
186 }
90adc88c
HJYP
187 }
188 else
189 {
190 /* errors happened */
191 }
192 g_free(theme_target);
193 g_free(theme_tmp);
194 }
195 }
196 g_dir_close(dir);
9f90caed
HJYP
197
198 /* remove remaining files. FIXME: will this cause problems? */
6fde7f51
AG
199 cmd = g_strdup_printf("rm -rf '%s'", tmp_dir);
200 g_spawn_command_line_sync(cmd, NULL, NULL, NULL, NULL);
201 g_free(cmd);
90adc88c
HJYP
202 }
203 }
204 }
205
206_out:
207 g_free(tmp_dir);
90adc88c 208 return (pid != -1);
2800b8a3
HJYP
209}
210
211gboolean install_icon_theme(GtkWindow* parent)
212{
213 GtkFileFilter* filter = gtk_file_filter_new();
214 char* file = NULL;
90adc88c 215 int res;
2800b8a3
HJYP
216 GtkWidget* fc = gtk_file_chooser_dialog_new( _("Select an icon theme"), NULL,
217 GTK_FILE_CHOOSER_ACTION_OPEN,
218 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
219 GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL );
df6716ee 220 gtk_window_set_transient_for(GTK_WINDOW(fc), GTK_WINDOW(app.dlg));
2800b8a3
HJYP
221 gtk_file_filter_add_pattern( filter, "*.tar.gz" );
222 gtk_file_filter_add_pattern( filter, "*.tar.bz2" );
223 gtk_file_filter_set_name( filter, _("*.tar.gz, *.tar.bz2 (Icon Theme)") );
224
225 gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(fc), filter );
226 gtk_file_chooser_set_filter( GTK_FILE_CHOOSER(fc), filter );
227
90adc88c
HJYP
228 res = gtk_dialog_run( (GtkDialog*)fc );
229 file = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(fc) );
2800b8a3
HJYP
230 gtk_widget_destroy( fc );
231
90adc88c
HJYP
232 if( res == GTK_RESPONSE_OK )
233 install_icon_theme_package(file);
d023f0a4 234
90adc88c 235 g_free(file);
d023f0a4 236 return TRUE;
2800b8a3 237}
0cd84ed6 238
2471c16d
HJYP
239gboolean remove_icon_theme(GtkWindow* parent, IconTheme* theme)
240{
241 gboolean ret = TRUE;
242 char* dir = g_build_filename(theme->base_dir, theme->name, NULL);
243 char* tmp_dir = g_build_filename(theme->base_dir, "tmp.XXXXXX", NULL);
244g_debug("tmp_dir = %s", tmp_dir);
245 /* move the theme to a tmp dir first. so we can make the
246 * removal atomic. */
247 if(mkdtemp(tmp_dir))
248 {
249 char* tmp_dest = g_build_filename(tmp_dir, theme->name, NULL);
250 if(g_rename(dir, tmp_dest) == 0)
251 {
5412361d 252 const char* argv[] = {
2471c16d
HJYP
253 "rm",
254 "-rf",
255 tmp_dir,
256 NULL
257 };
258 GPid pid;
5412361d 259 if(g_spawn_async(NULL, (char**)argv, NULL, G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL))
2471c16d 260 {
df6716ee 261 ret = show_progress_for_pid(GTK_WINDOW(app.dlg), "Remove icon theme", "Removing...", pid);
2471c16d
HJYP
262 }
263 }
264 g_free(tmp_dest);
265 }
266 else
267 ret = FALSE;
268
269 g_free(dir);
270 return ret;
271}