1 /*========================================================================
2 Gtk Theme Switch, a fast and small Gtk theme switching utility that has a
3 GUI and console interface.
5 Copyright (C) 2009 Maher Awamy <muhri@muhri.net>
6 Aaron Lehmann <aaronl@vitelus.com>
7 Joshua Kwan <joshk@triplehelix.org>
8 Pedro Villavicencio Garrido <pvillavi@gnome.cl>
9 Denis Briand <denis@narcan.fr>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License along
22 with this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 =========================================================================*/
27 #include <sys/types.h>
30 #include <sys/types.h>
40 static GList
*get_dirs(void);
41 static void preview_clicked(GtkWidget
*button
, gpointer data
);
42 static void update_newfont (void);
43 static void apply_clicked(GtkWidget
*button
, gpointer data
);
44 static void usage(void);
45 static void backup_gtkrc(gchar
*path_to_gtkrc
);
46 static void ok_clicked(gchar
*rc
);
47 static void preview_ok_clicked(gchar
*rc
);
49 static GtkTreeModel
*create_model (void);
50 void clist_insert (GtkTreeView
*clist
);
52 static void dock(void);
54 static int rightclick (GtkWidget
*w
, GdkEventButton
*event
, gpointer data
);
55 static void clist_insert(GtkWidget
*clist
);
57 static void preview(gchar
*rc_file
);
58 static void preview_window(gchar
*rc_file
);
59 static void send_refresh_signal(void);
60 static short is_themedir (gchar
*path
, gchar
**rc_file
);
61 static short is_installed_theme (gchar
*path
, gchar
**rc_file
);
62 static short install_tarball (gchar
*path
, gchar
**rc_file
);
63 static int switcheroo (gchar
*actual
);
64 static void install_clicked (GtkWidget
*w
, gpointer data
);
65 static void install_ok_clicked (GtkWidget
*w
, gpointer data
);
66 static void search_for_theme_or_die_trying (gchar
*actual
, gchar
**rc_file
);
67 static void set_font (GtkWidget
*w
, GtkWidget
*dialog
);
68 static void font_browse_clicked (GtkWidget
*w
, gpointer data
);
69 static short fgrep_gtkrc_for (gchar
*needle
);
70 static GList
*compare_glists (GList
*t1
, GList
*t2
, GCompareFunc cmpfunc
);
75 void on_eventbox_click();
77 #define INIT_GTK if (!using_gtk) { gtk_init (&argc, &argv); using_gtk = 1; }
84 gint preview_counter
= 0;
86 gchar
*homedir
, /* we use it a lot, so keep it handy */
87 *execname
, /* == argv[0] */
88 *newfont
; /* The name of a new font to use as the default if the user has
89 selected one. Otherwise NULL. */
91 GtkWidget
*dockwin
, *combo
=NULL
, *font_entry
, *use_font_button
, *box
, *install_button
, *browse
;
95 static short fgrep_gtkrc_for (gchar
*needle
)
97 gchar
*path_to_gtkrc
= g_strdup_printf("%s/.gtkrc-2.0", homedir
);
98 FILE *gtkrc
= fopen(path_to_gtkrc
, "r");
99 char tempbuf
[16384], *commentsearch
;
100 g_free (path_to_gtkrc
);
105 while (!feof (gtkrc
))
107 fgets (tempbuf
, 16383, gtkrc
);
109 for (commentsearch
= tempbuf
; *commentsearch
!= '\0'; ++commentsearch
)
110 if (*commentsearch
== '#') { *commentsearch
= '\0'; break; }
111 if (strstr(tempbuf
, needle
))
124 gchar
*dirname
, *localthemedir
, *globalthemedir
, *dname
;
128 gchar
*origdir
=g_get_current_dir(); /* back up cwd */
131 dirname
= g_strconcat(homedir
,"/.themes",NULL
);
133 dir
= opendir (dirname
);
136 /* ONE copy of the local theme directory for putting in the hash */
137 /* NB: Don't g_free() it!! */
138 localthemedir
= g_strdup(dirname
);
140 while ((dent
= readdir (dir
)))
142 gchar
* gtkrc_path
= g_strconcat(dent
->d_name
, "/gtk-2.0/gtkrc", NULL
);
143 stat(dent
->d_name
,&stats
);
144 if (!S_ISDIR(stats
.st_mode
)) continue;
145 if (strcmp(dent
->d_name
,"." ) == 0) continue;
146 if (strcmp(dent
->d_name
,"..") == 0) continue;
147 if (access(gtkrc_path
, R_OK
) == -1) continue;
149 dname
= g_strdup(dent
->d_name
);
150 g_hash_table_insert (hash
, dname
, localthemedir
);
151 list
= g_list_insert_sorted(list
, dname
, (GCompareFunc
)strcmp
);
160 dirname
= gtk_rc_get_theme_dir();
162 dir
= opendir (dirname
);
165 /* ONE copy of the global theme directory for putting in the hash */
166 /* NB: Don't g_free() it!! */
167 globalthemedir
= g_strdup(dirname
);
169 while ((dent
= readdir (dir
)))
171 gchar
* gtkrc_path
= g_strconcat(dent
->d_name
,"/gtk-2.0/gtkrc",NULL
);
172 stat(dent
->d_name
,&stats
);
173 if (!S_ISDIR(stats
.st_mode
)) continue;
174 if (strcmp(dent
->d_name
, "." ) == 0) continue;
175 if (strcmp(dent
->d_name
, "..") == 0) continue;
176 if (access(gtkrc_path
, R_OK
) == -1) continue;
178 dname
= g_strdup(dent
->d_name
);
179 g_hash_table_insert (hash
, dname
, globalthemedir
);
180 list
= g_list_insert_sorted(list
, dname
, (GCompareFunc
)strcmp
);
189 chdir(origdir
); /* Go back to where we were */
190 g_free(origdir
); /* Now go play like a good little program */
195 /* Find the first element in t2 that does not exist in t1.
196 * Uses the supplied cmpfunc() for determining equality of list->data
197 * strcmp() is the recommended compare function */
199 compare_glists (GList
*t1
, GList
*t2
, GCompareFunc cmpfunc
)
202 for (; t2
; t2
=t2
->next
)
205 for (tmp1
=t1
; tmp1
; tmp1
=tmp1
->next
)
206 if ((*cmpfunc
)(tmp1
->data
, t2
->data
) == 0) { matched
= 1; break; }
207 if (!matched
) return t2
;
213 preview_clicked(GtkWidget
*button
, gpointer data
)
215 G_CONST_RETURN gchar
*entry
;
220 entry
= gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo
)->entry
));
221 dir
= g_hash_table_lookup(hash
,entry
);
222 dir
= g_strconcat(dir
,"/",NULL
);
223 rc
= g_strconcat(dir
,entry
,NULL
);
224 actual
= g_strconcat(dir
,entry
,NULL
);
225 rc
= g_strconcat(rc
,"/gtk-2.0/gtkrc",NULL
);
232 /* Update 'newfont' */
233 static void update_newfont (void)
235 if (newfont
) g_free (newfont
);
237 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(use_font_button
)))
239 G_CONST_RETURN gchar
*newerfont
= gtk_entry_get_text (GTK_ENTRY(font_entry
));
240 if (newerfont
&& newerfont
[0])
242 newfont
= g_strdup(newerfont
);
249 apply_clicked(GtkWidget
*button
, gpointer data
)
251 G_CONST_RETURN gchar
*entry
= gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo
)->entry
));
252 gchar
*dir
= g_hash_table_lookup(hash
,entry
);
253 gchar
*name
= g_strdup_printf ("%s/%s/gtk-2.0/gtkrc", dir
, entry
);
254 /* GtkStyle *style;*/
261 /* make sure we get the ClientEvent ourselves */
262 while (gtk_events_pending())
263 gtk_main_iteration();
265 /* sync the font field with the GTK font */
266 /* style = gtk_rc_get_style (font_entry);
267 if (style && style->rc_style)
268 gtk_entry_set_text (GTK_ENTRY(font_entry), pango_font_description_to_string(style->rc_style->font_desc));*/
275 %s command line options:\n\
276 -h[elp]\t\t\t(display this help message)\n\
277 -d[ock]\t\t\t(open dock)\n\
278 file\t\t\t(switch to theme (install if theme is a tarball))\n\
279 -p[review] file\t\t(preview a theme (installs if file is a tarball))\n\
280 -i[nstall] theme.tar.gz\t(install a .tar.gz)\n\
281 -f[ont] fontstring\t(set GTK's main font to fontstring)\n\n\
283 Passing no command line arguments will cause %s to start in dock-mode)\n\n\
285 \"file\" represents any one of (looked for in the listed order):\n\
286 1) An absoulte or relative path to a GTK theme directory.\n\
287 A directory is considered a theme directory if it contains a\n\
289 2) A gzipped tar file which expands to a GTK theme directory as explained in 1).\n\
290 3) A GTK theme directory with the name passed located in ~/.themes.\n\
291 4) A GTK theme directory with the name passed located in the global\n\
293 If none of these files are located and found to be correct, the program will\n\
294 exit with an error.\n",
299 write_rc_file (gchar
*include_file
, gchar
*path
)
301 FILE *gtkrc
= fopen(path
, "w");
304 GtkWidget
*dialog
= gtk_message_dialog_new (GTK_WINDOW(dockwin
),
305 GTK_DIALOG_DESTROY_WITH_PARENT
,
308 "Unable to save your preferences to %s: %s.",
309 path
,strerror(errno
) );
310 gtk_window_set_title(GTK_WINDOW(dialog
), "Error");
311 gtk_dialog_run (GTK_DIALOG (dialog
));
312 gtk_widget_destroy (dialog
);
315 /* the caps stuff is bullshit for gnome */
316 fprintf(gtkrc
, "# -- THEME AUTO-WRITTEN BY gtk-theme-switch2 DO NOT EDIT\ninclude \"%s\"\n\n", include_file
);
318 /* User set a custom font to overrride defaults. */
319 fprintf (gtkrc
, "style \"user-font\"\n{\n font_name=\"%s\"\n}\nwidget_class \"*\" style \"user-font\"\n\n", newfont
);
320 fprintf(gtkrc
, "include \"%s/.gtkrc-2.0.mine\"\n\n# -- THEME AUTO-WRITTEN BY gtk-theme-switch2 DO NOT EDIT\n", homedir
);
325 backup_gtkrc(gchar
*path_to_gtkrc
)
328 if (!(gtkrc
= fopen(path_to_gtkrc
, "r")))
331 FILE *gtkrc_backup
= fopen(g_strdup_printf("%s/.gtkrc-2.0.bak", homedir
),"w");
333 while((c
= fgetc(gtkrc
)) != EOF
){
334 fputc(c
, gtkrc_backup
);
338 fclose(gtkrc_backup
);
342 ok_clicked (gchar
*rc_file
)
344 /* Write the config file to disk */
345 gchar
*path_to_gtkrc
= g_strdup_printf("%s/.gtkrc-2.0", homedir
);
346 backup_gtkrc(path_to_gtkrc
);
347 write_rc_file (rc_file
, path_to_gtkrc
);
348 send_refresh_signal();
349 g_free(path_to_gtkrc
);
353 preview_ok_clicked (gchar
*rc_file
)
355 /* Write the config file to disk */
356 gchar
*path_to_gtkrc
= g_strdup_printf("%s/.gtkrc-2.0", homedir
);
357 rename (rc_file
, path_to_gtkrc
);
358 send_refresh_signal();
359 g_free(path_to_gtkrc
);
363 install_clicked (GtkWidget
*w
, gpointer data
)
365 GtkWidget
*checkbutton
= gtk_check_button_new_with_label ("Switch to theme after installation");
366 GtkWidget
*fs
= gtk_file_selection_new ("Select a GTK theme tarball");
367 g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(fs
)->ok_button
), "clicked", G_CALLBACK(install_ok_clicked
), fs
);
368 g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(fs
)->cancel_button
), "clicked", G_CALLBACK(gtk_widget_destroy
), (gpointer
)fs
);
369 gtk_box_pack_start (GTK_BOX(GTK_FILE_SELECTION(fs
)->main_vbox
), checkbutton
, FALSE
, FALSE
, 0);
370 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton
), FALSE
);
371 g_object_set_data (G_OBJECT(fs
), "checkbutton", checkbutton
);
372 gtk_widget_show(checkbutton
);
377 install_ok_clicked (GtkWidget
*w
, gpointer data
)
379 gchar
*rc_file
, *beginning_of_theme_name
, *thn
;
380 G_CONST_RETURN gchar
*filename
;
383 gboolean cbstate
=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(data
), "checkbutton")));
384 filename
= gtk_file_selection_get_filename(GTK_FILE_SELECTION(data
));
385 thn
= g_strdup(filename
);
386 search_for_theme_or_die_trying(thn
, &rc_file
);
388 gtk_widget_destroy(GTK_WIDGET(data
));
389 /* ok, we're like evil or something, but that won't stop us */
390 for (i
=strlen(rc_file
); i
!= 0; --i
)
392 if (rc_file
[i
] == '/') ++slashes
;
393 if (slashes
== 2) { rc_file
[i
] = '\0'; break; }
395 beginning_of_theme_name
= rc_file
;
396 for (i
=strlen(rc_file
) /*different*/; i
!= 0; --i
)
398 if (rc_file
[i
] == '/') { beginning_of_theme_name
= &rc_file
[i
+1]; break; }
400 /* we've been very naugthy, but we should have the theme's NAME now..
401 * it's about time. */
402 /* get the list item that contains this */
403 pos
= g_list_position (glist
, g_list_find_custom (glist
, beginning_of_theme_name
, (GCompareFunc
) strcmp
));
405 /* set combo's item to the newly-installed theme */
406 /*FIXME gtk_list_select_item(GTK_LIST(GTK_COMBO(combo)->list), pos);*/
408 if (cbstate
) /* checkbutton pressed */
409 apply_clicked(NULL
, NULL
);
411 /* I guess we should free this... */
416 set_font (GtkWidget
*w
, GtkWidget
*dialog
)
418 if (newfont
) g_free (newfont
);
419 newfont
= gtk_font_selection_dialog_get_font_name (GTK_FONT_SELECTION_DIALOG(dialog
));
420 gtk_entry_set_text (GTK_ENTRY(font_entry
), newfont
);
421 gtk_widget_destroy (dialog
);
425 font_browse_clicked (GtkWidget
*w
, gpointer data
)
427 GtkWidget
*font_dialog
;
428 font_dialog
= gtk_font_selection_dialog_new ("Select Font");
429 gtk_font_selection_dialog_set_preview_text (GTK_FONT_SELECTION_DIALOG(font_dialog
), "Gtk Theme Switch");
430 /* gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG(font_dialog), gtk_entry_get_text(GTK_ENTRY(font_entry)));*/
431 g_signal_connect (G_OBJECT(GTK_FONT_SELECTION_DIALOG(font_dialog
)->ok_button
), "clicked", G_CALLBACK(set_font
), (gpointer
)font_dialog
);
432 g_signal_connect_swapped (G_OBJECT(GTK_FONT_SELECTION_DIALOG(font_dialog
)->cancel_button
), "clicked", G_CALLBACK(gtk_widget_destroy
), (gpointer
)font_dialog
);
434 gtk_widget_show (font_dialog
);
440 for (l
= kids
; l
; l
=l
->next
) {
441 kill(GPOINTER_TO_INT(l
->data
), 9);
449 GtkWidget
*label
, *button
, *pixmap
, *evbox
;
452 dockwin
= gtk_dialog_new();
453 gtk_widget_realize(dockwin
);
454 gtk_window_set_title(GTK_WINDOW(dockwin
),"Theme Dock");
455 /* gtk_window_set_policy(GTK_WINDOW(dockwin), TRUE, TRUE, FALSE);*/
456 gtk_window_set_resizable(GTK_WINDOW(dockwin
), TRUE
);
457 g_signal_connect(G_OBJECT(dockwin
),"destroy",G_CALLBACK(quit
),NULL
);
458 box
= gtk_hbox_new(FALSE
, 0);
459 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dockwin
)->vbox
), box
, FALSE
, FALSE
, 0);
460 tips
= gtk_tooltips_new();
461 label
= gtk_label_new("Theme: ");
462 gtk_box_pack_start(GTK_BOX(box
),label
,FALSE
,FALSE
,FALSE
);
464 combo
= gtk_combo_new();
466 gtk_combo_set_popdown_strings(GTK_COMBO(combo
), glist
);
467 gtk_box_pack_start(GTK_BOX(box
),combo
,TRUE
,TRUE
,FALSE
);
469 pixmap
= gtk_image_new_from_stock(GTK_STOCK_ADD
, GTK_ICON_SIZE_BUTTON
);
471 evbox
= gtk_event_box_new();
473 gtk_tooltips_set_tip(tips
,evbox
,"click here for more options","private");
474 gtk_widget_set_events(evbox
, GDK_BUTTON_PRESS
);
475 g_signal_connect(G_OBJECT(evbox
), "button_press_event", G_CALLBACK(on_eventbox_click
), NULL
);
477 gtk_container_add(GTK_CONTAINER(evbox
), pixmap
);
478 gtk_box_pack_start(GTK_BOX(box
),evbox
,FALSE
,FALSE
,FALSE
);
479 gtk_widget_show_all(box
);
481 box
= gtk_hbox_new(FALSE
, 0);
482 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dockwin
)->vbox
), box
, FALSE
, FALSE
, 0);
484 use_font_button
= gtk_check_button_new_with_label("Use font: ");
485 gtk_box_pack_start(GTK_BOX(box
), use_font_button
, FALSE
, FALSE
, 0);
486 gtk_widget_show(use_font_button
);
488 font_entry
= gtk_entry_new();
489 gtk_box_pack_start(GTK_BOX(box
), font_entry
, TRUE
, TRUE
, 0);
490 gtk_widget_show(font_entry
);
493 gtk_entry_set_text (GTK_ENTRY(font_entry
), newfont
);
494 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(use_font_button
), TRUE
);
499 /* GtkStyle *style = gtk_rc_get_style (font_entry);
500 if (style && style->rc_style)
501 gtk_entry_set_text (GTK_ENTRY(font_entry), pango_font_description_to_string(style->rc_style->font_desc));*/
504 newfont
= g_strdup(gtk_entry_get_text(GTK_ENTRY(font_entry
)));
506 if (newfont
!= 0 && newfont
[0])
508 /* Very dirty hack...
509 We want to only set the checkbutton to TRUE if the user specified
510 the font. If the name occurs in their ~/.gtkrc file, they probably did.
511 If it isn't, they probably didn't. So, "grep" the file for the font string. */
512 if (fgrep_gtkrc_for(newfont
))
513 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(use_font_button
), TRUE
);
516 browse
= gtk_button_new_with_label("Browse...");
517 g_signal_connect(G_OBJECT(browse
), "clicked", G_CALLBACK(font_browse_clicked
), NULL
);
518 gtk_box_pack_start(GTK_BOX(box
), browse
, FALSE
, FALSE
, 0);
520 button
= gtk_button_new_with_label("Apply");
521 g_signal_connect(G_OBJECT(button
),"clicked",G_CALLBACK(apply_clicked
),NULL
);
522 gtk_box_pack_start(GTK_BOX(GTK_DIALOG (dockwin
)->action_area
),button
,TRUE
,TRUE
,FALSE
);
523 gtk_widget_show(button
);
525 button
= gtk_button_new_with_label("Preview");
526 g_signal_connect(GTK_OBJECT(button
),"clicked",G_CALLBACK(preview_clicked
),NULL
);
527 gtk_box_pack_start(GTK_BOX(GTK_DIALOG (dockwin
)->action_area
),button
,TRUE
,TRUE
,FALSE
);
528 gtk_widget_show(button
);
530 install_button
= gtk_button_new_with_label("Install New Theme");
531 g_signal_connect(G_OBJECT(install_button
), "clicked", G_CALLBACK(install_clicked
), NULL
);
532 gtk_box_pack_start(GTK_BOX(GTK_DIALOG (dockwin
)->action_area
),install_button
,TRUE
,TRUE
,FALSE
);
534 gtk_widget_show(dockwin
);
538 void on_eventbox_click()
549 gtk_widget_hide(box
);
550 gtk_widget_hide(install_button
);
551 gtk_widget_hide(browse
);
557 gtk_widget_show(box
);
558 gtk_widget_show(install_button
);
559 gtk_widget_show(browse
);
564 static GtkTreeModel
*
571 gchar
*stuff
[4][2] = { { "blah1", "blah2" },
572 { "blah3", "blah4" },
573 { "blah5", "blah6" },
574 { "blah7", "blah8" } };
576 /* create list store */
577 store
= gtk_list_store_new (2,
581 /* add data to the list store */
582 for (i
= 0; i
< 4; i
++)
584 gtk_list_store_append (store
, &iter
);
585 gtk_list_store_set (store
, &iter
,
591 return GTK_TREE_MODEL (store
);
596 preview (gchar
*rc_file
)
599 gint got_it
= 0, it_died
= 0;
601 gchar
*path
= g_strdup_printf ("%s/.gtkrc.tmp-%i", homedir
, preview_counter
);
602 gchar
*command
= g_strdup_printf ("%s -_dont-use-this %s &", execname
, path
);
603 write_rc_file (rc_file
, path
);
605 pipe
= popen(command
,"r");
608 g_print("gts: error previewing\n");
612 fcntl(fileno(pipe
), F_SETFL
, O_NONBLOCK
);
614 line
= (gchar
*)g_malloc(1024);
616 fgets(line
,1024,pipe
);
617 line
[strlen(line
)-1] = '\0';
618 if (!got_it
&& !g_strncasecmp(line
,"pid=",4)) {
619 kids
= g_slist_append(kids
,GINT_TO_POINTER(atoi(line
+4)));
621 } else if (!it_died
&& !g_strncasecmp(line
,"die=",4)) {
622 kids
= g_slist_remove(kids
,GINT_TO_POINTER(atoi(line
+4)));
627 while (gtk_events_pending())
628 gtk_main_iteration();
640 preview_window (gchar
*rc_file
)
646 GtkWidget
*toggle_button
;
647 GtkWidget
*check_button
;
665 gchar
**argv
= &execname
;
666 gchar
*default_files
[] = { rc_file
, NULL
};
667 gchar
*bb
= "Type some text here\nto check if your\ntheme has a problem\nwith the text widget.\nAlso right-click here\nfor a popup-menu sample.\n";
670 gtk_rc_set_default_files (default_files
);
671 gtk_init (&argc
, &argv
);
673 win
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
674 gtk_window_set_title(GTK_WINDOW(win
), "Gtk Theme Switch theme preview");
675 /* gtk_window_set_policy(GTK_WINDOW(win), TRUE, TRUE, FALSE);*/
676 g_signal_connect(G_OBJECT(win
), "destroy", G_CALLBACK(quit_preview
), NULL
);
678 vbox
= gtk_vbox_new(FALSE
,0);
679 notebook
= gtk_notebook_new();
680 gtk_container_add (GTK_CONTAINER(win
), notebook
);
681 label
= gtk_label_new("Page 1");
682 gtk_notebook_append_page (GTK_NOTEBOOK(notebook
), vbox
, label
);
683 label
= gtk_label_new("Label");
684 button
= gtk_button_new_with_label("Button");
685 toggle_button
= gtk_toggle_button_new_with_label("Toggle Button");
686 check_button
= gtk_check_button_new_with_label("Check Button");
687 hbox
= gtk_hbox_new(FALSE
,0);
688 radio
= gtk_radio_button_new_with_label(NULL
,"Radio 1");
689 group
= gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio
));
690 radio2
= gtk_radio_button_new_with_label(group
,"Radio 2");
691 group
= gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio2
));
692 radio3
= gtk_radio_button_new_with_label(group
,"Radio 3");
693 gtk_box_pack_start((GtkBox
*)hbox
,radio
,FALSE
,FALSE
,FALSE
);
694 gtk_box_pack_start((GtkBox
*)hbox
,radio2
,FALSE
,FALSE
,FALSE
);
695 gtk_box_pack_start((GtkBox
*)hbox
,radio3
,FALSE
,FALSE
,FALSE
);
696 sw
= gtk_scrolled_window_new(NULL
,NULL
);
697 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
),GTK_POLICY_AUTOMATIC
,GTK_POLICY_AUTOMATIC
);
698 model
= create_model();
699 clist
= gtk_tree_view_new_with_model(model
);
700 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (clist
), TRUE
);
701 gtk_tree_view_set_search_column (GTK_TREE_VIEW (clist
),0);
702 g_object_unref (G_OBJECT (model
));
704 ok
= gtk_button_new_with_label("Ok");
705 g_signal_connect_swapped(G_OBJECT(ok
),"clicked",G_CALLBACK(preview_ok_clicked
), (gpointer
) rc_file
);
706 cancel
= gtk_button_new_with_label("Cancel");
707 g_signal_connect_swapped(G_OBJECT(cancel
),"clicked",G_CALLBACK(quit_preview
),NULL
);
708 hbox2
= gtk_hbox_new(FALSE
,0);
709 gtk_box_pack_start((GtkBox
*)hbox2
,ok
,TRUE
,TRUE
,FALSE
);
710 gtk_box_pack_start((GtkBox
*)hbox2
,cancel
,TRUE
,TRUE
,FALSE
);
711 gtk_container_add(GTK_CONTAINER(sw
),clist
);
712 gtk_box_pack_start((GtkBox
*)vbox
,label
,FALSE
,FALSE
,FALSE
);
713 gtk_box_pack_start((GtkBox
*)vbox
,button
,FALSE
,FALSE
,FALSE
);
714 gtk_box_pack_start((GtkBox
*)vbox
,toggle_button
,FALSE
,FALSE
,FALSE
);
715 gtk_box_pack_start((GtkBox
*)vbox
,check_button
,FALSE
,FALSE
,FALSE
);
716 gtk_box_pack_start((GtkBox
*)vbox
,hbox
,FALSE
,FALSE
,FALSE
);
717 gtk_box_pack_start((GtkBox
*)vbox
,sw
,TRUE
,TRUE
,FALSE
);
718 gtk_box_pack_start((GtkBox
*)vbox
,hbox2
,FALSE
,FALSE
,FALSE
);
720 vbox
= gtk_vbox_new (FALSE
, 0);
721 label
= gtk_label_new ("Page 2");
722 gtk_notebook_append_page (GTK_NOTEBOOK(notebook
), vbox
, label
);
723 label
= gtk_label_new ("Insensitive Label");
724 gtk_widget_set_sensitive (label
, 0);
725 gtk_box_pack_start (GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
726 button
= gtk_button_new_with_label ("Insensitive Button");
727 gtk_widget_set_sensitive (button
, 0);
728 gtk_box_pack_start (GTK_BOX(vbox
), button
, FALSE
, FALSE
, 0);
729 sw
= gtk_scrolled_window_new (NULL
, NULL
);
730 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
731 buff
= gtk_text_buffer_new(NULL
);
732 gtk_text_buffer_set_text(buff
,bb
, strlen(bb
));
733 text
= gtk_text_view_new();
734 gtk_text_view_set_buffer(GTK_TEXT_VIEW(text
), buff
);
736 gtk_container_add (GTK_CONTAINER(sw
), text
);
738 popup = gtk_menu_new ();
739 gtk_widget_show (popup);
740 item = gtk_menu_item_new_with_label ("Menu Entry 1");
741 gtk_widget_show (item);
742 gtk_menu_append(GTK_MENU(popup), item);
743 item = gtk_menu_item_new_with_label ("Menu Entry 2");
744 gtk_widget_show (item);
745 gtk_menu_append(GTK_MENU(popup), item);
746 item = gtk_menu_item_new_with_label ("Menu Entry 3");
747 gtk_widget_show (item);
748 gtk_menu_append(GTK_MENU(popup), item);
749 gtk_signal_connect(GTK_OBJECT(text), "button_press_event", GTK_SIGNAL_FUNC(rightclick), popup);
751 gtk_box_pack_start(GTK_BOX(vbox
), sw
, TRUE
, TRUE
, 0);
753 label
= gtk_label_new ("About");
754 vbox
= gtk_vbox_new (FALSE
, 0);
755 gtk_notebook_append_page (GTK_NOTEBOOK(notebook
), vbox
, label
);
756 label
= gtk_label_new ("Gtk Theme Switch\nby Maher Awamy <muhri@muhri.net>\nand Aaron Lehmann <aaronl@vitelus.com>\nhttp://www.muhri.net/nav.php3?node=gts");
757 gtk_box_pack_start (GTK_BOX(vbox
), label
, TRUE
, TRUE
, 0);
759 clist_insert(GTK_TREE_VIEW(clist
));
761 gtk_widget_show_all(win
);
764 g_print("pid=%d\n",getpid());
770 _exit (1); /* no change */
775 g_print("die=%d\n",getpid());
780 send_refresh_signal(void)
782 GdkEventClient event
;
783 event
.type
= GDK_CLIENT_EVENT
;
784 event
.send_event
= TRUE
;
786 event
.message_type
= gdk_atom_intern("_GTK_READ_RCFILES", FALSE
);
787 event
.data_format
= 8;
788 gdk_event_send_clientmessage_toall((GdkEvent
*)&event
);
791 /* Sets rc_file to the rc_file of the theme if the result is true.
792 * It is the caller's repsonsibility to free rc_file */
793 static short is_themedir (gchar
*path
, gchar
**rc_file
)
797 test_rc_file
= g_strdup_printf ("%s/gtk-2.0/gtkrc",path
);
798 if (stat(test_rc_file
, &info
) == 0 && (S_ISREG(info
.st_mode
) || S_ISLNK(info
.st_mode
)))
800 /* $path/gtk/gtkrc exists, and is a regular file */
801 *rc_file
= test_rc_file
;
806 g_free (test_rc_file
);
811 static short install_tarball (gchar
*path
, gchar
**rc_file
)
813 gchar
*command
, *themedir
;
815 GList
*new_list
, *new_theme
;
817 themedir
= g_strdup_printf ("%s/.themes", homedir
);
821 gchar
*cwd
= g_get_current_dir();
822 command
= g_strdup_printf ("tar --directory %s -xzf %s/%s 2>/dev/null", themedir
, cwd
, path
);
826 command
= g_strdup_printf ("tar --directory %s -xzf %s 2>/dev/null", themedir
, path
);
828 /* Ensure that ~/.themes exists */
829 mkdir (themedir
, S_IRUSR
| S_IWUSR
| S_IXUSR
);
831 result
= system(command
);
834 if (result
!= EXIT_SUCCESS
)
836 /* We don't do that anymore. Now we find the first new directory
837 * in either themedir, and presume that to be the name of the new theme.
838 * NOT FOOLPROOF, but good as long as you don't mess with stuff while the
839 * program is running. At least better than deriving the theme name from
840 * the tarball name. Ugh. */
842 new_list
= get_dirs();
843 new_theme
= compare_glists(glist
, new_list
, (GCompareFunc
)strcmp
);
846 result
= is_installed_theme(new_theme
->data
, rc_file
);
847 g_list_foreach (glist
, (GFunc
)g_free
, NULL
);
850 /* Update combo, but only in dock mode */
851 if (combo
) gtk_combo_set_popdown_strings(GTK_COMBO(combo
), glist
);
855 gchar
*interestingpath
, basename
[1024];
856 g_list_foreach (new_list
, (GFunc
)g_free
, NULL
);
857 g_list_free (new_list
);
858 /* fall back to our old hack if no new dir was created, say if the theme
859 * was already installed... */
861 /* Set rc_file to our best darn guess of the resultant gtk theme
862 * dir/gtk/gtkrc. This is very tricky. The hack that is used to is
863 * to return the segment in path between the last slash and the
864 * first dot or dash after that. */
865 interestingpath
= &path
[strlen(path
)-1];
866 while (interestingpath
!= path
&& *(interestingpath
-1) != '/') interestingpath
--;
867 strcpy (basename
, interestingpath
);
868 for (interestingpath
= &basename
[0]; *interestingpath
!= '\0'; ++interestingpath
)
870 if (*interestingpath
== '-' || *interestingpath
== '.')
872 *interestingpath
= '\0';
876 result
= is_installed_theme(basename
, rc_file
);
883 is_installed_theme (gchar
*path
, gchar
**rc_file
)
885 gchar
*gtk_rc_theme_dir
= gtk_rc_get_theme_dir();
886 /* don't strlen things twice when computing the size to use for fullpath */
887 gint path_len
= strlen(path
), homedir_len
= strlen(homedir
);
888 /* ditto with get_theme_dir */
889 gint gtk_rc_theme_dir_len
= strlen(gtk_rc_theme_dir
);
890 gchar
*fullpath
= (gchar
*) g_malloc (MAX(homedir_len
+path_len
+10,
891 gtk_rc_theme_dir_len
+path_len
+1));
894 /* use memcpy since we already have the lengths */
895 memcpy (fullpath
, homedir
, homedir_len
);
896 memcpy (fullpath
+homedir_len
, "/.themes/", 9);
897 /* add 1 to length so we get the null char too */
898 memcpy (fullpath
+homedir_len
+9, path
, path_len
+1);
900 if (is_themedir(fullpath
, rc_file
))
905 memcpy (fullpath
, gtk_rc_theme_dir
, gtk_rc_theme_dir_len
);
906 /* add 1 to length so we get the null char too */
907 memcpy (fullpath
+gtk_rc_theme_dir_len
, path
, path_len
+1);
909 result
= is_themedir(fullpath
, rc_file
);
917 search_for_theme_or_die_trying (gchar
*actual
, gchar
**rc_file
)
919 if (!is_themedir(actual
, rc_file
) &&
920 !install_tarball(actual
, rc_file
) &&
921 !is_installed_theme(actual
, rc_file
))
924 %s: Sorry, \"%s\" does not appear to be a valid theme directory or tarball!\n", execname
, actual
);
930 switcheroo (gchar
*actual
)
933 search_for_theme_or_die_trying (actual
, &rc_file
);
934 /* If we get here, we actually found the theme */
942 int main (int argc
, char *argv
[])
947 gint result
= EXIT_SUCCESS
;
953 homedir
= getenv("HOME");
954 hash
= g_hash_table_new (g_str_hash
, g_str_equal
);
956 if (argc
== 1) /* start in dock mode auto if no args */
962 else if (strcmp(argv
[1], "-_dont-use-this") == 0)
964 preview_window (argv
[2]); /* GARGARGAR */
965 } /* hehe, aaronl is crazy */
967 for (i
=1; i
!= argc
; ++i
)
969 if (argv
[i
][0] == '-')
971 /* Single-arg commands/options */
972 if (argv
[i
][1] == 'd')
979 /* Double-arg commands/options */
982 if (argv
[i
][1] == 'p')
986 if (!is_themedir(actual
, &rc_file
) && !install_tarball(actual
, &rc_file
) && !is_installed_theme(actual
, &rc_file
))
989 %s: Sorry, \"%s\" does not appear to be a valid theme directory or tarball!\n", execname
, actual
);
990 result
= EXIT_FAILURE
;
1000 if (argv
[i
][1] == 'i')
1003 if (!install_tarball(actual
, &rc_file
))
1006 %s: Sorry, \"%s\" does not appear to be a valid theme tarball!\n", execname
, actual
);
1008 result
= EXIT_FAILURE
;
1012 if (argv
[i
][1] == 'f')
1014 newfont
= g_strdup (argv
[++i
]);
1019 /* if this starts with a minus, it's either an unrecognized command
1020 * or -help. Perfect */
1022 result
= EXIT_FAILURE
;
1025 /* got here... fallback and assume it's a theme */
1026 glist
= get_dirs ();
1027 gtk_init (&argc
, &argv
);
1028 result
|= switcheroo(argv
[i
]);
1033 for (l
=kids
;l
;l
=l
->next
) {
1034 kill(GPOINTER_TO_INT(l
->data
), 9);
1042 void clist_insert(GtkTreeView
*clist
)
1044 GtkCellRenderer
*renderer
;
1045 GtkTreeViewColumn
*column
;
1048 renderer
= gtk_cell_renderer_text_new();
1049 column
= gtk_tree_view_column_new_with_attributes("Column 1",
1056 gtk_tree_view_column_set_sort_column_id(column
, 0);
1057 gtk_tree_view_append_column(clist
, column
);
1060 renderer
= gtk_cell_renderer_text_new();
1062 column
= gtk_tree_view_column_new_with_attributes("Column2",
1068 gtk_tree_view_column_set_sort_column_id(column
, 1);
1069 gtk_tree_view_append_column(clist
, column
);