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