/*
* 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 C style pre-processing in-line in the XML:
* #if [!]EXPRESS\n ... \n#endif\n
*/
lines = g_strsplit (ui_definition, "\n", -1);
for (ii = 0; lines[ii] != NULL; ii++) {
if (lines[ii][0] == '#') {
if (!strncmp (lines[ii], "#if ", 4)) {
gboolean not_express = lines[ii][4] == '!';
include = express_mode ^ not_express;
lines[ii][0] = '\0';
in_conditional = TRUE;
} else if (!strncmp (lines[ii], "#endif", 6)) {
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;
}