aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-misc-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'e-util/e-misc-utils.c')
-rw-r--r--e-util/e-misc-utils.c236
1 files changed, 236 insertions, 0 deletions
diff --git a/e-util/e-misc-utils.c b/e-util/e-misc-utils.c
index 3a52292af4..f7be7af5ac 100644
--- a/e-util/e-misc-utils.c
+++ b/e-util/e-misc-utils.c
@@ -2256,3 +2256,239 @@ e_binding_bind_object_text_property (gpointer source,
e_binding_transform_text_non_null,
NULL, NULL);
}
+
+typedef struct _EConnectNotifyData {
+ GConnectFlags flags;
+ GValue *old_value;
+
+ GCallback c_handler;
+ gpointer user_data;
+} EConnectNotifyData;
+
+static EConnectNotifyData *
+e_connect_notify_data_new (GCallback c_handler,
+ gpointer user_data,
+ guint32 connect_flags)
+{
+ EConnectNotifyData *connect_data;
+
+ connect_data = g_new0 (EConnectNotifyData, 1);
+ connect_data->flags = connect_flags;
+ connect_data->c_handler = c_handler;
+ connect_data->user_data = user_data;
+
+ return connect_data;
+}
+
+static void
+e_connect_notify_data_free (EConnectNotifyData *data)
+{
+ if (!data)
+ return;
+
+ if (data->old_value) {
+ g_value_unset (data->old_value);
+ g_free (data->old_value);
+ }
+ g_free (data);
+}
+
+static gboolean
+e_value_equal (GValue *value1,
+ GValue *value2)
+{
+ if (value1 == value2)
+ return TRUE;
+
+ if (!value1 || !value2)
+ return FALSE;
+
+ #define testType(_uc,_lc) G_STMT_START { \
+ if (G_VALUE_HOLDS_ ## _uc (value1)) \
+ return g_value_get_ ## _lc (value1) == g_value_get_ ## _lc (value2); \
+ } G_STMT_END
+
+ testType (BOOLEAN, boolean);
+ testType (BOXED, boxed);
+ testType (CHAR, schar);
+ testType (DOUBLE, double);
+ testType (ENUM, enum);
+ testType (FLAGS, flags);
+ testType (FLOAT, float);
+ testType (GTYPE, gtype);
+ testType (INT, int);
+ testType (INT64, int64);
+ testType (LONG, long);
+ testType (OBJECT, object);
+ testType (POINTER, pointer);
+ testType (UCHAR, uchar);
+ testType (UINT, uint);
+ testType (UINT64, uint64);
+ testType (ULONG, ulong);
+
+ #undef testType
+
+ if (G_VALUE_HOLDS_PARAM (value1)) {
+ GParamSpec *param1, *param2;
+
+ param1 = g_value_get_param (value1);
+ param2 = g_value_get_param (value2);
+
+ return param1 && param2 &&
+ g_strcmp0 (param1->name, param2->name) == 0 &&
+ param1->flags == param2->flags &&
+ param1->value_type == param2->value_type &&
+ param1->owner_type == param2->owner_type;
+ } else if (G_VALUE_HOLDS_STRING (value1)) {
+ const gchar *string1, *string2;
+
+ string1 = g_value_get_string (value1);
+ string2 = g_value_get_string (value2);
+
+ return g_strcmp0 (string1, string2) == 0;
+ } else if (G_VALUE_HOLDS_VARIANT (value1)) {
+ GVariant *variant1, *variant2;
+
+ variant1 = g_value_get_variant (value1);
+ variant2 = g_value_get_variant (value2);
+
+ return variant1 == variant2 ||
+ (variant1 && variant2 && g_variant_equal (variant1, variant2));
+ }
+
+ return FALSE;
+}
+
+static void
+e_signal_connect_notify_cb (gpointer instance,
+ GParamSpec *param,
+ gpointer user_data)
+{
+ EConnectNotifyData *connect_data = user_data;
+ GValue *value;
+
+ g_return_if_fail (connect_data != NULL);
+
+ value = g_new0 (GValue, 1);
+ g_value_init (value, param->value_type);
+ g_object_get_property (instance, param->name, value);
+
+ if (!e_value_equal (connect_data->old_value, value)) {
+ typedef void (* NotifyCBType) (gpointer instance, GParamSpec *param, gpointer user_data);
+ NotifyCBType c_handler = (NotifyCBType) connect_data->c_handler;
+
+ if (connect_data->old_value) {
+ g_value_unset (connect_data->old_value);
+ g_free (connect_data->old_value);
+ }
+ connect_data->old_value = value;
+
+ if (connect_data->flags == G_CONNECT_SWAPPED) {
+ c_handler (connect_data->user_data, param, instance);
+ } else {
+ c_handler (instance, param, connect_data->user_data);
+ }
+ } else {
+ g_value_unset (value);
+ g_free (value);
+ }
+}
+
+gulong
+e_signal_connect_notify (gpointer instance,
+ const gchar *notify_name,
+ GCallback c_handler,
+ gpointer user_data)
+{
+ EConnectNotifyData *connect_data;
+
+ g_return_val_if_fail (g_str_has_prefix (notify_name, "notify::"), 0);
+
+ connect_data = e_connect_notify_data_new (c_handler, user_data, 0);
+
+ return g_signal_connect_data (instance,
+ notify_name,
+ G_CALLBACK (e_signal_connect_notify_cb),
+ connect_data,
+ (GClosureNotify) e_connect_notify_data_free,
+ 0);
+}
+
+gulong
+e_signal_connect_notify_after (gpointer instance,
+ const gchar *notify_name,
+ GCallback c_handler,
+ gpointer user_data)
+{
+ EConnectNotifyData *connect_data;
+
+ g_return_val_if_fail (g_str_has_prefix (notify_name, "notify::"), 0);
+
+ connect_data = e_connect_notify_data_new (c_handler, user_data, G_CONNECT_AFTER);
+
+ return g_signal_connect_data (instance,
+ notify_name,
+ G_CALLBACK (e_signal_connect_notify_cb),
+ connect_data,
+ (GClosureNotify) e_connect_notify_data_free,
+ G_CONNECT_AFTER);
+}
+
+gulong
+e_signal_connect_notify_swapped (gpointer instance,
+ const gchar *notify_name,
+ GCallback c_handler,
+ gpointer user_data)
+{
+ EConnectNotifyData *connect_data;
+
+ g_return_val_if_fail (g_str_has_prefix (notify_name, "notify::"), 0);
+
+ connect_data = e_connect_notify_data_new (c_handler, user_data, G_CONNECT_SWAPPED);
+
+ return g_signal_connect_data (instance,
+ notify_name,
+ G_CALLBACK (e_signal_connect_notify_cb),
+ connect_data,
+ (GClosureNotify) e_connect_notify_data_free,
+ 0);
+}
+
+gulong
+e_signal_connect_notify_object (gpointer instance,
+ const gchar *notify_name,
+ GCallback c_handler,
+ gpointer gobject,
+ GConnectFlags connect_flags)
+{
+ EConnectNotifyData *connect_data;
+ GClosure *closure;
+
+ g_return_val_if_fail (g_str_has_prefix (notify_name, "notify::"), 0);
+
+ if (!gobject) {
+ if ((connect_flags & G_CONNECT_SWAPPED) != 0)
+ return e_signal_connect_notify_swapped (instance, notify_name, c_handler, gobject);
+ else if ((connect_flags & G_CONNECT_AFTER) != 0)
+ e_signal_connect_notify_after (instance, notify_name, c_handler, gobject);
+ else
+ g_warn_if_fail (connect_flags == 0);
+
+ return e_signal_connect_notify (instance, notify_name, c_handler, gobject);
+ }
+
+ g_return_val_if_fail (G_IS_OBJECT (gobject), 0);
+
+ connect_data = e_connect_notify_data_new (c_handler, gobject, connect_flags & G_CONNECT_SWAPPED);
+ closure = g_cclosure_new (
+ G_CALLBACK (e_signal_connect_notify_cb),
+ connect_data,
+ (GClosureNotify) e_connect_notify_data_free);
+
+ g_object_watch_closure (G_OBJECT (gobject), closure);
+
+ return g_signal_connect_closure (instance,
+ notify_name,
+ closure,
+ connect_flags & G_CONNECT_AFTER);
+}