1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * Copyright (C) 2004-2020 Shaun McCance <shaunm@gnome.org>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
17  *
18  * Author: Shaun McCance <shaunm@gnome.org>
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <stdarg.h>
26 
27 #include <gtk/gtk.h>
28 #include <glib/gi18n.h>
29 
30 #include "yelp-settings.h"
31 
32 struct _YelpSettingsPrivate {
33     GMutex        mutex;
34 
35     gchar         colors[YELP_SETTINGS_NUM_COLORS][8];
36     gchar        *setfonts[YELP_SETTINGS_NUM_FONTS];
37     gchar        *fonts[YELP_SETTINGS_NUM_FONTS];
38     gchar        *icons[YELP_SETTINGS_NUM_ICONS];
39     gint          icon_size;
40 
41     GtkSettings  *gtk_settings;
42     GtkIconTheme *gtk_icon_theme;
43 
44     gint          font_adjustment;
45 
46     gulong        gtk_theme_changed;
47     gulong        gtk_font_changed;
48     gulong        icon_theme_changed;
49 
50     gboolean      show_text_cursor;
51 
52     gboolean      editor_mode;
53 
54     GHashTable   *tokens;
55 };
56 
57 enum {
58     COLORS_CHANGED,
59     FONTS_CHANGED,
60     ICONS_CHANGED,
61     LAST_SIGNAL
62 };
63 static guint settings_signals[LAST_SIGNAL] = {0,};
64 
65 enum {
66   PROP_0,
67   PROP_GTK_SETTINGS,
68   PROP_GTK_ICON_THEME,
69   PROP_FONT_ADJUSTMENT,
70   PROP_SHOW_TEXT_CURSOR,
71   PROP_EDITOR_MODE
72 };
73 
74 static const gchar *icon_names[YELP_SETTINGS_NUM_ICONS];
75 
76 G_DEFINE_TYPE_WITH_PRIVATE (YelpSettings, yelp_settings, G_TYPE_OBJECT)
77 
78 static void           yelp_settings_constructed  (GObject              *object);
79 static void           yelp_settings_finalize     (GObject              *object);
80 static void           yelp_settings_get_property (GObject              *object,
81 						  guint                 prop_id,
82 						  GValue               *value,
83 						  GParamSpec           *pspec);
84 static void           yelp_settings_set_property (GObject              *object,
85 						  guint                 prop_id,
86 						  const GValue         *value,
87 						  GParamSpec           *pspec);
88 static void           yelp_settings_set_if_token (YelpSettings         *settings,
89                                                   const gchar          *token);
90 
91 static void           gtk_theme_changed          (GtkSettings          *gtk_settings,
92 						  GParamSpec           *pspec,
93 					          YelpSettings         *settings);
94 static void           gtk_font_changed           (GtkSettings          *gtk_settings,
95 						  GParamSpec           *pspec,
96 					          YelpSettings         *settings);
97 static void           icon_theme_changed         (GtkIconTheme         *theme,
98 						  YelpSettings         *settings);
99 
100 static void           rgb_to_hsv                 (GdkRGBA  color,
101 						  gdouble *h,
102 						  gdouble *s,
103 						  gdouble *v);
104 static void           hsv_to_hex                 (gdouble  h,
105 						  gdouble  s,
106 						  gdouble  v,
107 						  gchar    *str);
108 
109 /******************************************************************************/
110 
111 static void
yelp_settings_class_init(YelpSettingsClass * klass)112 yelp_settings_class_init (YelpSettingsClass *klass)
113 {
114     GObjectClass *object_class = G_OBJECT_CLASS (klass);
115     gint i;
116 
117     object_class->constructed  = yelp_settings_constructed;
118     object_class->finalize = yelp_settings_finalize;
119     object_class->get_property = yelp_settings_get_property;
120     object_class->set_property = yelp_settings_set_property;
121 
122     for (i = 0; i < YELP_SETTINGS_NUM_ICONS; i++) {
123 	switch (i) {
124 	case YELP_SETTINGS_ICON_BUG:
125 	    icon_names[i] = "yelp-note-bug";
126 	    break;
127 	case YELP_SETTINGS_ICON_IMPORTANT:
128 	    icon_names[i] = "yelp-note-important";
129 	    break;
130 	case YELP_SETTINGS_ICON_NOTE:
131 	    icon_names[i] = "yelp-note";
132 	    break;
133 	case YELP_SETTINGS_ICON_TIP:
134 	    icon_names[i] = "yelp-note-tip";
135 	    break;
136 	case YELP_SETTINGS_ICON_WARNING:
137 	    icon_names[i] = "yelp-note-warning";
138 	    break;
139 	default:
140 	    g_assert_not_reached ();
141 	}
142     }
143 
144     g_object_class_install_property (object_class,
145                                      PROP_GTK_SETTINGS,
146                                      g_param_spec_object ("gtk-settings",
147 							  "GtkSettings",
148 							  "A GtkSettings object to get settings from",
149 							  GTK_TYPE_SETTINGS,
150 							  G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
151 							  G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
152 
153     g_object_class_install_property (object_class,
154                                      PROP_GTK_ICON_THEME,
155                                      g_param_spec_object ("gtk-icon-theme",
156 							  "GtkIconTheme",
157 							  "A GtkIconTheme object to get icons from",
158 							  GTK_TYPE_ICON_THEME,
159 							  G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
160 							  G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
161 
162     g_object_class_install_property (object_class,
163                                      PROP_FONT_ADJUSTMENT,
164                                      g_param_spec_int ("font-adjustment",
165                                                        "Font Adjustment",
166                                                        "A size adjustment to add to font sizes",
167                                                        -3, 10, 0,
168                                                        G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
169                                                        G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
170 
171     g_object_class_install_property (object_class,
172                                      PROP_SHOW_TEXT_CURSOR,
173                                      g_param_spec_boolean ("show-text-cursor",
174                                                            "Show Text Cursor",
175                                                            "Show the text cursor or caret for accessible navigation",
176                                                            FALSE,
177                                                            G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
178                                                            G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
179 
180     g_object_class_install_property (object_class,
181                                      PROP_EDITOR_MODE,
182                                      g_param_spec_boolean ("editor-mode",
183                                                            "Editor Mode",
184                                                            "Enable features useful to editors",
185                                                            FALSE,
186                                                            G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
187                                                            G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
188 
189     settings_signals[COLORS_CHANGED] =
190 	g_signal_new ("colors-changed",
191 		      G_OBJECT_CLASS_TYPE (klass),
192 		      G_SIGNAL_RUN_LAST,
193 		      0, NULL, NULL,
194 		      g_cclosure_marshal_VOID__VOID,
195 		      G_TYPE_NONE, 0);
196 
197     settings_signals[FONTS_CHANGED] =
198 	g_signal_new ("fonts-changed",
199 		      G_OBJECT_CLASS_TYPE (klass),
200 		      G_SIGNAL_RUN_LAST,
201 		      0, NULL, NULL,
202 		      g_cclosure_marshal_VOID__VOID,
203 		      G_TYPE_NONE, 0);
204 
205     settings_signals[ICONS_CHANGED] =
206 	g_signal_new ("icons-changed",
207 		      G_OBJECT_CLASS_TYPE (klass),
208 		      G_SIGNAL_RUN_LAST,
209 		      0, NULL, NULL,
210 		      g_cclosure_marshal_VOID__VOID,
211 		      G_TYPE_NONE, 0);
212 }
213 
214 static void
yelp_settings_init(YelpSettings * settings)215 yelp_settings_init (YelpSettings *settings)
216 {
217     gint i;
218 
219     settings->priv = yelp_settings_get_instance_private (settings);
220     g_mutex_init (&settings->priv->mutex);
221     settings->priv->icon_size = 24;
222 
223     for (i = 0; i < YELP_SETTINGS_NUM_ICONS; i++)
224 	settings->priv->icons[i] = NULL;
225     for (i = 0; i < YELP_SETTINGS_NUM_FONTS; i++) {
226 	settings->priv->setfonts[i] = NULL;
227 	settings->priv->fonts[i] = NULL;
228     }
229 
230     settings->priv->tokens = g_hash_table_new_full (g_str_hash, g_str_equal,
231                                                     g_free, NULL);
232 }
233 
234 static void
yelp_settings_constructed(GObject * object)235 yelp_settings_constructed (GObject *object)
236 {
237     YelpSettings *settings = YELP_SETTINGS (object);
238     gboolean skip_dbus_checks = FALSE;
239     gchar *os_release = NULL;
240     const gchar *desktop;
241 
242     yelp_settings_set_if_token (settings, "action:install");
243 
244     g_file_get_contents ("/etc/os-release", &os_release, NULL, NULL);
245     if (os_release == NULL)
246         g_file_get_contents ("/usr/lib/os-release", &os_release, NULL, NULL);
247 
248     if (os_release != NULL) {
249         gint i;
250         gchar **lines = g_strsplit(os_release, "\n", -1);
251         gchar *osid = NULL, *osversion = NULL, *end;
252         g_free (os_release);
253 
254         for (i = 0; lines[i] != NULL; i++) {
255             if (g_str_has_prefix (lines[i], "ID=")) {
256                 if (lines[i][3] == '"') {
257                     end = strchr (lines[i] + 4, '"');
258                     if (end != NULL)
259                         osid = g_strndup (lines[i] + 4, end - lines[i] - 4);
260                 }
261                 else if (lines[i][3] == '\'') {
262                     end = strchr (lines[i] + 4, '\'');
263                     if (end != NULL)
264                         osid = g_strndup (lines[i] + 4, end - lines[i] - 4);
265                 }
266                 else {
267                     osid = g_strdup (lines[i] + 3);
268                 }
269             }
270             else if (g_str_has_prefix (lines[i], "VERSION_ID=")) {
271                 if (lines[i][11] == '"') {
272                     end = strchr (lines[i] + 12, '"');
273                     if (end != NULL)
274                         osversion = g_strndup (lines[i] + 12, end - lines[i] - 12);
275                 }
276                 else if (lines[i][11] == '\'') {
277                     end = strchr (lines[i] + 12, '\'');
278                     if (end != NULL)
279                         osversion = g_strndup (lines[i] + 12, end - lines[i] - 12);
280                 }
281                 else {
282                     osversion = g_strdup (lines[i] + 11);
283                 }
284             }
285         }
286 
287         if (osid) {
288             gchar *token = g_strconcat("platform:", osid, NULL);
289             yelp_settings_set_if_token (settings, token);
290             g_free (token);
291             if (osversion) {
292                 token = g_strconcat("platform:", osid, "-", osversion, NULL);
293                 yelp_settings_set_if_token (settings, token);
294                 g_free (token);
295                 g_free (osversion);
296             }
297             g_free (osid);
298         }
299 
300         g_strfreev(lines);
301     }
302 
303     desktop = g_getenv ("XDG_CURRENT_DESKTOP");
304     if (desktop != NULL) {
305         gchar **desktops = g_strsplit (desktop, ":", -1);
306         gint i;
307         gboolean xdg_gnome = FALSE, xdg_gnome_classic = FALSE;
308         for (i = 0; desktops[i]; i++) {
309             if (!g_ascii_strcasecmp (desktops[i], "gnome")) {
310                 xdg_gnome = TRUE;
311             }
312             else if (!g_ascii_strcasecmp (desktops[i], "gnome-classic")) {
313                 xdg_gnome_classic = TRUE;
314             }
315             else if (!g_ascii_strcasecmp (desktops[i], "kde")) {
316                 yelp_settings_set_if_token (settings, "platform:kde");
317                 skip_dbus_checks = TRUE;
318                 break;
319             }
320             else if (!g_ascii_strcasecmp (desktops[i], "mate")) {
321                 yelp_settings_set_if_token (settings, "platform:mate");
322                 yelp_settings_set_if_token (settings, "platform:gnome-panel");
323                 skip_dbus_checks = TRUE;
324                 break;
325             }
326             else if (!g_ascii_strcasecmp (desktops[i], "pantheon")) {
327                 yelp_settings_set_if_token (settings, "platform:pantheon");
328                 yelp_settings_set_if_token (settings, "platform:gnome-shell");
329                 skip_dbus_checks = TRUE;
330                 break;
331             }
332             else if (!g_ascii_strcasecmp (desktops[i], "unity")) {
333                 yelp_settings_set_if_token (settings, "platform:unity");
334                 skip_dbus_checks = TRUE;
335                 break;
336             }
337             else if (!g_ascii_strcasecmp (desktops[i], "x-cinnamon")) {
338                 yelp_settings_set_if_token (settings, "platform:cinnamon");
339                 yelp_settings_set_if_token (settings, "platform:gnome-shell");
340                 skip_dbus_checks = TRUE;
341                 break;
342             }
343             else if (!g_ascii_strcasecmp (desktops[i], "xfce")) {
344                 yelp_settings_set_if_token (settings, "platform:xfce");
345                 skip_dbus_checks = TRUE;
346                 break;
347             }
348         }
349         if (xdg_gnome) {
350             yelp_settings_set_if_token (settings, "platform:gnome-shell");
351             if (!xdg_gnome_classic)
352                 yelp_settings_set_if_token (settings, "platform:gnome-3");
353             skip_dbus_checks = TRUE;
354         }
355         if (xdg_gnome_classic) {
356             yelp_settings_set_if_token (settings, "platform:gnome-classic");
357             yelp_settings_set_if_token (settings, "platform:gnome-shell");
358             skip_dbus_checks = TRUE;
359         }
360         g_strfreev (desktops);
361     }
362 
363     if (!skip_dbus_checks) {
364         GDBusConnection *connection;
365         GVariant *ret, *names;
366         GVariantIter iter;
367         gchar *name;
368         gboolean env_shell, env_classic, env_panel, env_unity, env_xfce;
369         GError *error = NULL;
370 
371         connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
372         if (connection == NULL) {
373             g_warning ("Unable to connect to dbus: %s", error->message);
374             g_error_free (error);
375             return;
376         }
377 
378         ret = g_dbus_connection_call_sync (connection,
379                                            "org.freedesktop.DBus",
380                                            "/org/freedesktop/DBus",
381                                            "org.freedesktop.DBus",
382                                            "ListNames",
383                                            NULL,
384                                            G_VARIANT_TYPE ("(as)"),
385                                            G_DBUS_CALL_FLAGS_NONE,
386                                            -1, NULL, &error);
387         if (ret == NULL) {
388             g_warning ("Unable to query dbus: %s", error->message);
389             g_error_free (error);
390             return;
391         }
392         env_shell = env_classic = env_panel = env_unity = env_xfce = FALSE;
393         names = g_variant_get_child_value (ret, 0);
394         g_variant_iter_init (&iter, names);
395         while (g_variant_iter_loop (&iter, "&s", &name)) {
396             if (g_str_equal (name, "org.gnome.Panel"))
397                 env_panel = TRUE;
398             else if (g_str_equal (name, "org.gnome.Shell"))
399                 env_shell = TRUE;
400             else if (g_str_equal (name, "com.canonical.Unity"))
401                 env_unity = TRUE;
402             else if (g_str_equal (name, "org.xfce.Panel"))
403                 env_xfce = TRUE;
404         }
405         g_variant_unref (names);
406         g_variant_unref (ret);
407         if (env_shell) {
408             ret = g_dbus_connection_call_sync (connection,
409                                                    "org.gnome.Shell",
410                                                    "/org/gnome/Shell",
411                                                    "org.freedesktop.DBus.Properties",
412                                                    "Get",
413                                                    g_variant_new ("(ss)",
414                                                                   "org.gnome.Shell",
415                                                                   "Mode"),
416                                                    G_VARIANT_TYPE ("(v)"),
417                                                    G_DBUS_CALL_FLAGS_NONE,
418                                                    -1, NULL, &error);
419             if (ret == NULL) {
420                 g_warning ("Failed to get GNOME shell mode: %s", error->message);
421                 g_error_free (error);
422             } else {
423                 GVariant *v;
424                 g_variant_get (ret, "(v)", &v);
425                 if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING) &&
426                     g_str_equal (g_variant_get_string (v, NULL), "classic")) {
427                     env_classic = TRUE;
428                 }
429                 g_variant_unref (v);
430                 g_variant_unref (ret);
431             }
432         }
433 
434         if (env_classic)
435             yelp_settings_set_if_token (settings, "platform:gnome-classic");
436 
437         /* order is important:
438            gnome-shell also provides org.gnome.Panel
439            unity also provides org.gnome.Shell
440          */
441         if (env_unity)
442             yelp_settings_set_if_token (settings, "platform:unity");
443         else if (env_shell)
444             yelp_settings_set_if_token (settings, "platform:gnome-shell");
445         else if (env_xfce)
446             yelp_settings_set_if_token (settings, "platform:xfce");
447         else if (env_panel)
448             yelp_settings_set_if_token (settings, "platform:gnome-panel");
449     }
450 }
451 
452 static void
yelp_settings_finalize(GObject * object)453 yelp_settings_finalize (GObject *object)
454 {
455     YelpSettings *settings = YELP_SETTINGS (object);
456 
457     g_mutex_clear (&settings->priv->mutex);
458 
459     g_hash_table_destroy (settings->priv->tokens);
460 
461     G_OBJECT_CLASS (yelp_settings_parent_class)->finalize (object);
462 }
463 
464 static void
yelp_settings_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)465 yelp_settings_get_property (GObject    *object,
466 			    guint       prop_id,
467 			    GValue     *value,
468 			    GParamSpec *pspec)
469 {
470     YelpSettings *settings = YELP_SETTINGS (object);
471 
472     switch (prop_id) {
473     case PROP_GTK_SETTINGS:
474 	g_value_set_object (value, settings->priv->gtk_settings);
475 	break;
476     case PROP_GTK_ICON_THEME:
477 	g_value_set_object (value, settings->priv->gtk_icon_theme);
478 	break;
479     case PROP_FONT_ADJUSTMENT:
480         g_value_set_int (value, settings->priv->font_adjustment);
481         break;
482     case PROP_SHOW_TEXT_CURSOR:
483         g_value_set_boolean (value, settings->priv->show_text_cursor);
484         break;
485     case PROP_EDITOR_MODE:
486         g_value_set_boolean (value, settings->priv->editor_mode);
487         break;
488     default:
489 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
490 	break;
491     }
492 }
493 
494 static void
yelp_settings_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)495 yelp_settings_set_property (GObject      *object,
496 			    guint         prop_id,
497 			    const GValue *value,
498 			    GParamSpec   *pspec)
499 {
500     YelpSettings *settings = YELP_SETTINGS (object);
501 
502     switch (prop_id) {
503     case PROP_GTK_SETTINGS:
504 	if (settings->priv->gtk_settings) {
505 	    g_signal_handler_disconnect (settings->priv->gtk_settings,
506 					 settings->priv->gtk_theme_changed);
507 	    g_signal_handler_disconnect (settings->priv->gtk_settings,
508 					 settings->priv->gtk_font_changed);
509 	    g_object_unref (settings->priv->gtk_settings);
510 	}
511 	settings->priv->gtk_settings = g_value_get_object (value);
512 	if (settings->priv->gtk_settings != NULL) {
513 	    g_object_ref (settings->priv->gtk_settings);
514 	    settings->priv->gtk_theme_changed =
515 		g_signal_connect (settings->priv->gtk_settings,
516 				  "notify::gtk-theme-name",
517 				  (GCallback) gtk_theme_changed,
518 				  settings);
519 	    settings->priv->gtk_font_changed =
520 		g_signal_connect (settings->priv->gtk_settings,
521 				  "notify::gtk-font-name",
522 				  (GCallback) gtk_font_changed,
523 				  settings);
524 	    gtk_theme_changed (settings->priv->gtk_settings, NULL, settings);
525 	    gtk_font_changed (settings->priv->gtk_settings, NULL, settings);
526 	}
527 	else {
528 	    settings->priv->gtk_theme_changed = 0;
529 	    settings->priv->gtk_font_changed = 0;
530 	}
531 	break;
532     case PROP_GTK_ICON_THEME:
533 	if (settings->priv->gtk_icon_theme) {
534 	    g_signal_handler_disconnect (settings->priv->gtk_icon_theme,
535 					 settings->priv->icon_theme_changed);
536 	    g_object_unref (settings->priv->gtk_icon_theme);
537 	}
538 	settings->priv->gtk_icon_theme = g_value_get_object (value);
539 	if (settings->priv->gtk_icon_theme != NULL) {
540 	    gchar **search_path;
541 	    gint search_path_len, i;
542 	    gboolean append_search_path = TRUE;
543 	    gtk_icon_theme_get_search_path (settings->priv->gtk_icon_theme,
544 					    &search_path, &search_path_len);
545 	    for (i = search_path_len - 1; i >= 0; i--)
546 		if (g_str_equal (search_path[i], YELP_ICON_PATH)) {
547 		    append_search_path = FALSE;
548 		    break;
549 		}
550 	    if (append_search_path)
551 		gtk_icon_theme_append_search_path (settings->priv->gtk_icon_theme,
552 						   YELP_ICON_PATH);
553             append_search_path = TRUE;
554 	    for (i = search_path_len - 1; i >= 0; i--)
555 		if (g_str_equal (search_path[i], DATADIR"/yelp/icons")) {
556 		    append_search_path = FALSE;
557 		    break;
558 		}
559 	    if (append_search_path)
560 		gtk_icon_theme_append_search_path (settings->priv->gtk_icon_theme,
561                                                    DATADIR"/yelp/icons");
562 	    g_strfreev (search_path);
563 	    g_object_ref (settings->priv->gtk_icon_theme);
564 	    settings->priv->icon_theme_changed =
565 		g_signal_connect (settings->priv->gtk_icon_theme,
566 				  "changed",
567 				  (GCallback) icon_theme_changed,
568 				  settings);
569 	    icon_theme_changed (settings->priv->gtk_icon_theme, settings);
570 	}
571 	else {
572 	    settings->priv->icon_theme_changed = 0;
573 	}
574 	break;
575     case PROP_FONT_ADJUSTMENT:
576         settings->priv->font_adjustment = g_value_get_int (value);
577         gtk_font_changed (settings->priv->gtk_settings, NULL, settings);
578         break;
579     case PROP_SHOW_TEXT_CURSOR:
580         settings->priv->show_text_cursor = g_value_get_boolean (value);
581         break;
582     case PROP_EDITOR_MODE:
583         settings->priv->editor_mode = g_value_get_boolean (value);
584         break;
585     default:
586 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
587 	break;
588     }
589 }
590 
591 /******************************************************************************/
592 
593 YelpSettings *
yelp_settings_get_default(void)594 yelp_settings_get_default (void)
595 {
596     static GMutex mutex;
597     static YelpSettings *settings = NULL;
598     g_mutex_lock (&mutex);
599     if (settings == NULL)
600 	settings = g_object_new (YELP_TYPE_SETTINGS,
601 				 "gtk-settings", gtk_settings_get_default (),
602 				 "gtk-icon-theme", gtk_icon_theme_get_default (),
603 				 NULL);
604     g_mutex_unlock (&mutex);
605     return settings;
606 }
607 
608 /******************************************************************************/
609 
610 gchar *
yelp_settings_get_color(YelpSettings * settings,YelpSettingsColor color)611 yelp_settings_get_color (YelpSettings       *settings,
612 			 YelpSettingsColor   color)
613 {
614     gchar *colorstr;
615     g_return_val_if_fail (color < YELP_SETTINGS_NUM_COLORS, NULL);
616 
617     g_mutex_lock (&settings->priv->mutex);
618     colorstr = g_strdup (settings->priv->colors[color]);
619     g_mutex_unlock (&settings->priv->mutex);
620 
621     return colorstr;
622 }
623 
624 gchar **
yelp_settings_get_colors(YelpSettings * settings)625 yelp_settings_get_colors (YelpSettings *settings)
626 {
627     gchar **colors = g_new0 (gchar *, YELP_SETTINGS_NUM_COLORS + 1);
628     gint i;
629     for (i = 0; i < YELP_SETTINGS_NUM_COLORS; i++)
630 	colors[i] = yelp_settings_get_color (settings, i);
631     return colors;
632 }
633 
634 void
yelp_settings_set_colors(YelpSettings * settings,YelpSettingsColor first_color,...)635 yelp_settings_set_colors (YelpSettings      *settings,
636 			  YelpSettingsColor  first_color,
637 			  ...)
638 {
639     YelpSettingsColor color;
640     va_list args;
641 
642     g_mutex_lock (&settings->priv->mutex);
643     va_start (args, first_color);
644 
645     color = first_color;
646     while ((gint) color >= 0) {
647 	gchar *colorstr = va_arg (args, gchar *);
648 	gint i;
649 	for (i = 0; i < 7; i++) {
650 	    settings->priv->colors[color][i] = colorstr[i];
651 	    if (colorstr[i] == '\0')
652 		break;
653 	}
654 	settings->priv->colors[color][7] = '\0';
655 	color = va_arg (args, YelpSettingsColor);
656     }
657 
658     va_end (args);
659     g_mutex_unlock (&settings->priv->mutex);
660 
661     g_signal_emit (settings, settings_signals[COLORS_CHANGED], 0);
662 }
663 
664 const gchar*
yelp_settings_get_color_param(YelpSettingsColor color)665 yelp_settings_get_color_param (YelpSettingsColor color)
666 {
667     static const gchar *params[YELP_SETTINGS_NUM_COLORS] = {
668 	"color.background",
669 	"color.text",
670 	"color.text_light",
671 	"color.link",
672 	"color.link_visted",
673 	"color.gray_background",
674 	"color.dark_background",
675 	"color.gray_border",
676 	"color.blue_background",
677 	"color.blue_border",
678 	"color.red_background",
679 	"color.red_border",
680 	"color.yellow_background",
681 	"color.yellow_border"
682     };
683     g_return_val_if_fail (color < YELP_SETTINGS_NUM_COLORS, NULL);
684     return params[color];
685 }
686 
687 /******************************************************************************/
688 
689 gchar *
yelp_settings_get_font(YelpSettings * settings,YelpSettingsFont font)690 yelp_settings_get_font (YelpSettings     *settings,
691 			YelpSettingsFont  font)
692 {
693     gchar *ret;
694     g_return_val_if_fail (font < YELP_SETTINGS_NUM_FONTS, NULL);
695 
696     g_mutex_lock (&settings->priv->mutex);
697     if (settings->priv->setfonts[font])
698 	ret = g_strdup (settings->priv->setfonts[font]);
699     else
700 	ret = g_strdup (settings->priv->fonts[font]);
701     g_mutex_unlock (&settings->priv->mutex);
702 
703     return ret;
704 }
705 
706 gchar *
yelp_settings_get_font_family(YelpSettings * settings,YelpSettingsFont font)707 yelp_settings_get_font_family (YelpSettings     *settings,
708 			       YelpSettingsFont  font)
709 {
710     const gchar *def = (font == YELP_SETTINGS_FONT_VARIABLE) ? "Sans" : "Monospace";
711     gchar *desc, *ret, *c; /* do not free */
712     g_return_val_if_fail (font < YELP_SETTINGS_NUM_FONTS, NULL);
713 
714     g_mutex_lock (&settings->priv->mutex);
715 
716     if (settings->priv->setfonts[font])
717 	desc = g_strdup (settings->priv->setfonts[font]);
718     else
719 	desc = g_strdup (settings->priv->fonts[font]);
720 
721     if (desc == NULL) {
722 	ret = g_strdup (def);
723 	goto done;
724     }
725 
726     c = strrchr (desc, ' ');
727     if (c == NULL) {
728 	g_warning ("Cannot parse font: %s", desc);
729 	ret = g_strdup (def);
730 	goto done;
731     }
732 
733     ret = g_strndup (desc, c - desc);
734 
735  done:
736     g_mutex_unlock (&settings->priv->mutex);
737     return ret;
738 }
739 
740 gint
yelp_settings_get_font_size(YelpSettings * settings,YelpSettingsFont font)741 yelp_settings_get_font_size (YelpSettings     *settings,
742 			     YelpSettingsFont  font)
743 {
744     gchar *desc, *c; /* do not free */
745     gint ret;
746     g_return_val_if_fail (font < YELP_SETTINGS_NUM_FONTS, 0);
747 
748     g_mutex_lock (&settings->priv->mutex);
749 
750     if (settings->priv->setfonts[font])
751 	desc = g_strdup (settings->priv->setfonts[font]);
752     else
753 	desc = g_strdup (settings->priv->fonts[font]);
754 
755     if (desc == NULL) {
756 	ret = 10;
757 	goto done;
758     }
759 
760     c = strrchr (desc, ' ');
761     if (c == NULL) {
762 	g_warning ("Cannot parse font %s", desc);
763 	ret = 10;
764 	goto done;
765     }
766 
767     ret = g_ascii_strtod (c, NULL);
768 
769  done:
770     g_mutex_unlock (&settings->priv->mutex);
771     ret += settings->priv->font_adjustment;
772     ret = (ret < 5) ? 5 : ret;
773     return ret;
774 }
775 
776 void
yelp_settings_set_fonts(YelpSettings * settings,YelpSettingsFont first_font,...)777 yelp_settings_set_fonts (YelpSettings     *settings,
778 			 YelpSettingsFont  first_font,
779 			 ...)
780 {
781     YelpSettingsFont font;
782     va_list args;
783 
784     g_mutex_lock (&settings->priv->mutex);
785     va_start (args, first_font);
786 
787     font = first_font;
788     while ((gint) font >= 0) {
789 	gchar *fontname = va_arg (args, gchar *);
790 	if (settings->priv->setfonts[font] != NULL)
791 	    g_free (settings->priv->setfonts[font]);
792 	settings->priv->setfonts[font] = g_strdup (fontname);
793 	font = va_arg (args, YelpSettingsFont);
794     }
795 
796     va_end (args);
797     g_mutex_unlock (&settings->priv->mutex);
798 
799     g_signal_emit (settings, settings_signals[FONTS_CHANGED], 0);
800 }
801 
802 gint
yelp_settings_get_font_adjustment(YelpSettings * settings)803 yelp_settings_get_font_adjustment (YelpSettings *settings)
804 {
805     return settings->priv->font_adjustment;
806 }
807 
808 void
yelp_settings_set_font_adjustment(YelpSettings * settings,gint adjustment)809 yelp_settings_set_font_adjustment (YelpSettings *settings,
810                                    gint          adjustment)
811 {
812     g_object_set (settings, "font-adjustment", adjustment, NULL);
813 }
814 
815 /******************************************************************************/
816 
817 gint
yelp_settings_get_icon_size(YelpSettings * settings)818 yelp_settings_get_icon_size (YelpSettings *settings)
819 {
820     return settings->priv->icon_size;
821 }
822 
823 void
yelp_settings_set_icon_size(YelpSettings * settings,gint size)824 yelp_settings_set_icon_size (YelpSettings *settings,
825 			     gint          size)
826 {
827     settings->priv->icon_size = size;
828     if (settings->priv->gtk_icon_theme != NULL)
829 	icon_theme_changed (settings->priv->gtk_icon_theme, settings);
830 }
831 
832 gchar *
yelp_settings_get_icon(YelpSettings * settings,YelpSettingsIcon icon)833 yelp_settings_get_icon (YelpSettings     *settings,
834 			YelpSettingsIcon  icon)
835 {
836     gchar *ret;
837     g_return_val_if_fail (icon < YELP_SETTINGS_NUM_ICONS, NULL);
838 
839     g_mutex_lock (&settings->priv->mutex);
840     ret = g_strdup (settings->priv->icons[icon]);
841     g_mutex_unlock (&settings->priv->mutex);
842 
843     return ret;
844 }
845 
846 void
yelp_settings_set_icons(YelpSettings * settings,YelpSettingsIcon first_icon,...)847 yelp_settings_set_icons (YelpSettings     *settings,
848 			 YelpSettingsIcon  first_icon,
849 			 ...)
850 {
851     YelpSettingsIcon icon;
852     va_list args;
853 
854     g_mutex_lock (&settings->priv->mutex);
855     va_start (args, first_icon);
856 
857     icon = first_icon;
858     while ((gint) icon >= 0) {
859 	gchar *filename = va_arg (args, gchar *);
860 	if (settings->priv->icons[icon] != NULL)
861 	    g_free (settings->priv->icons[icon]);
862 	settings->priv->icons[icon] = g_filename_to_uri (filename, NULL, NULL);
863 	icon = va_arg (args, YelpSettingsIcon);
864     }
865 
866     va_end (args);
867     g_mutex_unlock (&settings->priv->mutex);
868 
869     g_signal_emit (settings, settings_signals[ICONS_CHANGED], 0);
870 }
871 
872 const gchar *
yelp_settings_get_icon_param(YelpSettingsIcon icon)873 yelp_settings_get_icon_param (YelpSettingsIcon icon)
874 {
875     static const gchar *params[YELP_SETTINGS_NUM_ICONS] = {
876 	"icons.note.bug",
877 	"icons.note.important",
878 	"icons.note",
879 	"icons.note.tip",
880 	"icons.note.warning"
881     };
882     g_return_val_if_fail (icon < YELP_SETTINGS_NUM_ICONS, NULL);
883     return params[icon];
884 }
885 
886 /******************************************************************************/
887 
888 gboolean
yelp_settings_get_show_text_cursor(YelpSettings * settings)889 yelp_settings_get_show_text_cursor (YelpSettings *settings)
890 {
891     return settings->priv->show_text_cursor;
892 }
893 
894 void
yelp_settings_set_show_text_cursor(YelpSettings * settings,gboolean show)895 yelp_settings_set_show_text_cursor (YelpSettings *settings,
896                                     gboolean      show)
897 {
898     g_object_set (settings, "show-text-cursor", show, NULL);
899 }
900 
901 gboolean
yelp_settings_get_editor_mode(YelpSettings * settings)902 yelp_settings_get_editor_mode (YelpSettings *settings)
903 {
904     return settings->priv->editor_mode;
905 }
906 
907 void
yelp_settings_set_editor_mode(YelpSettings * settings,gboolean editor_mode)908 yelp_settings_set_editor_mode (YelpSettings *settings,
909                                gboolean      editor_mode)
910 {
911     g_object_set (settings, "editor-mode", editor_mode, NULL);
912 }
913 
914 /******************************************************************************/
915 
916 static void
yelp_settings_set_if_token(YelpSettings * settings,const gchar * token)917 yelp_settings_set_if_token (YelpSettings *settings,
918                             const gchar  *token)
919 {
920     if (g_hash_table_lookup (settings->priv->tokens, token) == NULL) {
921         gchar *ins = g_strdup (token);
922         g_hash_table_insert (settings->priv->tokens, ins, ins);
923     }
924 }
925 
926 /******************************************************************************/
927 
928 gchar **
yelp_settings_get_all_params(YelpSettings * settings,gint extra,gint * end)929 yelp_settings_get_all_params (YelpSettings *settings,
930 			      gint          extra,
931 			      gint         *end)
932 {
933     gchar **params;
934     gint i, ix;
935     GString *malstr, *dbstr;
936     GList *envs, *envi;
937 
938     params = g_new0 (gchar *,
939                      (2*YELP_SETTINGS_NUM_COLORS) + (2*YELP_SETTINGS_NUM_ICONS) + extra + 9);
940 
941     for (i = 0; i < YELP_SETTINGS_NUM_COLORS; i++) {
942         gchar *val;
943         ix = 2 * i;
944         params[ix] = g_strdup (yelp_settings_get_color_param (i));
945         val = yelp_settings_get_color (settings, i);
946         params[ix + 1] = g_strdup_printf ("\"%s\"", val);
947         g_free (val);
948     }
949     for (i = 0; i < YELP_SETTINGS_NUM_ICONS; i++) {
950         gchar *val;
951         ix = 2 * (YELP_SETTINGS_NUM_COLORS + i);
952         params[ix] = g_strdup (yelp_settings_get_icon_param (i));
953         val = yelp_settings_get_icon (settings, i);
954         params[ix + 1] = g_strdup_printf ("\"%s\"", val);
955         g_free (val);
956     }
957     ix = 2 * (YELP_SETTINGS_NUM_COLORS + YELP_SETTINGS_NUM_ICONS);
958     params[ix++] = g_strdup ("icons.size.note");
959     params[ix++] = g_strdup_printf ("%i", yelp_settings_get_icon_size (settings));
960     params[ix++] = g_strdup ("yelp.editor_mode");
961     if (settings->priv->editor_mode)
962         params[ix++] = g_strdup ("true()");
963     else
964         params[ix++] = g_strdup ("false()");
965 
966     malstr = g_string_new ("'");
967     dbstr = g_string_new ("'");
968     envs = g_hash_table_get_keys (settings->priv->tokens);
969     for (envi = envs; envi != NULL; envi = envi->next) {
970         g_string_append_c (malstr, ' ');
971         g_string_append (malstr, (gchar *) envi->data);
972         if (g_str_has_prefix ((gchar *) envi->data, "platform:")) {
973             g_string_append_c (dbstr, ';');
974             g_string_append (dbstr, (gchar *) (envi->data) + 9);
975         }
976     }
977     g_string_append_c (malstr, '\'');
978     g_string_append_c (dbstr, '\'');
979     g_list_free (envs);
980     params[ix++] = g_strdup ("mal.if.custom");
981     params[ix++] = g_string_free (malstr, FALSE);
982     params[ix++] = g_strdup ("db.profile.os");
983     params[ix++] = g_string_free (dbstr, FALSE);
984 
985     params[ix] = NULL;
986 
987     if (end != NULL)
988 	*end = ix;
989     return params;
990 }
991 
992 /******************************************************************************/
993 
994 static void
gtk_theme_changed(GtkSettings * gtk_settings,GParamSpec * pspec,YelpSettings * settings)995 gtk_theme_changed (GtkSettings  *gtk_settings,
996 		   GParamSpec   *pspec,
997 		   YelpSettings *settings)
998 {
999     GtkStyleContext *context, *linkcontext;
1000     GtkWidget *tmpwin, *tmpbox, *tmpview, *tmplink;
1001     GdkRGBA base, text, link;
1002     gdouble    base_h, base_s, base_v;
1003     gdouble    text_h, text_s, text_v;
1004 
1005     g_mutex_lock (&settings->priv->mutex);
1006 
1007     tmpwin = gtk_offscreen_window_new ();
1008     tmpbox = gtk_grid_new ();
1009     tmpview = gtk_text_view_new ();
1010     tmplink = gtk_link_button_new ("http://projectmallard.org/");
1011     gtk_container_add (GTK_CONTAINER (tmpwin), tmpbox);
1012     gtk_container_add (GTK_CONTAINER (tmpbox), tmpview);
1013     gtk_container_add (GTK_CONTAINER (tmpbox), tmplink);
1014     gtk_widget_show_all (tmpwin);
1015 
1016     context = gtk_widget_get_style_context (tmpview);
1017     gtk_style_context_save (context);
1018 
1019     gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL);
1020     gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW);
1021     gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &text);
1022     gtk_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &base);
1023 
1024     gtk_style_context_restore (context);
1025 
1026     rgb_to_hsv (text, &text_h, &text_s, &text_v);
1027     rgb_to_hsv (base, &base_h, &base_s, &base_v);
1028 
1029     /* YELP_SETTINGS_COLOR_BASE */
1030     g_snprintf (settings->priv->colors[YELP_SETTINGS_COLOR_BASE], 8, "#%02X%02X%02X",
1031                 (guint) (base.red * 255), (guint) (base.green * 255), (guint) (base.blue * 255));
1032 
1033     /* YELP_SETTINGS_COLOR_TEXT */
1034     g_snprintf (settings->priv->colors[YELP_SETTINGS_COLOR_TEXT], 8, "#%02X%02X%02X",
1035                 (guint) (text.red * 255), (guint) (text.green * 255), (guint) (text.blue * 255));
1036 
1037     linkcontext = gtk_widget_get_style_context (tmplink);
1038     gtk_style_context_save (linkcontext);
1039 
1040     /* YELP_SETTINGS_COLOR_LINK */
1041     gtk_style_context_set_state (linkcontext, GTK_STATE_FLAG_LINK);
1042     gtk_style_context_get_color (linkcontext, GTK_STATE_FLAG_LINK, &link);
1043     g_snprintf (settings->priv->colors[YELP_SETTINGS_COLOR_LINK], 8, "#%02X%02X%02X",
1044                 (guint) (link.red * 255), (guint) (link.green * 255), (guint) (link.blue * 255));
1045 
1046     /* YELP_SETTINGS_COLOR_LINK_VISITED */
1047     gtk_style_context_set_state (linkcontext, GTK_STATE_FLAG_VISITED);
1048     gtk_style_context_get_color (linkcontext, GTK_STATE_FLAG_VISITED, &link);
1049     g_snprintf (settings->priv->colors[YELP_SETTINGS_COLOR_LINK_VISITED], 8, "#%02X%02X%02X",
1050                 (guint) (link.red * 255), (guint) (link.green * 255), (guint) (link.blue * 255));
1051 
1052 
1053     gtk_style_context_restore (linkcontext);
1054 
1055     /* YELP_SETTINGS_COLOR_TEXT_LIGHT */
1056     hsv_to_hex (text_h, text_s, text_v - ((text_v - base_v) * 0.25),
1057                 settings->priv->colors[YELP_SETTINGS_COLOR_TEXT_LIGHT]);
1058 
1059     /* YELP_SETTINGS_COLOR_GRAY */
1060     hsv_to_hex (base_h, base_s,
1061                 base_v - ((base_v - text_v) * 0.05),
1062                 settings->priv->colors[YELP_SETTINGS_COLOR_GRAY_BASE]);
1063     hsv_to_hex (base_h, base_s,
1064                 base_v - ((base_v - text_v) * 0.1),
1065                 settings->priv->colors[YELP_SETTINGS_COLOR_DARK_BASE]);
1066     hsv_to_hex (base_h, base_s,
1067                 base_v - ((base_v - text_v) * 0.26),
1068                 settings->priv->colors[YELP_SETTINGS_COLOR_GRAY_BORDER]);
1069 
1070     /* YELP_SETTINGS_COLOR_BLUE */
1071     hsv_to_hex (211, 0.1,
1072                 base_v - ((base_v - text_v) * 0.01),
1073                 settings->priv->colors[YELP_SETTINGS_COLOR_BLUE_BASE]);
1074     hsv_to_hex (211, 0.45,
1075                 base_v - ((base_v - text_v) * 0.19),
1076                 settings->priv->colors[YELP_SETTINGS_COLOR_BLUE_BORDER]);
1077 
1078     /* YELP_SETTINGS_COLOR_RED */
1079     hsv_to_hex (0, 0.13,
1080                 base_v - ((base_v - text_v) * 0.01),
1081                 settings->priv->colors[YELP_SETTINGS_COLOR_RED_BASE]);
1082     hsv_to_hex (0, 0.83,
1083                 base_v - ((base_v - text_v) * 0.06),
1084                 settings->priv->colors[YELP_SETTINGS_COLOR_RED_BORDER]);
1085 
1086     /* YELP_SETTINGS_COLOR_YELLOW */
1087     hsv_to_hex (60, 0.25,
1088                 base_v - ((base_v - text_v) * 0.01),
1089                 settings->priv->colors[YELP_SETTINGS_COLOR_YELLOW_BASE]);
1090     hsv_to_hex (60, 1.0,
1091                 base_v - ((base_v - text_v) * 0.07),
1092                 settings->priv->colors[YELP_SETTINGS_COLOR_YELLOW_BORDER]);
1093 
1094     gtk_widget_destroy (tmpwin);
1095 
1096     g_mutex_unlock (&settings->priv->mutex);
1097 
1098     g_signal_emit (settings, settings_signals[COLORS_CHANGED], 0);
1099 }
1100 
1101 static void
gtk_font_changed(GtkSettings * gtk_settings,GParamSpec * pspec,YelpSettings * settings)1102 gtk_font_changed (GtkSettings  *gtk_settings,
1103 		  GParamSpec   *pspec,
1104 		  YelpSettings *settings)
1105 {
1106     gchar *font, *c;
1107 
1108     /* This happens when font_adjustment is set during init */
1109     if (gtk_settings == NULL)
1110         return;
1111 
1112     g_free (settings->priv->fonts[YELP_SETTINGS_FONT_VARIABLE]);
1113     g_object_get (gtk_settings, "gtk-font-name", &font, NULL);
1114     settings->priv->fonts[YELP_SETTINGS_FONT_VARIABLE] = font;
1115 
1116     c = strrchr (font, ' ');
1117     if (c == NULL) {
1118 	g_warning ("Cannot parse font: %s", font);
1119 	font = g_strdup ("Monospace 10");
1120     }
1121     else {
1122 	font = g_strconcat ("Monospace", c, NULL);
1123     }
1124 
1125     g_free (settings->priv->fonts[YELP_SETTINGS_FONT_FIXED]);
1126     settings->priv->fonts[YELP_SETTINGS_FONT_FIXED] = font;
1127 
1128     g_signal_emit (settings, settings_signals[FONTS_CHANGED], 0);
1129 }
1130 
1131 static void
icon_theme_changed(GtkIconTheme * theme,YelpSettings * settings)1132 icon_theme_changed (GtkIconTheme *theme,
1133 		    YelpSettings *settings)
1134 {
1135     GtkIconInfo *info;
1136     gint i;
1137 
1138     g_mutex_lock (&settings->priv->mutex);
1139 
1140     for (i = 0; i < YELP_SETTINGS_NUM_ICONS; i++) {
1141 	if (settings->priv->icons[i] != NULL)
1142 	    g_free (settings->priv->icons[i]);
1143 	info = gtk_icon_theme_lookup_icon (theme,
1144 					   icon_names[i],
1145 					   settings->priv->icon_size,
1146 					   GTK_ICON_LOOKUP_NO_SVG);
1147 	if (info != NULL) {
1148 	    settings->priv->icons[i] = g_filename_to_uri (gtk_icon_info_get_filename (info),
1149                                                           NULL, NULL);
1150 	    g_object_unref (info);
1151 	}
1152 	else {
1153 	    settings->priv->icons[i] = NULL;
1154 	}
1155     }
1156 
1157     g_mutex_unlock (&settings->priv->mutex);
1158 
1159     g_signal_emit (settings, settings_signals[ICONS_CHANGED], 0);
1160 }
1161 
1162 gint
yelp_settings_cmp_icons(const gchar * icon1,const gchar * icon2)1163 yelp_settings_cmp_icons (const gchar *icon1,
1164                          const gchar *icon2)
1165 {
1166     static const gchar *icons[] = {
1167         "yelp-page-search-symbolic",
1168         "yelp-page-video-symbolic",
1169         "yelp-page-task-symbolic",
1170         "yelp-page-tip-symbolic",
1171         "yelp-page-problem-symbolic",
1172         "yelp-page-ui-symbolic",
1173         "yelp-page-symbolic",
1174         NULL
1175     };
1176     gint i;
1177     for (i = 0; icons[i] != NULL; i++) {
1178         gboolean eq1 = icon1 ? g_str_has_prefix (icon1, icons[i]) : FALSE;
1179         gboolean eq2 = icon2 ? g_str_has_prefix (icon2, icons[i]) : FALSE;
1180         if (eq1 && eq2)
1181             return 0;
1182         else if (eq1)
1183             return -1;
1184         else if (eq2)
1185             return 1;
1186     }
1187     if (icon1 == NULL && icon2 == NULL)
1188         return 0;
1189     else if (icon2 == NULL)
1190         return -1;
1191     else if (icon1 == NULL)
1192         return 1;
1193     else
1194         return strcmp (icon1, icon2);
1195 }
1196 
1197 /******************************************************************************/
1198 
1199 static void
rgb_to_hsv(GdkRGBA color,gdouble * h,gdouble * s,gdouble * v)1200 rgb_to_hsv (GdkRGBA color, gdouble *h, gdouble *s, gdouble *v)
1201 {
1202     gdouble min, max, delta;
1203 
1204     max = (color.red > color.green) ? color.red : color.green;
1205     max = (max > color.blue) ? max : color.blue;
1206     min = (color.red < color.green) ? color.red : color.green;
1207     min = (min < color.blue) ? min : color.blue;
1208 
1209     delta = max - min;
1210 
1211     *v = max;
1212     *s = 0;
1213     *h = 0;
1214 
1215     if (max != min) {
1216 	*s = delta / *v;
1217 
1218 	if (color.red == max)
1219 	    *h = (color.green - color.blue) / delta;
1220 	else if (color.green == max)
1221 	    *h = 2 + (color.blue - color.red) / delta;
1222 	else if (color.blue == max)
1223 	    *h = 4 + (color.red - color.green) / delta;
1224 
1225 	*h *= 60;
1226 	if (*h < 0.0)
1227 	    *h += 360;
1228     }
1229 }
1230 
1231 static void
hsv_to_hex(gdouble h,gdouble s,gdouble v,gchar * str)1232 hsv_to_hex (gdouble h, gdouble s, gdouble v, gchar *str)
1233 {
1234     gint hue;
1235     gdouble m1, m2, m3;
1236     gdouble r, g, b;
1237     guint red, green, blue;
1238 
1239     h /= 60;
1240     hue = (int) h;
1241     m1 = v * (1 - s);
1242     m2 = v * (1 - s * (h - hue));
1243     m3 = v * (1 - s * (-h + hue + 1));
1244 
1245     r = g = b = v;
1246     switch (hue) {
1247     case 0:
1248         b = m1; g = m3; break;
1249     case 1:
1250         b = m1; r = m2; break;
1251     case 2:
1252         r = m1; b = m3; break;
1253     case 3:
1254         r = m1; g = m2; break;
1255     case 4:
1256         g = m1; r = m3; break;
1257     case 5:
1258         g = m1; b = m2; break;
1259     default:
1260         g_assert_not_reached (); break;
1261     }
1262 
1263     red = r * 255;
1264     green = g * 255;
1265     blue = b * 255;
1266     g_snprintf (str, 8, "#%02X%02X%02X", red, green, blue);
1267 }
1268