Fix d/copyright (again)
[gtk-theme-switch.git] / main.c
1 /*========================================================================
2 Gtk Theme Switch, a fast and small Gtk theme switching utility that has a
3 GUI and console interface.
4
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>
10
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.
15
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.
20
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 =========================================================================*/
25
26 #include <gtk/gtk.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <dirent.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <fcntl.h>
37 #include <signal.h>
38 #include <errno.h>
39
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);
48 #ifdef SWITCH_GTK2
49 static GtkTreeModel *create_model (void);
50 void clist_insert (GtkTreeView *clist);
51 #endif
52 static void dock(void);
53 #ifndef SWITCH_GTK2
54 static int rightclick (GtkWidget *w, GdkEventButton *event, gpointer data);
55 static void clist_insert(GtkWidget *clist);
56 #endif
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);
71 void quit_preview();
72 void quit();
73 void hide_stuff();
74 void show_stuff();
75 void on_eventbox_click();
76
77 #define INIT_GTK if (!using_gtk) { gtk_init (&argc, &argv); using_gtk = 1; }
78
79
80 /* globals */
81 GHashTable *hash;
82 GList *glist=NULL;
83 GSList *kids=NULL;
84 gint preview_counter = 0;
85
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. */
90
91 GtkWidget *dockwin, *combo=NULL, *font_entry, *use_font_button, *box, *install_button, *browse;
92 gint hidden = 1;
93 /*end globals */
94
95 static short fgrep_gtkrc_for (gchar *needle)
96 {
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);
101
102 if (!gtkrc)
103 return 0;
104
105 while (!feof (gtkrc))
106 {
107 fgets (tempbuf, 16383, gtkrc);
108 /* Strip comments */
109 for (commentsearch = tempbuf; *commentsearch != '\0'; ++commentsearch)
110 if (*commentsearch == '#') { *commentsearch = '\0'; break; }
111 if (strstr(tempbuf, needle))
112 {
113 fclose (gtkrc);
114 return 1;
115 }
116 }
117 fclose (gtkrc);
118 return 0;
119 }
120
121 static GList*
122 get_dirs (void)
123 {
124 gchar *dirname, *localthemedir, *globalthemedir, *dname;
125 DIR *dir;
126 struct dirent *dent;
127 struct stat stats;
128 gchar *origdir=g_get_current_dir(); /* back up cwd */
129 GList *list=0;
130
131 dirname = g_strconcat(homedir,"/.themes",NULL);
132 chdir (dirname);
133 dir = opendir (dirname);
134 if (dir)
135 {
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);
139
140 while ((dent = readdir (dir)))
141 {
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;
148
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);
152 g_free(gtkrc_path);
153 }
154
155 closedir(dir);
156 }
157
158 g_free(dirname);
159
160 dirname = gtk_rc_get_theme_dir();
161 chdir (dirname);
162 dir = opendir (dirname);
163 if (dir)
164 {
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);
168
169 while ((dent = readdir (dir)))
170 {
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;
177
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);
181 g_free(gtkrc_path);
182 }
183
184 closedir(dir);
185 }
186
187 g_free(dirname);
188
189 chdir(origdir); /* Go back to where we were */
190 g_free(origdir); /* Now go play like a good little program */
191
192 return list;
193 }
194
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 */
198 static GList*
199 compare_glists (GList *t1, GList *t2, GCompareFunc cmpfunc)
200 {
201 GList *tmp1;
202 for (; t2; t2=t2->next)
203 {
204 short matched = 0;
205 for (tmp1=t1; tmp1; tmp1=tmp1->next)
206 if ((*cmpfunc)(tmp1->data, t2->data) == 0) { matched = 1; break; }
207 if (!matched) return t2;
208 }
209 return 0;
210 }
211
212 static void
213 preview_clicked(GtkWidget *button, gpointer data)
214 {
215 G_CONST_RETURN gchar *entry;
216 gchar *dir;
217 gchar *rc;
218 gchar *actual;
219
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);
226 update_newfont ();
227 preview(rc);
228 g_free(rc);
229 g_free(actual);
230 }
231
232 /* Update 'newfont' */
233 static void update_newfont (void)
234 {
235 if (newfont) g_free (newfont);
236
237 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(use_font_button)))
238 {
239 G_CONST_RETURN gchar *newerfont = gtk_entry_get_text (GTK_ENTRY(font_entry));
240 if (newerfont && newerfont[0])
241 {
242 newfont = g_strdup(newerfont);
243 }
244 }
245 else newfont = NULL;
246 }
247
248 static void
249 apply_clicked(GtkWidget *button, gpointer data)
250 {
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;*/
255
256 update_newfont ();
257
258 ok_clicked(name);
259 g_free(name);
260
261 /* make sure we get the ClientEvent ourselves */
262 while (gtk_events_pending())
263 gtk_main_iteration();
264
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));*/
269 }
270
271 static void
272 usage (void)
273 {
274 printf("\
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\
282 \
283 Passing no command line arguments will cause %s to start in dock-mode)\n\n\
284 \
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\
288 gtk/gtkrc file.\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\
292 \"gtk_themedir\"\n\
293 If none of these files are located and found to be correct, the program will\n\
294 exit with an error.\n",
295 execname, execname);
296 }
297
298 static void
299 write_rc_file (gchar *include_file, gchar *path)
300 {
301 FILE *gtkrc = fopen(path, "w");
302 /*XXX XXX*/
303 if (gtkrc == NULL) {
304 GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(dockwin),
305 GTK_DIALOG_DESTROY_WITH_PARENT,
306 GTK_MESSAGE_ERROR,
307 GTK_BUTTONS_CLOSE,
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);
313 quit();
314 }
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);
317 if (newfont)
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);
321 fclose (gtkrc);
322 }
323
324 static void
325 backup_gtkrc(gchar *path_to_gtkrc)
326 {
327 FILE *gtkrc;
328 if (!(gtkrc = fopen(path_to_gtkrc, "r")))
329 return;
330 int c;
331 FILE *gtkrc_backup = fopen(g_strdup_printf("%s/.gtkrc-2.0.bak", homedir),"w");
332
333 while((c = fgetc(gtkrc)) != EOF){
334 fputc(c, gtkrc_backup);
335 }
336
337 fclose(gtkrc);
338 fclose(gtkrc_backup);
339 }
340
341 static void
342 ok_clicked (gchar *rc_file)
343 {
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);
350 }
351
352 static void
353 preview_ok_clicked (gchar *rc_file)
354 {
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);
360 }
361
362 static void
363 install_clicked (GtkWidget *w, gpointer data)
364 {
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);
373 gtk_widget_show(fs);
374 }
375
376 static void
377 install_ok_clicked (GtkWidget *w, gpointer data)
378 {
379 gchar *rc_file, *beginning_of_theme_name, *thn;
380 G_CONST_RETURN gchar *filename;
381 gint i, pos;
382 short slashes=0;
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);
387 g_free(thn);
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)
391 {
392 if (rc_file[i] == '/') ++slashes;
393 if (slashes == 2) { rc_file[i] = '\0'; break; }
394 }
395 beginning_of_theme_name = rc_file;
396 for (i=strlen(rc_file) /*different*/; i != 0; --i)
397 {
398 if (rc_file[i] == '/') { beginning_of_theme_name = &rc_file[i+1]; break; }
399 }
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));
404 if (pos != -1)
405 /* set combo's item to the newly-installed theme */
406 /*FIXME gtk_list_select_item(GTK_LIST(GTK_COMBO(combo)->list), pos);*/
407
408 if (cbstate) /* checkbutton pressed */
409 apply_clicked(NULL, NULL);
410
411 /* I guess we should free this... */
412 g_free (rc_file);
413 }
414
415 static void
416 set_font (GtkWidget *w, GtkWidget *dialog)
417 {
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);
422 }
423
424 static void
425 font_browse_clicked (GtkWidget *w, gpointer data)
426 {
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);
433
434 gtk_widget_show (font_dialog);
435 }
436
437 void quit()
438 {
439 GSList *l;
440 for (l = kids; l ; l=l->next) {
441 kill(GPOINTER_TO_INT(l->data), 9);
442 }
443 exit(0);
444 }
445
446 static void
447 dock (void)
448 {
449 GtkWidget *label, *button, *pixmap, *evbox;
450 GtkTooltips *tips;
451
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);
463
464 combo = gtk_combo_new();
465 glist = get_dirs();
466 gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist);
467 gtk_box_pack_start(GTK_BOX(box),combo,TRUE,TRUE,FALSE);
468
469 pixmap = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON);
470
471 evbox = gtk_event_box_new();
472
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);
476
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);
480
481 box = gtk_hbox_new(FALSE, 0);
482 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dockwin)->vbox), box, FALSE, FALSE, 0);
483
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);
487
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);
491 if (newfont)
492 {
493 gtk_entry_set_text (GTK_ENTRY(font_entry), newfont);
494 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(use_font_button), TRUE);
495 g_free (newfont);
496 }
497 else
498 {
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));*/
502 }
503
504 newfont = g_strdup(gtk_entry_get_text(GTK_ENTRY(font_entry)));
505
506 if (newfont != 0 && newfont[0])
507 {
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);
514 }
515
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);
519
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);
524
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);
529
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);
533
534 gtk_widget_show(dockwin);
535
536 }
537
538 void on_eventbox_click()
539 {
540 if (hidden) {
541 show_stuff();
542 } else {
543 hide_stuff();
544 }
545 }
546
547 void hide_stuff()
548 {
549 gtk_widget_hide(box);
550 gtk_widget_hide(install_button);
551 gtk_widget_hide(browse);
552 hidden = 1;
553 }
554
555 void show_stuff()
556 {
557 gtk_widget_show(box);
558 gtk_widget_show(install_button);
559 gtk_widget_show(browse);
560 hidden = 0;
561 }
562
563
564 static GtkTreeModel *
565 create_model (void)
566 {
567
568 GtkListStore *store;
569 GtkTreeIter iter;
570 gint i;
571 gchar *stuff[4][2] = { { "blah1", "blah2" },
572 { "blah3", "blah4" },
573 { "blah5", "blah6" },
574 { "blah7", "blah8" } };
575
576 /* create list store */
577 store = gtk_list_store_new (2,
578 G_TYPE_STRING,
579 G_TYPE_STRING);
580
581 /* add data to the list store */
582 for (i = 0; i < 4; i++)
583 {
584 gtk_list_store_append (store, &iter);
585 gtk_list_store_set (store, &iter,
586 0, stuff[i][0],
587 1, stuff[i][1],
588 -1);
589 }
590
591 return GTK_TREE_MODEL (store);
592 }
593
594
595 static void
596 preview (gchar *rc_file)
597 {
598 FILE *pipe;
599 gint got_it = 0, it_died = 0;
600 gchar *line;
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);
604
605 pipe = popen(command,"r");
606
607 if (pipe == NULL) {
608 g_print("gts: error previewing\n");
609 return;
610 }
611
612 fcntl(fileno(pipe), F_SETFL, O_NONBLOCK);
613
614 line = (gchar *)g_malloc(1024);
615 while(!feof(pipe)) {
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)));
620 got_it = 1;
621 } else if (!it_died && !g_strncasecmp(line,"die=",4)) {
622 kids = g_slist_remove(kids,GINT_TO_POINTER(atoi(line+4)));
623 it_died = 1;
624 break;
625 }
626
627 while (gtk_events_pending())
628 gtk_main_iteration();
629 usleep(50000);
630 }
631
632 pclose(pipe);
633 g_free (line);
634 g_free (path);
635 g_free (command);
636 ++preview_counter;
637 }
638
639 static void
640 preview_window (gchar *rc_file)
641 {
642
643 GtkWidget *label;
644 GtkWidget *win;
645 GtkWidget *button;
646 GtkWidget *toggle_button;
647 GtkWidget *check_button;
648 GtkWidget *sw;
649 GtkWidget *clist;
650 GtkWidget *vbox;
651 GtkWidget *hbox;
652 GtkWidget *radio;
653 GtkWidget *radio2;
654 GtkWidget *radio3;
655 GtkWidget *ok;
656 GtkWidget *cancel;
657 GtkWidget *hbox2;
658 GtkWidget *notebook;
659 GtkWidget *text;
660 GtkTreeModel *model;
661 /* GtkWidget *popup;
662 GtkWidget *item;*/
663 GtkTextBuffer *buff;
664 gint argc=1;
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";
668 GSList *group;
669
670 gtk_rc_set_default_files (default_files);
671 gtk_init (&argc, &argv);
672
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);
677
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));
703
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);
719
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);
735
736 gtk_container_add (GTK_CONTAINER(sw), text);
737 /*
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);
750 */
751 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
752
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);
758
759 clist_insert(GTK_TREE_VIEW(clist));
760
761 gtk_widget_show_all(win);
762
763
764 g_print("pid=%d\n",getpid());
765
766 gtk_main ();
767
768 unlink (rc_file);
769
770 _exit (1); /* no change */
771 }
772
773 void quit_preview()
774 {
775 g_print("die=%d\n",getpid());
776 gtk_main_quit();
777 }
778
779 static void
780 send_refresh_signal(void)
781 {
782 GdkEventClient event;
783 event.type = GDK_CLIENT_EVENT;
784 event.send_event = TRUE;
785 event.window = NULL;
786 event.message_type = gdk_atom_intern("_GTK_READ_RCFILES", FALSE);
787 event.data_format = 8;
788 gdk_event_send_clientmessage_toall((GdkEvent *)&event);
789 }
790
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)
794 {
795 gchar *test_rc_file;
796 struct stat info;
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)))
799 {
800 /* $path/gtk/gtkrc exists, and is a regular file */
801 *rc_file = test_rc_file;
802 return 1;
803 }
804 else
805 {
806 g_free (test_rc_file);
807 return 0;
808 }
809 }
810
811 static short install_tarball (gchar *path, gchar **rc_file)
812 {
813 gchar *command, *themedir;
814 gint result;
815 GList *new_list, *new_theme;
816
817 themedir = g_strdup_printf ("%s/.themes", homedir);
818
819 if (path[0] != '/')
820 {
821 gchar *cwd = g_get_current_dir();
822 command = g_strdup_printf ("tar --directory %s -xzf %s/%s 2>/dev/null", themedir, cwd, path);
823 g_free (cwd);
824 }
825 else
826 command = g_strdup_printf ("tar --directory %s -xzf %s 2>/dev/null", themedir, path);
827
828 /* Ensure that ~/.themes exists */
829 mkdir (themedir, S_IRUSR | S_IWUSR | S_IXUSR);
830
831 result = system(command);
832 g_free (command);
833 g_free (themedir);
834 if (result != EXIT_SUCCESS)
835 return 0;
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. */
841
842 new_list = get_dirs();
843 new_theme = compare_glists(glist, new_list, (GCompareFunc)strcmp);
844 if (new_theme)
845 {
846 result = is_installed_theme(new_theme->data, rc_file);
847 g_list_foreach (glist, (GFunc)g_free, NULL);
848 g_list_free (glist);
849 glist = new_list;
850 /* Update combo, but only in dock mode */
851 if (combo) gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist);
852 }
853 else
854 {
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... */
860
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)
869 {
870 if (*interestingpath == '-' || *interestingpath == '.')
871 {
872 *interestingpath = '\0';
873 break;
874 }
875 }
876 result = is_installed_theme(basename, rc_file);
877 }
878
879 return result;
880 }
881
882 static short
883 is_installed_theme (gchar *path, gchar **rc_file)
884 {
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));
892 short result;
893
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);
899
900 if (is_themedir(fullpath, rc_file))
901 {
902 g_free(fullpath);
903 return 1;
904 }
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);
908
909 result = is_themedir(fullpath, rc_file);
910
911 g_free(fullpath);
912
913 return result;
914 }
915
916 static void
917 search_for_theme_or_die_trying (gchar *actual, gchar **rc_file)
918 {
919 if (!is_themedir(actual, rc_file) &&
920 !install_tarball(actual, rc_file) &&
921 !is_installed_theme(actual, rc_file))
922 {
923 fprintf (stderr, "\
924 %s: Sorry, \"%s\" does not appear to be a valid theme directory or tarball!\n", execname, actual);
925 exit (EXIT_FAILURE);
926 }
927 }
928
929 static gint
930 switcheroo (gchar *actual)
931 {
932 gchar *rc_file;
933 search_for_theme_or_die_trying (actual, &rc_file);
934 /* If we get here, we actually found the theme */
935 ok_clicked(rc_file);
936 g_free (rc_file);
937 return EXIT_SUCCESS;
938 }
939
940
941
942 int main (int argc, char *argv[])
943 {
944 gchar *rc_file;
945 gchar *actual;
946 gint i;
947 gint result = EXIT_SUCCESS;
948 GSList *l=NULL;
949 short using_gtk = 0;
950
951 newfont = 0;
952 execname = argv[0];
953 homedir = getenv("HOME");
954 hash = g_hash_table_new (g_str_hash, g_str_equal);
955
956 if (argc == 1) /* start in dock mode auto if no args */
957 {
958 INIT_GTK
959 dock();
960 }
961
962 else if (strcmp(argv[1], "-_dont-use-this") == 0)
963 {
964 preview_window (argv[2]); /* GARGARGAR */
965 } /* hehe, aaronl is crazy */
966
967 for (i=1; i != argc; ++i)
968 {
969 if (argv[i][0] == '-')
970 {
971 /* Single-arg commands/options */
972 if (argv[i][1] == 'd')
973 {
974 INIT_GTK
975 dock();
976 continue;
977 }
978
979 /* Double-arg commands/options */
980 if (i+1 != argc)
981 {
982 if (argv[i][1] == 'p')
983 {
984 glist = get_dirs ();
985 actual = argv[++i];
986 if (!is_themedir(actual, &rc_file) && !install_tarball(actual, &rc_file) && !is_installed_theme(actual, &rc_file))
987 {
988 fprintf (stderr, "\
989 %s: Sorry, \"%s\" does not appear to be a valid theme directory or tarball!\n", execname, actual);
990 result = EXIT_FAILURE;
991 }
992 else
993 {
994 preview (rc_file);
995 g_free (rc_file);
996 }
997 continue;
998 }
999
1000 if (argv[i][1] == 'i')
1001 {
1002 actual = argv[++i];
1003 if (!install_tarball(actual, &rc_file))
1004 {
1005 fprintf (stderr, "\
1006 %s: Sorry, \"%s\" does not appear to be a valid theme tarball!\n", execname, actual);
1007 }
1008 result = EXIT_FAILURE;
1009 continue;
1010 }
1011
1012 if (argv[i][1] == 'f')
1013 {
1014 newfont = g_strdup (argv[++i]);
1015 continue;
1016 }
1017 }
1018
1019 /* if this starts with a minus, it's either an unrecognized command
1020 * or -help. Perfect */
1021 usage();
1022 result = EXIT_FAILURE;
1023 continue;
1024 }
1025 /* got here... fallback and assume it's a theme */
1026 glist = get_dirs ();
1027 gtk_init (&argc, &argv);
1028 result |= switcheroo(argv[i]);
1029 }
1030
1031 if (using_gtk) {
1032 gtk_main();
1033 for (l=kids;l;l=l->next) {
1034 kill(GPOINTER_TO_INT(l->data), 9);
1035
1036 }
1037 }
1038
1039 return result;
1040 }
1041
1042 void clist_insert(GtkTreeView *clist)
1043 {
1044 GtkCellRenderer *renderer;
1045 GtkTreeViewColumn *column;
1046
1047
1048 renderer = gtk_cell_renderer_text_new();
1049 column = gtk_tree_view_column_new_with_attributes("Column 1",
1050 renderer,
1051 "text",
1052 0,
1053 NULL);
1054
1055
1056 gtk_tree_view_column_set_sort_column_id(column, 0);
1057 gtk_tree_view_append_column(clist, column);
1058
1059
1060 renderer = gtk_cell_renderer_text_new();
1061
1062 column = gtk_tree_view_column_new_with_attributes("Column2",
1063 renderer,
1064 "text",
1065 1,
1066 NULL);
1067
1068 gtk_tree_view_column_set_sort_column_id(column, 1);
1069 gtk_tree_view_append_column(clist, column);
1070
1071 return;
1072 }
This page took 0.057084 seconds and 4 git commands to generate.