1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2001      Mikael Hallendal <micke@imendio.com>
4  * Copyright (C) 2004,2008 Imendio AB
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 #include "config.h"
23 #include <string.h>
24 #include <stdlib.h>
25 #include <gtk/gtk.h>
26 #ifdef GDK_WINDOWING_QUARTZ
27 #include <CoreFoundation/CoreFoundation.h>
28 #endif
29 #include "ige-conf.h"
30 #include "dh-util.h"
31 
32 static GList *views;
33 
34 static GtkBuilder *
get_builder_file(const gchar * filename,const gchar * root,const gchar * domain,const gchar * first_required_widget,va_list args)35 get_builder_file (const gchar *filename,
36                   const gchar *root,
37                   const gchar *domain,
38                   const gchar *first_required_widget,
39                   va_list args)
40 {
41         GtkBuilder  *builder;
42         const char  *name;
43         GObject    **object_ptr;
44 
45         builder = gtk_builder_new ();
46         if (!gtk_builder_add_from_file (builder, filename, NULL)) {
47                 g_warning ("Couldn't find necessary UI file '%s'", filename);
48                 g_object_unref (builder);
49                 return NULL;
50         }
51 
52         for (name = first_required_widget; name; name = va_arg (args, char *)) {
53                 object_ptr = va_arg (args, void *);
54                 *object_ptr = gtk_builder_get_object (builder, name);
55 
56                 if (!*object_ptr) {
57                         g_warning ("UI file '%s' is missing widget '%s'.",
58                                    filename, name);
59                         continue;
60                 }
61         }
62 
63         return builder;
64 }
65 
66 GtkBuilder *
dh_util_builder_get_file(const gchar * filename,const gchar * root,const gchar * domain,const gchar * first_required_widget,...)67 dh_util_builder_get_file (const gchar *filename,
68                           const gchar *root,
69                           const gchar *domain,
70                           const gchar *first_required_widget,
71                           ...)
72 {
73         va_list     args;
74         GtkBuilder *builder;
75 
76         va_start (args, first_required_widget);
77         builder = get_builder_file (filename,
78                                     root,
79                                     domain,
80                                     first_required_widget,
81                                     args);
82         va_end (args);
83 
84         return builder;
85 }
86 
87 void
dh_util_builder_connect(GtkBuilder * builder,gpointer user_data,gchar * first_widget,...)88 dh_util_builder_connect (GtkBuilder *builder,
89                          gpointer    user_data,
90                          gchar     *first_widget,
91                          ...)
92 {
93         va_list      args;
94         const gchar *name;
95         const gchar *signal;
96         GObject     *object;
97         gpointer    *callback;
98 
99         va_start (args, first_widget);
100 
101         for (name = first_widget; name; name = va_arg (args, char *)) {
102                 signal = va_arg (args, void *);
103                 callback = va_arg (args, void *);
104 
105                 object = gtk_builder_get_object (builder, name);
106                 if (!object) {
107                         g_warning ("UI file is missing widget '%s', aborting",
108                                    name);
109                         continue;
110                 }
111 
112                 g_signal_connect (object,
113                                   signal,
114                                   G_CALLBACK (callback),
115                                   user_data);
116         }
117 
118         va_end (args);
119 }
120 
121 #ifdef GDK_WINDOWING_QUARTZ
122 static gchar *
cf_string_to_utf8(CFStringRef str)123 cf_string_to_utf8 (CFStringRef str)
124 {
125   CFIndex  len;
126   gchar   *ret;
127 
128   len = CFStringGetMaximumSizeForEncoding (CFStringGetLength (str),
129                                            kCFStringEncodingUTF8) + 1;
130 
131   ret = g_malloc (len);
132   ret[len] = '\0';
133 
134   if (CFStringGetCString (str, ret, len, kCFStringEncodingUTF8))
135     return ret;
136 
137   g_free (ret);
138   return NULL;
139 }
140 
141 static gchar *
util_get_mac_data_dir(void)142 util_get_mac_data_dir (void)
143 {
144         const gchar *env;
145         CFBundleRef  cf_bundle;
146         UInt32       type;
147         UInt32       creator;
148         CFURLRef     cf_url;
149         CFStringRef  cf_string;
150         gchar       *ret, *tmp;
151 
152         /* The environment variable overrides all. */
153         env = g_getenv ("DEVHELP_DATADIR");
154         if (env) {
155                 return g_strdup (env);
156         }
157 
158         cf_bundle = CFBundleGetMainBundle ();
159         if (!cf_bundle) {
160                 return NULL;
161         }
162 
163         /* Only point into the bundle if it's an application. */
164         CFBundleGetPackageInfo (cf_bundle, &type, &creator);
165         if (type != 'APPL') {
166                 return NULL;
167         }
168 
169         cf_url = CFBundleCopyBundleURL (cf_bundle);
170         cf_string = CFURLCopyFileSystemPath (cf_url, kCFURLPOSIXPathStyle);
171         ret = cf_string_to_utf8 (cf_string);
172         CFRelease (cf_string);
173         CFRelease (cf_url);
174 
175         tmp = g_build_filename (ret, "Contents", "Resources", NULL);
176         g_free (ret);
177 
178         return tmp;
179 }
180 #endif
181 
182 gchar *
dh_util_build_data_filename(const gchar * first_part,...)183 dh_util_build_data_filename (const gchar *first_part,
184                              ...)
185 {
186         gchar        *datadir = NULL;
187         va_list       args;
188         const gchar  *part;
189         gchar       **strv;
190         gint          i;
191         gchar        *ret;
192 
193         va_start (args, first_part);
194 
195 #ifdef GDK_WINDOWING_QUARTZ
196         datadir = util_get_mac_data_dir ();
197 #endif
198 
199         if (datadir == NULL) {
200                 datadir = g_strdup (DATADIR);
201         }
202 
203         /* 2 = 1 initial component + terminating NULL element. */
204         strv = g_malloc (sizeof (gchar *) * 2);
205         strv[0] = (gchar *) datadir;
206 
207         i = 1;
208         for (part = first_part; part; part = va_arg (args, char *), i++) {
209                 /* +2 = 1 new element + terminating NULL element. */
210                 strv = g_realloc (strv, sizeof (gchar*) * (i + 2));
211                 strv[i] = (gchar *) part;
212         }
213 
214         strv[i] = NULL;
215         ret = g_build_filenamev (strv);
216         g_free (strv);
217 
218         g_free (datadir);
219 
220         va_end (args);
221 
222         return ret;
223 }
224 
225 typedef struct {
226         gchar *name;
227         guint  timeout_id;
228 } DhUtilStateItem;
229 
230 static void
util_state_item_free(DhUtilStateItem * item)231 util_state_item_free (DhUtilStateItem *item)
232 {
233         g_free (item->name);
234         if (item->timeout_id) {
235                 g_source_remove (item->timeout_id);
236         }
237         g_slice_free (DhUtilStateItem, item);
238 }
239 
240 static void
util_state_setup_widget(GtkWidget * widget,const gchar * name)241 util_state_setup_widget (GtkWidget   *widget,
242                          const gchar *name)
243 {
244         DhUtilStateItem *item;
245 
246         item = g_slice_new0 (DhUtilStateItem);
247         item->name = g_strdup (name);
248 
249         g_object_set_data_full (G_OBJECT (widget),
250                                 "dh-util-state",
251                                 item,
252                                 (GDestroyNotify) util_state_item_free);
253 }
254 
255 static gchar *
util_state_get_key(const gchar * name,const gchar * key)256 util_state_get_key (const gchar *name,
257                     const gchar *key)
258 {
259         return g_strdup_printf ("/apps/devhelp/state/%s/%s", name, key);
260 }
261 
262 static void
util_state_schedule_save(GtkWidget * widget,GSourceFunc func)263 util_state_schedule_save (GtkWidget   *widget,
264                           GSourceFunc  func)
265 
266 {
267         DhUtilStateItem *item;
268 
269         item = g_object_get_data (G_OBJECT (widget), "dh-util-state");
270         if (item->timeout_id) {
271 		g_source_remove (item->timeout_id);
272 	}
273 
274 	item->timeout_id = g_timeout_add (500,
275                                           func,
276                                           widget);
277 }
278 
279 static void
util_state_save_window(GtkWindow * window,const gchar * name)280 util_state_save_window (GtkWindow   *window,
281                         const gchar *name)
282 {
283         gchar          *key;
284         GdkWindowState  state;
285         gboolean        maximized;
286         gint            width, height;
287         gint            x, y;
288 
289 #if GTK_CHECK_VERSION (2,14,0)
290         state = gdk_window_get_state (gtk_widget_get_window (GTK_WIDGET (window)));
291 #else
292         state = gdk_window_get_state (GTK_WIDGET (window)->window);
293 #endif
294         if (state & GDK_WINDOW_STATE_MAXIMIZED) {
295                 maximized = TRUE;
296         } else {
297                 maximized = FALSE;
298         }
299 
300         key = util_state_get_key (name, "maximized");
301         ige_conf_set_bool (ige_conf_get (), key, maximized);
302         g_free (key);
303 
304         /* If maximized don't save the size and position. */
305         if (maximized) {
306                 return;
307         }
308 
309         gtk_window_get_size (GTK_WINDOW (window), &width, &height);
310 
311         key = util_state_get_key (name, "width");
312         ige_conf_set_int (ige_conf_get (), key, width);
313         g_free (key);
314 
315         key = util_state_get_key (name, "height");
316         ige_conf_set_int (ige_conf_get (), key, height);
317         g_free (key);
318 
319         gtk_window_get_position (GTK_WINDOW (window), &x, &y);
320 
321         key = util_state_get_key (name, "x_position");
322         ige_conf_set_int (ige_conf_get (), key, x);
323         g_free (key);
324 
325         key = util_state_get_key (name, "y_position");
326         ige_conf_set_int (ige_conf_get (), key, y);
327         g_free (key);
328 }
329 
330 static void
util_state_restore_window(GtkWindow * window,const gchar * name)331 util_state_restore_window (GtkWindow   *window,
332                            const gchar *name)
333 {
334         gchar     *key;
335         gboolean   maximized;
336         gint       width, height;
337         gint       x, y;
338         GdkScreen *screen;
339         gint       max_width, max_height;
340 
341         key = util_state_get_key (name, "width");
342         ige_conf_get_int (ige_conf_get (), key, &width);
343         g_free (key);
344 
345         key = util_state_get_key (name, "height");
346         ige_conf_get_int (ige_conf_get (), key, &height);
347         g_free (key);
348 
349         key = util_state_get_key (name, "x_position");
350         ige_conf_get_int (ige_conf_get (), key, &x);
351         g_free (key);
352 
353         key = util_state_get_key (name, "y_position");
354         ige_conf_get_int (ige_conf_get (), key, &y);
355         g_free (key);
356 
357         if (width > 1 && height > 1) {
358                 screen = gtk_widget_get_screen (GTK_WIDGET (window));
359                 max_width = gdk_screen_get_width (screen);
360                 max_height = gdk_screen_get_height (screen);
361 
362                 width = CLAMP (width, 0, max_width);
363                 height = CLAMP (height, 0, max_height);
364 
365                 x = CLAMP (x, 0, max_width - width);
366                 y = CLAMP (y, 0, max_height - height);
367 
368                 gtk_window_set_default_size (window, width, height);
369         }
370 
371         gtk_window_move (window, x, y);
372 
373         key = util_state_get_key (name, "maximized");
374         ige_conf_get_bool (ige_conf_get (), key, &maximized);
375         g_free (key);
376 
377         if (maximized) {
378                 gtk_window_maximize (window);
379         }
380 }
381 
382 static gboolean
util_state_window_timeout_cb(gpointer window)383 util_state_window_timeout_cb (gpointer window)
384 {
385         DhUtilStateItem *item;
386 
387         item = g_object_get_data (window, "dh-util-state");
388         if (item) {
389                 item->timeout_id = 0;
390                 util_state_save_window (window, item->name);
391         }
392 
393 	return FALSE;
394 }
395 
396 static gboolean
util_state_window_configure_event_cb(GtkWidget * window,GdkEventConfigure * event,gpointer user_data)397 util_state_window_configure_event_cb (GtkWidget         *window,
398                                       GdkEventConfigure *event,
399                                       gpointer           user_data)
400 {
401 	util_state_schedule_save (window, util_state_window_timeout_cb);
402 	return FALSE;
403 }
404 
405 static gboolean
util_state_paned_timeout_cb(gpointer paned)406 util_state_paned_timeout_cb (gpointer paned)
407 {
408         DhUtilStateItem *item;
409 
410         item = g_object_get_data (paned, "dh-util-state");
411         if (item) {
412                 gchar *key;
413 
414                 item->timeout_id = 0;
415 
416                 key = util_state_get_key (item->name, "position");
417                 ige_conf_set_int (ige_conf_get (),
418                                   key,
419                                   gtk_paned_get_position (paned));
420                 g_free (key);
421         }
422 
423 	return FALSE;
424 }
425 
426 static gboolean
util_state_paned_changed_cb(GtkWidget * paned,gpointer user_data)427 util_state_paned_changed_cb (GtkWidget *paned,
428                              gpointer   user_data)
429 {
430 	util_state_schedule_save (paned, util_state_paned_timeout_cb);
431 	return FALSE;
432 }
433 
434 void
dh_util_state_manage_window(GtkWindow * window,const gchar * name)435 dh_util_state_manage_window (GtkWindow   *window,
436                              const gchar *name)
437 {
438         util_state_setup_widget (GTK_WIDGET (window), name);
439 
440         g_signal_connect (window, "configure-event",
441                           G_CALLBACK (util_state_window_configure_event_cb),
442                           NULL);
443 
444         util_state_restore_window (window, name);
445 }
446 
447 void
dh_util_state_manage_paned(GtkPaned * paned,const gchar * name)448 dh_util_state_manage_paned (GtkPaned    *paned,
449                             const gchar *name)
450 {
451         gchar *key;
452         gint   position;
453 
454         util_state_setup_widget (GTK_WIDGET (paned), name);
455 
456         key = util_state_get_key (name, "position");
457         if (ige_conf_get_int (ige_conf_get (), key, &position)) {
458                 gtk_paned_set_position (paned, position);
459         }
460         g_free (key);
461 
462         g_signal_connect (paned, "notify::position",
463                           G_CALLBACK (util_state_paned_changed_cb),
464                           NULL);
465 }
466 
467 GSList *
dh_util_state_load_books_disabled(void)468 dh_util_state_load_books_disabled (void)
469 {
470         gchar *key;
471         GSList *books_disabled = NULL;
472 
473         key = util_state_get_key ("main/contents", "books_disabled");
474         ige_conf_get_string_list (ige_conf_get (), key, &books_disabled);
475         g_free(key);
476 
477         return books_disabled;
478 }
479 
480 void
dh_util_state_store_books_disabled(GSList * books_disabled)481 dh_util_state_store_books_disabled (GSList *books_disabled)
482 {
483         gchar *key;
484 
485         key = util_state_get_key ("main/contents", "books_disabled");
486         ige_conf_set_string_list (ige_conf_get (), key, books_disabled);
487         g_free(key);
488 }
489 
490 static gboolean
util_state_notebook_timeout_cb(gpointer notebook)491 util_state_notebook_timeout_cb (gpointer notebook)
492 {
493         DhUtilStateItem *item;
494 
495         item = g_object_get_data (notebook, "dh-util-state");
496         if (item) {
497                 GtkWidget   *page;
498                 const gchar *page_name;
499 
500                 item->timeout_id = 0;
501 
502                 page = gtk_notebook_get_nth_page (
503                         notebook,
504                         gtk_notebook_get_current_page (notebook));
505                 page_name = dh_util_state_get_notebook_page_name (page);
506                 if (page_name) {
507                         gchar *key;
508 
509                         key = util_state_get_key (item->name, "selected_tab");
510                         ige_conf_set_string (ige_conf_get (), key, page_name);
511                         g_free (key);
512                 }
513         }
514 
515 	return FALSE;
516 }
517 
518 static void
util_state_notebook_switch_page_cb(GtkWidget * notebook,gpointer page,guint page_num,gpointer user_data)519 util_state_notebook_switch_page_cb (GtkWidget       *notebook,
520                                     gpointer         page,
521                                     guint            page_num,
522                                     gpointer         user_data)
523 {
524 	util_state_schedule_save (notebook, util_state_notebook_timeout_cb);
525 }
526 
527 void
dh_util_state_set_notebook_page_name(GtkWidget * page,const gchar * page_name)528 dh_util_state_set_notebook_page_name (GtkWidget   *page,
529                                       const gchar *page_name)
530 {
531         g_object_set_data_full (G_OBJECT (page),
532                                 "dh-util-state-tab-name",
533                                 g_strdup (page_name),
534                                 g_free);
535 }
536 
537 const gchar *
dh_util_state_get_notebook_page_name(GtkWidget * page)538 dh_util_state_get_notebook_page_name (GtkWidget *page)
539 {
540         return g_object_get_data (G_OBJECT (page),
541                                   "dh-util-state-tab-name");
542 }
543 
544 void
dh_util_state_manage_notebook(GtkNotebook * notebook,const gchar * name,const gchar * default_tab)545 dh_util_state_manage_notebook (GtkNotebook *notebook,
546                                const gchar *name,
547                                const gchar *default_tab)
548 {
549         gchar     *key;
550         gchar     *tab;
551         gint       i;
552 
553         util_state_setup_widget (GTK_WIDGET (notebook), name);
554 
555         key = util_state_get_key (name, "selected_tab");
556         if (!ige_conf_get_string (ige_conf_get (), key, &tab)) {
557                 tab = g_strdup (default_tab);
558         }
559         g_free (key);
560 
561         for (i = 0; i < gtk_notebook_get_n_pages (notebook); i++) {
562                 GtkWidget   *page;
563                 const gchar *page_name;
564 
565                 page = gtk_notebook_get_nth_page (notebook, i);
566                 page_name = dh_util_state_get_notebook_page_name (page);
567                 if (page_name && strcmp (page_name, tab) == 0) {
568                         gtk_notebook_set_current_page (notebook, i);
569                         gtk_widget_grab_focus (page);
570                         break;
571                 }
572         }
573 
574         g_free (tab);
575 
576         g_signal_connect (notebook, "switch-page",
577                           G_CALLBACK (util_state_notebook_switch_page_cb),
578                           NULL);
579 }
580 
581 static gboolean
split_font_string(const gchar * name_and_size,gchar ** name,gdouble * size)582 split_font_string (const gchar  *name_and_size,
583                    gchar       **name,
584                    gdouble      *size)
585 {
586 	PangoFontDescription *desc;
587 	PangoFontMask         mask;
588 	gboolean              retval = FALSE;
589 
590 	desc = pango_font_description_from_string (name_and_size);
591 	if (!desc) {
592 		return FALSE;
593 	}
594 
595 	mask = (PANGO_FONT_MASK_FAMILY | PANGO_FONT_MASK_SIZE);
596         if ((pango_font_description_get_set_fields (desc) & mask) == mask) {
597 		*size = PANGO_PIXELS (pango_font_description_get_size (desc));
598 		*name = g_strdup (pango_font_description_get_family (desc));
599 		retval = TRUE;
600 	}
601 
602 	pango_font_description_free (desc);
603 
604 	return retval;
605 }
606 
607 #define DH_CONF_PATH                  "/apps/devhelp"
608 #define DH_CONF_USE_SYSTEM_FONTS      DH_CONF_PATH "/ui/use_system_fonts"
609 #define DH_CONF_VARIABLE_FONT         DH_CONF_PATH "/ui/variable_font"
610 #define DH_CONF_FIXED_FONT            DH_CONF_PATH "/ui/fixed_font"
611 #define DH_CONF_SYSTEM_VARIABLE_FONT  "/desktop/gnome/interface/font_name"
612 #define DH_CONF_SYSTEM_FIXED_FONT     "/desktop/gnome/interface/monospace_font_name"
613 
614 void
dh_util_font_get_variable(gchar ** name,gdouble * size,gboolean use_system_fonts)615 dh_util_font_get_variable (gchar    **name,
616                            gdouble   *size,
617                            gboolean   use_system_fonts)
618 {
619 	IgeConf *conf;
620 	gchar   *name_and_size;
621 
622 	conf = ige_conf_get ();
623 
624 	if (use_system_fonts) {
625 #ifdef GDK_WINDOWING_QUARTZ
626                 name_and_size = g_strdup ("Lucida Grande 14");
627 #else
628 		ige_conf_get_string (conf,
629                                      DH_CONF_SYSTEM_VARIABLE_FONT,
630                                      &name_and_size);
631 #endif
632 	} else {
633 		ige_conf_get_string (conf,
634                                      DH_CONF_VARIABLE_FONT,
635                                      &name_and_size);
636 	}
637 
638         if (!split_font_string (name_and_size, name, size)) {
639                 *name = g_strdup ("sans");
640                 *size = 12;
641         }
642 
643         g_free (name_and_size);
644 }
645 
646 void
dh_util_font_get_fixed(gchar ** name,gdouble * size,gboolean use_system_fonts)647 dh_util_font_get_fixed (gchar    **name,
648                         gdouble   *size,
649                         gboolean   use_system_fonts)
650 {
651 	IgeConf *conf;
652 	gchar   *name_and_size;
653 
654 	conf = ige_conf_get ();
655 
656 	if (use_system_fonts) {
657 #ifdef GDK_WINDOWING_QUARTZ
658                 name_and_size = g_strdup ("Monaco 14");
659 #else
660 		ige_conf_get_string (conf,
661                                      DH_CONF_SYSTEM_FIXED_FONT,
662                                      &name_and_size);
663 #endif
664 	} else {
665 		ige_conf_get_string (conf,
666                                      DH_CONF_FIXED_FONT,
667                                      &name_and_size);
668 	}
669 
670         if (!split_font_string (name_and_size, name, size)) {
671                 *name = g_strdup ("monospace");
672                 *size = 12;
673         }
674 
675         g_free (name_and_size);
676 }
677 
678 static void
view_destroy_cb(GtkWidget * view,gpointer user_data)679 view_destroy_cb (GtkWidget *view,
680                  gpointer   user_data)
681 {
682         views = g_list_remove (views, view);
683 }
684 
685 static void
view_setup_fonts(WebKitWebView * view)686 view_setup_fonts (WebKitWebView *view)
687 {
688         IgeConf           *conf;
689         WebKitWebSettings *settings;
690         gboolean           use_system_fonts;
691 	gchar             *variable_name;
692 	gdouble            variable_size;
693 	gchar             *fixed_name;
694 	gdouble            fixed_size;
695 
696         conf = ige_conf_get ();
697 
698         settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
699 
700 	ige_conf_get_bool (conf,
701                            DH_CONF_USE_SYSTEM_FONTS,
702                            &use_system_fonts);
703 
704         dh_util_font_get_variable (&variable_name, &variable_size,
705                                    use_system_fonts);
706         dh_util_font_get_fixed (&fixed_name, &fixed_size,
707                                    use_system_fonts);
708 
709         g_object_set (settings,
710                       "monospace-font-family", fixed_name,
711                       "default-monospace-font-size", (guint) fixed_size,
712                       "sans-serif-font-family", variable_name,
713                       "serif-font-family", variable_name,
714                       "default-font-size", (guint) variable_size,
715                       NULL);
716 
717         g_free (variable_name);
718         g_free (fixed_name);
719 }
720 
721 static void
font_notify_cb(IgeConf * conf,const gchar * path,gpointer user_data)722 font_notify_cb (IgeConf     *conf,
723                 const gchar *path,
724                 gpointer     user_data)
725 {
726         GList *l;
727 
728         for (l = views; l; l = l->next) {
729                 view_setup_fonts (l->data);
730         }
731 }
732 
733 void
dh_util_font_add_web_view(WebKitWebView * view)734 dh_util_font_add_web_view (WebKitWebView *view)
735 {
736         static gboolean setup;
737 
738         if (!setup) {
739                 IgeConf *conf;
740 
741                 conf = ige_conf_get ();
742 
743 		ige_conf_notify_add (conf,
744                                      DH_CONF_USE_SYSTEM_FONTS,
745                                      font_notify_cb,
746                                      NULL);
747 		ige_conf_notify_add (conf,
748                                      DH_CONF_SYSTEM_VARIABLE_FONT,
749                                      font_notify_cb,
750                                      NULL);
751 		ige_conf_notify_add (conf,
752                                      DH_CONF_SYSTEM_FIXED_FONT,
753                                      font_notify_cb,
754                                      NULL);
755 		ige_conf_notify_add (conf,
756                                      DH_CONF_VARIABLE_FONT,
757                                      font_notify_cb,
758                                      NULL);
759 		ige_conf_notify_add (conf,
760                                      DH_CONF_FIXED_FONT,
761                                      font_notify_cb,
762                                      NULL);
763 
764                 setup = TRUE;
765         }
766 
767         views = g_list_prepend (views, view);
768 
769         g_signal_connect (view, "destroy",
770                           G_CALLBACK (view_destroy_cb),
771                           NULL);
772 
773         view_setup_fonts (view);
774 }
775 
776 gint
dh_util_cmp_book(DhLink * a,DhLink * b)777 dh_util_cmp_book (DhLink *a, DhLink *b)
778 {
779         const gchar *name_a;
780         const gchar *name_b;
781         gchar       *name_a_casefold;
782         gchar       *name_b_casefold;
783         int          rc;
784 
785         name_a = dh_link_get_name (a);
786         if (!name_a) {
787                 name_a = "";
788         }
789 
790         name_b = dh_link_get_name (b);
791         if (!name_b) {
792                 name_b = "";
793         }
794 
795         if (g_ascii_strncasecmp (name_a, "the ", 4) == 0) {
796                 name_a += 4;
797         }
798         if (g_ascii_strncasecmp (name_b, "the ", 4) == 0) {
799                 name_b += 4;
800         }
801 
802         name_a_casefold = g_utf8_casefold (name_a, -1);
803         name_b_casefold = g_utf8_casefold (name_b, -1);
804 
805         rc = strcmp (name_a_casefold, name_b_casefold);
806 
807         g_free (name_a_casefold);
808         g_free (name_b_casefold);
809 
810         return rc;
811 }
812 
813