1 /*
2  * Copyright © 2006 Novell, Inc.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  */
19 
20 ////////////////////////////////////////////////////
21 //themer stuff
22 #include <engine.h>
23 #include <signal.h>
24 typedef enum _EngineCol
25 {
26     ENGINE_COL_DLNAME,
27     ENGINE_COL_NAME,
28     ENGINE_COL_VER,
29     ENGINE_COL_LAST_COMPAT,
30     ENGINE_COL_MARKUP,
31     ENGINE_COL_ICON,
32     ENGINE_COL_COUNT
33 } EngineCol;
34 typedef struct _EngineData
35 {
36     const gchar * canname;
37     gchar * dlname;
38     GtkWidget * vbox;
39     EngineMetaInfo meta;
40 } EngineData;
41 typedef struct _FindEngine
42 {
43     const gchar * canname;
44     gboolean found;
45     gint i;
46     EngineData * d;
47 } FindEngine;
48 GSList * SettingList = NULL;
49 GSList * EngineList = NULL;
50 GtkWidget * EngineCombo;
51 GtkListStore * EngineModel;
52 GtkWidget * EngineContainer;
53 //GtkWidget * PreviewImage[BX_COUNT];
54 //GtkWidget * ButtonImage[BX_COUNT];
55 gboolean apply=FALSE;
56 gboolean changed=FALSE;
57 GKeyFile * global_theme_file;
58 GKeyFile * global_settings_file;
59 #ifdef USE_DBUS
60 DBusConnection *dbcon;
61 #endif
62 gchar * active_engine = NULL;
63 
display_part(const gchar * p)64 static gchar* display_part(const gchar *p)
65 {
66   gchar *name = g_strdup(p);
67   gchar *tmp;
68 
69   if ((tmp = g_strrstr(name,":"))) {
70     *tmp++ = 0;
71     tmp = g_strdup(tmp);
72     g_free(name);
73     name = tmp;
74   }
75 
76   if ((tmp = g_strrstr(name,"."))) {
77     *tmp = 0;
78   }
79 
80   return name;
81 }
82 
get_setting_list()83 GSList * get_setting_list()
84 {
85     return SettingList;
86 }
scaler_new(gdouble low,gdouble high,gdouble prec)87 GtkWidget * scaler_new(gdouble low, gdouble high, gdouble prec)
88 {
89     GtkWidget * w;
90     w = gtk_hscale_new_with_range(low,high,prec);
91     gtk_scale_set_value_pos(GTK_SCALE(w),GTK_POS_RIGHT);
92     gtk_range_set_update_policy(GTK_RANGE(w),GTK_UPDATE_DISCONTINUOUS);
93     gtk_widget_set_size_request(w,100,-1);
94     return w;
95 }
add_color_alpha_value(gchar * caption,gchar * basekey,gchar * sect,gboolean active)96 void add_color_alpha_value(gchar * caption, gchar * basekey, gchar * sect, gboolean active)
97 {
98     GtkWidget * w;
99     gchar * colorkey;
100     gchar * alphakey;
101     colorkey = g_strdup_printf(active?"active_%s":"inactive_%s",basekey);
102     alphakey = g_strdup_printf(active?"active_%s_alpha":"inactive_%s_alpha",
103             basekey);
104 
105     w = gtk_label_new(caption);
106     table_append(w,FALSE);
107 
108     w = gtk_color_button_new();
109     table_append(w,FALSE);
110     register_setting(w,ST_COLOR,sect,colorkey);
111 
112     w = scaler_new(0.0,1.0,0.01);
113     table_append(w,TRUE);
114     register_setting(w,ST_FLOAT,sect,alphakey);
115     //we don't g_free because they are registered with register_setting
116 }
make_labels(gchar * header)117 void make_labels(gchar * header)
118 {
119     table_append(gtk_label_new(header),FALSE);
120     table_append(gtk_label_new("Color"),FALSE);
121     table_append(gtk_label_new("Opacity"),FALSE);
122 }
build_frame(GtkWidget * vbox,gchar * title,gboolean is_hbox)123 GtkWidget * build_frame(GtkWidget * vbox, gchar * title, gboolean is_hbox)
124 {
125     GtkWidget * frame;
126     GtkWidget * box;
127     frame = gtk_frame_new(title);
128     gtk_box_pack_startC(vbox,frame,TRUE,TRUE,0);
129     box = is_hbox?gtk_hbox_new(FALSE,2):gtk_vbox_new(FALSE, 2);
130     gtk_container_set_border_widthC(box,8);
131     gtk_container_addC(frame,box);
132     return box;
133 }
register_img_file_setting(GtkWidget * widget,gchar * section,gchar * key,GtkImage * image)134 SettingItem * register_img_file_setting(GtkWidget * widget, gchar * section, gchar * key, GtkImage * image)
135 {
136     SettingItem * item = register_setting(widget,ST_IMG_FILE,section,key);
137     gtk_file_chooser_button_set_width_chars(GTK_FILE_CHOOSER_BUTTON(widget),0);
138     item->image = image;
139     item->preview = GTK_IMAGE(gtk_image_new());
140     gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(widget),GTK_WIDGET(item->preview));
141     g_signal_connect(widget,"update-preview",G_CALLBACK(update_preview_cb),
142             item->preview);
143     return item;
144 }
register_setting(GtkWidget * widget,SettingType type,gchar * section,gchar * key)145 SettingItem * register_setting(GtkWidget * widget, SettingType type, gchar * section, gchar * key)
146 {
147     SettingItem * item;
148     item = malloc(sizeof(SettingItem));
149     item->type = type;
150     item->key = g_strdup(key);
151     item->section = g_strdup(section);
152     item->widget = widget;
153     item->fvalue = g_strdup("");
154     SettingList = g_slist_append(SettingList,item);
155     switch(item->type)
156     {
157         case ST_BOOL:
158         case ST_SFILE_BOOL:
159             g_signal_connect(widget,"toggled",
160                     G_CALLBACK(cb_apply_setting),
161                     item);
162             break;
163         case ST_INT:
164         case ST_SFILE_INT:
165             g_signal_connect(widget,"value-changed",
166                     G_CALLBACK(cb_apply_setting),
167                     item);
168             break;
169         case ST_FLOAT:
170             g_signal_connect(widget,"value-changed",
171                     G_CALLBACK(cb_apply_setting),
172                     item);
173             break;
174         case ST_COLOR:
175             g_signal_connect(widget,"color-set",
176                     G_CALLBACK(cb_apply_setting),
177                     item);
178             break;
179         case ST_FONT:
180             g_signal_connect(widget,"font-set",
181                     G_CALLBACK(cb_apply_setting),
182                     item);
183             break;
184         case ST_IMG_FILE:
185             g_signal_connect(widget,"selection-changed",
186                     G_CALLBACK(cb_apply_setting),
187                     item);
188             break;
189         case ST_STRING_COMBO:
190             g_signal_connect(gtk_bin_get_child(GTK_BIN(widget)),"changed",
191                     G_CALLBACK(cb_apply_setting),
192                     item);
193             break;
194         case ST_SFILE_INT_COMBO:
195             g_signal_connect(widget,"changed",
196                     G_CALLBACK(cb_apply_setting),
197                     item);
198             break;
199         case ST_ENGINE_COMBO:
200             g_signal_connect(widget,"changed",
201                     G_CALLBACK(cb_apply_setting),
202                     item);
203         default:
204             break;
205             //unconnected types
206     }
207     return item;
208 }
209 
210 static gint current_table_width;
211 static GtkTable * current_table;
212 static gint current_table_col;
213 static gint current_table_row;
214 
table_new(gint width,gboolean same,gboolean labels)215 void table_new(gint width, gboolean same, gboolean labels)
216 {
217     //WARNING - clobbers all the current_table_ vars.
218     current_table = GTK_TABLE(gtk_table_new(width,1,same));
219     gtk_table_set_row_spacings(current_table,8);
220     gtk_table_set_col_spacings(current_table,8);
221     current_table_col = labels?1:0;
222     current_table_row = 0;
223     current_table_width = width;
224 }
table_append(GtkWidget * child,gboolean stretch)225 void table_append(GtkWidget * child,gboolean stretch)
226 {
227     gtk_table_attach(current_table,child,current_table_col,current_table_col+1,
228             current_table_row,current_table_row+1,
229             (stretch?GTK_EXPAND:GTK_SHRINK)|GTK_FILL,
230             (stretch?GTK_EXPAND:GTK_SHRINK)|GTK_FILL,
231             0,0);
232     current_table_col++;
233     if (current_table_col == current_table_width)
234     {
235         current_table_col=0;
236         current_table_row++;
237 //        gtk_table_resize(current_table,current_table_width,current_table_row+1);
238     }
239 }
table_append_separator()240 void table_append_separator()
241 {
242     current_table_col=0;
243     current_table_row++;
244 //    gtk_table_resize(current_table,current_table_width,current_table_row+1);
245     gtk_table_attach_defaults(current_table,
246             gtk_hseparator_new(),
247             0,current_table_width,
248             current_table_row,
249             current_table_row+1);
250     current_table_row++;
251 //    gtk_table_resize(current_table,current_table_width,current_table_row+1);
252 }
get_current_table()253 GtkTable * get_current_table()
254 {
255     return current_table;
256 }
send_reload_signal()257 void send_reload_signal()
258 {
259 #ifdef USE_DBUS
260     DBusMessage *message;
261     message = dbus_message_new_signal("/","org.metascape.emerald.dbus.Signal","Reload");
262     dbus_connection_send(dbcon,message,NULL);
263     dbus_message_unref(message);
264 #else
265     Atom wmAtom = 0;
266     Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
267 
268     char buffer[128];
269     char *part = display_part(getenv("DISPLAY"));
270 
271     sprintf(buffer, "_COMPIZ_DM_S%s", part);
272     free(part);
273 
274     if (dpy)
275         wmAtom = XInternAtom(dpy,buffer,0);
276 
277     if (wmAtom) {
278         XEvent clientEvent;
279 	Status missed;
280         Window w = XGetSelectionOwner(dpy,wmAtom);
281 	Atom ReloadIt = XInternAtom(dpy, "emerald-sigusr1", 0);
282 	clientEvent.xclient.type = ClientMessage;
283 	clientEvent.xclient.window = w;
284 	clientEvent.xclient.message_type = ReloadIt;
285 	clientEvent.xclient.format = 32;
286 	clientEvent.xclient.display = dpy;
287 	clientEvent.xclient.data.l[0]    = 0;
288 	clientEvent.xclient.data.l[1]    = 0;
289 	clientEvent.xclient.data.l[2]    = 0;
290 	clientEvent.xclient.data.l[3]    = 0;
291 	clientEvent.xclient.data.l[4]    = 0;
292 	missed = XSendEvent(dpy,w,
293 			    False,
294 			    NoEventMask,
295 			    &clientEvent);
296 	XSync (dpy, False);
297     } else {
298         /* The old way */
299         gchar * args[]=
300 	    {"killall","-u",(gchar *)g_get_user_name(),"-SIGUSR1","emerald",NULL};
301 	gchar * ret=NULL;
302 	if (!g_spawn_sync(NULL,args,NULL,G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_SEARCH_PATH,
303 			  NULL,NULL,&ret,NULL,NULL,NULL) || !ret)
304 	    g_warning("Couldn't find running emerald, no reload signal sent.");
305     }
306 #endif
307 }
apply_settings()308 void apply_settings()
309 {
310     gchar * file = g_strjoin("/",g_get_home_dir(),".emerald/theme/theme.ini",NULL);
311     gchar * path = g_strjoin("/",g_get_home_dir(),".emerald/theme/",NULL);
312     gchar * at;
313     g_slist_foreach(SettingList,(GFunc) write_setting,global_theme_file);
314     g_key_file_set_string(global_theme_file,"theme","version",VERSION);
315     g_mkdir_with_parents(path,00755);
316     at = g_key_file_to_data(global_theme_file,NULL,NULL);
317     if (at)
318     {
319         g_file_set_contents(file,at,-1,NULL);
320         g_free(at);
321     }
322     g_free(file);
323     g_free(path);
324     send_reload_signal();
325 }
cb_apply_setting(GtkWidget * w,gpointer p)326 void cb_apply_setting(GtkWidget * w, gpointer p)
327 {
328     SettingItem * item = p;
329     if (item->type == ST_IMG_FILE)
330     {
331         gchar * s;
332         if (!(s=gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(item->widget))))
333             return; // for now just ignore setting it to an invalid name
334         if (!strcmp(s,item->fvalue))
335         {
336             g_free(s);
337             return;
338         }
339         g_free(item->fvalue);
340         item->fvalue=s;
341         check_file(item,s);
342     }
343     write_setting(p,(gpointer) global_theme_file);
344     if (apply)
345         apply_settings();
346     else
347     {
348         changed=TRUE;
349     }
350 }
351 #ifdef USE_DBUS
setup_dbus()352 void setup_dbus()
353 {
354     dbcon = dbus_bus_get (DBUS_BUS_SESSION,NULL);
355     dbus_connection_setup_with_g_main(dbcon,NULL);
356 }
357 #endif
write_setting(SettingItem * item,gpointer p)358 void write_setting(SettingItem * item, gpointer p)
359 {
360     GKeyFile * f = (GKeyFile *) p;
361     switch(item->type)
362     {
363         case ST_BOOL:
364             g_key_file_set_boolean(f,item->section,item->key,get_bool(item));
365             break;
366         case ST_INT:
367             g_key_file_set_integer(f,item->section,item->key,get_int(item));
368             break;
369         case ST_FLOAT:
370             g_key_file_set_string(f,item->section,item->key,get_float_str(item));
371             break;
372         case ST_COLOR:
373             g_key_file_set_string(f,item->section,item->key,get_color(item));
374             break;
375         case ST_FONT:
376             g_key_file_set_string(f,item->section,item->key,get_font(item));
377             break;
378         case ST_META_STRING:
379             g_key_file_set_string(f,item->section,item->key,get_string(item));
380             break;
381         case ST_STRING_COMBO:
382             g_key_file_set_string(f,item->section,item->key,get_string_combo(item));
383             break;
384         case ST_IMG_FILE:
385             //g_key_file_set_string(f,item->section,item->key,get_img_file(item));
386             {
387                 gchar * s = g_strdup_printf("%s/.emerald/theme/%s.%s.png",g_get_home_dir(),item->section,item->key);
388                 GdkPixbuf * pbuf = gtk_image_get_pixbuf(item->image);
389                 if (pbuf)
390                 {
391                     gdk_pixbuf_savev(pbuf,s,"png",NULL,NULL,NULL);
392                 }
393                 else
394                 {
395                     g_unlink(s); // to really clear out a clear'd image
396                 }
397                 g_free(s);
398             }
399             break;
400         case ST_ENGINE_COMBO:
401             {
402                 EngineMetaInfo emi;
403                 const gchar * active_engine = get_engine_combo(item);
404                 if (get_engine_meta_info(active_engine,&emi))
405                     g_key_file_set_string(f,"engine_version",active_engine,emi.version);
406                 g_key_file_set_string(f,item->section,item->key,active_engine);
407                 do_engine(active_engine);
408             }
409             break;
410         case ST_SFILE_INT:
411             if (f==global_theme_file)
412             {
413                 g_key_file_set_integer(global_settings_file,item->section,
414                         item->key,get_int(item));
415                 write_setting_file();
416             }
417             break;
418         case ST_SFILE_BOOL:
419             if (f==global_theme_file)
420             {
421                 g_key_file_set_boolean(global_settings_file,item->section,
422                         item->key,get_bool(item));
423                 write_setting_file();
424             }
425             break;
426         case ST_SFILE_INT_COMBO:
427             if (f==global_theme_file)
428             {
429                 g_key_file_set_integer(global_settings_file,item->section,
430                         item->key,get_sf_int_combo(item));
431                 write_setting_file();
432             }
433             break;
434         default:
435             break;
436             //unhandled types
437     }
438 }
write_setting_file()439 void write_setting_file()
440 {
441     gchar * file = g_strjoin("/",g_get_home_dir(),".emerald/settings.ini",NULL);
442     gchar * path = g_strjoin("/",g_get_home_dir(),".emerald/",NULL);
443     gchar * at;
444     g_mkdir_with_parents(path,00755);
445     at = g_key_file_to_data(global_settings_file,NULL,NULL);
446     if (at)
447     {
448         g_file_set_contents(file,at,-1,NULL);
449         g_free(at);
450     }
451     g_free(file);
452     g_free(path);
453 }
454 
455 gchar * globalStr = NULL;
456 gchar globalFloatStr[G_ASCII_DTOSTR_BUF_SIZE+1];
457 
get_bool(SettingItem * item)458 gboolean get_bool(SettingItem * item)
459 {
460     return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(item->widget));
461 }
get_float(SettingItem * item)462 gdouble get_float(SettingItem * item)
463 {
464     if(!strcmp(G_OBJECT_TYPE_NAME(item->widget),"GtkSpinButton")) {
465          return gtk_spin_button_get_value((GtkSpinButton *)item->widget);
466     }
467     else {
468         return gtk_range_get_value(GTK_RANGE(item->widget));
469     }
470 }
get_int(SettingItem * item)471 gint get_int(SettingItem * item)
472 {
473     return get_float(item);
474 }
get_float_str(SettingItem * item)475 const gchar * get_float_str(SettingItem * item)
476 {
477     g_ascii_dtostr(globalFloatStr,G_ASCII_DTOSTR_BUF_SIZE,
478             get_float(item));
479     return globalFloatStr;
480 }
get_color(SettingItem * item)481 const gchar * get_color(SettingItem * item)
482 {
483     GdkColor c;
484     if (globalStr)
485         g_free(globalStr);
486     gtk_color_button_get_color(GTK_COLOR_BUTTON(item->widget),&c);
487     globalStr = g_strdup_printf("#%02x%02x%02x",c.red>>8,c.green>>8,c.blue>>8);
488     return globalStr;
489 }
get_font(SettingItem * item)490 const gchar * get_font(SettingItem * item)
491 {
492     return gtk_font_button_get_font_name(GTK_FONT_BUTTON(item->widget));
493 }
get_string(SettingItem * item)494 const gchar * get_string(SettingItem * item)
495 {
496     return gtk_entry_get_text(GTK_ENTRY(item->widget));
497 }
check_file(SettingItem * item,gchar * f)498 void check_file(SettingItem * item,gchar * f)
499 {
500     GdkPixbuf * p;
501     p = gdk_pixbuf_new_from_file(f,NULL);
502     if(p)
503     {
504         gtk_image_set_from_pixbuf(item->image,p);
505         gtk_image_set_from_pixbuf(item->preview,p);
506     }
507     else
508     {
509         gtk_image_clear(item->image);
510         gtk_image_clear(item->preview);
511     }
512     if(p)
513         g_object_unref(p);
514 }
get_img_file(SettingItem * item)515 const gchar * get_img_file(SettingItem * item)
516 {
517     return item->fvalue;
518 }
get_string_combo(SettingItem * item)519 const gchar * get_string_combo(SettingItem * item)
520 {
521     const gchar * s;
522     s= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(item->widget))));
523     if (strlen(s))
524         return s;
525     s="IT::HNXC:Default Layout (Blank Entry)";
526     return s;
527 }
show_engine_named(EngineData * d,gpointer p)528 void show_engine_named(EngineData * d, gpointer p)
529 {
530     gchar * nam = p;
531     if (!strcmp(nam,d->canname))
532     {
533         gtk_container_add(GTK_CONTAINER(EngineContainer),d->vbox);
534         gtk_widget_show_all(EngineContainer);
535     }
536 }
do_engine(const gchar * nam)537 void do_engine(const gchar * nam)
538 {
539     GtkWidget * w;
540     if (active_engine && !strcmp(active_engine,nam))
541         return;
542     if (active_engine)
543         g_free(active_engine);
544     active_engine = g_strdup(nam);
545     if ((w=gtk_bin_get_child(GTK_BIN(EngineContainer))))
546         gtk_container_remove(GTK_CONTAINER(EngineContainer),w);
547     g_slist_foreach(EngineList,(GFunc) show_engine_named, (gpointer) nam);
548 
549 }
search_engine(EngineData * d,gpointer p)550 void search_engine(EngineData * d, gpointer p)
551 {
552     FindEngine * fe = p;
553     if (!fe->found)
554     {
555         if (!strcmp(d->canname,fe->canname))
556         {
557             fe->d = d;
558             fe->found=TRUE;
559         }
560         else
561             fe->i++;
562     }
563 }
get_engine_meta_info(const gchar * engine,EngineMetaInfo * inf)564 gboolean get_engine_meta_info(const gchar * engine, EngineMetaInfo * inf)
565 {
566     FindEngine fe;
567     fe.canname = engine;
568     fe.found = FALSE;
569     fe.i=0;
570     fe.d=NULL;
571     g_slist_foreach(EngineList,(GFunc) search_engine, &fe);
572     if (fe.found)
573         memcpy(inf,&(fe.d->meta),sizeof(EngineMetaInfo));
574     return fe.found;
575 }
set_engine_combo(SettingItem * item,gchar * val)576 void set_engine_combo(SettingItem * item, gchar * val)
577 {
578     FindEngine fe;
579     fe.canname = val;
580     fe.found=FALSE;
581     fe.i = 0;
582     g_slist_foreach(EngineList,(GFunc) search_engine,&fe);
583     if (fe.found)
584     {
585         gtk_combo_box_set_active(GTK_COMBO_BOX(item->widget),fe.i);
586     }
587     else
588     {
589         fe.canname = "legacy";
590         fe.found=FALSE;
591         fe.i=0;
592         g_slist_foreach(EngineList,(GFunc) search_engine,&fe);
593         if (fe.found)
594             gtk_combo_box_set_active(GTK_COMBO_BOX(item->widget),fe.i);
595     }
596     do_engine(fe.canname);
597 }
get_engine_combo(SettingItem * item)598 const gchar * get_engine_combo(SettingItem * item)
599 {
600     static gchar * s = NULL;
601     GtkTreeIter i;
602     if(s) g_free(s);
603     //s = gtk_combo_box_get_active_text(GTK_COMBO_BOX(item->widget));
604     if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(item->widget),&i))
605     {
606         gtk_tree_model_get(GTK_TREE_MODEL(EngineModel),&i,ENGINE_COL_NAME,&s,-1);
607         if (!strlen(s))
608         {
609             g_free(s);
610             s=g_strdup("legacy");
611         }
612     }
613     return s;
614 }
get_sf_int_combo(SettingItem * item)615 gint get_sf_int_combo(SettingItem * item)
616 {
617     return gtk_combo_box_get_active(GTK_COMBO_BOX(item->widget));
618 }
update_preview(GtkFileChooser * fc,gchar * filename,GtkImage * img)619 void update_preview(GtkFileChooser * fc, gchar * filename, GtkImage * img)
620 {
621     GdkPixbuf * pixbuf;
622     gboolean have_preview;
623     pixbuf = gdk_pixbuf_new_from_file(filename,NULL);
624     have_preview = (pixbuf != NULL);
625     gtk_image_set_from_pixbuf(GTK_IMAGE(img),pixbuf);
626     if (pixbuf)
627         g_object_unref(pixbuf);
628     gtk_file_chooser_set_preview_widget_active(fc,have_preview);
629 }
update_preview_cb(GtkFileChooser * file_chooser,gpointer data)630 void update_preview_cb(GtkFileChooser * file_chooser, gpointer data)
631 {
632     gchar * filename;
633     filename = gtk_file_chooser_get_preview_filename(file_chooser);
634     update_preview(file_chooser,filename,GTK_IMAGE(data));
635     g_free(filename);
636 }
set_img_file(SettingItem * item,gchar * f)637 void set_img_file(SettingItem * item,gchar * f)
638 {
639     g_free(item->fvalue);
640     item->fvalue = g_strdup(f);
641     gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(item->widget),f);
642     check_file(item,f);
643 }
set_bool(SettingItem * item,gboolean b)644 void set_bool(SettingItem * item, gboolean b)
645 {
646     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item->widget),b);
647 }
set_float(SettingItem * item,gdouble f)648 void set_float(SettingItem * item, gdouble f)
649 {
650     if(!strcmp(G_OBJECT_TYPE_NAME(item->widget),"GtkSpinButton")) {
651          gtk_spin_button_set_value((GtkSpinButton *)item->widget, f);
652     }
653     else {
654          gtk_range_set_value(GTK_RANGE(item->widget),f);
655     }
656 }
set_int(SettingItem * item,gint i)657 void set_int(SettingItem * item, gint i)
658 {
659     set_float(item,i);
660 }
set_float_str(SettingItem * item,gchar * s)661 void set_float_str(SettingItem * item, gchar * s)
662 {
663     set_float(item,g_ascii_strtod(s,NULL));
664 }
set_color(SettingItem * item,gchar * s)665 void set_color(SettingItem * item, gchar * s)
666 {
667     GdkColor c;
668     gdk_color_parse(s,&c);
669     gtk_color_button_set_color(GTK_COLOR_BUTTON(item->widget),&c);
670 }
set_font(SettingItem * item,gchar * f)671 void set_font(SettingItem * item, gchar * f)
672 {
673     gtk_font_button_set_font_name(GTK_FONT_BUTTON(item->widget),f);
674 }
set_string(SettingItem * item,gchar * s)675 void set_string(SettingItem * item, gchar * s)
676 {
677     gtk_entry_set_text(GTK_ENTRY(item->widget),s);
678 }
set_string_combo(SettingItem * item,gchar * s)679 void set_string_combo(SettingItem * item, gchar * s)
680 {
681     gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(item->widget))),s);
682 }
set_sf_int_combo(SettingItem * item,gint i)683 void set_sf_int_combo(SettingItem * item, gint i)
684 {
685     gtk_combo_box_set_active(GTK_COMBO_BOX(item->widget),i);
686 }
read_setting(SettingItem * item,gpointer * p)687 void read_setting(SettingItem * item, gpointer * p)
688 {
689     GKeyFile * f = (GKeyFile *) p;
690     GError * e = NULL;
691     gboolean b;
692     gint i;
693     gchar * s;
694     switch(item->type)
695     {
696         case ST_BOOL:
697             b = g_key_file_get_boolean(f,item->section,item->key,&e);
698             if (!e)
699                 set_bool(item,b);
700             break;
701         case ST_INT:
702             i = g_key_file_get_integer(f,item->section,item->key,&e);
703             if (!e)
704                 set_int(item,i);
705             break;
706         case ST_FLOAT:
707             s = g_key_file_get_string(f,item->section,item->key,&e);
708             if (!e && s)
709             {
710                 set_float_str(item,s);
711                 g_free(s);
712             }
713             break;
714         case ST_COLOR:
715             s = g_key_file_get_string(f,item->section,item->key,&e);
716             if (!e && s)
717             {
718                 set_color(item,s);
719                 g_free(s);
720             }
721             break;
722         case ST_FONT:
723             s = g_key_file_get_string(f,item->section,item->key,&e);
724             if (!e && s)
725             {
726                 set_font(item,s);
727                 g_free(s);
728             }
729             break;
730         case ST_META_STRING:
731             s = g_key_file_get_string(f,item->section,item->key,&e);
732             if (!e && s)
733             {
734                 set_string(item,s);
735                 g_free(s);
736             }
737             break;
738         case ST_STRING_COMBO:
739             s = g_key_file_get_string(f,item->section,item->key,&e);
740             if (!e && s)
741             {
742                 set_string_combo(item,s);
743                 g_free(s);
744             }
745             break;
746         case ST_IMG_FILE:
747             /*s = g_key_file_get_string(f,item->section,item->key,&e);
748             if (!e && s)
749             {
750                 set_img_file(item,s);
751                 g_free(s);
752             }*/
753             s = g_strdup_printf("%s/.emerald/theme/%s.%s.png",g_get_home_dir(),item->section,item->key);
754             set_img_file(item,s);
755             g_free(s);
756             break;
757         case ST_ENGINE_COMBO:
758             s = g_key_file_get_string(f,item->section,item->key,&e);
759             if (!e && s)
760             {
761                 set_engine_combo(item,s);
762                 g_free(s);
763             }
764             break;
765         case ST_SFILE_INT:
766             if (f==global_theme_file)
767             {
768                 i = g_key_file_get_integer(global_settings_file,
769                         item->section,item->key,&e);
770                 if (!e)
771                     set_int(item,i);
772             }
773             break;
774         case ST_SFILE_BOOL:
775             if (f==global_theme_file)
776             {
777                 b = g_key_file_get_boolean(global_settings_file,
778                         item->section,item->key,&e);
779                 if (!e)
780                     set_bool(item,b);
781             }
782             break;
783         case ST_SFILE_INT_COMBO:
784             if (f==global_theme_file)
785             {
786                 i = g_key_file_get_integer(global_settings_file,
787                         item->section,item->key,&e);
788                 if (!e)
789                     set_sf_int_combo(item,i);
790             }
791             break;
792         default:
793             break;
794             //unhandled types
795     }
796 }
init_settings()797 void init_settings()
798 {
799     gchar * file = g_strjoin("/",g_get_home_dir(),".emerald/theme/theme.ini",NULL);
800     g_key_file_load_from_file(global_theme_file,file,G_KEY_FILE_KEEP_COMMENTS,NULL);
801     g_free(file);
802     file = g_strjoin("/",g_get_home_dir(),".emerald/settings.ini",NULL);
803     g_key_file_load_from_file(global_settings_file,file,G_KEY_FILE_KEEP_COMMENTS,NULL);
804     g_free(file);
805     g_slist_foreach(SettingList,(GFunc) read_setting,global_theme_file);
806 }
807 
set_changed(gboolean schanged)808 void set_changed(gboolean schanged)
809 {
810     changed=schanged;
811 }
set_apply(gboolean sapply)812 void set_apply(gboolean sapply)
813 {
814     apply=sapply;
815 }
cb_clear_file(GtkWidget * button,gpointer p)816 void cb_clear_file(GtkWidget * button, gpointer p)
817 {
818     SettingItem * item = p;
819     check_file(item,"");
820     item->fvalue="";
821     gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(item->widget));
822     write_setting(p,global_theme_file);
823     if (apply) apply_settings();
824 }
init_key_files()825 void init_key_files()
826 {
827     global_theme_file = g_key_file_new();
828     global_settings_file = g_key_file_new();
829 }
layout_engine_list(GtkWidget * vbox)830 void layout_engine_list(GtkWidget * vbox)
831 {
832     GtkWidget * hbox;
833     EngineCombo = gtk_combo_box_new();
834     hbox = gtk_hbox_new(FALSE,2);
835     gtk_box_pack_startC(vbox,hbox,FALSE,FALSE,0);
836     gtk_box_pack_startC(hbox,gtk_label_new(_("Select\nEngine")),FALSE,FALSE,0);
837     gtk_box_pack_startC(hbox,EngineCombo,FALSE,FALSE,0);
838     gtk_box_pack_startC(vbox,gtk_hseparator_new(),FALSE,FALSE,0);
839     EngineContainer = gtk_alignment_new(0,0,1,1); // really only needed for the bin-ness
840     gtk_box_pack_startC(vbox,EngineContainer,TRUE,TRUE,0);
841 }
canonize_name(gchar * dlname)842 static gchar * canonize_name(gchar * dlname)
843 {
844     gchar * end;
845     gchar * begin = g_strrstr(dlname,"/lib");
846     if (!begin)
847         return g_strdup("");
848     begin+=4;
849     begin = g_strdup(begin);
850     end = g_strrstr(begin,".so");
851     end[0]='\0';
852     return begin;
853 }
engine_comp(EngineData * d,gpointer p)854 static void engine_comp(EngineData * d,gpointer p)
855 {
856     FindEngine * e = p;
857     if (!strcmp(e->canname, d->canname))
858         e->found=TRUE;
859 }
engine_is_unique(gchar * canname)860 static gboolean engine_is_unique (gchar * canname)
861 {
862     FindEngine e;
863     e.canname=canname;
864     e.found=FALSE;
865     g_slist_foreach(EngineList,(GFunc)engine_comp,&e);
866     return !e.found;
867 }
append_engine(gchar * dlname)868 static void append_engine(gchar * dlname)
869 {
870     gchar * can;
871     gchar * err;
872     (void) dlerror();
873     void * hand = dlopen(dlname,RTLD_NOW);
874     err = dlerror();
875     if (!hand || err)
876     {
877         g_warning("%s", err);
878         if (hand)
879             dlclose(hand);
880         return;
881     }
882     can = canonize_name(dlname);
883     if (engine_is_unique(can))
884     {
885         layout_settings_proc lay;
886         lay = dlsym(hand,"layout_engine_settings");
887         if ((err=dlerror()))
888             g_warning("%s", err);
889         if (lay)
890         {
891             get_meta_info_proc meta;
892             EngineData * d = malloc(sizeof(EngineData));
893             GtkTreeIter i;
894             const gchar * format =
895                 "<b>%s</b> (%s)\n"
896                 "<i><small>%s</small></i>";
897             meta = dlsym(hand,"get_meta_info");
898             if ((err=dlerror()))
899                 g_warning("%s", err);
900                 d->meta.description=g_strdup("No Description");
901                 d->meta.version=g_strdup("0.0");
902                 d->meta.last_compat=g_strdup("0.0");
903                 d->meta.icon=gtk_widget_render_icon(EngineCombo,GTK_STOCK_MISSING_IMAGE,
904                         GTK_ICON_SIZE_LARGE_TOOLBAR,"themeengine");
905             if (meta)
906                 meta(&(d->meta));
907             else
908                 g_warning("Engine %s has no meta info, please update it, using defaults.",dlname);
909 
910             d->dlname = dlname;
911             d->canname = can;
912             d->vbox = gtk_vbox_new(FALSE,2);
913             g_object_ref(d->vbox);
914             lay(d->vbox);
915             EngineList = g_slist_append(EngineList,d);
916             gtk_list_store_append(EngineModel,&i);
917 
918             gtk_list_store_set(EngineModel,&i,ENGINE_COL_DLNAME,d->dlname,ENGINE_COL_NAME,d->canname,
919                     ENGINE_COL_VER,d->meta.version,ENGINE_COL_LAST_COMPAT,d->meta.last_compat,
920                     ENGINE_COL_ICON,d->meta.icon,ENGINE_COL_MARKUP,
921                     g_markup_printf_escaped(format,d->canname,d->meta.version,d->meta.description),
922                     -1);
923             //gtk_combo_box_prepend_text(GTK_COMBO_BOX(EngineCombo),d->canname);
924         }
925     }
926     dlclose(hand);
927 }
engine_scan_dir(gchar * dir)928 static void engine_scan_dir(gchar * dir)
929 {
930     GDir * d;
931     d = g_dir_open(dir,0,NULL);
932     if (d)
933     {
934         gchar * n;
935         GPatternSpec * ps;
936         ps = g_pattern_spec_new("lib*.so");
937         while ((n = (gchar *) g_dir_read_name(d)))
938         {
939             if (g_pattern_match_string(ps,n))
940             {
941                 gchar * dln = g_strjoin("/",dir,n,NULL);
942                 append_engine(dln);
943             }
944         }
945         g_pattern_spec_free(ps);
946         g_dir_close(d);
947     }
948 }
init_engine_list()949 void init_engine_list()
950 {
951     //presumes the container & combo are created
952     //presumes the combo is NOT registered
953     GtkCellRenderer * r;
954 
955     EngineModel = gtk_list_store_new(ENGINE_COL_COUNT,G_TYPE_STRING,
956             G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING,GDK_TYPE_PIXBUF);
957     gchar * local_engine_dir = g_strjoin("/",g_get_home_dir(),".emerald/engines",NULL);
958     gtk_combo_box_set_model(GTK_COMBO_BOX(EngineCombo),GTK_TREE_MODEL(EngineModel));
959     r = gtk_cell_renderer_pixbuf_new();
960     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(EngineCombo),r,FALSE);
961     gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(EngineCombo),r,"pixbuf",ENGINE_COL_ICON);
962     r = gtk_cell_renderer_text_new();
963     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(EngineCombo),r,TRUE);
964     gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(EngineCombo),r,"markup",ENGINE_COL_MARKUP);
965     engine_scan_dir(local_engine_dir);
966     g_free(local_engine_dir);
967     engine_scan_dir(ENGINE_DIR);
968 
969     register_setting(EngineCombo,ST_ENGINE_COMBO,"engine","engine");
970 }
build_notebook_page(gchar * title,GtkWidget * notebook)971 GtkWidget * build_notebook_page(gchar * title, GtkWidget * notebook)
972 {
973     GtkWidget * vbox;
974     vbox = gtk_vbox_new(FALSE,2);
975     gtk_container_set_border_widthC(vbox,8);
976     gtk_notebook_append_page(GTK_NOTEBOOK(notebook),vbox,
977             gtk_label_new(title));
978     return vbox;
979 }
980 
981