medit/moo/mooui/moouixml.c

719 lines
24 KiB
C

/*
* mooui/moouixml.c
*
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* See COPYING file that comes with this distribution.
*/
#include "mooui/moouixml.h"
#include "mooutils/moocompat.h"
#include "mooutils/moomarshals.h"
#include <gtk/gtk.h>
static void moo_ui_xml_class_init (MooUIXMLClass *klass);
static void moo_ui_xml_init (MooUIXML *xml);
static void moo_ui_xml_finalize (GObject *object);
static void moo_ui_xml_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void moo_ui_xml_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void moo_ui_xml_set_ui (MooUIXML *xml,
const char *ui);
static void moo_ui_xml_set_markup (MooUIXML *xml,
MooMarkupDoc *doc);
enum {
PROP_0,
PROP_UI,
PROP_MARKUP
};
enum {
CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = {0};
/* MOO_TYPE_UI_XML */
G_DEFINE_TYPE (MooUIXML, moo_ui_xml, G_TYPE_OBJECT)
static void moo_ui_xml_class_init (MooUIXMLClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = moo_ui_xml_finalize;
gobject_class->set_property = moo_ui_xml_set_property;
gobject_class->get_property = moo_ui_xml_get_property;
g_object_class_install_property (gobject_class,
PROP_UI,
g_param_spec_string ("ui",
"ui",
"ui",
NULL,
(GParamFlags) (
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT)));
g_object_class_install_property (gobject_class,
PROP_MARKUP,
g_param_spec_boxed ("markup",
"markup",
"markup",
MOO_TYPE_MARKUP_DOC,
(GParamFlags) (
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT)));
signals[CHANGED] =
g_signal_new ("changed",
G_OBJECT_CLASS_TYPE (klass),
(GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_STRUCT_OFFSET (MooUIXMLClass, changed),
NULL, NULL,
_moo_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void moo_ui_xml_init (MooUIXML *xml)
{
xml->doc = NULL;
}
static void moo_ui_xml_finalize (GObject *object)
{
MooUIXML *xml = MOO_UI_XML (object);
if (xml->doc) moo_markup_doc_unref (xml->doc);
xml->doc = NULL;
G_OBJECT_CLASS (moo_ui_xml_parent_class)->finalize (object);
}
static void moo_ui_xml_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MooUIXML *xml = MOO_UI_XML (object);
switch (prop_id)
{
case PROP_UI:
g_value_set_string (value,
moo_ui_xml_get_ui (xml));
break;
case PROP_MARKUP:
g_value_set_boxed (value,
moo_ui_xml_get_markup (xml));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void moo_ui_xml_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MooUIXML *xml = MOO_UI_XML (object);
switch (prop_id)
{
case PROP_UI:
moo_ui_xml_set_ui (xml, g_value_get_string (value));
break;
case PROP_MARKUP:
if (g_value_get_boxed (value))
moo_ui_xml_set_markup (xml, MOO_MARKUP_DOC (g_value_get_boxed (value)));
else
moo_ui_xml_set_markup (xml, NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void moo_ui_xml_set_ui (MooUIXML *xml,
const char *ui)
{
if (!ui)
{
if (xml->doc) moo_markup_doc_unref (xml->doc);
xml->doc = NULL;
}
else {
if (!xml->doc)
{
GError *err = NULL;
xml->doc = moo_markup_parse_memory (ui, -1, &err);
if (!xml->doc) {
g_critical ("moo_ui_xml_set_ui: could not parse markup\n%s", ui);
if (err) {
g_critical ("%s", err->message);
g_error_free (err);
}
}
}
else
{
g_critical ("%s: implement me", G_STRLOC);
}
}
}
static void moo_ui_xml_set_markup (MooUIXML *xml,
MooMarkupDoc *doc)
{
if (doc == xml->doc) return;
if (xml->doc) moo_markup_doc_unref (xml->doc);
xml->doc = doc;
if (xml->doc) moo_markup_doc_ref (xml->doc);
}
char *moo_ui_xml_get_ui (MooUIXML *xml)
{
g_return_val_if_fail (MOO_IS_UI_XML (xml), NULL);
if (!xml->doc) return NULL;
return moo_markup_node_get_string (MOO_MARKUP_NODE (xml->doc));
}
const MooMarkupDoc *moo_ui_xml_get_markup (MooUIXML *xml)
{
g_return_val_if_fail (MOO_IS_UI_XML (xml), NULL);
return xml->doc;
}
gboolean moo_ui_xml_add_ui_from_string (MooUIXML *xml,
const char *ui,
int len,
GError **error)
{
GError *err = NULL;
g_return_val_if_fail (MOO_IS_UI_XML (xml) && ui != NULL, FALSE);
if (xml->doc) {
g_critical ("%s: implement me", G_STRLOC);
return FALSE;
}
xml->doc = moo_markup_parse_memory (ui, len, &err);
if (xml->doc)
{
if (error) *error = NULL;
g_signal_emit (xml, signals[CHANGED], 0);
return TRUE;
}
if (err)
{
if (error) *error = err;
else g_error_free (err);
}
return FALSE;
}
gboolean moo_ui_xml_add_ui_from_file (MooUIXML *xml,
const char *file,
GError **error)
{
g_return_val_if_fail (MOO_IS_UI_XML (xml) && file != NULL, FALSE);
if (xml->doc) {
g_critical ("%s: implement me", G_STRLOC);
return FALSE;
}
GError *err = NULL;
xml->doc = moo_markup_parse_file (file, &err);
if (xml->doc)
{
if (error) *error = NULL;
g_signal_emit (xml, signals[CHANGED], 0);
return TRUE;
}
if (err)
{
if (error) *error = err;
else g_error_free (err);
}
return FALSE;
}
static void erase_container (GtkContainer *container)
{
GList *children = gtk_container_get_children (container);
GList *l;
for (l = children; l; l = l->next)
gtk_container_remove (container, GTK_WIDGET (l->data));
g_list_free (children);
}
static GtkWidget *create_widget (MooMarkupElement *node,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips,
GtkWidget *widget);
static GtkWidget *create_menu_bar (MooMarkupElement *node,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips,
GtkMenuBar *menubar);
static GtkWidget *create_menu (MooMarkupElement *node,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips,
GtkMenu *menu);
static gboolean create_menu_item (MooMarkupElement *node,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips,
GtkMenuShell *menu_shell,
int position);
static GtkWidget *create_toolbar (MooMarkupElement *node,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips,
GtkToolbar *toolbar);
static gboolean create_tool_item (MooMarkupElement *node,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips,
GtkToolbar *toolbar,
int position);
GtkWidget *moo_ui_xml_create_widget (MooUIXML *xml,
const char *path,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips)
{
MooMarkupElement *ui, *node;
g_return_val_if_fail (MOO_IS_UI_XML (xml) && path != NULL, NULL);
g_return_val_if_fail (xml->doc != NULL, NULL);
ui = moo_markup_get_root_element (xml->doc, "ui");
g_return_val_if_fail (ui != NULL, NULL);
node = moo_markup_get_element (MOO_MARKUP_NODE (ui), path);
g_return_val_if_fail (node != NULL, NULL);
return create_widget (node, actions, accel_group, tooltips, NULL);
}
gboolean moo_ui_xml_has_widget (MooUIXML *xml,
const char *path)
{
MooMarkupElement *ui, *node;
g_return_val_if_fail (MOO_IS_UI_XML (xml) && path != NULL, FALSE);
if (!xml->doc) return FALSE;
ui = moo_markup_get_root_element (xml->doc, "ui");
g_return_val_if_fail (ui != NULL, FALSE);
node = moo_markup_get_element (MOO_MARKUP_NODE (ui), path);
return node != NULL;
}
static GtkWidget *create_widget (MooMarkupElement *node,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips,
GtkWidget *widget)
{
if (!g_ascii_strcasecmp (node->name, "menubar")) {
g_return_val_if_fail (!widget || GTK_IS_MENU_BAR (widget), NULL);
return create_menu_bar (node, actions, accel_group, tooltips, GTK_MENU_BAR (widget));
}
else if (!g_ascii_strcasecmp (node->name, "menu")) {
g_return_val_if_fail (!widget || GTK_IS_MENU (widget), NULL);
return create_menu (node, actions, accel_group, tooltips, GTK_MENU (widget));
}
else if (!g_ascii_strcasecmp (node->name, "toolbar")) {
g_return_val_if_fail (!widget || GTK_IS_TOOLBAR (widget), NULL);
return create_toolbar (node, actions, accel_group, tooltips, GTK_TOOLBAR (widget));
}
else
{
g_critical ("cannot create widget '%s'", node->name);
return NULL;
}
}
GtkWidget *moo_ui_xml_update_widget (MooUIXML *xml,
GtkWidget *widget,
const char *path,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips)
{
MooMarkupElement *ui, *node;
g_return_val_if_fail (MOO_IS_UI_XML (xml) && path != NULL, NULL);
g_return_val_if_fail (xml->doc != NULL, NULL);
ui = moo_markup_get_root_element (xml->doc, "ui");
g_return_val_if_fail (ui != NULL, NULL);
node = moo_markup_get_element (MOO_MARKUP_NODE (ui), path);
g_return_val_if_fail (node != NULL, NULL);
return create_widget (node, actions, accel_group, tooltips, widget);
}
static GtkWidget *create_menu_bar (MooMarkupElement *node,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips,
GtkMenuBar *menubar)
{
MooMarkupNode *child;
if (menubar)
erase_container (GTK_CONTAINER (menubar));
else
menubar = GTK_MENU_BAR (gtk_menu_bar_new ());
for (child = node->children; child != NULL; child = child->next)
{
if (MOO_MARKUP_IS_ELEMENT (child))
g_return_val_if_fail (create_menu_item (MOO_MARKUP_ELEMENT (child),
actions,
accel_group, tooltips,
GTK_MENU_SHELL (menubar), -1),
GTK_WIDGET (menubar));
}
return GTK_WIDGET (menubar);
}
static GtkWidget *create_menu (MooMarkupElement *node,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips,
GtkMenu *menu)
{
gboolean start = TRUE;
gboolean need_separator = FALSE;
MooMarkupNode *ch;
if (menu)
erase_container (GTK_CONTAINER (menu));
else
menu = GTK_MENU (gtk_menu_new ());
gtk_menu_set_accel_group (GTK_MENU (menu), accel_group);
for (ch = node->children; ch != NULL; ch = ch->next)
{
MooMarkupElement *child;
if (!MOO_MARKUP_IS_ELEMENT (ch)) continue;
child = MOO_MARKUP_ELEMENT (ch);
if (!g_ascii_strcasecmp (child->name, "menu") ||
!g_ascii_strcasecmp (child->name, "item"))
{
if (need_separator) {
GtkWidget *sep = gtk_separator_menu_item_new ();
gtk_widget_show (sep);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), sep);
need_separator = FALSE;
}
g_return_val_if_fail (create_menu_item (child, actions,
accel_group, tooltips,
GTK_MENU_SHELL (menu), -1),
GTK_WIDGET (menu));
start = FALSE;
}
else if (!g_ascii_strcasecmp (child->name, "placeholder"))
{
}
else if (!g_ascii_strcasecmp (child->name, "separator"))
{
if (!start) need_separator = TRUE;
}
else
{
g_critical ("unknown node %s\n", child->name);
return GTK_WIDGET (menu);
}
}
return GTK_WIDGET (menu);
}
static gboolean create_menu_item (MooMarkupElement *node,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips,
GtkMenuShell *menu_shell,
int position)
{
GtkWidget *menuitem = NULL;
const char *action_name = moo_markup_get_prop (node, "action");
MooAction *action = NULL;
if (action_name)
{
action = moo_action_group_get_action (actions, action_name);
if (!action) g_critical ("could not find action '%s'", action_name);
}
if (action)
{
if (action->dead) return TRUE;
menuitem = moo_action_create_menu_item (action, menu_shell, position);
}
else
{
const char *stock_id = moo_markup_get_prop (node, "stock");
if (stock_id)
{
menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
}
else
{
const char *label = moo_markup_get_prop (node, "label");
if (!label) {
g_warning ("could not get label for menu");
label = moo_markup_get_prop (node, "name");
if (!label) {
g_warning ("using node name as label");
label = moo_markup_get_prop (node, "name");
if (!label) label = "";
}
}
if (!label)
menuitem = gtk_menu_item_new ();
else
menuitem = gtk_menu_item_new_with_mnemonic (label);
}
if (menuitem)
{
gtk_widget_show (menuitem);
if (position >= 0)
gtk_menu_shell_insert (menu_shell, menuitem, position);
else
gtk_menu_shell_append (menu_shell, menuitem);
}
}
if (!menuitem) return FALSE;
if (!g_ascii_strcasecmp (node->name, "menu")) {
GtkWidget *menu = create_menu (node, actions, accel_group, tooltips, NULL);
gtk_widget_show (menu);
g_return_val_if_fail (menu != NULL, FALSE);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu);
}
return TRUE;
}
static GtkWidget *create_toolbar (MooMarkupElement *node,
MooActionGroup *actions,
GtkAccelGroup *accel_group,
GtkTooltips *tooltips,
GtkToolbar *toolbar)
{
gboolean start = TRUE;
gboolean need_separator = FALSE;
MooMarkupNode *ch;
if (toolbar)
erase_container (GTK_CONTAINER (toolbar));
else
toolbar = GTK_TOOLBAR (gtk_toolbar_new ());
for (ch = node->children; ch != NULL; ch = ch->next)
{
MooMarkupElement *child;
if (!MOO_MARKUP_IS_ELEMENT (ch)) continue;
child = MOO_MARKUP_ELEMENT (ch);
if (!g_ascii_strcasecmp (child->name, "item"))
{
if (need_separator) {
#if GTK_CHECK_VERSION(2,4,0)
GtkToolItem *sep = gtk_separator_tool_item_new ();
gtk_widget_show (GTK_WIDGET (sep));
gtk_toolbar_insert (toolbar, sep, -1);
#else /* !GTK_CHECK_VERSION(2,4,0) */
gtk_toolbar_append_space (toolbar);
#endif /* !GTK_CHECK_VERSION(2,4,0) */
need_separator = FALSE;
}
create_tool_item (child, actions, accel_group, tooltips, toolbar, -1);
start = FALSE;
}
else if (!g_ascii_strcasecmp (child->name, "separator"))
{
if (!start) need_separator = TRUE;
}
else
g_critical ("unknown toolbar item '%s'", child->name);
}
return GTK_WIDGET (toolbar);
}
static gboolean create_tool_item (MooMarkupElement *node,
MooActionGroup *actions,
G_GNUC_UNUSED GtkAccelGroup *accel_group,
G_GNUC_UNUSED GtkTooltips *tooltips,
GtkToolbar *toolbar,
int position)
{
const char *action_name = moo_markup_get_prop (node, "action");
const char *label, *tip;
#if GTK_CHECK_VERSION(2,4,0)
GtkToolItem *item;
#else /* !GTK_CHECK_VERSION(2,4,0) */
GtkWidget *item;
#endif /* !GTK_CHECK_VERSION(2,4,0) */
if (action_name)
{
MooAction *action = moo_action_group_get_action (actions, action_name);
if (!action)
{
g_critical ("could not find action '%s'", action_name);
}
else
{
if (action->dead) return TRUE;
if (!moo_action_create_tool_item (action, toolbar, position))
g_critical ("could not create tool item for action %s", action_name);
else
return TRUE;
}
}
label = moo_markup_get_prop (node, "label");
if (!label) {
g_warning ("could not get label for toolbar button");
label = moo_markup_get_prop (node, "name");
if (!label) {
g_warning ("using node name as label");
label = moo_markup_get_prop (node, "name");
if (!label) label = "";
}
}
tip = moo_markup_get_prop (node, "tooltip");
#if GTK_CHECK_VERSION(2,4,0)
item = gtk_tool_button_new (NULL, label ? label : "");
if (tip) gtk_tool_item_set_tooltip (item, tooltips, tip, tip);
gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (item), TRUE);
gtk_widget_show (GTK_WIDGET (item));
gtk_toolbar_insert (toolbar, item, position);
gtk_container_child_set (GTK_CONTAINER (toolbar), GTK_WIDGET (item),
"homogeneous", FALSE, NULL);
#else /* !GTK_CHECK_VERSION(2,4,0) */
item = gtk_toolbar_insert_item (toolbar, label ? label : "",
tip, tip,
NULL, NULL, NULL, position);
gtk_button_set_use_underline (GTK_BUTTON (item), TRUE);
#endif /* !GTK_CHECK_VERSION(2,4,0) */
return TRUE;
}
MooUIXML *moo_ui_xml_new (void)
{
return MOO_UI_XML (g_object_new (MOO_TYPE_UI_XML, NULL));
}
MooUIXML *moo_ui_xml_new_from_string (const char *xml,
GError **error)
{
GError *err = NULL;
MooMarkupDoc *doc;
g_return_val_if_fail (xml != NULL, NULL);
doc = moo_markup_parse_memory (xml, -1, &err);
if (!doc) {
if (err) {
if (error) *error = err;
else g_error_free (err);
}
else if (error)
*error = NULL;
return NULL;
}
return MOO_UI_XML (g_object_new (MOO_TYPE_UI_XML,
"markup", doc,
NULL));
}
MooUIXML *moo_ui_xml_new_from_file (const char *file,
GError **error)
{
GError *err = NULL;
MooMarkupDoc *doc;
g_return_val_if_fail (file != NULL, NULL);
doc = moo_markup_parse_file (file, &err);
if (!doc) {
if (err) {
if (error) *error = err;
else g_error_free (err);
}
else if (error)
*error = NULL;
return NULL;
}
return MOO_UI_XML (g_object_new (MOO_TYPE_UI_XML,
"markup", doc,
NULL));
}