343 lines
8.6 KiB
C
343 lines
8.6 KiB
C
/*
|
|
* 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/>
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* SECTION: e-ui-manager
|
|
* @short_description: construct menus and toolbars from a UI definition
|
|
* @include: e-util/e-ui-manager.h
|
|
*
|
|
* This is a #GtkUIManager with support for Evolution's "express" mode,
|
|
* which influences the parsing of UI definitions.
|
|
**/
|
|
|
|
#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;
|
|
}
|