mx/mx/mx-icon-theme.c

1037 lines
28 KiB
C

/*
* mx-icon-theme.c: A freedesktop icon theme object
*
* Copyright 2010 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Author: Chris Lord <chris@linux.intel.com>
*
*/
#include <stdlib.h>
#include <string.h>
#include <gio/gio.h>
#include "mx-icon-theme.h"
#include "mx-marshal.h"
#include "mx-texture-cache.h"
#include "mx-private.h"
#include "mx-settings.h"
G_DEFINE_TYPE (MxIconTheme, mx_icon_theme, G_TYPE_OBJECT)
enum
{
CHANGED,
LAST_SIGNAL
};
#define ICON_THEME_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), MX_TYPE_ICON_THEME, MxIconThemePrivate))
typedef enum
{
MX_FIXED,
MX_SCALABLE,
MX_THRESHOLD
} MxIconType;
typedef struct
{
gint size;
gchar *path;
MxIconType type;
gint min_size;
gint max_size;
gint threshold;
} MxIconData;
struct _MxIconThemePrivate
{
guint override_theme : 1;
GList *search_paths;
GHashTable *icon_hash;
GHashTable *theme_path_hash;
gchar *theme;
GKeyFile *theme_file;
GList *theme_fallbacks;
GKeyFile *hicolor_file;
};
enum
{
PROP_0,
PROP_THEME_NAME
};
static void
mx_icon_theme_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
MxIconTheme *theme = MX_ICON_THEME (object);
switch (property_id)
{
case PROP_THEME_NAME:
g_value_set_string (value, mx_icon_theme_get_theme_name (theme));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
mx_icon_theme_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
case PROP_THEME_NAME:
mx_icon_theme_set_theme_name (MX_ICON_THEME (object),
g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
mx_icon_theme_dispose (GObject *object)
{
G_OBJECT_CLASS (mx_icon_theme_parent_class)->dispose (object);
}
static void
mx_icon_theme_finalize (GObject *object)
{
MxIconTheme *self = MX_ICON_THEME (object);
MxIconThemePrivate *priv = self->priv;
mx_icon_theme_set_search_paths (self, NULL);
g_hash_table_unref (priv->icon_hash);
g_hash_table_unref (priv->theme_path_hash);
g_free (priv->theme);
if (priv->theme_file)
g_key_file_free (priv->theme_file);
while (priv->theme_fallbacks)
{
g_key_file_free ((GKeyFile *)priv->theme_fallbacks->data);
priv->theme_fallbacks = g_list_delete_link (priv->theme_fallbacks,
priv->theme_fallbacks);
}
if (priv->hicolor_file)
g_key_file_free (priv->hicolor_file);
G_OBJECT_CLASS (mx_icon_theme_parent_class)->finalize (object);
}
static void
mx_icon_theme_class_init (MxIconThemeClass *klass)
{
GParamSpec *pspec;
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (MxIconThemePrivate));
object_class->get_property = mx_icon_theme_get_property;
object_class->set_property = mx_icon_theme_set_property;
object_class->dispose = mx_icon_theme_dispose;
object_class->finalize = mx_icon_theme_finalize;
pspec = g_param_spec_string ("theme-name",
"Theme name",
"The name of the currently loaded theme.",
NULL,
MX_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_THEME_NAME, pspec);
}
static GKeyFile *
mx_icon_theme_load_theme (MxIconTheme *self, const gchar *name)
{
GList *p;
GKeyFile *key_file;
MxIconThemePrivate *priv = self->priv;
key_file = g_key_file_new ();
for (p = priv->search_paths; p; p = p->next)
{
const gchar *path = p->data;
gchar *key_path = g_build_filename (path, name, "index.theme", NULL);
gboolean success = g_key_file_load_from_file (key_file, key_path, 0, NULL);
g_free (key_path);
if (success)
{
g_hash_table_insert (priv->theme_path_hash,
key_file,
g_strdup (name));
return key_file;
}
}
g_key_file_free (key_file);
return NULL;
}
static void
mx_icon_theme_icon_data_free (MxIconData *data)
{
g_free (data->path);
g_free (data);
}
static void
mx_icon_theme_icon_hash_free (gpointer data)
{
GList *icon_list = (GList *)data;
while (icon_list)
{
mx_icon_theme_icon_data_free ((MxIconData *)icon_list->data);
icon_list = g_list_delete_link (icon_list, icon_list);
}
}
static MxIconData *
mx_icon_theme_icon_data_new (gint size,
const gchar *path,
MxIconType type,
gint min_size,
gint max_size,
gint threshold)
{
MxIconData *data = g_new (MxIconData, 1);
data->size = size;
data->path = g_strdup (path);
data->type = type;
data->min_size = min_size;
data->max_size = max_size;
data->threshold = threshold;
return data;
}
static gboolean
mx_icon_theme_equal_func (GThemedIcon *icon,
GThemedIcon *search)
{
gint i;
const gchar * const *names1 = g_themed_icon_get_names (icon);
const gchar * const *names2 = g_themed_icon_get_names (search);
for (i = 0; names1[i] && names2[i]; i++)
if (!g_str_equal (names1[i], names2[i]))
return FALSE;
return TRUE;
}
static guint
mx_icon_theme_hash (GThemedIcon *icon)
{
const gchar * const *names = g_themed_icon_get_names (icon);
return g_str_hash (names[0]);
}
static gboolean
mx_icon_theme_find_name_cb (gpointer key,
gpointer value,
gpointer user_data)
{
gchar *name1 = (gchar *)value;
gchar *name2 = (gchar *)user_data;
return g_str_equal (name1, name2);
}
static void
mx_icon_theme_load_fallbacks (MxIconTheme *theme,
GKeyFile *theme_file,
gboolean root)
{
MxIconThemePrivate *priv = theme->priv;
gchar *fallbacks = g_key_file_get_string (theme_file,
"Icon Theme",
"Inherits",
NULL);
/* If this isn't the root theme, add it to the list of fallbacks */
if (!root)
priv->theme_fallbacks = g_list_append (priv->theme_fallbacks,
theme_file);
/* Check the list of fallbacks in this theme and add any that we haven't
* already.
*/
if (fallbacks)
{
gint i = 0;
gint fallbacks_len = strlen (fallbacks);
g_strdelimit (fallbacks, ",", '\0');
while (i < fallbacks_len)
{
gchar *fallback = fallbacks + i;
i += strlen (fallback) + 1;
/* Skip hicolor, we keep it loaded all the time */
if (g_str_equal (fallback, "hicolor"))
continue;
/* Skip if we've already loaded this theme */
if (g_hash_table_find (priv->theme_path_hash,
mx_icon_theme_find_name_cb,
fallback))
continue;
/* Load this theme and store itself and its fallbacks in the
* list of fallbacks.
*/
theme_file = mx_icon_theme_load_theme (theme, fallback);
if (theme_file)
mx_icon_theme_load_fallbacks (theme, theme_file, FALSE);
}
g_free (fallbacks);
}
}
static void
mx_icon_theme_changed_cb (MxSettings *settings,
GParamSpec *pspec,
MxIconTheme *self)
{
gchar *theme;
if (self->priv->override_theme)
return;
g_object_get (settings, "icon-theme", &theme, NULL);
mx_icon_theme_set_theme_name (self, theme);
g_free (theme);
self->priv->override_theme = FALSE;
}
static void
mx_icon_theme_init (MxIconTheme *self)
{
gint i;
gchar *path;
const gchar *theme;
const gchar *datadir;
const gchar * const *datadirs;
MxIconThemePrivate *priv = self->priv = ICON_THEME_PRIVATE (self);
/* /usr/share/pixmaps, /usr/share/icons and $HOME/.icons are named in the
* icon theme spec, but we'll interpret this to look in the system data
* dirs, as most other (well, gtk) toolkits do.
*/
datadirs = g_get_system_data_dirs ();
for (i = 0; datadirs[i]; i++)
{
datadir = datadirs[i];
path = g_build_filename (G_DIR_SEPARATOR_S, datadir, "pixmaps", NULL);
priv->search_paths = g_list_prepend (priv->search_paths, path);
path = g_build_filename (G_DIR_SEPARATOR_S, datadir, "icons", NULL);
priv->search_paths = g_list_prepend (priv->search_paths, path);
}
datadir = g_get_user_data_dir ();
path = g_build_filename (G_DIR_SEPARATOR_S, datadir, "pixmaps", NULL);
priv->search_paths = g_list_prepend (priv->search_paths, path);
path = g_build_filename (G_DIR_SEPARATOR_S, datadir, "icons", NULL);
priv->search_paths = g_list_prepend (priv->search_paths, path);
path = g_build_filename (g_get_home_dir (), ".icons", NULL);
priv->search_paths = g_list_prepend (priv->search_paths, path);
priv->icon_hash = g_hash_table_new_full ((GHashFunc)mx_icon_theme_hash,
(GEqualFunc)mx_icon_theme_equal_func,
g_object_unref,
mx_icon_theme_icon_hash_free);
priv->theme_path_hash = g_hash_table_new_full (g_direct_hash,
g_str_equal,
NULL,
g_free);
priv->hicolor_file = mx_icon_theme_load_theme (self, "hicolor");
if (!priv->hicolor_file)
g_warning ("Error loading fallback icon theme");
theme = g_getenv ("MX_ICON_THEME");
if (theme)
mx_icon_theme_set_theme_name (self, theme);
else
{
MxSettings *settings = mx_settings_get_default ();
g_signal_connect (settings, "notify::icon-theme",
G_CALLBACK (mx_icon_theme_changed_cb), self);
mx_icon_theme_changed_cb (settings, NULL, self);
}
}
/**
* mx_icon_theme_new:
*
* Create a new #MxIconTheme. In most cicumstances, it is more useful to use
* #mx_icon_theme_get_default to load the default icon theme.
*
* Returns: a newly allocated #MxIconTheme.
*/
MxIconTheme *
mx_icon_theme_new (void)
{
return g_object_new (MX_TYPE_ICON_THEME, NULL);
}
/**
* mx_icon_theme_get_default:
*
* Return the default #MxIconTheme object used by the toolkit.
*
* Return value: (transfer none): an #MxIconTheme.
*/
MxIconTheme *
mx_icon_theme_get_default (void)
{
static MxIconTheme *default_icon_theme = NULL;
if (!default_icon_theme)
default_icon_theme = mx_icon_theme_new ();
return default_icon_theme;
}
/**
* mx_icon_theme_get_theme_name:
* @theme: A #MxIconTheme
*
* Get the value of the #MxIconTheme:theme-name property.
*
* Returns: the current value of the "theme-name" property.
*/
const gchar *
mx_icon_theme_get_theme_name (MxIconTheme *theme)
{
g_return_val_if_fail (MX_IS_ICON_THEME (theme), NULL);
return theme->priv->theme;
}
/**
* mx_icon_theme_set_theme_name:
* @theme: A #MxIconTheme
* @theme_name: the name of an icon theme to load, or %NULL
*
* Set the value of the #MxIconTheme:theme-name property. This will cause the
* icon theme to be loaded if it differs from the existing theme name. If the
* theme could not be loaded, it will fall back to using the default icon theme
* (hicolor).
*
* This will override the system's theme setting. To revert to the system
* icon theme, this function can be called with a %NULL @theme_name argument.
*
*/
void
mx_icon_theme_set_theme_name (MxIconTheme *theme,
const gchar *theme_name)
{
MxIconThemePrivate *priv;
g_return_if_fail (MX_IS_ICON_THEME (theme));
priv = theme->priv;
if (!theme_name)
{
if (priv->override_theme)
{
gchar *system_theme = NULL;
MxSettings *settings = mx_settings_get_default ();
g_object_get (settings, "icon-theme", &system_theme, NULL);
priv->override_theme = FALSE;
mx_icon_theme_set_theme_name (theme, system_theme);
priv->override_theme = FALSE;
g_free (system_theme);
}
return;
}
priv->override_theme = TRUE;
if (g_str_equal (theme_name, "hicolor"))
return;
if (priv->theme && g_str_equal (priv->theme, theme_name))
return;
/* Clear old data */
g_hash_table_remove_all (priv->icon_hash);
g_free (priv->theme);
if (priv->theme_file)
{
g_hash_table_remove (priv->theme_path_hash, priv->theme_file);
g_key_file_free (priv->theme_file);
}
while (priv->theme_fallbacks)
{
g_hash_table_remove (priv->theme_path_hash, priv->theme_fallbacks->data);
g_key_file_free ((GKeyFile *)priv->theme_fallbacks->data);
priv->theme_fallbacks = g_list_delete_link (priv->theme_fallbacks,
priv->theme_fallbacks);
}
/* Load new theme file */
priv->theme = g_strdup (theme_name);
priv->theme_file = mx_icon_theme_load_theme (theme, theme_name);
if (!priv->theme_file)
{
g_warning ("Error loading \"%s\" icon theme", priv->theme);
return;
}
/* Load fallbacks */
mx_icon_theme_load_fallbacks (theme, priv->theme_file, TRUE);
g_object_notify (G_OBJECT (theme), "theme-name");
}
static void
mx_icon_theme_collect_dirs (GString *string,
const gchar *path,
const gchar *root)
{
GDir *dir;
gchar *full_path;
const gchar *file;
full_path = g_build_filename (root, path, NULL);
dir = g_dir_open (full_path, 0, NULL);
g_free (full_path);
if (!dir)
return;
while ((file = g_dir_read_name (dir)))
{
gchar *new_dir = g_build_filename (root, path, file, NULL);
gboolean result = g_file_test (new_dir, G_FILE_TEST_IS_DIR);
g_free (new_dir);
if (result)
{
gchar *rel_dir = g_build_filename (path, file, NULL);
g_string_append (string, rel_dir);
g_string_append (string, ",");
mx_icon_theme_collect_dirs (string, rel_dir, root);
g_free (rel_dir);
}
}
g_dir_close (dir);
}
static GList *
mx_icon_theme_theme_load_icon (MxIconTheme *self,
GKeyFile *theme_file,
const gchar *icon,
GIcon *store_icon,
gboolean store_fail)
{
gchar *dirs;
const gchar *theme;
GList *data = NULL;
MxIconThemePrivate *priv = self->priv;
dirs = g_key_file_get_string (theme_file,
"Icon Theme",
"Directories",
NULL);
theme = g_hash_table_lookup (priv->theme_path_hash, theme_file);
if (!dirs)
{
GList *p;
GString *string;
/* Icon theme hasn't specified directories, so recurse and
* collect all of them.
*/
string = g_string_new ("");
for (p = priv->search_paths; p; p = p->next)
{
const gchar *search_path = p->data;
gchar *path = g_build_filename (search_path,
theme,
NULL);
mx_icon_theme_collect_dirs (string, "", path);
g_free (path);
}
/* Chop off the trailing comma */
g_string_truncate (string, string->len - 1);
dirs = string->str;
g_string_free (string, FALSE);
}
if (dirs)
{
gint i;
gint dirs_len = strlen (dirs);
g_strdelimit (dirs, ",", '\0');
i = 0;
while (i < dirs_len)
{
GList *p;
MxIconType type;
gchar *type_string;
gint size, min, max, threshold;
const gchar *dir = dirs + i;
i += strlen (dir) + 1;
size = g_key_file_get_integer (theme_file,
dir,
"Size",
NULL);
if (!size)
{
/* Try to get size from dir name */
size = atoi (dir);
if (!size)
continue;
}
type_string = g_key_file_get_string (theme_file,
dir,
"Type",
NULL);
type = MX_FIXED;
min = max = threshold = 0;
if (type_string)
{
if (g_str_equal (type_string, "Scalable"))
{
type = MX_SCALABLE;
min = g_key_file_get_integer (theme_file,
dir,
"MinSize",
NULL);
if (!min)
min = size;
max = g_key_file_get_integer (theme_file,
dir,
"MaxSize",
NULL);
if (!max)
max = size;
}
else if (g_str_equal (type_string, "Threshold"))
{
type = MX_THRESHOLD;
threshold = g_key_file_get_integer (theme_file,
dir,
"Threshold",
NULL);
if (!threshold)
threshold = 2;
min = size - threshold;
max = size + threshold;
}
g_free (type_string);
}
for (p = priv->search_paths; p; p = p->next)
{
gchar *file;
MxIconData *icon_data = NULL;
const gchar *search_path = p->data;
gchar *path = g_build_filename (search_path,
theme,
dir,
NULL);
/* Try png first, then svg and xpm */
file = g_strconcat (path, G_DIR_SEPARATOR_S, icon, ".png", NULL);
if (!g_file_test (file, G_FILE_TEST_EXISTS |
G_FILE_TEST_IS_REGULAR))
{
g_free (file);
file = g_strconcat (path, G_DIR_SEPARATOR_S, icon, ".svg",
NULL);
if (!g_file_test (file, G_FILE_TEST_EXISTS |
G_FILE_TEST_IS_REGULAR))
{
g_free (file);
file = g_strconcat (path, G_DIR_SEPARATOR_S, icon, ".xpm",
NULL);
if (!g_file_test (file, G_FILE_TEST_EXISTS |
G_FILE_TEST_IS_REGULAR))
{
g_free (file);
file = NULL;
}
}
}
if (file)
{
icon_data = mx_icon_theme_icon_data_new (size,
file,
type,
min,
max,
threshold);
g_free (file);
data = g_list_prepend (data, icon_data);
}
g_free (path);
}
}
g_free (dirs);
}
if (data || store_fail)
{
data = g_list_reverse (data);
if (!store_icon)
store_icon = g_themed_icon_new_with_default_fallbacks (icon);
else
store_icon = g_object_ref (store_icon);
g_hash_table_insert (priv->icon_hash, store_icon, data);
}
return data;
}
static GList *
mx_icon_theme_load_icon (MxIconTheme *theme,
const gchar *icon_name,
GIcon *store_icon)
{
GList *data, *f;
MxIconThemePrivate *priv = theme->priv;
/* Try the set theme */
if (priv->theme_file &&
(data = mx_icon_theme_theme_load_icon (theme, priv->theme_file,
icon_name, store_icon, FALSE)))
return data;
/* Try the inherited themes */
for (f = priv->theme_fallbacks; f; f = f->next)
{
GKeyFile *theme_file = f->data;
if ((data = mx_icon_theme_theme_load_icon (theme, theme_file, icon_name,
store_icon, FALSE)))
return data;
}
/* Try the hicolor theme */
if (priv->hicolor_file &&
(data = mx_icon_theme_theme_load_icon (theme, priv->hicolor_file,
icon_name, store_icon, TRUE)))
return data;
return NULL;
}
static GList *
mx_icon_theme_copy_data_list (GList *data)
{
GList *d, *new_data = g_list_copy (data);
for (d = new_data; d; d = d->next)
{
MxIconData *icon_data;
d->data = g_memdup (d->data, sizeof (MxIconData));
icon_data = d->data;
icon_data->path = g_strdup (icon_data->path);
}
return new_data;
}
static GList *
mx_icon_theme_get_icons (MxIconTheme *theme,
const gchar *icon_name)
{
gint i;
GIcon *icon;
GList *data;
const gchar * const *names = NULL;
MxIconThemePrivate *priv = theme->priv;
/* Load the icon, or a fallback */
icon = g_themed_icon_new_with_default_fallbacks (icon_name);
names = g_themed_icon_get_names (G_THEMED_ICON (icon));
if (!names)
{
g_object_unref (icon);
return NULL;
}
data = NULL;
for (i = 0; names[i]; i++)
{
/* See if we've loaded this before */
GIcon *single_icon = g_themed_icon_new (names[i]);
gboolean success = g_hash_table_lookup_extended (priv->icon_hash,
single_icon,
NULL,
(gpointer *)&data);
g_object_unref (single_icon);
/* Found in cache on first hit, break */
if (success && (i == 0))
break;
/* Found in cache after searching the disk, store again as a new icon */
if (success)
{
/* If we found this as a fallback, store it so we don't look on
* disk again.
*/
if (data)
data = mx_icon_theme_copy_data_list (data);
g_hash_table_insert (priv->icon_hash, g_object_ref (icon), data);
break;
}
/* Try to load from disk */
if ((data = mx_icon_theme_load_icon (theme, names[i], icon)))
break;
}
g_object_unref (icon);
return data;
}
static MxIconData *
mx_icon_theme_lookup_internal (MxIconTheme *theme,
const gchar *icon_name,
gint size)
{
MxIconData *best_match;
GList *d, *data;
gint distance;
data = mx_icon_theme_get_icons (theme, icon_name);
if (!data)
return NULL;
best_match = NULL;
distance = G_MAXINT;
for (d = data; d; d = d->next)
{
gint current_distance;
MxIconData *current = d->data;
switch (current->type)
{
case MX_FIXED:
current_distance = ABS (size - current->size);
break;
case MX_SCALABLE:
case MX_THRESHOLD:
if (size < current->min_size)
current_distance = current->min_size - size;
else if (size > current->max_size)
current_distance = size - current->max_size;
else
current_distance = 0;
break;
default:
g_warning ("Unknown icon type in cache");
current_distance = G_MAXINT - 1;
break;
}
if (current_distance < distance)
{
distance = current_distance;
best_match = current;
}
}
if (!best_match)
{
g_warning ("No match found, but icon is in cache");
return NULL;
}
return best_match;
}
/**
* mx_icon_theme_lookup:
* @theme: an #MxIconTheme
* @icon_name: The name of the icon
* @size: The desired size of the icon
*
* If the icon is available, returns a #CoglHandle of the icon.
*
* Return value: (transfer none): a #CoglHandle of the icon, or %NULL.
*/
CoglHandle
mx_icon_theme_lookup (MxIconTheme *theme,
const gchar *icon_name,
gint size)
{
MxTextureCache *texture_cache;
MxIconData *icon_data;
g_return_val_if_fail (MX_IS_ICON_THEME (theme), NULL);
g_return_val_if_fail (icon_name, NULL);
g_return_val_if_fail (size > 0, NULL);
if (!(icon_data = mx_icon_theme_lookup_internal (theme, icon_name, size)))
return NULL;
texture_cache = mx_texture_cache_get_default ();
return mx_texture_cache_get_cogl_texture (texture_cache, icon_data->path);
}
/**
* mx_icon_theme_lookup_texture:
* @theme: an #MxIconTheme
* @icon_name: The name of the icon
* @size: The desired size of the icon
*
* If the icon is available, returns a #ClutterTexture of the icon.
*
* Return value: (transfer none): a #ClutterTexture of the icon, or %NULL.
*/
ClutterTexture *
mx_icon_theme_lookup_texture (MxIconTheme *theme,
const gchar *icon_name,
gint size)
{
MxTextureCache *texture_cache;
MxIconData *icon_data;
g_return_val_if_fail (MX_IS_ICON_THEME (theme), NULL);
g_return_val_if_fail (icon_name, NULL);
g_return_val_if_fail (size > 0, NULL);
if (!(icon_data = mx_icon_theme_lookup_internal (theme, icon_name, size)))
return NULL;
texture_cache = mx_texture_cache_get_default ();
return mx_texture_cache_get_texture (texture_cache, icon_data->path);
}
gboolean
mx_icon_theme_has_icon (MxIconTheme *theme,
const gchar *icon_name)
{
g_return_val_if_fail (MX_IS_ICON_THEME (theme), FALSE);
g_return_val_if_fail (icon_name, FALSE);
if (mx_icon_theme_get_icons (theme, icon_name))
return TRUE;
else
return FALSE;
}
/**
* mx_icon_theme_get_search_paths:
* @theme: a #MxIconTheme
*
* Gets the directories the #MxIconTheme will search in to find icons.
*
* Return value: (element-type utf8) (transfer none): the search paths
*/
const GList *
mx_icon_theme_get_search_paths (MxIconTheme *theme)
{
g_return_val_if_fail (MX_IS_ICON_THEME (theme), NULL);
return theme->priv->search_paths;
}
/**
* mx_icon_theme_set_search_paths:
* @theme: a #MxIconTheme
* @paths: (element-type utf8): a list of search paths
*
* Sets the directories the #MxIconTheme will search in to find icons.
* By default, it will look in the default system and local icon
* directories.
*/
void
mx_icon_theme_set_search_paths (MxIconTheme *theme,
const GList *paths)
{
GList *p;
MxIconThemePrivate *priv;
g_return_if_fail (MX_IS_ICON_THEME (theme));
priv = theme->priv;
while (priv->search_paths)
{
g_free (priv->search_paths->data);
priv->search_paths = g_list_delete_link (priv->search_paths,
priv->search_paths);
}
priv->search_paths = g_list_copy ((GList *)paths);
for (p = priv->search_paths; p; p = p->next)
p->data = g_strdup ((const gchar *)p->data);
}