Finish theme installation support.
[lxde/lxappearance.git] / src / icon-theme.c
1 /*
2 * icon-theme.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 "icon-theme.h"
23 #include "lxappearance2.h"
24 #include <string.h>
25 #include "utils.h"
26
27 gint icon_theme_cmp_name(IconTheme* t, const char* name)
28 {
29 return strcmp(t->name, name);
30 }
31
32 gint icon_theme_cmp_disp_name(IconTheme* t1, IconTheme* t2)
33 {
34 return g_utf8_collate(t1->disp_name, t2->disp_name);
35 }
36
37 static void icon_theme_free(IconTheme* theme)
38 {
39 g_free(theme->comment);
40 g_free(theme->name);
41 if(theme->disp_name != theme->name)
42 g_free(theme->disp_name);
43 g_slice_free(IconTheme, theme);
44 }
45
46 void load_icon_themes_from_dir(const char* theme_dir, GKeyFile* kf)
47 {
48 GDir* dir = g_dir_open(theme_dir, 0, NULL);
49 if(dir)
50 {
51 const char* name;
52 while(name = g_dir_read_name(dir))
53 {
54 /* skip "default" */
55 if(G_UNLIKELY(strcmp(name, "default") == 0))
56 continue;
57 /* test if we already have this in list */
58 if(!g_slist_find_custom(app.icon_themes, name, (GCompareFunc)icon_theme_cmp_name))
59 {
60 IconTheme* theme = g_slice_new0(IconTheme);
61 char* index_theme;
62 char* cursor_subdir;
63
64 theme->name = g_strdup(name);
65 index_theme = g_build_filename(theme_dir, name, "index.theme", NULL);
66 if(g_key_file_load_from_file(kf, index_theme, 0, NULL))
67 {
68 /* skip hidden ones */
69 if(!g_key_file_get_boolean(kf, "Icon Theme", "Hidden", NULL))
70 {
71 theme->disp_name = g_key_file_get_locale_string(kf, "Icon Theme", "Name", NULL, NULL);
72 /* test if this is a icon theme or it's a cursor theme */
73 theme->comment = g_key_file_get_locale_string(kf, "Icon Theme", "Comment", NULL, NULL);
74
75 /* icon theme must have this key, so it has icons if it has this key */
76 theme->has_icon = g_key_file_has_key(kf, "Icon Theme", "Directories", NULL);
77 }
78 }
79 else
80 theme->disp_name = theme->name;
81 g_free(index_theme);
82
83 cursor_subdir = g_build_filename(theme_dir, name, "cursors", NULL);
84 /* it contains a cursor theme */
85 if(g_file_test(cursor_subdir, G_FILE_TEST_IS_DIR))
86 theme->has_cursor = TRUE;
87 g_free(cursor_subdir);
88
89 if(theme->has_icon || theme->has_cursor)
90 app.icon_themes = g_slist_insert_sorted(app.icon_themes, theme,
91 (GCompareFunc)icon_theme_cmp_disp_name);
92 else /* this dir contains no icon or cursor theme, drop it. */
93 icon_theme_free(theme);
94 }
95 }
96 g_dir_close(dir);
97 }
98 }
99
100 static void load_icon_themes()
101 {
102 const char* const *dirs = g_get_system_data_dirs();
103 const char* const *pdir;
104 char* dir_path;
105 GKeyFile* kf = g_key_file_new();
106
107 dir_path = g_build_filename(g_get_home_dir(), ".icons", NULL);
108 load_icon_themes_from_dir(dir_path, kf);
109 g_free(dir_path);
110
111 dir_path = g_build_filename(g_get_user_data_dir(), "icons", NULL);
112 load_icon_themes_from_dir(dir_path, kf);
113 g_free(dir_path);
114
115 for(pdir = dirs; *pdir; ++pdir)
116 {
117 dir_path = g_build_filename(*pdir, "icons", NULL);
118 load_icon_themes_from_dir(dir_path, kf);
119 g_free(dir_path);
120 }
121 g_key_file_free(kf);
122 }
123
124 static void icon_sizes_init(GtkBuilder* b)
125 {
126 const char* names[] = {
127 "gtk-menu",
128 "gtk-button",
129 "gtk-small-toolbar",
130 "gtk-large-toolbar",
131 "gtk-dnd",
132 "gtk-dialog"
133 };
134 char* sizes_str;
135 g_object_get(gtk_settings_get_default(), "gtk-icon-sizes", &sizes_str, NULL);
136 g_debug("%s", sizes_str);
137 }
138
139 static void on_icon_theme_sel_changed(GtkTreeSelection* tree_sel, gpointer user_data)
140 {
141 GtkTreeModel* model;
142 GtkTreeIter it;
143 if(gtk_tree_selection_get_selected(tree_sel, &model, &it))
144 {
145 IconTheme* theme;
146 gtk_tree_model_get(model, &it, 1, &theme, -1);
147 if(g_strcmp0(theme->name, app.icon_theme))
148 {
149 g_free(app.icon_theme);
150 app.icon_theme = g_strdup(theme->name);
151 g_object_set(gtk_settings_get_default(), "gtk-icon-theme-name", app.icon_theme, NULL);
152
153 lxappearance_changed();
154 }
155 }
156 }
157
158 static void on_install_theme_clicked(GtkButton* btn, gpointer user_data)
159 {
160 install_icon_theme(gtk_widget_get_toplevel(btn));
161 }
162
163 void icon_theme_init(GtkBuilder* b)
164 {
165 GSList* l;
166 GtkTreeIter it;
167 GtkTreeIter icon_theme_sel_it = {0};
168 GtkTreeIter cursor_theme_sel_it = {0};
169 GtkTreeSelection* sel;
170
171 app.icon_theme_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
172 app.cursor_theme_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
173 app.icon_theme_view = GTK_WIDGET(gtk_builder_get_object(b, "icon_theme_view"));
174 app.cursor_theme_view = GTK_WIDGET(gtk_builder_get_object(b, "cursor_theme_view"));
175
176 /* load icon themes */
177 load_icon_themes();
178
179 for(l = app.icon_themes; l; l=l->next)
180 {
181 IconTheme* theme = (IconTheme*)l->data;
182
183 if(theme->has_icon)
184 {
185 gtk_list_store_insert_with_values(app.icon_theme_store, &it, -1, 0, theme->disp_name, 1, theme, -1);
186 if(!icon_theme_sel_it.user_data)
187 {
188 if(strcmp(theme->name, app.icon_theme) == 0)
189 icon_theme_sel_it = it;
190 }
191 }
192
193 if(theme->has_cursor)
194 {
195 gtk_list_store_insert_with_values(app.cursor_theme_store, &it, -1, 0, theme->disp_name, 1, theme, -1);
196 if(!cursor_theme_sel_it.user_data)
197 {
198 if(g_strcmp0(theme->name, app.cursor_theme) == 0)
199 cursor_theme_sel_it = it;
200 }
201 }
202 }
203
204 /* select the currently used theme from the list */
205 gtk_tree_view_set_model(app.icon_theme_view, GTK_TREE_MODEL(app.icon_theme_store));
206 sel = gtk_tree_view_get_selection(app.icon_theme_view);
207 if(icon_theme_sel_it.user_data)
208 {
209 GtkTreePath* tp = gtk_tree_model_get_path(GTK_TREE_MODEL(app.icon_theme_store), &icon_theme_sel_it);
210 gtk_tree_selection_select_iter(sel, &icon_theme_sel_it);
211 gtk_tree_view_scroll_to_cell(app.icon_theme_view, tp, NULL, FALSE, 0, 0);
212 gtk_tree_path_free(tp);
213 }
214 g_signal_connect(sel, "changed", G_CALLBACK(on_icon_theme_sel_changed), NULL);
215
216 gtk_tree_view_set_model(app.cursor_theme_view, GTK_TREE_MODEL(app.cursor_theme_store));
217 sel = gtk_tree_view_get_selection(app.cursor_theme_view);
218 if(cursor_theme_sel_it.user_data)
219 {
220 GtkTreePath* tp = gtk_tree_model_get_path(GTK_TREE_MODEL(app.cursor_theme_store), &cursor_theme_sel_it);
221 gtk_tree_selection_select_iter(sel, &cursor_theme_sel_it);
222 gtk_tree_view_scroll_to_cell(app.cursor_theme_view, tp, NULL, FALSE, 0, 0);
223 gtk_tree_path_free(tp);
224 }
225
226 /* load "gtk-icon-sizes" */
227 icon_sizes_init(b);
228
229 GtkWidget* btn = gtk_builder_get_object(b, "install_icon_theme");
230 g_signal_connect(btn, "clicked", G_CALLBACK(on_install_theme_clicked), NULL);
231 }