diff options
Diffstat (limited to 'mate-dictionary/libgdict/gdict-source-loader.c')
-rw-r--r-- | mate-dictionary/libgdict/gdict-source-loader.c | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/mate-dictionary/libgdict/gdict-source-loader.c b/mate-dictionary/libgdict/gdict-source-loader.c new file mode 100644 index 00000000..71d919af --- /dev/null +++ b/mate-dictionary/libgdict/gdict-source-loader.c @@ -0,0 +1,600 @@ +/* gdict-source-loader.c - Source loader for Gdict + * + * Copyright (C) 2005 Emmanuele Bassi <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + */ + +/** + * SECTION:gdict-source-loader + * @short_description: Loader object for a set of dictionary sources + * + * #GdictSourceLoader allows searching for dictionary source definition + * files inside a set of paths and return a #GdictSource using its name. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <glib.h> +#include <glib/gstdio.h> +#include <glib/gi18n-lib.h> + +#include "gdict-source-loader.h" +#include "gdict-utils.h" +#include "gdict-enum-types.h" +#include "gdict-marshal.h" +#include "gdict-private.h" + +#define GDICT_SOURCE_FILE_SUFFIX ".desktop" + +#define GDICT_SOURCE_LOADER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GDICT_TYPE_SOURCE_LOADER, GdictSourceLoaderPrivate)) + +struct _GdictSourceLoaderPrivate +{ + GSList *paths; + + GSList *sources; + GHashTable *sources_by_name; + + guint paths_dirty : 1; +}; + +enum +{ + PROP_0, + + PROP_PATHS, + PROP_SOURCES +}; + +enum +{ + SOURCE_LOADED, + + LAST_SIGNAL +}; + +static guint loader_signals[LAST_SIGNAL] = { 0 }; + + + +G_DEFINE_TYPE (GdictSourceLoader, gdict_source_loader, G_TYPE_OBJECT); + + +static void +gdict_source_loader_finalize (GObject *object) +{ + GdictSourceLoaderPrivate *priv = GDICT_SOURCE_LOADER_GET_PRIVATE (object); + + if (priv->paths) + { + g_slist_foreach (priv->paths, + (GFunc) g_free, + NULL); + g_slist_free (priv->paths); + + priv->paths = NULL; + } + + if (priv->sources_by_name) + g_hash_table_destroy (priv->sources_by_name); + + if (priv->sources) + { + g_slist_foreach (priv->sources, + (GFunc) g_object_unref, + NULL); + g_slist_free (priv->sources); + + priv->sources = NULL; + } + + G_OBJECT_CLASS (gdict_source_loader_parent_class)->finalize (object); +} + +static void +gdict_source_loader_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_PATHS: + break; + case PROP_SOURCES: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdict_source_loader_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_PATHS: + break; + case PROP_SOURCES: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdict_source_loader_class_init (GdictSourceLoaderClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = gdict_source_loader_set_property; + gobject_class->get_property = gdict_source_loader_get_property; + gobject_class->finalize = gdict_source_loader_finalize; + + /** + * GdictSourceLoader:paths + * + * The search paths used by this object + * + * Since: 1.0 + */ + g_object_class_install_property (gobject_class, + PROP_PATHS, + g_param_spec_pointer ("paths", + _("Paths"), + _("Search paths used by this object"), + G_PARAM_READABLE)); + /** + * GdictSourceLoader:sources + * + * The #GdictSource objects found by this object + * + * Since: 1.0 + */ + g_object_class_install_property (gobject_class, + PROP_SOURCES, + g_param_spec_pointer ("sources", + _("Sources"), + _("Dictionary sources found"), + G_PARAM_READABLE)); + + /** + * GdictSourceLoader::source-loaded + * @loader: the object which received the signal + * @source: the new #GdictSource object found + * + * This signal is emitted when a new dictionary source has been added + * to the list. + * + * Since: 1.0 + */ + loader_signals[SOURCE_LOADED] = + g_signal_new ("source-loaded", + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdictSourceLoaderClass, source_loaded), + NULL, NULL, + gdict_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GDICT_TYPE_SOURCE); + + g_type_class_add_private (klass, sizeof (GdictSourceLoaderPrivate)); +} + +static void +gdict_source_loader_init (GdictSourceLoader *loader) +{ + GdictSourceLoaderPrivate *priv; + + priv = GDICT_SOURCE_LOADER_GET_PRIVATE (loader); + loader->priv = priv; + + priv->paths = NULL; + /* add the default, system-wide path */ + priv->paths = g_slist_prepend (priv->paths, g_strdup (GDICTSOURCESDIR)); + + priv->sources = NULL; + priv->sources_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + NULL); + + /* ensure that the sources list will be updated */ + priv->paths_dirty = TRUE; +} + +/** + * gdict_source_loader_new: + * + * Creates a new #GdictSourceLoader object. This object is used to search + * into a list of paths for dictionary source files. See #GdictSource for + * more informations about the format of dictionary source files. + * + * Return value: a new #GdictSourceLoader object + * + * Since: 1.0 + */ +GdictSourceLoader * +gdict_source_loader_new (void) +{ + return g_object_new (GDICT_TYPE_SOURCE_LOADER, NULL); +} + +/** + * gdict_source_loader_update: + * @loader: a #GdictSourceLoader + * + * Queue an update of the sources inside @loader. + * + * Since: 1.0 + */ +void +gdict_source_loader_update (GdictSourceLoader *loader) +{ + g_return_if_fail (GDICT_IS_SOURCE_LOADER (loader)); + + loader->priv->paths_dirty = TRUE; +} + +/** + * gdict_source_loader_add_search_path: + * @loader: a #GdictSourceLoader + * @path: a path to be added to the search path list + * + * Adds @path to the search paths list of @loader. + * + * Since: 1.0 + */ +void +gdict_source_loader_add_search_path (GdictSourceLoader *loader, + const gchar *path) +{ + GSList *l; + + g_return_if_fail (GDICT_IS_SOURCE_LOADER (loader)); + g_return_if_fail (path != NULL); + + /* avoid duplications */ + for (l = loader->priv->paths; l != NULL; l = l->next) + if (strcmp (path, (gchar *) l->data) == 0) + return; + + loader->priv->paths = g_slist_append (loader->priv->paths, g_strdup (path)); + loader->priv->paths_dirty = TRUE; +} + +/** + * gdict_source_loader_get_paths: + * @loader: a #GdictSourceLoader + * + * Gets the list of paths used by @loader to search for dictionary source + * files. + * + * Return value: a list containing the paths. The returned list is owned + * by the #GdictSourceLoader object and should never be free or modified. + * + * Since: 1.0 + */ +const GSList * +gdict_source_loader_get_paths (GdictSourceLoader *loader) +{ + g_return_val_if_fail (GDICT_IS_SOURCE_LOADER (loader), NULL); + + return loader->priv->paths; +} + +/* create the list of dictionary source files, by scanning the search path + * directories for .desktop files; we disavow symlinks and sub-directories + * for the time being. + */ +static GSList * +build_source_filenames (GdictSourceLoader *loader) +{ + GSList *retval, *d; + + g_assert (GDICT_IS_SOURCE_LOADER (loader)); + + if (!loader->priv->paths) + return NULL; + + retval = NULL; + for (d = loader->priv->paths; d != NULL; d = d->next) + { + gchar *path = (gchar *) d->data; + const gchar *filename; + GDir *dir; + + dir = g_dir_open (path, 0, NULL); + if (!dir) + continue; + + do + { + filename = g_dir_read_name (dir); + if (filename) + { + gchar *full_path; + + if (!g_str_has_suffix (filename, GDICT_SOURCE_FILE_SUFFIX)) + break; + + full_path = g_build_filename (path, filename, NULL); + if (g_file_test (full_path, G_FILE_TEST_IS_REGULAR)) + { + retval = g_slist_prepend (retval, full_path); + } + } + } + while (filename != NULL); + + g_dir_close (dir); + } + + return g_slist_reverse (retval); +} + +static void +gdict_source_loader_update_sources (GdictSourceLoader *loader) +{ + GSList *filenames, *f; + + g_assert (GDICT_IS_SOURCE_LOADER (loader)); + + g_slist_foreach (loader->priv->sources, + (GFunc) g_object_unref, + NULL); + g_slist_free (loader->priv->sources); + loader->priv->sources = NULL; + + filenames = build_source_filenames (loader); + for (f = filenames; f != NULL; f = f->next) + { + GdictSource *source; + GError *load_err; + gchar *path = (gchar *) f->data; + + g_assert (path != NULL); + + source = gdict_source_new (); + + load_err = NULL; + gdict_source_load_from_file (source, path, &load_err); + if (load_err) + { + g_warning ("Unable to load dictionary source at '%s': %s\n", + path, + load_err->message); + g_error_free (load_err); + + continue; + } + + loader->priv->sources = g_slist_append (loader->priv->sources, + source); + g_hash_table_replace (loader->priv->sources_by_name, + g_strdup (gdict_source_get_name (source)), + source); + + g_signal_emit (loader, loader_signals[SOURCE_LOADED], 0, source); + } + + g_slist_foreach (filenames, + (GFunc) g_free, + NULL); + g_slist_free (filenames); + + loader->priv->paths_dirty = FALSE; +} + +/** + * gdict_source_loader_get_names: + * @loader: a #GdictSourceLoader + * @length: return location for the number of source names, or %NULL + * + * Retrieves the list of dictionary source names available into the + * search paths of @loader. + * + * Return value: a newly allocated, %NULL terminated array of strings. You + * should free the returned string array with g_strfreev() + * + * Since: 1.0 + */ +gchar ** +gdict_source_loader_get_names (GdictSourceLoader *loader, + gsize *length) +{ + GSList *l; + gchar **names; + gsize i; + + g_return_val_if_fail (GDICT_IS_SOURCE_LOADER (loader), NULL); + + if (loader->priv->paths_dirty) + gdict_source_loader_update_sources (loader); + + names = g_new0 (gchar *, g_slist_length (loader->priv->sources) + 1); + + i = 0; + for (l = loader->priv->sources; l != NULL; l = l->next) + { + GdictSource *s = GDICT_SOURCE (l->data); + + g_assert (s != NULL); + + names[i++] = g_strdup (gdict_source_get_name (s)); + } + names[i] = NULL; + + if (length) + *length = i; + + return names; +} + +/** + * gdict_source_loader_get_sources: + * @loader: a #GdictSourceLoader + * + * Retrieves the list of dictionary sources available into the search + * paths of @loader, in form of #GdictSource objects. + * + * Return value: a list of #GdictSource objects. The returned list + * is owned by the #GdictSourceLoader object, and should never be + * freed or modified. + * + * Since: 1.0 + */ +const GSList * +gdict_source_loader_get_sources (GdictSourceLoader *loader) +{ + g_return_val_if_fail (GDICT_IS_SOURCE_LOADER (loader), NULL); + + if (loader->priv->paths_dirty) + gdict_source_loader_update_sources (loader); + + return loader->priv->sources; +} + +/** + * gdict_source_loader_get_source: + * @loader: a #GdictSourceLoader + * @name: a name of a dictionary source + * + * Retrieves a dictionary source using @name. You can use the returned + * #GdictSource object to create the right #GdictContext for that + * dictionary source. + * + * Return value: a referenced #GdictSource object. You should de-reference + * it using g_object_unref() when you finished using it. + * + * Since: 1.0 + */ +GdictSource * +gdict_source_loader_get_source (GdictSourceLoader *loader, + const gchar *name) +{ + GdictSource *retval; + + g_return_val_if_fail (GDICT_IS_SOURCE_LOADER (loader), NULL); + g_return_val_if_fail (name != NULL, NULL); + + if (loader->priv->paths_dirty) + gdict_source_loader_update_sources (loader); + + retval = g_hash_table_lookup (loader->priv->sources_by_name, name); + if (retval) + return g_object_ref (retval); + + return NULL; +} + +/** + * gdict_source_loader_remove_source: + * @loader: a #GdictSourceLoader + * @name: name of a dictionary source + * + * Removes the dictionary source @name from @loader. This function will + * also remove the dictionary source definition file bound to it. + * + * Return value: %TRUE if the dictionary source was successfully removed + * + * Since: 1.0 + */ +gboolean +gdict_source_loader_remove_source (GdictSourceLoader *loader, + const gchar *name) +{ + GdictSourceLoaderPrivate *priv; + GSList *l; + + g_return_val_if_fail (GDICT_IS_SOURCE_LOADER (loader), FALSE); + g_return_val_if_fail (name != NULL, FALSE); + + priv = loader->priv; + + if (priv->paths_dirty) + gdict_source_loader_update_sources (loader); + + for (l = priv->sources; l != NULL; l = l->next) + { + GdictSource *s = GDICT_SOURCE (l->data); + + if (strcmp (gdict_source_get_name (s), name) == 0) + { + gchar *filename; + + g_object_get (G_OBJECT (s), "filename", &filename, NULL); + + if (g_unlink (filename) == -1) + { + g_warning ("Unable to remove filename '%s' for the " + "dictionary source '%s'\n", + filename, + name); + + return FALSE; + } + + g_hash_table_remove (priv->sources_by_name, name); + + priv->sources = g_slist_remove_link (priv->sources, l); + + g_object_unref (s); + g_slist_free (l); + + return TRUE; + } + } + + return FALSE; +} + +/** + * gdict_source_loader_has_source: + * @loader: a #GdictSourceLoader + * @source_name: the name of a dictionary source + * + * Checks whether @loader has a dictionary source with name @source_name. + * + * Return value: %TRUE if the dictionary source is known + * + * Since: 0.12 + */ +gboolean +gdict_source_loader_has_source (GdictSourceLoader *loader, + const gchar *source_name) +{ + g_return_val_if_fail (GDICT_IS_SOURCE_LOADER (loader), FALSE); + g_return_val_if_fail (source_name != NULL, FALSE); + + if (loader->priv->paths_dirty) + gdict_source_loader_update_sources (loader); + + return (g_hash_table_lookup (loader->priv->sources_by_name, source_name) != NULL); +} |