Adding upstream version 0.2.
[debian/lxappearance.git] / src / main-dlg.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #include <gtk/gtk.h>
6 #include <gdk/gdkx.h>
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <string.h>
12
13 /* for kill & waitpid */
14 #include <sys/types.h>
15 #include <signal.h>
16 #include <sys/wait.h>
17
18 #include "main-dlg.h"
19 #include "main-dlg-ui.h"
20 #include "glade-support.h"
21
22 #define GET_WIDGET( name ) name = lookup_widget( dlg, #name )
23
24 #define INIT_LIST(name, prop) \
25 GET_WIDGET( name##_view ); \
26 name##_list = init_tree_view( name##_view, G_CALLBACK(on_list_sel_changed), prop ); \
27 load_##name##s( name##_list, name##_name );
28
29 #define enable_apply() gtk_dialog_set_response_sensitive( main_dlg, GTK_RESPONSE_APPLY, TRUE )
30 #define disable_apply() gtk_dialog_set_response_sensitive( main_dlg, GTK_RESPONSE_APPLY, FALSE )
31
32 extern gboolean under_lxde; /* wether lxde-xsettings daemon is active */
33
34 extern GtkWidget* main_dlg; /* defined in main.c */
35
36 static GtkTreeView* gtk_theme_view = NULL;
37 static GtkListStore* gtk_theme_list = NULL;
38
39 static GtkTreeView* icon_theme_view = NULL;
40 static GtkListStore* icon_theme_list = NULL;
41
42 static char* gtk_theme_name = NULL;
43 static char* icon_theme_name = NULL;
44 static char* font_name = NULL;
45 static GtkToolbarStyle tb_style = GTK_TOOLBAR_BOTH;
46
47 extern char tmp_rc_file[];
48 static char* rc_file = NULL;
49
50 /*
51 static GtkTreeView* font_view = NULL;
52 static GtkListStore* font_list = NULL;
53 */
54 static GtkWidget* demo_box = NULL;
55 static GtkWidget* demo_socket = NULL;
56 static GPid demo_pid = 0;
57
58 /* older versions of glib don't provde these API */
59 #if ! GLIB_CHECK_VERSION(2, 8, 0)
60
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #include <unistd.h>
64 #include <string.h>
65 #include <errno.h>
66
67 int mkdir_with_parents(const gchar *pathname, int mode)
68 {
69 struct stat statbuf;
70 char *dir, *sep;
71 dir = g_strdup( pathname );
72 sep = dir[0] == '/' ? dir + 1 : dir;
73 do {
74 sep = strchr( sep, '/' );
75 if( G_LIKELY( sep ) )
76 *sep = '\0';
77
78 if( stat( dir, &statbuf) == 0 )
79 {
80 if( ! S_ISDIR(statbuf.st_mode) ) /* parent not dir */
81 goto err;
82 }
83 else /* stat failed */
84 {
85 if( errno == ENOENT ) /* not exists */
86 {
87 if( mkdir( dir, mode ) == -1 )
88 goto err;
89 }
90 else
91 goto err; /* unknown error */
92 }
93
94 if( G_LIKELY( sep ) )
95 {
96 *sep = '/';
97 ++sep;
98 }
99 else
100 break;
101 }while( sep );
102 g_free( dir );
103 return 0;
104 err:
105 g_free( dir );
106 return -1;
107 }
108 #endif
109
110
111 static void reload_demo_process()
112 {
113 char* argv[5];
114 char wid[16];
115
116 if( demo_pid > 0 ) /* kill old demo */
117 {
118 int stat;
119 kill( demo_pid, SIGTERM );
120 waitpid( demo_pid, &stat, 0 );
121 demo_pid = 0;
122 }
123
124 g_snprintf( wid, 16, "%ld", gtk_socket_get_id(demo_socket) );
125
126 argv[0] = g_get_prgname();
127 argv[1] = "demo";
128 argv[2] = wid;
129 argv[3] = tmp_rc_file;
130 argv[4] = NULL;
131 g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &demo_pid, NULL );
132
133 /* reloading demo means the current selected theme is changed */
134 enable_apply();
135 }
136
137 static void write_rc_file( const char* path )
138 {
139 FILE* f;
140 static char* tb_styles[] = {
141 "GTK_TOOLBAR_ICONS",
142 "GTK_TOOLBAR_TEXT",
143 "GTK_TOOLBAR_BOTH",
144 "GTK_TOOLBAR_BOTH_HORIZ"
145 };
146
147 if( f = fopen( path, "w" ) )
148 {
149 fputs( "# DO NOT EDIT! This file will be overwritten by LXAppearance.\n"
150 "# Any customization should be done in ~/.gtkrc-2.0.mine\n\n", f );
151
152 fprintf( f, "gtk-theme-name=\"%s\"\n", gtk_theme_name );
153 fprintf( f, "gtk-icon-theme-name=\"%s\"\n", icon_theme_name );
154 fprintf( f, "gtk-font-name=\"%s\"\n", font_name );
155 fprintf( f, "gtk-toolbar-style=%d\n", tb_style );
156
157 fprintf( f, "include \"%s/.gtkrc-2.0.mine\"\n", g_get_home_dir() );
158
159 fclose( f );
160 }
161 }
162
163 static void create_lxde_config_dir()
164 {
165 char* dir = g_build_filename( g_get_user_config_dir(), "lxde", NULL );
166 #if GTK_CHECK_VERSION( 2, 8, 0 )
167 g_mkdir_with_parents( dir, 0755 );
168 #else
169 mkdir_with_parents( dir, 0755 );
170 #endif
171 g_free( dir );
172 }
173
174 static void write_lxde_config()
175 {
176 FILE* f;
177 char* file, *data;
178 gsize len;
179 GKeyFile* kf = g_key_file_new();
180 gboolean ret;
181
182 file = g_build_filename( g_get_user_config_dir(), "lxde/config", NULL );
183 ret = g_key_file_load_from_file( kf, file, G_KEY_FILE_KEEP_COMMENTS, NULL );
184
185 if( ! ret )
186 {
187 const char** dir, **dirs = g_get_system_data_dirs();
188 create_lxde_config_dir();
189 /* load system-wide config file */
190 for( dir = dirs; *dir; ++dir )
191 {
192 char* path = g_build_filename( *dir, "lxde/config", NULL );
193 ret = g_key_file_load_from_file( kf, path, 0, NULL );
194 g_free( path );
195 if( ret )
196 break;
197 }
198 }
199
200 g_key_file_set_string( kf, "GTK", "sNet/ThemeName", gtk_theme_name );
201 g_key_file_set_string( kf, "GTK", "sNet/IconThemeName", icon_theme_name );
202 g_key_file_set_string( kf, "GTK", "sGtk/FontName", font_name );
203 g_key_file_set_integer( kf, "GTK", "iGtk/ToolbarStyle", tb_style );
204
205 data = g_key_file_to_data( kf, &len, NULL );
206 g_key_file_free( kf );
207
208 if( f = fopen( file, "w" ) )
209 {
210 fwrite( data, sizeof(char), len, f );
211 fclose( f );
212 }
213 g_free( data );
214 }
215
216 static void reload_theme()
217 {
218
219 gtk_rc_reparse_all();
220 }
221
222 static void on_list_sel_changed( GtkTreeSelection* sel, const char* prop )
223 {
224 GtkTreeIter it;
225 GtkTreeModel* model;
226 if( gtk_tree_selection_get_selected( sel, &model, &it ) )
227 {
228 char* name;
229 gtk_tree_model_get( model, &it, 0, &name, -1 );
230
231 if( model == gtk_theme_list ) /* gtk+ theme */
232 {
233 if( name && gtk_theme_name && 0 == strcmp( name, gtk_theme_name ) )
234 goto out;
235 g_free( gtk_theme_name );
236 gtk_theme_name = name;
237
238 if( under_lxde )
239 g_object_set( gtk_settings_get_default(), "gtk-theme-name", name, NULL );
240 }
241 else if( model == icon_theme_list ) /* icon theme */
242 {
243 if( name && icon_theme_name && 0 == strcmp( name, icon_theme_name ) )
244 goto out;
245 g_free( icon_theme_name );
246 icon_theme_name = name;
247
248 if( under_lxde )
249 g_object_set( gtk_settings_get_default(), "gtk-icon-theme-name", name, NULL );
250 }
251
252 if( under_lxde )
253 {
254 enable_apply();
255 }
256 else
257 {
258 write_rc_file( tmp_rc_file );
259 //gtk_rc_reparse_all_for_settings(gtk_settings_get_default(), TRUE);
260 reload_demo_process();
261 }
262
263 return;
264 out:
265 g_free( name );
266 }
267 }
268
269 static gint sort_func( GtkTreeModel* model, GtkTreeIter* it1, GtkTreeIter* it2, gpointer data )
270 {
271 char* str1, *str2;
272 int ret;
273 gtk_tree_model_get( model, it1, 0, &str1, -1 );
274 gtk_tree_model_get( model, it2, 0, &str2, -1 );
275 ret = g_ascii_strcasecmp( str1, str2 );
276 g_free( str1 );
277 g_free( str2 );
278 return ret;
279 }
280
281 static GtkListStore* init_tree_view( GtkTreeView* view, GCallback on_sel_changed, const char* prop )
282 {
283 GtkTreeViewColumn* col;
284 GtkListStore* list;
285 GtkTreeSelection* sel;
286 col = gtk_tree_view_column_new_with_attributes( NULL, gtk_cell_renderer_text_new(), "text", 0, NULL );
287 gtk_tree_view_append_column( view, col );
288 sel = gtk_tree_view_get_selection(view);
289 g_signal_connect( sel, "changed", on_sel_changed, prop );
290
291 list = gtk_list_store_new( 1, G_TYPE_STRING );
292 gtk_tree_sortable_set_sort_func( (GtkTreeSortable*)list, 0, sort_func, NULL, NULL );
293 gtk_tree_view_set_model( view, (GtkTreeModel*)list );
294 g_object_unref( list );
295 return list;
296 }
297
298 static void load_themes_from_dir( GtkListStore* list,
299 const char* dir_path,
300 const char* lookup,
301 GtkTreeSelection* sel,
302 const char* init_sel )
303 {
304 GDir* dir;
305 if( dir = g_dir_open( dir_path, 0, NULL ) )
306 {
307 char* name;
308 while( name = g_dir_read_name( dir ) )
309 {
310 char* file = g_build_filename( dir_path, name, lookup, NULL );
311 if( g_file_test( file, G_FILE_TEST_EXISTS ) )
312 {
313 GtkTreeIter it;
314 gtk_list_store_append( list, &it );
315 gtk_list_store_set( list, &it, 0, name, -1 );
316 if( 0 == strcmp( name, init_sel ) )
317 {
318 GtkTreeView* view;
319 GtkTreePath* tp;
320 gtk_tree_selection_select_iter( sel, &it );
321 view = gtk_tree_selection_get_tree_view( sel );
322 tp = gtk_tree_model_get_path( (GtkTreeModel*)list, &it );
323 gtk_tree_view_scroll_to_cell( view, tp, NULL, FALSE, 0, 0 );
324 gtk_tree_path_free( tp );
325 }
326 }
327 g_free( file );
328 }
329 g_dir_close( dir );
330 }
331 }
332
333 static void load_from_data_dirs( GtkListStore* list,
334 const char* relative_path,
335 const char* lookup,
336 GtkTreeSelection* sel,
337 const char* init_sel )
338 {
339 const char* const *dirs = g_get_system_data_dirs();
340 const char* const *dir;
341 char* dir_path;
342 for( dir = dirs; *dir; ++dir )
343 {
344 dir_path = g_build_filename( *dir, relative_path, NULL );
345 load_themes_from_dir( list, dir_path, lookup, sel, init_sel );
346 g_free( dir_path );
347 }
348 dir_path = g_build_filename( g_get_user_data_dir(), relative_path, NULL );
349 load_themes_from_dir( list, dir_path, lookup, sel, init_sel );
350 g_free( dir_path );
351 }
352
353 static void load_gtk_themes( GtkListStore* list, const char* cur_sel )
354 {
355 char* path;
356 GtkTreeSelection* sel = gtk_tree_view_get_selection( gtk_theme_view );
357 load_from_data_dirs( list, "themes", "gtk-2.0", sel, cur_sel );
358 path = g_build_filename( g_get_home_dir(), ".themes", NULL );
359 load_themes_from_dir( list, path, "gtk-2.0", sel, cur_sel );
360 g_free( path );
361 gtk_tree_sortable_set_sort_column_id( (GtkTreeSortable*)list, 0, GTK_SORT_ASCENDING );
362 }
363
364 static void load_icon_themes( GtkListStore* list, const char* cur_sel )
365 {
366 char* path;
367 GtkTreeSelection* sel = gtk_tree_view_get_selection( icon_theme_view );
368 load_from_data_dirs( list, "icons", "index.theme", sel, cur_sel );
369 path = g_build_filename( g_get_home_dir(), ".icons", NULL );
370 load_themes_from_dir( list, path, "index.theme", sel, cur_sel );
371 g_free( path );
372 gtk_tree_sortable_set_sort_column_id( (GtkTreeSortable*)list, 0, GTK_SORT_ASCENDING );
373 }
374
375 /*
376 static void load_fonts( GtkListStore* list )
377 {
378
379 }
380 */
381
382 gboolean center_win( GtkWindow* dlg )
383 {
384 gtk_widget_show( dlg );
385 return FALSE;
386 }
387
388 static void on_demo_loaded( GtkSocket* socket, GtkWidget* dlg )
389 {
390 /* sleep for 0.8 sec for loading of the demo window */
391 /* FIXME: we need a better way to do this, such as IPC */
392 g_timeout_add_full( G_PRIORITY_LOW, 800, (GSourceFunc)center_win, dlg, NULL );
393 g_signal_handlers_disconnect_by_func( socket, on_demo_loaded, dlg );
394 }
395
396 void main_dlg_init( GtkWidget* dlg )
397 {
398 char* files[] = { tmp_rc_file, NULL };
399 char** def_files = gtk_rc_get_default_files();
400 char** file;
401
402 /* no lxde-settings daemon, use gtkrc-2.0 */
403 if( ! under_lxde )
404 {
405 for( file = def_files; *file; ++file )
406 {
407 if( 0 == access( *file, W_OK ) )
408 rc_file = *file;
409 }
410 if( rc_file )
411 rc_file = g_strdup( rc_file );
412 else
413 rc_file = g_build_filename( g_get_home_dir(), ".gtkrc-2.0", NULL );
414 }
415
416 g_object_get( gtk_settings_get_default(),
417 "gtk-theme-name", &gtk_theme_name,
418 "gtk-icon-theme-name", &icon_theme_name,
419 "gtk-font-name", &font_name,
420 "gtk-toolbar-style", &tb_style,
421 NULL );
422
423 if( ! gtk_theme_name )
424 gtk_theme_name = g_strdup( "Raleigh" );
425 if( ! icon_theme_name )
426 gtk_theme_name = g_strdup( "hicolor" );
427 if( ! font_name )
428 font_name = g_strdup( "Sans 10" );
429
430 /* no lxde-settings daemon, use gtkrc-2.0 */
431 if( ! under_lxde )
432 write_rc_file( tmp_rc_file );
433
434 INIT_LIST( gtk_theme, "gtk-theme-name" )
435 INIT_LIST( icon_theme, "gtk-icon-theme-name" )
436 gtk_font_button_set_font_name( (GtkFontButton*)lookup_widget(dlg, "font"), font_name );
437
438 gtk_combo_box_set_active( (GtkComboBox*)lookup_widget(dlg, "tb_style"), tb_style < 4 ? tb_style : 3 );
439
440 GET_WIDGET( demo_box );
441 gtk_widget_show( demo_box );
442
443 if( under_lxde )
444 {
445 /* XSettings daemon of LXDE is running, gtkrc is useless.
446 * We should set properties of GtkSettings object on the fly.
447 * This will cause problems with some themes, but we have no choice.
448 */
449 show_demo( (GdkNativeWindow)demo_box );
450 gtk_widget_show_all( dlg );
451 }
452 else
453 {
454 /* no lxde-settings daemon, use gtkrc-2.0 and load preview in another process */
455 demo_socket = gtk_socket_new();
456 g_signal_connect( demo_socket, "plug-added", G_CALLBACK(on_demo_loaded), dlg );
457 g_signal_connect( demo_socket, "plug-removed", G_CALLBACK(gtk_true), NULL );
458 gtk_widget_show( demo_socket );
459 gtk_container_add( (GtkContainer*)demo_box, demo_socket );
460
461 gtk_widget_realize( dlg );
462 reload_demo_process();
463 }
464
465 disable_apply();
466 }
467
468 static void reload_all_programs( gboolean icon_only )
469 {
470 GdkEventClient event;
471 event.type = GDK_CLIENT_EVENT;
472 event.send_event = TRUE;
473 event.window = NULL;
474
475 if( under_lxde )
476 {
477 event.message_type = gdk_atom_intern("LXDE_SETTINGS", FALSE);
478 event.data.b[0] = 0; /* LXS_RELOAD */
479 }
480 else
481 {
482 if( icon_only )
483 event.message_type = gdk_atom_intern("_GTK_LOAD_ICONTHEMES", FALSE);
484 else
485 event.message_type = gdk_atom_intern("_GTK_READ_RCFILES", FALSE);
486 }
487 event.data_format = 8;
488 gdk_event_send_clientmessage_toall((GdkEvent *)&event);
489 }
490
491 void
492 on_apply_clicked (GtkButton *button,
493 gpointer user_data)
494 {
495 if( under_lxde )
496 write_lxde_config();
497 else
498 write_rc_file( rc_file );
499
500 reload_all_programs( FALSE );
501 disable_apply();
502 }
503
504
505 void
506 on_font_changed (GtkFontButton *fontbutton,
507 gpointer user_data)
508 {
509 const char* name = gtk_font_button_get_font_name(fontbutton);
510 if( name && font_name && 0 == strcmp( name, font_name ) )
511 return;
512 g_free( font_name );
513 font_name = g_strdup( name );
514
515 if( under_lxde )
516 {
517 g_object_set( gtk_settings_get_default(), "gtk-font-name", font_name, NULL );
518
519 enable_apply();
520 }
521 else
522 {
523 write_rc_file( tmp_rc_file );
524 reload_demo_process();
525 }
526 }
527
528 void
529 on_install_theme_clicked (GtkButton *button,
530 gpointer user_data)
531 {
532 GtkFileFilter* filter = gtk_file_filter_new();
533 GtkWidget* fc = gtk_file_chooser_dialog_new( _("Select an icon theme"), NULL,
534 GTK_FILE_CHOOSER_ACTION_OPEN,
535 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
536 GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL );
537
538 gtk_file_filter_add_pattern( filter, "*.tar.gz" );
539 gtk_file_filter_add_pattern( filter, "*.tar.bz2" );
540 gtk_file_filter_set_name( filter, _("*.tar.gz, *.tar.bz2 (Icon Theme)") );
541
542 gtk_file_chooser_add_filter( fc, filter );
543 gtk_file_chooser_set_filter( (GtkFileChooser*)fc, filter );
544
545 if( gtk_dialog_run( (GtkDialog*)fc ) == GTK_RESPONSE_OK )
546 {
547 char* file = gtk_file_chooser_get_filename( fc );
548 char* argv[]={
549 PACKAGE_DATA_DIR"/lxappearance/install-icon-theme.sh",
550 file, NULL };
551 int status = 0;
552 char* stdo = NULL;
553 if( g_spawn_sync( NULL, argv, NULL, 0, NULL, NULL, &stdo, NULL, &status, NULL ) && 0 == status )
554 {
555 char* sep = stdo ? strchr( stdo, '\n' ) : NULL;
556 if( sep )
557 *sep = '\0';
558
559 /* reload all icon themes */
560 gtk_list_store_clear( icon_theme_list );
561 load_icon_themes( icon_theme_list, stdo ? stdo : "" );
562 }
563 g_free( file );
564 }
565
566 gtk_widget_destroy( fc );
567 }
568
569
570 void
571 on_remove_theme_clicked (GtkButton *button,
572 gpointer user_data)
573 {
574
575 }
576
577
578 void
579 on_tb_style_changed (GtkComboBox *combobox,
580 gpointer user_data)
581 {
582 int sel = gtk_combo_box_get_active( combobox );
583 if( sel == tb_style || sel < 0 )
584 return;
585 tb_style = sel;
586
587 if( under_lxde )
588 {
589 g_object_set( gtk_settings_get_default(), "gtk-toolbar-style", tb_style, NULL );
590 enable_apply();
591 }
592 else
593 {
594 write_rc_file( tmp_rc_file );
595 reload_demo_process();
596 }
597 }
598