aboutsummaryrefslogblamecommitdiffstats
path: root/e-util/e-ui-manager.c
blob: d7c249af06c81734ca0088664b387fdcb0f23f8e (plain) (tree)


















                                                                             
                           

























































































































































































































































































































                                                                           
/*
 * e-ui-manager.c
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * This program 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
 *
 */

#include "e-ui-manager.h"
#include "e-util-private.h"

#include <string.h>

#define E_UI_MANAGER_GET_PRIVATE(obj) \
    (G_TYPE_INSTANCE_GET_PRIVATE \
    ((obj), E_TYPE_UI_MANAGER, EUIManagerPrivate))

/*
 * --- NOTE TO SELF ---
 *
 * While creating this class I was tempted to add an "id" property which
 * EPluginUI could extract from a given EUIManager instead of having the
 * public EPluginUI functions take a separate "id" argument.  Seemed like
 * a nice cleanup until I remembered that an EUIManager instance can have
 * multiple IDs ("aliases"), as in the case of EShellWindow's UI manager.
 * So the UI Manager ID and the instance still need to be kept separate.
 *
 * Mentioning it here in case I forget why I didn't go through with it.
 */

struct _EUIManagerPrivate {
    guint express_mode : 1;
};

enum {
    PROP_0,
    PROP_EXPRESS_MODE
};

static gpointer parent_class;

static void
ui_manager_set_property (GObject *object,
                         guint property_id,
                         const GValue *value,
                         GParamSpec *pspec)
{
    switch (property_id) {
        case PROP_EXPRESS_MODE:
            e_ui_manager_set_express_mode (
                E_UI_MANAGER (object),
                g_value_get_boolean (value));
            return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
ui_manager_get_property (GObject *object,
                         guint property_id,
                         GValue *value,
                         GParamSpec *pspec)
{
    switch (property_id) {
        case PROP_EXPRESS_MODE:
            g_value_set_boolean (
                value, e_ui_manager_get_express_mode (
                E_UI_MANAGER (object)));
            return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static gchar *
ui_manager_filter_ui (EUIManager *ui_manager,
                      const gchar *ui_definition)
{
    gchar **lines;
    gchar *filtered;
    gboolean express_mode;
    gboolean in_conditional = FALSE;
    gboolean include = TRUE;
    gint ii;

    express_mode = e_ui_manager_get_express_mode (ui_manager);

    /*
     * Very simple line based pre-processing based on comments:
     * <!-- if [!]EXPRESS -->\n ... \n<!-- endif -->\n
     */

    lines = g_strsplit (ui_definition, "\n", -1);

    for (ii = 0; lines[ii] != NULL; ii++) {
        gchar *cp;

        if ((cp = strstr (lines[ii], "<!-- if "))) {
            gboolean not_express = lines[ii][8] == '!';
            include = express_mode ^ not_express;
            lines[ii][0] = '\0';
            in_conditional = TRUE;
        } else if ((cp = strstr (lines[ii], "<!-- endif"))) {
            lines[ii][0] = '\0';
            include = TRUE;
            in_conditional = FALSE;
        }
        if (!include)
            lines[ii][0] = '\0';
    }

    filtered = g_strjoinv ("\n", lines);

    g_strfreev (lines);

    return filtered;
}

static void
ui_manager_class_init (EUIManagerClass *class)
{
    GObjectClass *object_class;

    parent_class = g_type_class_peek_parent (class);
    g_type_class_add_private (class, sizeof (EUIManagerPrivate));

    object_class = G_OBJECT_CLASS (class);
    object_class->set_property = ui_manager_set_property;
    object_class->get_property = ui_manager_get_property;

    class->filter_ui = ui_manager_filter_ui;

    g_object_class_install_property (
        object_class,
        PROP_EXPRESS_MODE,
        g_param_spec_boolean (
            "express-mode",
            "Express Mode",
            NULL,
            FALSE,
            G_PARAM_READWRITE |
            G_PARAM_CONSTRUCT));
}

static void
ui_manager_init (EUIManager *ui_manager)
{
    ui_manager->priv = E_UI_MANAGER_GET_PRIVATE (ui_manager);
}

GType
e_ui_manager_get_type (void)
{
    static GType type = 0;

    if (G_UNLIKELY (type == 0)) {
        static const GTypeInfo type_info = {
            sizeof (EUIManagerClass),
            (GBaseInitFunc) NULL,
            (GBaseFinalizeFunc) NULL,
            (GClassInitFunc) ui_manager_class_init,
            (GClassFinalizeFunc) NULL,
            NULL,  /* class_data */
            sizeof (EUIManager),
            0,     /* n_preallocs */
            (GInstanceInitFunc) ui_manager_init,
            NULL   /* value_table */
        };

        type = g_type_register_static (
            GTK_TYPE_UI_MANAGER, "EUIManager", &type_info, 0);
    }

    return type;
}

/**
 * e_ui_manager_new:
 *
 * Returns a new #EUIManager instance.
 *
 * Returns: a new #EUIManager instance
 **/
GtkUIManager *
e_ui_manager_new (void)
{
    return g_object_new (E_TYPE_UI_MANAGER, NULL);
}

/**
 * e_ui_manager_get_express_mode:
 * @ui_manager: an #EUIManager
 *
 * Returns the "express mode" flag in @ui_manager.
 *
 * Returns: %TRUE if @ui_manager is set to express mode
 **/
gboolean
e_ui_manager_get_express_mode (EUIManager *ui_manager)
{
    g_return_val_if_fail (E_IS_UI_MANAGER (ui_manager), FALSE);

    return ui_manager->priv->express_mode;
}

/**
 * e_ui_manager_set_express_mode:
 * @ui_manager: an #EUIManager
 * @express_mode: express mode flag
 *
 * Sets the "express mode" flag in @ui_manager, which influences how
 * UI definitions are loaded.
 **/
void
e_ui_manager_set_express_mode (EUIManager *ui_manager,
                               gboolean express_mode)
{
    g_return_if_fail (E_IS_UI_MANAGER (ui_manager));

    ui_manager->priv->express_mode = express_mode;

    g_object_notify (G_OBJECT (ui_manager), "express-mode");
}

/**
 * e_ui_manager_add_ui_from_file:
 * @ui_manager: an #EUIManager
 * @basename: basename of the UI definition file
 *
 * Loads a UI definition into @ui_manager from Evolution's UI directory.
 * If the EUIManager:express-mode property is %TRUE, a simplified version
 * of the UI may be presented.
 *
 * Failure here is fatal, since the application can't function without
 * its core UI definitions.
 *
 * Returns: The merge ID for the merged UI.  The merge ID can be used to
 *          unmerge the UI with gtk_ui_manager_remove_ui().
 **/
guint
e_ui_manager_add_ui_from_file (EUIManager *ui_manager,
                               const gchar *basename)
{
    EUIManagerClass *class;
    gchar *filename;
    gchar *contents;
    guint merge_id = 0;
    GError *error = NULL;

    g_return_val_if_fail (E_IS_UI_MANAGER (ui_manager), 0);
    g_return_val_if_fail (basename != NULL, 0);

    class = E_UI_MANAGER_GET_CLASS (ui_manager);
    g_return_val_if_fail (class->filter_ui != NULL, 0);

    filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL);

    if (g_file_get_contents (filename, &contents, NULL, &error)) {
        gchar *filtered;

        /* We could call e_ui_manager_add_ui_from_string() here,
         * but if an error occurs we'd like to include the file
         * name in the error message. */

        filtered = class->filter_ui (ui_manager, contents);

        merge_id = gtk_ui_manager_add_ui_from_string (
            GTK_UI_MANAGER (ui_manager), filtered, -1, &error);

        g_free (filtered);
        g_free (contents);
    }

    g_free (filename);

    if (error != NULL) {
        g_error ("%s: %s", basename, error->message);
        g_assert_not_reached ();
    }

    return merge_id;
}

/**
 * e_ui_manager_add_ui_from_string:
 * @ui_manager: an #EUIManager
 * @ui_definition: the UI XML in NULL terminated string form
 * @error: return location for a #GError, or %NULL
 *
 * Loads the given UI definition into @ui_manager.  If the
 * EUIManager:express-mode property is %TRUE, a simplified version of
 * the UI may be presented.
 *
 * Failure here is <i>not</i> fatal, since the function is primarily
 * used to load UI definitions for plugins, which we can get by without.
 *
 * Returns: The merge ID for the merged UI.  The merge ID can be used to
 *          unmerge the UI with gtk_ui_manager_remove_ui().
 **/
guint
e_ui_manager_add_ui_from_string (EUIManager *ui_manager,
                                 const gchar *ui_definition,
                                 GError **error)
{
    EUIManagerClass *class;
    gchar *filtered;
    guint merge_id;

    g_return_val_if_fail (E_IS_UI_MANAGER (ui_manager), 0);
    g_return_val_if_fail (ui_definition != NULL, 0);

    class = E_UI_MANAGER_GET_CLASS (ui_manager);
    g_return_val_if_fail (class->filter_ui != NULL, 0);

    filtered = class->filter_ui (ui_manager, ui_definition);

    merge_id = gtk_ui_manager_add_ui_from_string (
        GTK_UI_MANAGER (ui_manager), filtered, -1, error);

    g_free (filtered);

    return merge_id;
}