Blame src/dh-book.c

Packit 116408
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
Packit 116408
/*
Packit 116408
 * Copyright (C) 2002 CodeFactory AB
Packit 116408
 * Copyright (C) 2002 Mikael Hallendal <micke@imendio.com>
Packit 116408
 * Copyright (C) 2004-2008 Imendio AB
Packit 116408
 * Copyright (C) 2010 Lanedo GmbH
Packit 116408
 * Copyright (C) 2017, 2018 Sébastien Wilmet <swilmet@gnome.org>
Packit 116408
 *
Packit 116408
 * This program is free software; you can redistribute it and/or
Packit 116408
 * modify it under the terms of the GNU General Public License as
Packit 116408
 * published by the Free Software Foundation; either version 2 of the
Packit 116408
 * License, or (at your option) any later version.
Packit 116408
 *
Packit 116408
 * This program is distributed in the hope that it will be useful,
Packit 116408
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 116408
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 116408
 * General Public License for more details.
Packit 116408
 *
Packit 116408
 * You should have received a copy of the GNU General Public License
Packit 116408
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
Packit 116408
 */
Packit 116408
Packit 116408
#include "config.h"
Packit 116408
#include "dh-book.h"
Packit 116408
#include <glib/gi18n-lib.h>
Packit 116408
#include "dh-link.h"
Packit 116408
#include "dh-parser.h"
Packit 116408
#include "dh-util.h"
Packit 116408
Packit 116408
/**
Packit 116408
 * SECTION:dh-book
Packit 116408
 * @Title: DhBook
Packit 116408
 * @Short_description: A book, usually the documentation for one library
Packit 116408
 *
Packit 116408
 * A #DhBook usually contains the documentation for one library (or
Packit 116408
 * application), for example GLib or GTK+. A #DhBook corresponds to one index
Packit 116408
 * file. An index file is a file with the extension `*.devhelp`, `*.devhelp2`,
Packit 116408
 * `*.devhelp.gz` or `*.devhelp2.gz`.
Packit 116408
 *
Packit 116408
 * #DhBook creates a #GFileMonitor on the index file, and emits the
Packit 116408
 * #DhBook::updated or #DhBook::deleted signal in case the index file has
Packit 116408
 * changed on the filesystem. #DhBookManager listens to those #DhBook signals,
Packit 116408
 * and emits in turn the #DhBookManager::book-deleted and
Packit 116408
 * #DhBookManager::book-created signals.
Packit 116408
 */
Packit 116408
Packit 116408
/* Timeout to wait for new events on the index file so that they are merged and
Packit 116408
 * we don't spam unneeded signals.
Packit 116408
 */
Packit 116408
#define EVENT_MERGE_TIMEOUT_SECS (2)
Packit 116408
Packit 116408
enum {
Packit 116408
        /* FIXME: a boolean property would be a better API instead of the
Packit 116408
         * ::enabled and ::disabled signals. Or this whole concept can be
Packit 116408
         * removed from DhBook, by introducing DhBookSelection, see:
Packit 116408
         * https://bugzilla.gnome.org/show_bug.cgi?id=784491#c3
Packit 116408
         */
Packit 116408
        SIGNAL_ENABLED,
Packit 116408
        SIGNAL_DISABLED,
Packit 116408
Packit 116408
        SIGNAL_UPDATED,
Packit 116408
        SIGNAL_DELETED,
Packit 116408
        N_SIGNALS
Packit 116408
};
Packit 116408
Packit 116408
typedef enum {
Packit 116408
        BOOK_MONITOR_EVENT_NONE,
Packit 116408
        BOOK_MONITOR_EVENT_UPDATED,
Packit 116408
        BOOK_MONITOR_EVENT_DELETED
Packit 116408
} BookMonitorEvent;
Packit 116408
Packit 116408
typedef struct {
Packit 116408
        GFile *index_file;
Packit 116408
Packit 116408
        gchar *id;
Packit 116408
        gchar *title;
Packit 116408
        gchar *language;
Packit 116408
Packit 116408
        /* The book tree of DhLink*. */
Packit 116408
        GNode *tree;
Packit 116408
Packit 116408
        /* List of DhLink*. */
Packit 116408
        GList *links;
Packit 116408
Packit 116408
        DhCompletion *completion;
Packit 116408
Packit 116408
        GFileMonitor *index_file_monitor;
Packit 116408
        BookMonitorEvent last_monitor_event;
Packit 116408
        guint monitor_event_timeout_id;
Packit 116408
Packit 116408
        guint enabled : 1;
Packit 116408
} DhBookPrivate;
Packit 116408
Packit 116408
G_DEFINE_TYPE_WITH_PRIVATE (DhBook, dh_book, G_TYPE_OBJECT);
Packit 116408
Packit 116408
static guint signals[N_SIGNALS] = { 0 };
Packit 116408
Packit 116408
static void
Packit 116408
dh_book_dispose (GObject *object)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv;
Packit 116408
Packit 116408
        priv = dh_book_get_instance_private (DH_BOOK (object));
Packit 116408
Packit 116408
        g_clear_object (&priv->completion);
Packit 116408
        g_clear_object (&priv->index_file_monitor);
Packit 116408
Packit 116408
        if (priv->monitor_event_timeout_id != 0) {
Packit 116408
                g_source_remove (priv->monitor_event_timeout_id);
Packit 116408
                priv->monitor_event_timeout_id = 0;
Packit 116408
        }
Packit 116408
Packit 116408
        G_OBJECT_CLASS (dh_book_parent_class)->dispose (object);
Packit 116408
}
Packit 116408
Packit 116408
static void
Packit 116408
dh_book_finalize (GObject *object)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv;
Packit 116408
Packit 116408
        priv = dh_book_get_instance_private (DH_BOOK (object));
Packit 116408
Packit 116408
        g_clear_object (&priv->index_file);
Packit 116408
        g_free (priv->id);
Packit 116408
        g_free (priv->title);
Packit 116408
        g_free (priv->language);
Packit 116408
        _dh_util_free_book_tree (priv->tree);
Packit 116408
        g_list_free_full (priv->links, (GDestroyNotify)dh_link_unref);
Packit 116408
Packit 116408
        G_OBJECT_CLASS (dh_book_parent_class)->finalize (object);
Packit 116408
}
Packit 116408
Packit 116408
static void
Packit 116408
dh_book_class_init (DhBookClass *klass)
Packit 116408
{
Packit 116408
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
Packit 116408
Packit 116408
        object_class->dispose = dh_book_dispose;
Packit 116408
        object_class->finalize = dh_book_finalize;
Packit 116408
Packit 116408
        /**
Packit 116408
         * DhBook::enabled:
Packit 116408
         * @book: the #DhBook emitting the signal.
Packit 116408
         */
Packit 116408
        signals[SIGNAL_ENABLED] =
Packit 116408
                g_signal_new ("enabled",
Packit 116408
                              G_TYPE_FROM_CLASS (klass),
Packit 116408
                              G_SIGNAL_RUN_LAST,
Packit 116408
                              0,
Packit 116408
                              NULL, NULL, NULL,
Packit 116408
                              G_TYPE_NONE,
Packit 116408
                              0);
Packit 116408
Packit 116408
        /**
Packit 116408
         * DhBook::disabled:
Packit 116408
         * @book: the #DhBook emitting the signal.
Packit 116408
         */
Packit 116408
        signals[SIGNAL_DISABLED] =
Packit 116408
                g_signal_new ("disabled",
Packit 116408
                              G_TYPE_FROM_CLASS (klass),
Packit 116408
                              G_SIGNAL_RUN_LAST,
Packit 116408
                              0,
Packit 116408
                              NULL, NULL, NULL,
Packit 116408
                              G_TYPE_NONE,
Packit 116408
                              0);
Packit 116408
Packit 116408
        /**
Packit 116408
         * DhBook::updated:
Packit 116408
         * @book: the #DhBook emitting the signal.
Packit 116408
         *
Packit 116408
         * The ::updated signal is emitted when the index file has been
Packit 116408
         * modified (but the file still exists).
Packit 116408
         */
Packit 116408
        signals[SIGNAL_UPDATED] =
Packit 116408
                g_signal_new ("updated",
Packit 116408
                              G_TYPE_FROM_CLASS (klass),
Packit 116408
                              G_SIGNAL_RUN_LAST,
Packit 116408
                              0,
Packit 116408
                              NULL, NULL, NULL,
Packit 116408
                              G_TYPE_NONE,
Packit 116408
                              0);
Packit 116408
Packit 116408
        /**
Packit 116408
         * DhBook::deleted:
Packit 116408
         * @book: the #DhBook emitting the signal.
Packit 116408
         *
Packit 116408
         * The ::deleted signal is emitted when the index file has been deleted
Packit 116408
         * from the filesystem.
Packit 116408
         */
Packit 116408
        signals[SIGNAL_DELETED] =
Packit 116408
                g_signal_new ("deleted",
Packit 116408
                              G_TYPE_FROM_CLASS (klass),
Packit 116408
                              G_SIGNAL_RUN_LAST,
Packit 116408
                              0,
Packit 116408
                              NULL, NULL, NULL,
Packit 116408
                              G_TYPE_NONE,
Packit 116408
                              0);
Packit 116408
}
Packit 116408
Packit 116408
static void
Packit 116408
dh_book_init (DhBook *book)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv = dh_book_get_instance_private (book);
Packit 116408
Packit 116408
        priv->enabled = TRUE;
Packit 116408
        priv->last_monitor_event = BOOK_MONITOR_EVENT_NONE;
Packit 116408
}
Packit 116408
Packit 116408
static gboolean
Packit 116408
monitor_event_timeout_cb (gpointer data)
Packit 116408
{
Packit 116408
        DhBook *book = DH_BOOK (data);
Packit 116408
        DhBookPrivate *priv = dh_book_get_instance_private (book);
Packit 116408
        BookMonitorEvent last_monitor_event = priv->last_monitor_event;
Packit 116408
Packit 116408
        /* Reset event */
Packit 116408
        priv->last_monitor_event = BOOK_MONITOR_EVENT_NONE;
Packit 116408
        priv->monitor_event_timeout_id = 0;
Packit 116408
Packit 116408
        /* We'll get either is_deleted OR is_updated, not possible to have both
Packit 116408
         * or none.
Packit 116408
         */
Packit 116408
        switch (last_monitor_event)
Packit 116408
        {
Packit 116408
        case BOOK_MONITOR_EVENT_DELETED:
Packit 116408
                /* Emit the signal, but make sure we hold a reference while
Packit 116408
                 * doing it.
Packit 116408
                 */
Packit 116408
                g_object_ref (book);
Packit 116408
                g_signal_emit (book, signals[SIGNAL_DELETED], 0);
Packit 116408
                g_object_unref (book);
Packit 116408
                break;
Packit 116408
Packit 116408
        case BOOK_MONITOR_EVENT_UPDATED:
Packit 116408
                /* Emit the signal, but make sure we hold a reference while
Packit 116408
                 * doing it.
Packit 116408
                 */
Packit 116408
                g_object_ref (book);
Packit 116408
                g_signal_emit (book, signals[SIGNAL_UPDATED], 0);
Packit 116408
                g_object_unref (book);
Packit 116408
                break;
Packit 116408
Packit 116408
        case BOOK_MONITOR_EVENT_NONE:
Packit 116408
        default:
Packit 116408
                break;
Packit 116408
        }
Packit 116408
Packit 116408
        /* book can be destroyed here. */
Packit 116408
Packit 116408
        return G_SOURCE_REMOVE;
Packit 116408
}
Packit 116408
Packit 116408
static void
Packit 116408
index_file_changed_cb (GFileMonitor      *file_monitor,
Packit 116408
                       GFile             *file,
Packit 116408
                       GFile             *other_file,
Packit 116408
                       GFileMonitorEvent  event_type,
Packit 116408
                       DhBook            *book)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv = dh_book_get_instance_private (book);
Packit 116408
        gboolean reset_timeout = FALSE;
Packit 116408
Packit 116408
        /* CREATED may happen if the file is deleted and then created right
Packit 116408
         * away, as we're merging events.
Packit 116408
         */
Packit 116408
        if (event_type == G_FILE_MONITOR_EVENT_CHANGED ||
Packit 116408
            event_type == G_FILE_MONITOR_EVENT_CREATED) {
Packit 116408
                priv->last_monitor_event = BOOK_MONITOR_EVENT_UPDATED;
Packit 116408
                reset_timeout = TRUE;
Packit 116408
        } else if (event_type == G_FILE_MONITOR_EVENT_DELETED) {
Packit 116408
                priv->last_monitor_event = BOOK_MONITOR_EVENT_DELETED;
Packit 116408
                reset_timeout = TRUE;
Packit 116408
        }
Packit 116408
Packit 116408
        if (reset_timeout) {
Packit 116408
                if (priv->monitor_event_timeout_id != 0)
Packit 116408
                        g_source_remove (priv->monitor_event_timeout_id);
Packit 116408
Packit 116408
                priv->monitor_event_timeout_id = g_timeout_add_seconds (EVENT_MERGE_TIMEOUT_SECS,
Packit 116408
                                                                        monitor_event_timeout_cb,
Packit 116408
                                                                        book);
Packit 116408
        }
Packit 116408
}
Packit 116408
Packit 116408
/**
Packit 116408
 * dh_book_new:
Packit 116408
 * @index_file: the index file.
Packit 116408
 *
Packit 116408
 * Returns: (nullable): a new #DhBook object, or %NULL if parsing the index file
Packit 116408
 * failed.
Packit 116408
 */
Packit 116408
DhBook *
Packit 116408
dh_book_new (GFile *index_file)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv;
Packit 116408
        DhBook *book;
Packit 116408
        gchar *language = NULL;
Packit 116408
        GError *error = NULL;
Packit 116408
Packit 116408
        g_return_val_if_fail (G_IS_FILE (index_file), NULL);
Packit 116408
Packit 116408
        book = g_object_new (DH_TYPE_BOOK, NULL);
Packit 116408
        priv = dh_book_get_instance_private (book);
Packit 116408
Packit 116408
        priv->index_file = g_object_ref (index_file);
Packit 116408
Packit 116408
        /* Parse file storing contents in the book struct. */
Packit 116408
        if (!dh_parser_read_file (priv->index_file,
Packit 116408
                                  &priv->title,
Packit 116408
                                  &priv->id,
Packit 116408
                                  &language,
Packit 116408
                                  &priv->tree,
Packit 116408
                                  &priv->links,
Packit 116408
                                  &error)) {
Packit 116408
                /* It's fine if the file doesn't exist, as DhBookManager tries
Packit 116408
                 * to create a DhBook for each possible index file in a certain
Packit 116408
                 * book directory.
Packit 116408
                 */
Packit 116408
                if (error != NULL &&
Packit 116408
                    !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
Packit 116408
                        gchar *parse_name;
Packit 116408
Packit 116408
                        parse_name = g_file_get_parse_name (priv->index_file);
Packit 116408
Packit 116408
                        g_warning ("Failed to read “%s”: %s",
Packit 116408
                                   parse_name,
Packit 116408
                                   error->message);
Packit 116408
Packit 116408
                        g_free (parse_name);
Packit 116408
                }
Packit 116408
Packit 116408
                g_clear_error (&error);
Packit 116408
Packit 116408
                /* Deallocate the book, as we are not going to add it in the
Packit 116408
                 * manager.
Packit 116408
                 */
Packit 116408
                g_object_unref (book);
Packit 116408
                return NULL;
Packit 116408
        }
Packit 116408
Packit 116408
        /* Rewrite language, if any, including the prefix we want to use when
Packit 116408
         * seeing it, to standarize how the language group is shown.
Packit 116408
         * FIXME: maybe instead of a string, have a DhLanguage object which
Packit 116408
         * canonicalizes the string.
Packit 116408
         */
Packit 116408
        dh_util_ascii_strtitle (language);
Packit 116408
        priv->language = (language != NULL ?
Packit 116408
                          g_strdup_printf (_("Language: %s"), language) :
Packit 116408
                          g_strdup (_("Language: Undefined")));
Packit 116408
        g_free (language);
Packit 116408
Packit 116408
        /* Setup monitor for changes */
Packit 116408
Packit 116408
        priv->index_file_monitor = g_file_monitor_file (priv->index_file,
Packit 116408
                                                        G_FILE_MONITOR_NONE,
Packit 116408
                                                        NULL,
Packit 116408
                                                        &error);
Packit 116408
Packit 116408
        if (error != NULL) {
Packit 116408
                gchar *parse_name;
Packit 116408
Packit 116408
                parse_name = g_file_get_parse_name (priv->index_file);
Packit 116408
Packit 116408
                g_warning ("Failed to create file monitor for file “%s”: %s",
Packit 116408
                           parse_name,
Packit 116408
                           error->message);
Packit 116408
Packit 116408
                g_free (parse_name);
Packit 116408
                g_clear_error (&error);
Packit 116408
        }
Packit 116408
Packit 116408
        if (priv->index_file_monitor != NULL) {
Packit 116408
                g_signal_connect_object (priv->index_file_monitor,
Packit 116408
                                         "changed",
Packit 116408
                                         G_CALLBACK (index_file_changed_cb),
Packit 116408
                                         book,
Packit 116408
                                         0);
Packit 116408
        }
Packit 116408
Packit 116408
        return book;
Packit 116408
}
Packit 116408
Packit 116408
/**
Packit 116408
 * dh_book_get_index_file:
Packit 116408
 * @book: a #DhBook.
Packit 116408
 *
Packit 116408
 * Returns: (transfer none): the index file.
Packit 116408
 */
Packit 116408
GFile *
Packit 116408
dh_book_get_index_file (DhBook *book)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv;
Packit 116408
Packit 116408
        g_return_val_if_fail (DH_IS_BOOK (book), NULL);
Packit 116408
Packit 116408
        priv = dh_book_get_instance_private (book);
Packit 116408
Packit 116408
        return priv->index_file;
Packit 116408
}
Packit 116408
Packit 116408
/**
Packit 116408
 * dh_book_get_id:
Packit 116408
 * @book: a #DhBook.
Packit 116408
 *
Packit 116408
 * Gets the book ID. In the Devhelp index file format version 2, it is actually
Packit 116408
 * the “name”, not the ID, but “book ID” is clearer, “book name” can be confused
Packit 116408
 * with the title.
Packit 116408
 *
Packit 116408
 * Returns: the book ID.
Packit 116408
 */
Packit 116408
const gchar *
Packit 116408
dh_book_get_id (DhBook *book)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv;
Packit 116408
Packit 116408
        g_return_val_if_fail (DH_IS_BOOK (book), NULL);
Packit 116408
Packit 116408
        priv = dh_book_get_instance_private (book);
Packit 116408
Packit 116408
        return priv->id;
Packit 116408
}
Packit 116408
Packit 116408
/**
Packit 116408
 * dh_book_get_title:
Packit 116408
 * @book: a #DhBook.
Packit 116408
 *
Packit 116408
 * Returns: the book title.
Packit 116408
 */
Packit 116408
const gchar *
Packit 116408
dh_book_get_title (DhBook *book)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv;
Packit 116408
Packit 116408
        g_return_val_if_fail (DH_IS_BOOK (book), NULL);
Packit 116408
Packit 116408
        priv = dh_book_get_instance_private (book);
Packit 116408
Packit 116408
        return priv->title;
Packit 116408
}
Packit 116408
Packit 116408
/**
Packit 116408
 * dh_book_get_language:
Packit 116408
 * @book: a #DhBook.
Packit 116408
 *
Packit 116408
 * Returns: the programming language used in @book.
Packit 116408
 */
Packit 116408
const gchar *
Packit 116408
dh_book_get_language (DhBook *book)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv;
Packit 116408
Packit 116408
        g_return_val_if_fail (DH_IS_BOOK (book), NULL);
Packit 116408
Packit 116408
        priv = dh_book_get_instance_private (book);
Packit 116408
Packit 116408
        return priv->language;
Packit 116408
}
Packit 116408
Packit 116408
/**
Packit 116408
 * dh_book_get_links:
Packit 116408
 * @book: a #DhBook.
Packit 116408
 *
Packit 116408
 * Returns: (element-type DhLink) (transfer none) (nullable): the list of
Packit 116408
 * <emphasis>all</emphasis> #DhLink's part of @book, or %NULL if the book is
Packit 116408
 * disabled.
Packit 116408
 */
Packit 116408
GList *
Packit 116408
dh_book_get_links (DhBook *book)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv;
Packit 116408
Packit 116408
        g_return_val_if_fail (DH_IS_BOOK (book), NULL);
Packit 116408
Packit 116408
        priv = dh_book_get_instance_private (book);
Packit 116408
Packit 116408
        return priv->enabled ? priv->links : NULL;
Packit 116408
}
Packit 116408
Packit 116408
/**
Packit 116408
 * dh_book_get_tree:
Packit 116408
 * @book: a #DhBook.
Packit 116408
 *
Packit 116408
 * Gets the general structure of the book, as a tree. The tree contains only
Packit 116408
 * #DhLink's of type %DH_LINK_TYPE_BOOK or %DH_LINK_TYPE_PAGE. The other
Packit 116408
 * #DhLink's are not contained in the tree. To have a list of
Packit 116408
 * <emphasis>all</emphasis> #DhLink's part of the book, you need to call
Packit 116408
 * dh_book_get_links().
Packit 116408
 *
Packit 116408
 * Returns: (transfer none) (nullable): the tree of #DhLink's part of the @book,
Packit 116408
 * or %NULL if the book is disabled.
Packit 116408
 */
Packit 116408
GNode *
Packit 116408
dh_book_get_tree (DhBook *book)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv;
Packit 116408
Packit 116408
        g_return_val_if_fail (DH_IS_BOOK (book), NULL);
Packit 116408
Packit 116408
        priv = dh_book_get_instance_private (book);
Packit 116408
Packit 116408
        return priv->enabled ? priv->tree : NULL;
Packit 116408
}
Packit 116408
Packit 116408
/**
Packit 116408
 * dh_book_get_completion:
Packit 116408
 * @book: a #DhBook.
Packit 116408
 *
Packit 116408
 * Returns: (transfer none): the #DhCompletion of @book.
Packit 116408
 * Since: 3.28
Packit 116408
 */
Packit 116408
DhCompletion *
Packit 116408
dh_book_get_completion (DhBook *book)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv;
Packit 116408
Packit 116408
        g_return_val_if_fail (DH_IS_BOOK (book), NULL);
Packit 116408
Packit 116408
        priv = dh_book_get_instance_private (book);
Packit 116408
Packit 116408
        if (priv->completion == NULL) {
Packit 116408
                GList *l;
Packit 116408
Packit 116408
                priv->completion = dh_completion_new ();
Packit 116408
Packit 116408
                for (l = priv->links; l != NULL; l = l->next) {
Packit 116408
                        DhLink *link = l->data;
Packit 116408
                        const gchar *str;
Packit 116408
Packit 116408
                        /* Do not provide completion for book titles. Normally
Packit 116408
                         * the user doesn't need it, it's more convenient to
Packit 116408
                         * choose a book with the DhBookTree.
Packit 116408
                         */
Packit 116408
                        if (dh_link_get_link_type (link) == DH_LINK_TYPE_BOOK)
Packit 116408
                                continue;
Packit 116408
Packit 116408
                        str = dh_link_get_name (link);
Packit 116408
                        dh_completion_add_string (priv->completion, str);
Packit 116408
                }
Packit 116408
Packit 116408
                dh_completion_sort (priv->completion);
Packit 116408
        }
Packit 116408
Packit 116408
        return priv->completion;
Packit 116408
}
Packit 116408
Packit 116408
/**
Packit 116408
 * dh_book_get_enabled:
Packit 116408
 * @book: a #DhBook.
Packit 116408
 *
Packit 116408
 * Returns: whether the book is enabled.
Packit 116408
 */
Packit 116408
gboolean
Packit 116408
dh_book_get_enabled (DhBook *book)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv;
Packit 116408
Packit 116408
        g_return_val_if_fail (DH_IS_BOOK (book), FALSE);
Packit 116408
Packit 116408
        priv = dh_book_get_instance_private (book);
Packit 116408
Packit 116408
        return priv->enabled;
Packit 116408
}
Packit 116408
Packit 116408
/**
Packit 116408
 * dh_book_set_enabled:
Packit 116408
 * @book: a #DhBook.
Packit 116408
 * @enabled: the new value.
Packit 116408
 *
Packit 116408
 * Enables or disables the book.
Packit 116408
 */
Packit 116408
void
Packit 116408
dh_book_set_enabled (DhBook   *book,
Packit 116408
                     gboolean  enabled)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv;
Packit 116408
Packit 116408
        g_return_if_fail (DH_IS_BOOK (book));
Packit 116408
Packit 116408
        priv = dh_book_get_instance_private (book);
Packit 116408
Packit 116408
        enabled = enabled != FALSE;
Packit 116408
Packit 116408
        /* Create DhCompletion, because if all the DhCompletion objects need to
Packit 116408
         * be created (synchronously) at the time of the first completion, it
Packit 116408
         * can make the GUI not responsive (measured time was for example 40ms
Packit 116408
         * to create the DhCompletion's for 17 books, which is not a lot of
Packit 116408
         * books). On application startup it is less a problem.
Packit 116408
         */
Packit 116408
        if (enabled)
Packit 116408
                dh_book_get_completion (book);
Packit 116408
Packit 116408
        if (priv->enabled != enabled) {
Packit 116408
                priv->enabled = enabled;
Packit 116408
                g_signal_emit (book,
Packit 116408
                               enabled ? signals[SIGNAL_ENABLED] : signals[SIGNAL_DISABLED],
Packit 116408
                               0);
Packit 116408
        }
Packit 116408
}
Packit 116408
Packit 116408
/**
Packit 116408
 * dh_book_cmp_by_id:
Packit 116408
 * @a: a #DhBook.
Packit 116408
 * @b: a #DhBook.
Packit 116408
 *
Packit 116408
 * Compares the #DhBook's by their IDs, with g_ascii_strcasecmp().
Packit 116408
 *
Packit 116408
 * Returns: an integer less than, equal to, or greater than zero, if @a is <, ==
Packit 116408
 * or > than @b.
Packit 116408
 */
Packit 116408
gint
Packit 116408
dh_book_cmp_by_id (DhBook *a,
Packit 116408
                   DhBook *b)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv_a;
Packit 116408
        DhBookPrivate *priv_b;
Packit 116408
Packit 116408
        if (a == NULL || b == NULL)
Packit 116408
                return -1;
Packit 116408
Packit 116408
        priv_a = dh_book_get_instance_private (a);
Packit 116408
        priv_b = dh_book_get_instance_private (b);
Packit 116408
Packit 116408
        if (priv_a->id == NULL || priv_b->id == NULL)
Packit 116408
                return -1;
Packit 116408
Packit 116408
        return g_ascii_strcasecmp (priv_a->id, priv_b->id);
Packit 116408
}
Packit 116408
Packit 116408
/**
Packit 116408
 * dh_book_cmp_by_title:
Packit 116408
 * @a: a #DhBook.
Packit 116408
 * @b: a #DhBook.
Packit 116408
 *
Packit 116408
 * Compares the #DhBook's by their title.
Packit 116408
 *
Packit 116408
 * Returns: an integer less than, equal to, or greater than zero, if @a is <, ==
Packit 116408
 * or > than @b.
Packit 116408
 */
Packit 116408
gint
Packit 116408
dh_book_cmp_by_title (DhBook *a,
Packit 116408
                      DhBook *b)
Packit 116408
{
Packit 116408
        DhBookPrivate *priv_a;
Packit 116408
        DhBookPrivate *priv_b;
Packit 116408
Packit 116408
        if (a == NULL || b == NULL)
Packit 116408
                return -1;
Packit 116408
Packit 116408
        priv_a = dh_book_get_instance_private (a);
Packit 116408
        priv_b = dh_book_get_instance_private (b);
Packit 116408
Packit 116408
        if (priv_a->title == NULL || priv_b->title == NULL)
Packit 116408
                return -1;
Packit 116408
Packit 116408
        return g_utf8_collate (priv_a->title, priv_b->title);
Packit 116408
}