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