diff options
author | Srinivasa Ragavan <sragavan@gnome.org> | 2012-02-29 19:20:51 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2012-03-03 22:02:33 +0800 |
commit | 449e05f1b232fa2a1a92ae7c08c0f2b8870ac23a (patch) | |
tree | 0fdbe7ea1b08d7fa6249b521b7cf22c3c11983ed /libevolution-utils | |
parent | f728daff220aae4d58946c6d10e6392cdbae758e (diff) | |
download | gsoc2013-evolution-449e05f1b232fa2a1a92ae7c08c0f2b8870ac23a.tar.gz gsoc2013-evolution-449e05f1b232fa2a1a92ae7c08c0f2b8870ac23a.tar.zst gsoc2013-evolution-449e05f1b232fa2a1a92ae7c08c0f2b8870ac23a.zip |
Move EAlert* and e-xml-utils to libevolution-utils.
Diffstat (limited to 'libevolution-utils')
-rw-r--r-- | libevolution-utils/e-alert-dialog.c | 405 | ||||
-rw-r--r-- | libevolution-utils/e-alert-dialog.h | 80 | ||||
-rw-r--r-- | libevolution-utils/e-alert-sink.c | 93 | ||||
-rw-r--r-- | libevolution-utils/e-alert-sink.h | 62 | ||||
-rw-r--r-- | libevolution-utils/e-alert.c | 996 | ||||
-rw-r--r-- | libevolution-utils/e-alert.h | 119 | ||||
-rw-r--r-- | libevolution-utils/e-xml-utils.c | 448 | ||||
-rw-r--r-- | libevolution-utils/e-xml-utils.h | 93 |
8 files changed, 2296 insertions, 0 deletions
diff --git a/libevolution-utils/e-alert-dialog.c b/libevolution-utils/e-alert-dialog.c new file mode 100644 index 0000000000..5f426aacd0 --- /dev/null +++ b/libevolution-utils/e-alert-dialog.c @@ -0,0 +1,405 @@ +/* + * 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/> + * + * + * Authors: + * Michael Zucchi <notzed@ximian.com> + * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * Copyright (C) 2009 Intel Corporation + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> + +#include "e-alert-dialog.h" + +#include "e-util.h" + +#define E_ALERT_DIALOG_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_ALERT_DIALOG, EAlertDialogPrivate)) + +struct _EAlertDialogPrivate { + GtkWidget *content_area; /* not referenced */ + EAlert *alert; +}; + +enum { + PROP_0, + PROP_ALERT +}; + +G_DEFINE_TYPE ( + EAlertDialog, + e_alert_dialog, + GTK_TYPE_DIALOG) + +static void +alert_dialog_set_alert (EAlertDialog *dialog, + EAlert *alert) +{ + g_return_if_fail (E_IS_ALERT (alert)); + g_return_if_fail (dialog->priv->alert == NULL); + + dialog->priv->alert = g_object_ref (alert); +} + +static void +alert_dialog_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ALERT: + alert_dialog_set_alert ( + E_ALERT_DIALOG (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +alert_dialog_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ALERT: + g_value_set_object ( + value, e_alert_dialog_get_alert ( + E_ALERT_DIALOG (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +alert_dialog_dispose (GObject *object) +{ + EAlertDialogPrivate *priv; + + priv = E_ALERT_DIALOG_GET_PRIVATE (object); + + if (priv->alert) { + g_signal_handlers_disconnect_matched ( + priv->alert, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, object); + g_object_unref (priv->alert); + priv->alert = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_alert_dialog_parent_class)->dispose (object); +} + +static void +alert_dialog_constructed (GObject *object) +{ + EAlert *alert; + EAlertDialog *dialog; + GtkWidget *action_area; + GtkWidget *content_area; + GtkWidget *container; + GtkWidget *widget; + PangoAttribute *attr; + PangoAttrList *list; + GList *actions; + const gchar *primary, *secondary; + gint default_response; + gint min_width = -1, prefer_width = -1; + gint height; + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_alert_dialog_parent_class)->constructed (object); + + dialog = E_ALERT_DIALOG (object); + alert = e_alert_dialog_get_alert (dialog); + + default_response = e_alert_get_default_response (alert); + + gtk_window_set_title (GTK_WINDOW (dialog), " "); + + action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog)); + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + + gtk_widget_ensure_style (GTK_WIDGET (dialog)); + gtk_container_set_border_width (GTK_CONTAINER (action_area), 12); + gtk_container_set_border_width (GTK_CONTAINER (content_area), 0); + + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + + /* Forward EAlert::response signals to GtkDialog::response. */ + g_signal_connect_swapped ( + alert, "response", + G_CALLBACK (gtk_dialog_response), dialog); + + /* Add buttons from actions. */ + actions = e_alert_peek_actions (alert); + if (!actions) { + GtkAction *action; + + /* Make sure there is at least one action, thus the dialog can be closed. */ + action = gtk_action_new ( + "alert-response-0", _("_Dismiss"), NULL, NULL); + e_alert_add_action (alert, action, GTK_RESPONSE_CLOSE); + g_object_unref (action); + + actions = e_alert_peek_actions (alert); + } + + while (actions != NULL) { + GtkWidget *button; + gpointer data; + + /* These actions are already wired to trigger an + * EAlert::response signal when activated, which + * will in turn call to gtk_dialog_response(), + * so we can add buttons directly to the action + * area without knowing their response IDs. + * (XXX Well, kind of. See below.) */ + + button = gtk_button_new (); + + gtk_widget_set_can_default (button, TRUE); + + gtk_activatable_set_related_action ( + GTK_ACTIVATABLE (button), + GTK_ACTION (actions->data)); + + gtk_box_pack_end ( + GTK_BOX (action_area), + button, FALSE, FALSE, 0); + + /* This is set in e_alert_add_action(). */ + data = g_object_get_data ( + actions->data, "e-alert-response-id"); + + /* Normally GtkDialog sets the initial focus widget to + * the button corresponding to the default response, but + * because the buttons are not directly tied to response + * IDs, we have set both the default widget and the + * initial focus widget ourselves. */ + if (GPOINTER_TO_INT (data) == default_response) { + gtk_widget_grab_default (button); + gtk_widget_grab_focus (button); + } + + actions = g_list_next (actions); + } + + widget = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (widget), 12); + gtk_box_pack_start (GTK_BOX (content_area), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + container = widget; + + widget = e_alert_create_image (alert, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + widget = gtk_vbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + dialog->priv->content_area = widget; + gtk_widget_show (widget); + + container = widget; + + primary = e_alert_get_primary_text (alert); + secondary = e_alert_get_secondary_text (alert); + + list = pango_attr_list_new (); + attr = pango_attr_scale_new (PANGO_SCALE_LARGE); + pango_attr_list_insert (list, attr); + attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD); + pango_attr_list_insert (list, attr); + + widget = gtk_label_new (primary); + gtk_label_set_attributes (GTK_LABEL (widget), list); + gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE); + gtk_label_set_selectable (GTK_LABEL (widget), TRUE); + gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_set_can_focus (widget, FALSE); + gtk_widget_show (widget); + + widget = gtk_label_new (secondary); + gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE); + gtk_label_set_selectable (GTK_LABEL (widget), TRUE); + gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_set_can_focus (widget, FALSE); + gtk_widget_show (widget); + + widget = GTK_WIDGET (dialog); + + height = gtk_widget_get_allocated_height (widget); + gtk_widget_get_preferred_width_for_height ( + widget, height, &min_width, &prefer_width); + if (min_width < prefer_width) + gtk_window_set_default_size ( + GTK_WINDOW (dialog), MIN ( + (min_width + prefer_width) / 2, + min_width * 5 / 4), -1); + + pango_attr_list_unref (list); +} + +static void +e_alert_dialog_class_init (EAlertDialogClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EAlertDialogPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = alert_dialog_set_property; + object_class->get_property = alert_dialog_get_property; + object_class->dispose = alert_dialog_dispose; + object_class->constructed = alert_dialog_constructed; + + g_object_class_install_property ( + object_class, + PROP_ALERT, + g_param_spec_object ( + "alert", + "Alert", + "Alert to be displayed", + E_TYPE_ALERT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_alert_dialog_init (EAlertDialog *dialog) +{ + dialog->priv = E_ALERT_DIALOG_GET_PRIVATE (dialog); +} + +GtkWidget * +e_alert_dialog_new (GtkWindow *parent, + EAlert *alert) +{ + g_return_val_if_fail (E_IS_ALERT (alert), NULL); + + return g_object_new ( + E_TYPE_ALERT_DIALOG, + "alert", alert, "transient-for", parent, NULL); +} + +GtkWidget * +e_alert_dialog_new_for_args (GtkWindow *parent, + const gchar *tag, + ...) +{ + GtkWidget *dialog; + EAlert *alert; + va_list ap; + + g_return_val_if_fail (tag != NULL, NULL); + + va_start (ap, tag); + alert = e_alert_new_valist (tag, ap); + va_end (ap); + + dialog = e_alert_dialog_new (parent, alert); + + g_object_unref (alert); + + return dialog; +} + +gint +e_alert_run_dialog (GtkWindow *parent, + EAlert *alert) +{ + GtkWidget *dialog; + gint response; + + g_return_val_if_fail (E_IS_ALERT (alert), 0); + + dialog = e_alert_dialog_new (parent, alert); + response = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + return response; +} + +gint +e_alert_run_dialog_for_args (GtkWindow *parent, + const gchar *tag, + ...) +{ + EAlert *alert; + gint response; + va_list ap; + + g_return_val_if_fail (tag != NULL, 0); + + va_start (ap, tag); + alert = e_alert_new_valist (tag, ap); + va_end (ap); + + response = e_alert_run_dialog (parent, alert); + + g_object_unref (alert); + + return response; +} + +/** + * e_alert_dialog_get_alert: + * @dialog: an #EAlertDialog + * + * Returns the #EAlert associated with @dialog. + * + * Returns: the #EAlert associated with @dialog + **/ +EAlert * +e_alert_dialog_get_alert (EAlertDialog *dialog) +{ + g_return_val_if_fail (E_IS_ALERT_DIALOG (dialog), NULL); + + return dialog->priv->alert; +} + +/** + * e_alert_dialog_get_content_area: + * @dialog: an #EAlertDialog + * + * Returns the vertical box containing the primary and secondary labels. + * Use this to pack additional widgets into the dialog with the proper + * horizontal alignment (maintaining the left margin below the image). + * + * Returns: the content area #GtkBox + **/ +GtkWidget * +e_alert_dialog_get_content_area (EAlertDialog *dialog) +{ + g_return_val_if_fail (E_IS_ALERT_DIALOG (dialog), NULL); + + return dialog->priv->content_area; +} diff --git a/libevolution-utils/e-alert-dialog.h b/libevolution-utils/e-alert-dialog.h new file mode 100644 index 0000000000..a8d1b1d6d5 --- /dev/null +++ b/libevolution-utils/e-alert-dialog.h @@ -0,0 +1,80 @@ +/* + * 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/> + * + * + * Authors: + * Michael Zucchi <notzed@ximian.com> + * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * Copyright (C) 2009 Intel Corporation + */ + +#ifndef E_ALERT_DIALOG_H +#define E_ALERT_DIALOG_H + +#include <gtk/gtk.h> +#include <e-util/e-alert.h> + +/* Standard GObject macros */ +#define E_TYPE_ALERT_DIALOG \ + (e_alert_dialog_get_type ()) +#define E_ALERT_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_ALERT_DIALOG, EAlertDialog)) +#define E_ALERT_DIALOG_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_ALERT_DIALOG, EAlertDialogClass)) +#define E_IS_ALERT_DIALOG(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_ALERT_DIALOG)) +#define E_IS_ALERT_DIALOG_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_ALERT_DIALOG)) +#define E_ALERT_DIALOG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_ALERT_DIALOG, EAlertDialogClass)) + +G_BEGIN_DECLS + +typedef struct _EAlertDialog EAlertDialog; +typedef struct _EAlertDialogClass EAlertDialogClass; +typedef struct _EAlertDialogPrivate EAlertDialogPrivate; + +struct _EAlertDialog { + GtkDialog parent; + EAlertDialogPrivate *priv; +}; + +struct _EAlertDialogClass { + GtkDialogClass parent_class; +}; + +GType e_alert_dialog_get_type (void); +GtkWidget * e_alert_dialog_new (GtkWindow *parent, + EAlert *alert); +GtkWidget * e_alert_dialog_new_for_args (GtkWindow *parent, + const gchar *tag, + ...) G_GNUC_NULL_TERMINATED; +gint e_alert_run_dialog (GtkWindow *parent, + EAlert *alert); +gint e_alert_run_dialog_for_args (GtkWindow *parent, + const gchar *tag, + ...) G_GNUC_NULL_TERMINATED; +EAlert * e_alert_dialog_get_alert (EAlertDialog *dialog); +GtkWidget * e_alert_dialog_get_content_area (EAlertDialog *dialog); + +G_END_DECLS + +#endif /* E_ALERT_DIALOG_H */ diff --git a/libevolution-utils/e-alert-sink.c b/libevolution-utils/e-alert-sink.c new file mode 100644 index 0000000000..ae3a7361e1 --- /dev/null +++ b/libevolution-utils/e-alert-sink.c @@ -0,0 +1,93 @@ +/* + * e-alert-sink.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-alert-sink + * @short_description: an interface to handle alerts + * @include: e-util/e-alert-sink.h + * + * A widget that implements #EAlertSink means it can handle #EAlerts, + * usually by displaying them to the user. + **/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-alert-sink.h" + +#include "e-alert-dialog.h" + +G_DEFINE_INTERFACE ( + EAlertSink, + e_alert_sink, + GTK_TYPE_WIDGET) + +static void +alert_sink_fallback (GtkWidget *widget, + EAlert *alert) +{ + GtkWidget *dialog; + gpointer parent; + + parent = gtk_widget_get_toplevel (widget); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + dialog = e_alert_dialog_new (parent, alert); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +static void +alert_sink_submit_alert (EAlertSink *alert_sink, + EAlert *alert) +{ + /* This is just a lame fallback handler. Implementors + * are strongly encouraged to override this method. */ + alert_sink_fallback (GTK_WIDGET (alert_sink), alert); +} + +static void +e_alert_sink_default_init (EAlertSinkInterface *interface) +{ + interface->submit_alert = alert_sink_submit_alert; +} + +/** + * e_alert_sink_submit_alert: + * @alert_sink: an #EAlertSink + * @alert: an #EAlert + * + * This function is a place to pass #EAlert objects. Beyond that it has no + * well-defined behavior. It's up to the widget implementing the #EAlertSink + * interface to decide what to do with them. + **/ +void +e_alert_sink_submit_alert (EAlertSink *alert_sink, + EAlert *alert) +{ + EAlertSinkInterface *interface; + + g_return_if_fail (E_IS_ALERT_SINK (alert_sink)); + g_return_if_fail (E_IS_ALERT (alert)); + + interface = E_ALERT_SINK_GET_INTERFACE (alert_sink); + g_return_if_fail (interface->submit_alert != NULL); + + interface->submit_alert (alert_sink, alert); +} diff --git a/libevolution-utils/e-alert-sink.h b/libevolution-utils/e-alert-sink.h new file mode 100644 index 0000000000..da5ae7b06f --- /dev/null +++ b/libevolution-utils/e-alert-sink.h @@ -0,0 +1,62 @@ +/* + * e-alert-sink.h + * + * 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/> + * + */ + +#ifndef E_ALERT_SINK_H +#define E_ALERT_SINK_H + +#include <gtk/gtk.h> +#include <e-util/e-alert.h> + +/* Standard GObject macros */ +#define E_TYPE_ALERT_SINK \ + (e_alert_sink_get_type ()) +#define E_ALERT_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_ALERT_SINK, EAlertSink)) +#define E_ALERT_SINK_INTERFACE(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_ALERT_SINK, EAlertSinkInterface)) +#define E_IS_ALERT_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_ALERT_SINK)) +#define E_IS_ALERT_SINK_INTERFACE(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_ALERT_SINK)) +#define E_ALERT_SINK_GET_INTERFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE \ + ((obj), E_TYPE_ALERT_SINK, EAlertSinkInterface)) + +G_BEGIN_DECLS + +typedef struct _EAlertSink EAlertSink; +typedef struct _EAlertSinkInterface EAlertSinkInterface; + +struct _EAlertSinkInterface { + GTypeInterface parent_interface; + + void (*submit_alert) (EAlertSink *alert_sink, + EAlert *alert); +}; + +GType e_alert_sink_get_type (void); +void e_alert_sink_submit_alert (EAlertSink *alert_sink, + EAlert *alert); + +G_END_DECLS + +#endif /* E_ALERT_SINK_H */ diff --git a/libevolution-utils/e-alert.c b/libevolution-utils/e-alert.c new file mode 100644 index 0000000000..9ee7e05581 --- /dev/null +++ b/libevolution-utils/e-alert.c @@ -0,0 +1,996 @@ +/* + * 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/> + * + * + * Authors: + * Michael Zucchi <notzed@ximian.com> + * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * Copyright (C) 2009 Intel Corporation + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <sys/types.h> + +#include <libxml/parser.h> +#include <libxml/xmlmemory.h> + +#include <gtk/gtk.h> +#include <glib/gi18n.h> + +#include <libedataserver/e-xml-utils.h> + +#include "e-util.h" +#include "e-util-private.h" +#include "e-alert.h" +#include "e-alert-sink.h" + +#define d(x) + +#define E_ALERT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_ALERT, EAlertPrivate)) + +typedef struct _EAlertButton EAlertButton; + +struct _e_alert { + const gchar *id; + GtkMessageType message_type; + gint default_response; + const gchar *primary_text; + const gchar *secondary_text; + EAlertButton *buttons; +}; + +struct _e_alert_table { + const gchar *domain; + const gchar *translation_domain; + GHashTable *alerts; +}; + +struct _EAlertButton { + EAlertButton *next; + const gchar *stock_id; + const gchar *label; + gint response_id; +}; + +static GHashTable *alert_table; + +/* ********************************************************************** */ + +static EAlertButton default_ok_button = { + NULL, GTK_STOCK_OK, NULL, GTK_RESPONSE_OK +}; + +static struct _e_alert default_alerts[] = { + { "error", GTK_MESSAGE_ERROR, GTK_RESPONSE_OK, + "{0}", "{1}", &default_ok_button }, + { "warning", GTK_MESSAGE_WARNING, GTK_RESPONSE_OK, + "{0}", "{1}", &default_ok_button } +}; + +/* ********************************************************************** */ + +struct _EAlertPrivate { + gchar *tag; + GPtrArray *args; + gchar *primary_text; + gchar *secondary_text; + struct _e_alert *definition; + GtkMessageType message_type; + gint default_response; + guint timeout_id; + + /* It may occur to one that we could use a GtkActionGroup here, + * but we need to preserve the button order and GtkActionGroup + * uses a hash table, which does not preserve order. */ + GQueue actions; +}; + +enum { + PROP_0, + PROP_ARGS, + PROP_TAG, + PROP_MESSAGE_TYPE, + PROP_PRIMARY_TEXT, + PROP_SECONDARY_TEXT +}; + +enum { + RESPONSE, + LAST_SIGNAL +}; + +static gulong signals[LAST_SIGNAL]; + +G_DEFINE_TYPE ( + EAlert, + e_alert, + G_TYPE_OBJECT) + +static gint +map_response (const gchar *name) +{ + GEnumClass *class; + GEnumValue *value; + + class = g_type_class_ref (GTK_TYPE_RESPONSE_TYPE); + value = g_enum_get_value_by_name (class, name); + g_type_class_unref (class); + + return (value != NULL) ? value->value : 0; +} + +static GtkMessageType +map_type (const gchar *nick) +{ + GEnumClass *class; + GEnumValue *value; + + class = g_type_class_ref (GTK_TYPE_MESSAGE_TYPE); + value = g_enum_get_value_by_nick (class, nick); + g_type_class_unref (class); + + return (value != NULL) ? value->value : GTK_MESSAGE_ERROR; +} + +/* + * XML format: + * + * <error id="error-id" type="info|warning|question|error"? + * response="default_response"? > + * <primary> Primary error text.</primary>? + * <secondary> Secondary error text.</secondary>? + * <button stock="stock-button-id"? label="button label"? + * response="response_id"? /> * + * </error> + */ + +static void +e_alert_load (const gchar *path) +{ + xmlDocPtr doc = NULL; + xmlNodePtr root, error, scan; + struct _e_alert *e; + EAlertButton *lastbutton; + struct _e_alert_table *table; + gchar *tmp; + + d(printf("loading error file %s\n", path)); + + doc = e_xml_parse_file (path); + if (doc == NULL) { + g_warning("Error file '%s' not found", path); + return; + } + + root = xmlDocGetRootElement (doc); + if (root == NULL + || strcmp((gchar *)root->name, "error-list") != 0 + || (tmp = (gchar *)xmlGetProp(root, (const guchar *)"domain")) == NULL) { + g_warning("Error file '%s' invalid format", path); + xmlFreeDoc (doc); + return; + } + + table = g_hash_table_lookup (alert_table, tmp); + if (table == NULL) { + gchar *tmp2; + + table = g_malloc0 (sizeof (*table)); + table->domain = g_strdup (tmp); + table->alerts = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (alert_table, (gpointer) table->domain, table); + + tmp2 = (gchar *) xmlGetProp ( + root, (const guchar *) "translation-domain"); + if (tmp2) { + table->translation_domain = g_strdup (tmp2); + xmlFree (tmp2); + + tmp2 = (gchar *) xmlGetProp ( + root, (const guchar *) "translation-localedir"); + if (tmp2) { + bindtextdomain (table->translation_domain, tmp2); + xmlFree (tmp2); + } + } + } else + g_warning ( + "Error file '%s', domain '%s' " + "already used, merging", path, tmp); + xmlFree (tmp); + + for (error = root->children; error; error = error->next) { + if (!strcmp((gchar *)error->name, "error")) { + tmp = (gchar *)xmlGetProp(error, (const guchar *)"id"); + if (tmp == NULL) + continue; + + e = g_malloc0 (sizeof (*e)); + e->id = g_strdup (tmp); + + xmlFree (tmp); + lastbutton = (EAlertButton *) &e->buttons; + + tmp = (gchar *)xmlGetProp(error, (const guchar *)"type"); + e->message_type = map_type (tmp); + if (tmp) + xmlFree (tmp); + + tmp = (gchar *)xmlGetProp(error, (const guchar *)"default"); + if (tmp) { + e->default_response = map_response (tmp); + xmlFree (tmp); + } + + for (scan = error->children; scan; scan = scan->next) { + if (!strcmp((gchar *)scan->name, "primary")) { + if ((tmp = (gchar *) xmlNodeGetContent (scan))) { + e->primary_text = g_strdup ( + dgettext (table-> + translation_domain, tmp)); + xmlFree (tmp); + } + } else if (!strcmp((gchar *)scan->name, "secondary")) { + if ((tmp = (gchar *) xmlNodeGetContent (scan))) { + e->secondary_text = g_strdup ( + dgettext (table-> + translation_domain, tmp)); + xmlFree (tmp); + } + } else if (!strcmp((gchar *)scan->name, "button")) { + EAlertButton *button; + gchar *label = NULL; + gchar *stock_id = NULL; + + button = g_new0 (EAlertButton, 1); + tmp = (gchar *)xmlGetProp(scan, (const guchar *)"stock"); + if (tmp) { + stock_id = g_strdup (tmp); + button->stock_id = stock_id; + xmlFree (tmp); + } + tmp = (gchar *) xmlGetProp ( + scan, (xmlChar *) "label"); + if (tmp) { + label = g_strdup ( + dgettext (table-> + translation_domain, + tmp)); + button->label = label; + xmlFree (tmp); + } + tmp = (gchar *) xmlGetProp ( + scan, (xmlChar *) "response"); + if (tmp) { + button->response_id = + map_response (tmp); + xmlFree (tmp); + } + + if (stock_id == NULL && label == NULL) { + g_warning ( + "Error file '%s': " + "missing button " + "details in error " + "'%s'", path, e->id); + g_free (stock_id); + g_free (label); + g_free (button); + } else { + lastbutton->next = button; + lastbutton = button; + } + } + } + + g_hash_table_insert (table->alerts, (gpointer) e->id, e); + } + } + + xmlFreeDoc (doc); +} + +static void +e_alert_load_tables (void) +{ + GDir *dir; + const gchar *d; + gchar *base; + struct _e_alert_table *table; + gint i; + + if (alert_table != NULL) + return; + + alert_table = g_hash_table_new (g_str_hash, g_str_equal); + + /* setup system alert types */ + table = g_malloc0 (sizeof (*table)); + table->domain = "builtin"; + table->alerts = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; i < G_N_ELEMENTS (default_alerts); i++) + g_hash_table_insert ( + table->alerts, (gpointer) + default_alerts[i].id, &default_alerts[i]); + g_hash_table_insert (alert_table, (gpointer) table->domain, table); + + /* look for installed alert tables */ + base = g_build_filename (EVOLUTION_PRIVDATADIR, "errors", NULL); + dir = g_dir_open (base, 0, NULL); + if (dir == NULL) { + g_free (base); + return; + } + + while ((d = g_dir_read_name (dir))) { + gchar *path; + + if (d[0] == '.') + continue; + + path = g_build_filename (base, d, NULL); + e_alert_load (path); + g_free (path); + } + + g_dir_close (dir); + g_free (base); +} + +static void +alert_action_activate (EAlert *alert, + GtkAction *action) +{ + GObject *object; + gpointer data; + + object = G_OBJECT (action); + data = g_object_get_data (object, "e-alert-response-id"); + e_alert_response (alert, GPOINTER_TO_INT (data)); +} + +static gchar * +alert_format_string (const gchar *format, + GPtrArray *args) +{ + GString *string; + const gchar *end, *newstart; + gint id; + + string = g_string_sized_new (strlen (format)); + + while (format + && (newstart = strchr (format, '{')) + && (end = strchr (newstart + 1, '}'))) { + g_string_append_len (string, format, newstart - format); + id = atoi (newstart + 1); + if (id < args->len) { + g_string_append (string, args->pdata[id]); + } else + g_warning ( + "Error references argument %d " + "not supplied by caller", id); + format = end + 1; + } + + g_string_append (string, format); + + return g_string_free (string, FALSE); +} + +static void +alert_set_tag (EAlert *alert, + const gchar *tag) +{ + struct _e_alert *definition; + struct _e_alert_table *table; + gchar *domain, *id; + + alert->priv->tag = g_strdup (tag); + + g_return_if_fail (alert_table); + + domain = g_alloca (strlen (tag) + 1); + strcpy (domain, tag); + id = strchr (domain, ':'); + if (id) + *id++ = 0; + else { + g_warning ("Alert tag '%s' is missing a domain", tag); + return; + } + + table = g_hash_table_lookup (alert_table, domain); + g_return_if_fail (table); + + definition = g_hash_table_lookup (table->alerts, id); + g_warn_if_fail (definition); + + alert->priv->definition = definition; +} + +static gboolean +alert_timeout_cb (EAlert *alert) +{ + e_alert_response (alert, alert->priv->default_response); + + return FALSE; +} + +static void +alert_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EAlert *alert = (EAlert *) object; + + switch (property_id) { + case PROP_TAG: + alert_set_tag ( + E_ALERT (object), + g_value_get_string (value)); + return; + + case PROP_ARGS: + alert->priv->args = g_value_dup_boxed (value); + return; + + case PROP_MESSAGE_TYPE: + e_alert_set_message_type ( + E_ALERT (object), + g_value_get_enum (value)); + return; + + case PROP_PRIMARY_TEXT: + e_alert_set_primary_text ( + E_ALERT (object), + g_value_get_string (value)); + return; + + case PROP_SECONDARY_TEXT: + e_alert_set_secondary_text ( + E_ALERT (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +alert_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EAlert *alert = (EAlert *) object; + + switch (property_id) { + case PROP_TAG: + g_value_set_string (value, alert->priv->tag); + return; + + case PROP_ARGS: + g_value_set_boxed (value, alert->priv->args); + return; + + case PROP_MESSAGE_TYPE: + g_value_set_enum ( + value, e_alert_get_message_type ( + E_ALERT (object))); + return; + + case PROP_PRIMARY_TEXT: + g_value_set_string ( + value, e_alert_get_primary_text ( + E_ALERT (object))); + return; + + case PROP_SECONDARY_TEXT: + g_value_set_string ( + value, e_alert_get_secondary_text ( + E_ALERT (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +alert_dispose (GObject *object) +{ + EAlert *alert = E_ALERT (object); + + if (alert->priv->timeout_id > 0) { + g_source_remove (alert->priv->timeout_id); + alert->priv->timeout_id = 0; + } + + while (!g_queue_is_empty (&alert->priv->actions)) { + GtkAction *action; + + action = g_queue_pop_head (&alert->priv->actions); + g_signal_handlers_disconnect_by_func ( + action, G_CALLBACK (alert_action_activate), object); + g_object_unref (action); + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_alert_parent_class)->dispose (object); +} + +static void +alert_finalize (GObject *object) +{ + EAlertPrivate *priv; + + priv = E_ALERT_GET_PRIVATE (object); + + g_free (priv->tag); + g_free (priv->primary_text); + g_free (priv->secondary_text); + + g_ptr_array_free (priv->args, TRUE); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_alert_parent_class)->finalize (object); +} + +static void +alert_constructed (GObject *object) +{ + EAlert *alert; + EAlertButton *button; + struct _e_alert *definition; + gint ii = 0; + + alert = E_ALERT (object); + definition = alert->priv->definition; + g_return_if_fail (definition != NULL); + + e_alert_set_message_type (alert, definition->message_type); + e_alert_set_default_response (alert, definition->default_response); + + /* Build actions out of the button definitions. */ + button = definition->buttons; + while (button != NULL) { + GtkAction *action; + gchar *action_name; + + action_name = g_strdup_printf ("alert-response-%d", ii++); + + if (button->stock_id != NULL) { + action = gtk_action_new ( + action_name, NULL, NULL, button->stock_id); + e_alert_add_action ( + alert, action, button->response_id); + g_object_unref (action); + + } else if (button->label != NULL) { + action = gtk_action_new ( + action_name, button->label, NULL, NULL); + e_alert_add_action ( + alert, action, button->response_id); + g_object_unref (action); + } + + g_free (action_name); + + button = button->next; + } + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_alert_parent_class)->constructed (object); +} + +static void +e_alert_class_init (EAlertClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (EAlertPrivate)); + + object_class->set_property = alert_set_property; + object_class->get_property = alert_get_property; + object_class->dispose = alert_dispose; + object_class->finalize = alert_finalize; + object_class->constructed = alert_constructed; + + g_object_class_install_property ( + object_class, + PROP_ARGS, + g_param_spec_boxed ( + "args", + "Arguments", + "Arguments for formatting the alert", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_TAG, + g_param_spec_string ( + "tag", + "alert tag", + "A tag describing the alert", + "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_MESSAGE_TYPE, + g_param_spec_enum ( + "message-type", + NULL, + NULL, + GTK_TYPE_MESSAGE_TYPE, + GTK_MESSAGE_ERROR, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_PRIMARY_TEXT, + g_param_spec_string ( + "primary-text", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_SECONDARY_TEXT, + g_param_spec_string ( + "secondary-text", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + signals[RESPONSE] = g_signal_new ( + "response", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAlertClass, response), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); + + e_alert_load_tables (); +} + +static void +e_alert_init (EAlert *alert) +{ + alert->priv = E_ALERT_GET_PRIVATE (alert); + + g_queue_init (&alert->priv->actions); +} + +/** + * e_alert_new: + * @tag: alert identifier + * @arg0: The first argument for the alert formatter. The list must + * be NULL terminated. + * + * Creates a new EAlert. The @tag argument is used to determine + * which alert to use, it is in the format domain:alert-id. The NULL + * terminated list of arguments, starting with @arg0 is used to fill + * out the alert definition. + * + * Returns: a new #EAlert + **/ +EAlert * +e_alert_new (const gchar *tag, + ...) +{ + EAlert *e; + va_list va; + + va_start (va, tag); + e = e_alert_new_valist (tag, va); + va_end (va); + + return e; +} + +EAlert * +e_alert_new_valist (const gchar *tag, + va_list va) +{ + EAlert *alert; + GPtrArray *args; + gchar *tmp; + + args = g_ptr_array_new_with_free_func (g_free); + + tmp = va_arg (va, gchar *); + while (tmp) { + g_ptr_array_add (args, g_strdup (tmp)); + tmp = va_arg (va, gchar *); + } + + alert = e_alert_new_array (tag, args); + + g_ptr_array_unref (args); + + return alert; +} + +EAlert * +e_alert_new_array (const gchar *tag, + GPtrArray *args) +{ + return g_object_new (E_TYPE_ALERT, "tag", tag, "args", args, NULL); +} + +gint +e_alert_get_default_response (EAlert *alert) +{ + g_return_val_if_fail (E_IS_ALERT (alert), 0); + + return alert->priv->default_response; +} + +void +e_alert_set_default_response (EAlert *alert, + gint response_id) +{ + g_return_if_fail (E_IS_ALERT (alert)); + + alert->priv->default_response = response_id; +} + +GtkMessageType +e_alert_get_message_type (EAlert *alert) +{ + g_return_val_if_fail (E_IS_ALERT (alert), GTK_MESSAGE_OTHER); + + return alert->priv->message_type; +} + +void +e_alert_set_message_type (EAlert *alert, + GtkMessageType message_type) +{ + g_return_if_fail (E_IS_ALERT (alert)); + + alert->priv->message_type = message_type; + + g_object_notify (G_OBJECT (alert), "message-type"); +} + +const gchar * +e_alert_get_primary_text (EAlert *alert) +{ + g_return_val_if_fail (E_IS_ALERT (alert), NULL); + + if (alert->priv->primary_text != NULL) + goto exit; + + if (alert->priv->definition == NULL) + goto exit; + + if (alert->priv->definition->primary_text == NULL) + goto exit; + + if (alert->priv->args == NULL) + goto exit; + + alert->priv->primary_text = alert_format_string ( + alert->priv->definition->primary_text, + alert->priv->args); + +exit: + return alert->priv->primary_text; +} + +void +e_alert_set_primary_text (EAlert *alert, + const gchar *primary_text) +{ + g_return_if_fail (E_IS_ALERT (alert)); + + g_free (alert->priv->primary_text); + alert->priv->primary_text = g_strdup (primary_text); + + g_object_notify (G_OBJECT (alert), "primary-text"); +} + +const gchar * +e_alert_get_secondary_text (EAlert *alert) +{ + g_return_val_if_fail (E_IS_ALERT (alert), NULL); + + if (alert->priv->secondary_text != NULL) + goto exit; + + if (alert->priv->definition == NULL) + goto exit; + + if (alert->priv->definition->secondary_text == NULL) + goto exit; + + if (alert->priv->args == NULL) + goto exit; + + alert->priv->secondary_text = alert_format_string ( + alert->priv->definition->secondary_text, + alert->priv->args); + +exit: + return alert->priv->secondary_text; +} + +void +e_alert_set_secondary_text (EAlert *alert, + const gchar *secondary_text) +{ + g_return_if_fail (E_IS_ALERT (alert)); + + g_free (alert->priv->secondary_text); + alert->priv->secondary_text = g_strdup (secondary_text); + + g_object_notify (G_OBJECT (alert), "secondary-text"); +} + +const gchar * +e_alert_get_stock_id (EAlert *alert) +{ + const gchar *stock_id; + + g_return_val_if_fail (E_IS_ALERT (alert), NULL); + + switch (e_alert_get_message_type (alert)) { + case GTK_MESSAGE_INFO: + stock_id = GTK_STOCK_DIALOG_INFO; + break; + case GTK_MESSAGE_WARNING: + stock_id = GTK_STOCK_DIALOG_WARNING; + break; + case GTK_MESSAGE_QUESTION: + stock_id = GTK_STOCK_DIALOG_QUESTION; + break; + case GTK_MESSAGE_ERROR: + stock_id = GTK_STOCK_DIALOG_ERROR; + break; + default: + stock_id = GTK_STOCK_MISSING_IMAGE; + g_warn_if_reached (); + break; + } + + return stock_id; +} + +void +e_alert_add_action (EAlert *alert, + GtkAction *action, + gint response_id) +{ + g_return_if_fail (E_IS_ALERT (alert)); + g_return_if_fail (GTK_ACTION (action)); + + g_object_set_data ( + G_OBJECT (action), "e-alert-response-id", + GINT_TO_POINTER (response_id)); + + g_signal_connect_swapped ( + action, "activate", + G_CALLBACK (alert_action_activate), alert); + + g_queue_push_tail (&alert->priv->actions, g_object_ref (action)); +} + +GList * +e_alert_peek_actions (EAlert *alert) +{ + g_return_val_if_fail (E_IS_ALERT (alert), NULL); + + return g_queue_peek_head_link (&alert->priv->actions); +} + +GtkWidget * +e_alert_create_image (EAlert *alert, + GtkIconSize size) +{ + const gchar *stock_id; + + g_return_val_if_fail (E_IS_ALERT (alert), NULL); + + stock_id = e_alert_get_stock_id (alert); + + return gtk_image_new_from_stock (stock_id, size); +} + +void +e_alert_response (EAlert *alert, + gint response_id) +{ + g_return_if_fail (E_IS_ALERT (alert)); + + g_signal_emit (alert, signals[RESPONSE], 0, response_id); +} + +/** + * e_alert_start_timer: + * @alert: an #EAlert + * @seconds: seconds until timeout occurs + * + * Starts an internal timer for @alert. When the timer expires, @alert + * will emit the default response. There is only one timer per #EAlert, + * so calling this function repeatedly on the same #EAlert will restart + * its timer each time. If @seconds is zero, the timer is cancelled and + * no response will be emitted. + **/ +void +e_alert_start_timer (EAlert *alert, + guint seconds) +{ + g_return_if_fail (E_IS_ALERT (alert)); + + if (alert->priv->timeout_id > 0) { + g_source_remove (alert->priv->timeout_id); + alert->priv->timeout_id = 0; + } + + if (seconds > 0) + alert->priv->timeout_id = g_timeout_add_seconds ( + seconds, (GSourceFunc) alert_timeout_cb, alert); +} + +void +e_alert_submit (EAlertSink *alert_sink, + const gchar *tag, + ...) +{ + va_list va; + + va_start (va, tag); + e_alert_submit_valist (alert_sink, tag, va); + va_end (va); +} + +void +e_alert_submit_valist (EAlertSink *alert_sink, + const gchar *tag, + va_list va) +{ + EAlert *alert; + + g_return_if_fail (E_IS_ALERT_SINK (alert_sink)); + g_return_if_fail (tag != NULL); + + alert = e_alert_new_valist (tag, va); + e_alert_sink_submit_alert (alert_sink, alert); + g_object_unref (alert); +} diff --git a/libevolution-utils/e-alert.h b/libevolution-utils/e-alert.h new file mode 100644 index 0000000000..f62e612235 --- /dev/null +++ b/libevolution-utils/e-alert.h @@ -0,0 +1,119 @@ +/* + * 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/> + * + * + * Authors: + * Michael Zucchi <notzed@ximian.com> + * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * Copyright (C) 2009 Intel Corporation + * + */ + +#ifndef E_ALERT_H +#define E_ALERT_H + +#include <stdarg.h> +#include <gtk/gtk.h> + +/* Standard GObject macros */ +#define E_TYPE_ALERT \ + (e_alert_get_type ()) +#define E_ALERT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_ALERT, EAlert)) +#define E_ALERT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_ALERT, EAlertClass)) +#define E_IS_ALERT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_ALERT)) +#define E_IS_ALERT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_ALERT)) +#define E_ALERT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_ALERT, EAlertClass)) + +/* takes filename, returns OK if yes */ +#define E_ALERT_ASK_FILE_EXISTS_OVERWRITE \ + "system:ask-save-file-exists-overwrite" +/* takes filename, reason */ +#define E_ALERT_NO_SAVE_FILE "system:no-save-file" +/* takes filename, reason */ +#define E_ALERT_NO_LOAD_FILE "system:no-save-file" + +G_BEGIN_DECLS + +struct _EAlertSink; + +typedef struct _EAlert EAlert; +typedef struct _EAlertClass EAlertClass; +typedef struct _EAlertPrivate EAlertPrivate; + +struct _EAlert { + GObject parent; + EAlertPrivate *priv; +}; + +struct _EAlertClass { + GObjectClass parent_class; + + /* Signals */ + void (*response) (EAlert *alert, + gint response_id); +}; + +GType e_alert_get_type (void); +EAlert * e_alert_new (const gchar *tag, + ...) G_GNUC_NULL_TERMINATED; +EAlert * e_alert_new_valist (const gchar *tag, + va_list va); +EAlert * e_alert_new_array (const gchar *tag, + GPtrArray *args); +gint e_alert_get_default_response (EAlert *alert); +void e_alert_set_default_response (EAlert *alert, + gint response_id); +GtkMessageType e_alert_get_message_type (EAlert *alert); +void e_alert_set_message_type (EAlert *alert, + GtkMessageType message_type); +const gchar * e_alert_get_primary_text (EAlert *alert); +void e_alert_set_primary_text (EAlert *alert, + const gchar *primary_text); +const gchar * e_alert_get_secondary_text (EAlert *alert); +void e_alert_set_secondary_text (EAlert *alert, + const gchar *secondary_text); +const gchar * e_alert_get_stock_id (EAlert *alert); +void e_alert_add_action (EAlert *alert, + GtkAction *action, + gint response_id); +GList * e_alert_peek_actions (EAlert *alert); +GtkWidget * e_alert_create_image (EAlert *alert, + GtkIconSize size); +void e_alert_response (EAlert *alert, + gint response_id); +void e_alert_start_timer (EAlert *alert, + guint seconds); + +void e_alert_submit (struct _EAlertSink *alert_sink, + const gchar *tag, + ...) G_GNUC_NULL_TERMINATED; +void e_alert_submit_valist (struct _EAlertSink *alert_sink, + const gchar *tag, + va_list va); + +G_END_DECLS + +#endif /* E_ALERT_H */ diff --git a/libevolution-utils/e-xml-utils.c b/libevolution-utils/e-xml-utils.c new file mode 100644 index 0000000000..0247f91e27 --- /dev/null +++ b/libevolution-utils/e-xml-utils.c @@ -0,0 +1,448 @@ +/* + * 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/> + * + * + * Authors: + * Chris Lahey <clahey@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <locale.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <math.h> +#include <string.h> + +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <libxml/parser.h> +#include <libxml/xmlmemory.h> + +#include "e-util.h" +#include "e-xml-utils.h" + +/* Returns the first child with the name child_name and the "lang" + * attribute that matches the current LC_MESSAGES, or else, the first + * child with the name child_name and no "lang" attribute. + */ +xmlNode * +e_xml_get_child_by_name_by_lang (const xmlNode *parent, + const xmlChar *child_name, + const gchar *lang) +{ +#ifdef G_OS_WIN32 + gchar *freeme = NULL; +#endif + xmlNode *child; + /* This is the default version of the string. */ + xmlNode *C = NULL; + + g_return_val_if_fail (parent != NULL, NULL); + g_return_val_if_fail (child_name != NULL, NULL); + + if (lang == NULL) { +#ifndef G_OS_WIN32 +#ifdef HAVE_LC_MESSAGES + lang = setlocale (LC_MESSAGES, NULL); +#else + lang = setlocale (LC_CTYPE, NULL); +#endif +#else + lang = freeme = g_win32_getlocale (); +#endif + } + for (child = parent->xmlChildrenNode; child != NULL; child = child->next) { + if (xmlStrcmp (child->name, child_name) == 0) { + xmlChar *this_lang = xmlGetProp ( + child, (const guchar *)"lang"); + if (this_lang == NULL) { + C = child; + } else if (xmlStrcmp (this_lang, (xmlChar *) lang) == 0) { +#ifdef G_OS_WIN32 + g_free (freeme); +#endif + return child; + } + } + } +#ifdef G_OS_WIN32 + g_free (freeme); +#endif + return C; +} + +static xmlNode * +e_xml_get_child_by_name_by_lang_list_with_score (const xmlNode *parent, + const gchar *name, + const GList *lang_list, + gint *best_lang_score) +{ + xmlNodePtr best_node = NULL, node; + + for (node = parent->xmlChildrenNode; node != NULL; node = node->next) { + xmlChar *lang; + + if (node->name == NULL || strcmp ((gchar *) node->name, name) != 0) { + continue; + } + lang = xmlGetProp (node, (const guchar *)"xml:lang"); + if (lang != NULL) { + const GList *l; + gint i; + + for (l = lang_list, i = 0; + l != NULL && i < *best_lang_score; + l = l->next, i++) { + if (strcmp ((gchar *) l->data, (gchar *) lang) == 0) { + best_node = node; + *best_lang_score = i; + } + } + } else { + if (best_node == NULL) { + best_node = node; + } + } + xmlFree (lang); + if (*best_lang_score == 0) { + return best_node; + } + } + + return best_node; +} + +xmlNode * +e_xml_get_child_by_name_by_lang_list (const xmlNode *parent, + const gchar *name, + const GList *lang_list) +{ + gint best_lang_score = INT_MAX; + + g_return_val_if_fail (parent != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + + if (lang_list == NULL) { + const gchar * const *language_names; + + language_names = g_get_language_names (); + while (*language_names != NULL) + lang_list = g_list_append ( + (GList *) lang_list, (gchar *) * language_names++); + } + return e_xml_get_child_by_name_by_lang_list_with_score + (parent,name, + lang_list, + &best_lang_score); +} + +xmlNode * +e_xml_get_child_by_name_no_lang (const xmlNode *parent, + const gchar *name) +{ + xmlNodePtr node; + + g_return_val_if_fail (parent != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + + for (node = parent->xmlChildrenNode; node != NULL; node = node->next) { + xmlChar *lang; + + if (node->name == NULL || strcmp ((gchar *) node->name, name) != 0) { + continue; + } + lang = xmlGetProp (node, (const guchar *)"xml:lang"); + if (lang == NULL) { + return node; + } + xmlFree (lang); + } + + return NULL; +} + +gint +e_xml_get_integer_prop_by_name (const xmlNode *parent, + const xmlChar *prop_name) +{ + g_return_val_if_fail (parent != NULL, 0); + g_return_val_if_fail (prop_name != NULL, 0); + + return e_xml_get_integer_prop_by_name_with_default (parent, prop_name, 0); +} + +gint +e_xml_get_integer_prop_by_name_with_default (const xmlNode *parent, + const xmlChar *prop_name, + gint def) +{ + xmlChar *prop; + gint ret_val = def; + + g_return_val_if_fail (parent != NULL, 0); + g_return_val_if_fail (prop_name != NULL, 0); + + prop = xmlGetProp ((xmlNode *) parent, prop_name); + if (prop != NULL) { + (void) sscanf ((gchar *)prop, "%d", &ret_val); + xmlFree (prop); + } + return ret_val; +} + +void +e_xml_set_integer_prop_by_name (xmlNode *parent, + const xmlChar *prop_name, + gint value) +{ + gchar *valuestr; + + g_return_if_fail (parent != NULL); + g_return_if_fail (prop_name != NULL); + + valuestr = g_strdup_printf ("%d", value); + xmlSetProp (parent, prop_name, (guchar *) valuestr); + g_free (valuestr); +} + +guint +e_xml_get_uint_prop_by_name (const xmlNode *parent, + const xmlChar *prop_name) +{ + g_return_val_if_fail (parent != NULL, 0); + g_return_val_if_fail (prop_name != NULL, 0); + + return e_xml_get_uint_prop_by_name_with_default (parent, prop_name, 0); +} + +guint +e_xml_get_uint_prop_by_name_with_default (const xmlNode *parent, + const xmlChar *prop_name, + guint def) +{ + xmlChar *prop; + guint ret_val = def; + + g_return_val_if_fail (parent != NULL, 0); + g_return_val_if_fail (prop_name != NULL, 0); + + prop = xmlGetProp ((xmlNode *) parent, prop_name); + if (prop != NULL) { + (void) sscanf ((gchar *)prop, "%u", &ret_val); + xmlFree (prop); + } + return ret_val; +} + +void +e_xml_set_uint_prop_by_name (xmlNode *parent, + const xmlChar *prop_name, + guint value) +{ + gchar *valuestr; + + g_return_if_fail (parent != NULL); + g_return_if_fail (prop_name != NULL); + + valuestr = g_strdup_printf ("%u", value); + xmlSetProp (parent, prop_name, (guchar *) valuestr); + g_free (valuestr); +} + +gboolean +e_xml_get_bool_prop_by_name (const xmlNode *parent, + const xmlChar *prop_name) +{ + g_return_val_if_fail (parent != NULL, 0); + g_return_val_if_fail (prop_name != NULL, 0); + + return e_xml_get_bool_prop_by_name_with_default (parent, + prop_name, + FALSE); +} + +gboolean +e_xml_get_bool_prop_by_name_with_default (const xmlNode *parent, + const xmlChar *prop_name, + gboolean def) +{ + xmlChar *prop; + gboolean ret_val = def; + + g_return_val_if_fail (parent != NULL, 0); + g_return_val_if_fail (prop_name != NULL, 0); + + prop = xmlGetProp ((xmlNode *) parent, prop_name); + if (prop != NULL) { + if (g_ascii_strcasecmp ((gchar *)prop, "true") == 0) { + ret_val = TRUE; + } else if (g_ascii_strcasecmp ((gchar *)prop, "false") == 0) { + ret_val = FALSE; + } + xmlFree (prop); + } + return ret_val; +} + +void +e_xml_set_bool_prop_by_name (xmlNode *parent, + const xmlChar *prop_name, + gboolean value) +{ + g_return_if_fail (parent != NULL); + g_return_if_fail (prop_name != NULL); + + if (value) { + xmlSetProp (parent, prop_name, (const guchar *)"true"); + } else { + xmlSetProp (parent, prop_name, (const guchar *)"false"); + } +} + +gdouble +e_xml_get_double_prop_by_name (const xmlNode *parent, + const xmlChar *prop_name) +{ + g_return_val_if_fail (parent != NULL, 0); + g_return_val_if_fail (prop_name != NULL, 0); + + return e_xml_get_double_prop_by_name_with_default (parent, prop_name, 0.0); +} + +gdouble +e_xml_get_double_prop_by_name_with_default (const xmlNode *parent, + const xmlChar *prop_name, + gdouble def) +{ + xmlChar *prop; + gdouble ret_val = def; + + g_return_val_if_fail (parent != NULL, 0); + g_return_val_if_fail (prop_name != NULL, 0); + + prop = xmlGetProp ((xmlNode *) parent, prop_name); + if (prop != NULL) { + ret_val = e_flexible_strtod ((gchar *) prop, NULL); + xmlFree (prop); + } + return ret_val; +} + +void +e_xml_set_double_prop_by_name (xmlNode *parent, + const xmlChar *prop_name, + gdouble value) +{ + gchar buffer[E_ASCII_DTOSTR_BUF_SIZE]; + gchar *format; + + g_return_if_fail (parent != NULL); + g_return_if_fail (prop_name != NULL); + + if (fabs (value) < 1e9 && fabs (value) > 1e-5) { + format = g_strdup_printf ("%%.%df", DBL_DIG); + } else { + format = g_strdup_printf ("%%.%dg", DBL_DIG); + } + e_ascii_dtostr (buffer, sizeof (buffer), format, value); + g_free (format); + + xmlSetProp (parent, prop_name, (const guchar *) buffer); +} + +gchar * +e_xml_get_string_prop_by_name (const xmlNode *parent, + const xmlChar *prop_name) +{ + g_return_val_if_fail (parent != NULL, NULL); + g_return_val_if_fail (prop_name != NULL, NULL); + + return e_xml_get_string_prop_by_name_with_default (parent, prop_name, NULL); +} + +gchar * +e_xml_get_string_prop_by_name_with_default (const xmlNode *parent, + const xmlChar *prop_name, + const gchar *def) +{ + xmlChar *prop; + gchar *ret_val; + + g_return_val_if_fail (parent != NULL, NULL); + g_return_val_if_fail (prop_name != NULL, NULL); + + prop = xmlGetProp ((xmlNode *) parent, prop_name); + if (prop != NULL) { + ret_val = g_strdup ((gchar *) prop); + xmlFree (prop); + } else { + ret_val = g_strdup (def); + } + return ret_val; +} + +void +e_xml_set_string_prop_by_name (xmlNode *parent, + const xmlChar *prop_name, + const gchar *value) +{ + g_return_if_fail (parent != NULL); + g_return_if_fail (prop_name != NULL); + + if (value != NULL) { + xmlSetProp (parent, prop_name, (guchar *) value); + } +} + +gchar * +e_xml_get_translated_string_prop_by_name (const xmlNode *parent, + const xmlChar *prop_name) +{ + xmlChar *prop; + gchar *ret_val = NULL; + gchar *combined_name; + + g_return_val_if_fail (parent != NULL, NULL); + g_return_val_if_fail (prop_name != NULL, NULL); + + prop = xmlGetProp ((xmlNode *) parent, prop_name); + if (prop != NULL) { + ret_val = g_strdup ((gchar *) prop); + xmlFree (prop); + return ret_val; + } + + combined_name = g_strdup_printf("_%s", prop_name); + prop = xmlGetProp ((xmlNode *) parent, (guchar *) combined_name); + if (prop != NULL) { + ret_val = g_strdup (gettext ((gchar *) prop)); + xmlFree (prop); + } + g_free (combined_name); + + return ret_val; +} + diff --git a/libevolution-utils/e-xml-utils.h b/libevolution-utils/e-xml-utils.h new file mode 100644 index 0000000000..fa123a1d04 --- /dev/null +++ b/libevolution-utils/e-xml-utils.h @@ -0,0 +1,93 @@ +/* + * 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/> + * + * + * Authors: + * Chris Lahey <clahey@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef __E_XML_UTILS__ +#define __E_XML_UTILS__ + +#include <glib.h> + +#include <libxml/tree.h> + +G_BEGIN_DECLS + +/* lang set to NULL means use the current locale. */ +xmlNode *e_xml_get_child_by_name_by_lang (const xmlNode *parent, + const xmlChar *child_name, + const gchar *lang); +/* lang_list set to NULL means use the current locale. */ +xmlNode *e_xml_get_child_by_name_by_lang_list (const xmlNode *parent, + const gchar *name, + const GList *lang_list); +xmlNode *e_xml_get_child_by_name_no_lang (const xmlNode *parent, + const gchar *name); + +gint e_xml_get_integer_prop_by_name (const xmlNode *parent, + const xmlChar *prop_name); +gint e_xml_get_integer_prop_by_name_with_default (const xmlNode *parent, + const xmlChar *prop_name, + gint def); +void e_xml_set_integer_prop_by_name (xmlNode *parent, + const xmlChar *prop_name, + gint value); + +guint e_xml_get_uint_prop_by_name (const xmlNode *parent, + const xmlChar *prop_name); +guint e_xml_get_uint_prop_by_name_with_default (const xmlNode *parent, + const xmlChar *prop_name, + guint def); +void e_xml_set_uint_prop_by_name (xmlNode *parent, + const xmlChar *prop_name, + guint value); + +gboolean e_xml_get_bool_prop_by_name (const xmlNode *parent, + const xmlChar *prop_name); +gboolean e_xml_get_bool_prop_by_name_with_default (const xmlNode *parent, + const xmlChar *prop_name, + gboolean def); +void e_xml_set_bool_prop_by_name (xmlNode *parent, + const xmlChar *prop_name, + gboolean value); + +gdouble e_xml_get_double_prop_by_name (const xmlNode *parent, + const xmlChar *prop_name); +gdouble e_xml_get_double_prop_by_name_with_default (const xmlNode *parent, + const xmlChar *prop_name, + gdouble def); +void e_xml_set_double_prop_by_name ( xmlNode *parent, + const xmlChar *prop_name, + gdouble value); + +gchar *e_xml_get_string_prop_by_name (const xmlNode *parent, + const xmlChar *prop_name); +gchar *e_xml_get_string_prop_by_name_with_default (const xmlNode *parent, + const xmlChar *prop_name, + const gchar *def); +void e_xml_set_string_prop_by_name (xmlNode *parent, + const xmlChar *prop_name, + const gchar *value); + +gchar *e_xml_get_translated_string_prop_by_name (const xmlNode *parent, + const xmlChar *prop_name); + +G_END_DECLS + +#endif /* __E_XML_UTILS__ */ |