Support removing installed themes.
[lxde/lxappearance.git] / src / lxappearance2.c
CommitLineData
0cd84ed6
HJYP
1/*
2 * lxappearance2.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#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
15c155a6
HJYP
26#include "lxappearance2.h"
27
0cd84ed6
HJYP
28#include <gtk/gtk.h>
29#include <glib/gi18n.h>
15c155a6
HJYP
30
31#include <X11/X.h>
32#include <X11/Xatom.h>
33#include <X11/Xlib.h>
34#include <gdk/gdkx.h>
ae63f687 35#include <string.h>
15c155a6 36
0cd84ed6
HJYP
37#include "widget-theme.h"
38#include "icon-theme.h"
39#include "cursor-theme.h"
e597424e 40#include "other.h"
0cd84ed6 41
15c155a6
HJYP
42LXAppearance app = {0};
43
44Atom lxsession_atom = 0;
ae63f687 45static const char* lxsession_name = NULL;
15c155a6
HJYP
46
47static void check_lxsession()
48{
49 lxsession_atom = XInternAtom( GDK_DISPLAY(), "_LXSESSION", True );
50 if( lxsession_atom != None )
51 {
52 XGrabServer( GDK_DISPLAY() );
53 if( XGetSelectionOwner( GDK_DISPLAY(), lxsession_atom ) )
ae63f687 54 {
15c155a6 55 app.use_lxsession = TRUE;
ae63f687
HJYP
56 lxsession_name = g_getenv("DESKTOP_SESSION");
57 }
15c155a6
HJYP
58 XUngrabServer( GDK_DISPLAY() );
59 }
60}
61
0cd84ed6
HJYP
62static GOptionEntry option_entries[] =
63{
64 { NULL }
65};
66
ae63f687
HJYP
67static void save_cursor_theme_name()
68{
69 char* dir_path;
70 if(!app.cursor_theme || !g_strcmp0(app.cursor_theme, "default"))
71 return;
72
73 dir_path = g_build_filename(g_get_home_dir(), ".icons/default", NULL);
74 if(0 == g_mkdir_with_parents(dir_path, 0700))
75 {
76 char* index_theme = g_build_filename(dir_path, "index.theme", NULL);
77 char* content = g_strdup_printf(
78 "# This file is written by LXAppearance. Do not edit."
79 "[Icon Theme]\n"
80 "Name=Default\n"
81 "Comment=Default Cursor Theme\n"
82 "Inherits=%s\n", app.cursor_theme);
83 g_file_set_contents(index_theme, content, -1, NULL);
84 g_free(content);
85 g_free(index_theme);
86 }
87 g_free(dir_path);
88
89 /*
90 dir_path = g_build_filename(g_get_home_dir(), ".Xdefaults", NULL);
91 Xcursor.theme: name
92 Xcursor.size: [size]
93 g_file_set_contents(dir_path, "", -1, NULL);
94 g_free(dir_path);
95 */
96}
97
98static void reload_all_programs()
15c155a6 99{
ae63f687
HJYP
100 GdkEventClient event;
101 event.type = GDK_CLIENT_EVENT;
102 event.send_event = TRUE;
103 event.window = NULL;
15c155a6 104
ae63f687
HJYP
105 if( app.use_lxsession )
106 {
107 event.message_type = gdk_atom_intern_static_string("_LXSESSION");
108 event.data.b[0] = 0; /* LXS_RELOAD */
109 }
110 else
111 {
112 /* if( icon_only )
113 event.message_type = gdk_atom_intern("_GTK_LOAD_ICONTHEMES", FALSE);
114 */
115 event.message_type = gdk_atom_intern("_GTK_READ_RCFILES", FALSE);
116 }
117 event.data_format = 8;
118 gdk_event_send_clientmessage_toall((GdkEvent *)&event);
119}
120
9bb12c12
HJYP
121static const char* bool2str(gboolean val)
122{
123 return val ? "TRUE" : "FALSE";
124}
125
ae63f687
HJYP
126static void lxappearance_save_gtkrc()
127{
128 static const char* tb_styles[]={
129 "GTK_TOOLBAR_ICONS",
130 "GTK_TOOLBAR_TEXT",
131 "GTK_TOOLBAR_BOTH",
132 "GTK_TOOLBAR_BOTH_HORIZ"
133 };
134 static const char* tb_icon_sizes[]={
135 "GTK_ICON_SIZE_INVALID",
136 "GTK_ICON_SIZE_MENU",
137 "GTK_ICON_SIZE_SMALL_TOOLBAR",
138 "GTK_ICON_SIZE_LARGE_TOOLBAR",
139 "GTK_ICON_SIZE_BUTTON",
140 "GTK_ICON_SIZE_DND",
141 "GTK_ICON_SIZE_DIALOG"
142 };
143
3518acea 144 char* file_path = g_build_filename(g_get_home_dir(), ".gtkrc-2.0", NULL);
ae63f687
HJYP
145 char* content = g_strdup_printf(
146 "# DO NOT EDIT! This file will be overwritten by LXAppearance.\n"
147 "# Any customization should be done in ~/.gtkrc-2.0.mine instead.\n\n"
148 "gtk-theme-name=\"%s\"\n"
149 "gtk-icon-theme-name=\"%s\"\n"
150 "gtk-font-name=\"%s\"\n"
151 "gtk-toolbar-style=%s\n"
152 "gtk-toolbar-icon-size=%s\n"
153 "gtk-cursor-theme-name=\"%s\"\n"
154 "gtk-cursor-theme-size=%d\n"
9bb12c12
HJYP
155#if GTK_CHECK_VERSION(2, 14, 0)
156 "gtk-enable-event-sounds=%s\n"
157 "gtk-enable-input-feedback-sounds=%s\n"
158#endif
ae63f687
HJYP
159 "include \"%s/.gtkrc-2.0.mine\"\n",
160 app.widget_theme,
161 app.icon_theme,
162 app.default_font,
163 tb_styles[app.toolbar_style],
164 tb_icon_sizes[app.toolbar_icon_size],
165 app.cursor_theme,
166 app.cursor_theme_size,
9bb12c12
HJYP
167#if GTK_CHECK_VERSION(2, 14, 0)
168 bool2str(app.enable_event_sound),
169 bool2str(app.enable_input_feedback),
170#endif
ae63f687
HJYP
171 g_get_home_dir());
172
173 g_file_set_contents(file_path, content, -1, NULL);
174 g_free(content);
175 g_free(file_path);
15c155a6
HJYP
176}
177
178static void lxappearance_save_lxsession()
179{
ae63f687
HJYP
180 char* rel_path = g_strconcat("lxsession/", lxsession_name, "/desktop.conf", NULL);
181 char* user_config_file = g_build_filename(g_get_user_config_dir(), rel_path, NULL);
182 char* buf;
183 int len;
184 GKeyFile* kf = g_key_file_new();
185
186 if(!g_key_file_load_from_file(kf, user_config_file, G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
187 {
188 /* the user config file doesn't exist, create its parent dir */
189 len = strlen(user_config_file) - strlen("/desktop.conf");
190 user_config_file[len] = '\0';
191 g_debug("user_config_file = %s", user_config_file);
192 g_mkdir_with_parents(user_config_file, 0700);
193 user_config_file[len] = '/';
194
195 g_key_file_load_from_dirs(kf, rel_path, g_get_system_config_dirs(), NULL, G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, NULL);
196 }
197
198 g_free(rel_path);
15c155a6 199
ae63f687
HJYP
200 g_key_file_set_string( kf, "GTK", "sNet/ThemeName", app.widget_theme );
201 g_key_file_set_string( kf, "GTK", "sGtk/FontName", app.default_font );
202
203 g_key_file_set_string( kf, "GTK", "sNet/IconThemeName", app.icon_theme );
204
205 g_key_file_set_string( kf, "GTK", "sGtk/CursorThemeName", app.cursor_theme );
206 g_key_file_set_integer( kf, "GTK", "iGtk/CursorThemeSize", app.cursor_theme_size );
207 save_cursor_theme_name();
208
209 g_key_file_set_integer( kf, "GTK", "iGtk/ToolbarStyle", app.toolbar_style );
210 g_key_file_set_integer( kf, "GTK", "iGtk/ToolbarIconSize", app.toolbar_icon_size );
211
9bb12c12
HJYP
212#if GTK_CHECK_VERSION(2, 14, 0)
213 g_key_file_set_integer( kf, "GTK", "iGtk/ToolbarStyle", app.toolbar_style );
214 g_key_file_set_integer( kf, "GTK", "iGtk/ToolbarIconSize", app.toolbar_icon_size );
215
216 /* "Net/SoundThemeName\0" "gtk-sound-theme-name\0" */
217 g_key_file_set_integer( kf, "GTK", "iNet/EnableEventSounds", app.enable_event_sound);
218 g_key_file_set_integer( kf, "GTK", "iNet/EnableInputFeedbackSounds", app.enable_input_feedback);
219#endif
220
ae63f687
HJYP
221 buf = g_key_file_to_data( kf, &len, NULL );
222 g_key_file_free(kf);
223
224 g_file_set_contents(user_config_file, buf, len, NULL);
225 g_free(buf);
226 g_free(user_config_file);
15c155a6
HJYP
227}
228
229static void on_dlg_response(GtkDialog* dlg, int res, gpointer user_data)
230{
231 switch(res)
232 {
233 case GTK_RESPONSE_APPLY:
234
235 if(app.use_lxsession)
236 lxappearance_save_lxsession();
237 else
238 lxappearance_save_gtkrc();
239
ae63f687
HJYP
240 reload_all_programs();
241
15c155a6
HJYP
242 app.changed = FALSE;
243 gtk_dialog_set_response_sensitive(app.dlg, GTK_RESPONSE_APPLY, FALSE);
244 break;
245 case 1: /* about dialog */
246 {
247 GtkBuilder* b = gtk_builder_new();
248 if(gtk_builder_add_from_file(b, PACKAGE_UI_DIR "/about.ui", NULL))
249 {
250 GtkWidget* dlg = GTK_WIDGET(gtk_builder_get_object(b, "dlg"));
251 gtk_dialog_run(dlg);
252 gtk_widget_destroy(dlg);
253 }
254 g_object_unref(b);
255 }
256 break;
257 default:
258 gtk_main_quit();
259 }
260}
261
262static void settings_init()
263{
e597424e
HJYP
264 GtkSettings* settings = gtk_settings_get_default();
265 g_object_get(settings,
266 "gtk-theme-name", &app.widget_theme,
ae63f687 267 "gtk-font-name", &app.default_font,
e597424e
HJYP
268 "gtk-icon-theme-name", &app.icon_theme,
269 "gtk-cursor-theme-name", &app.cursor_theme,
4e409096 270 "gtk-cursor-theme-size", &app.cursor_theme_size,
e597424e
HJYP
271 "gtk-toolbar-style", &app.toolbar_style,
272 "gtk-toolbar-icon-size", &app.toolbar_icon_size,
9bb12c12
HJYP
273#if GTK_CHECK_VERSION(2, 14, 0)
274 "gtk-enable-event-sounds", &app.enable_event_sound,
275 "gtk-enable-input-feedback-sounds", &app.enable_input_feedback,
276#endif
e597424e 277 NULL);
15c155a6 278 /* try to figure out cursor theme used. */
15c155a6
HJYP
279 if(!app.cursor_theme || g_strcmp0(app.cursor_theme, "default") == 0)
280 {
281 /* get the real theme name from default. */
282 GKeyFile* kf = g_key_file_new();
283 char* fpath = g_build_filename(g_get_home_dir(), ".icons/default/index.theme", NULL);
284 gboolean ret = g_key_file_load_from_file(kf, fpath, 0, NULL);
285 g_free(fpath);
286
287 if(!ret)
288 ret = g_key_file_load_from_data_dirs(kf, "icons/default/index.theme", NULL, 0, NULL);
289
290 if(ret)
291 {
e597424e 292 g_free(app.cursor_theme);
15c155a6
HJYP
293 app.cursor_theme = g_key_file_get_string(kf, "Icon Theme", "Inherits", NULL);
294 g_debug("cursor theme name: %s", app.cursor_theme);
295 }
296 g_key_file_free(kf);
297 }
298}
299
0cd84ed6
HJYP
300int main(int argc, char** argv)
301{
302 GError* err = NULL;
303 GtkBuilder* b;
0cd84ed6
HJYP
304
305 /* gettext support */
306#ifdef ENABLE_NLS
307 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
308 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
309 textdomain ( GETTEXT_PACKAGE );
310#endif
311
0cd84ed6
HJYP
312 /* initialize GTK+ and parse the command line arguments */
313 if( G_UNLIKELY( ! gtk_init_with_args( &argc, &argv, "", option_entries, GETTEXT_PACKAGE, &err ) ) )
314 {
315 g_print( "Error: %s\n", err->message );
316 return 1;
317 }
318
15c155a6
HJYP
319 /* check if we're under LXSession */
320 check_lxsession();
321
322 /* load config values */
323 settings_init();
324
0cd84ed6
HJYP
325 /* create GUI here */
326 b = gtk_builder_new();
327 if(!gtk_builder_add_from_file(b, PACKAGE_UI_DIR "/lxappearance.ui", NULL))
328 return 1;
329
e597424e
HJYP
330 app.dlg = GTK_WIDGET(gtk_builder_get_object(b, "dlg"));
331
0cd84ed6
HJYP
332 widget_theme_init(b);
333 icon_theme_init(b);
334 cursor_theme_init(b);
e597424e 335 other_init(b);
0cd84ed6 336
15c155a6 337 g_signal_connect(app.dlg, "response", G_CALLBACK(on_dlg_response), NULL);
0cd84ed6 338
15c155a6 339 gtk_window_present(GTK_WINDOW(app.dlg));
0cd84ed6
HJYP
340 g_object_unref(b);
341
342 gtk_main();
343
344 return 0;
345}
15c155a6
HJYP
346
347void lxappearance_changed()
348{
349 if(!app.changed)
350 {
351 app.changed = TRUE;
352 gtk_dialog_set_response_sensitive(app.dlg, GTK_RESPONSE_APPLY, TRUE);
353 }
354}