270 lines
8.2 KiB
C
270 lines
8.2 KiB
C
/*
|
|
* mooutils/bind.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 "mooutils/bind.h"
|
|
|
|
|
|
static void button_clicked (GtkButton *btn, GtkEntry *entry)
|
|
{
|
|
MooQueryTextFunc func = (MooQueryTextFunc)g_object_get_data (G_OBJECT (btn), "moo_query_text_func");
|
|
MooTransformTextFunc text_func = (MooTransformTextFunc)g_object_get_data (G_OBJECT (btn), "moo_transform_text_func");
|
|
void *data = g_object_get_data (G_OBJECT (btn), "moo_bind_button_data");
|
|
const char *text = func (data, GTK_WIDGET (btn));
|
|
if (!text) return;
|
|
if (text_func) {
|
|
char *transformed = text_func (text, data);
|
|
gtk_entry_set_text (entry, transformed);
|
|
g_free (transformed);
|
|
}
|
|
else
|
|
gtk_entry_set_text (entry, text);
|
|
}
|
|
|
|
|
|
void moo_bind_button (GtkButton *button,
|
|
GtkEntry *entry,
|
|
MooQueryTextFunc func,
|
|
MooTransformTextFunc text_func,
|
|
gpointer data)
|
|
{
|
|
g_return_if_fail (button != NULL && entry != NULL && func != NULL);
|
|
g_object_set_data (G_OBJECT (button), "moo_query_text_func", (gpointer)func);
|
|
g_object_set_data (G_OBJECT (button), "moo_transform_text_func", (gpointer)text_func);
|
|
g_object_set_data (G_OBJECT (button), "moo_bind_button_data", data);
|
|
g_signal_connect (button, "clicked", G_CALLBACK (button_clicked), entry);
|
|
}
|
|
|
|
|
|
char *moo_quote_text (const char *text,
|
|
G_GNUC_UNUSED gpointer data)
|
|
{
|
|
return text ? g_strdup_printf ("\"%s\"", text) : NULL;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* BindProperty
|
|
*/
|
|
|
|
typedef struct {
|
|
GObject *source;
|
|
GObject *target;
|
|
GParamSpec *source_prop;
|
|
GParamSpec *target_prop;
|
|
gboolean alive;
|
|
MooTransformPropFunc transform;
|
|
gpointer data;
|
|
GDestroyNotify destroy_notify;
|
|
} PropWatcher;
|
|
|
|
|
|
static void source_died (PropWatcher *watcher);
|
|
static void target_died (PropWatcher *watcher);
|
|
static void watcher_check (PropWatcher *watcher);
|
|
static void watcher_die (PropWatcher *watcher);
|
|
|
|
|
|
static PropWatcher*
|
|
prop_watcher_new (GObject *target,
|
|
const char *target_prop,
|
|
GObject *source,
|
|
const char *source_prop,
|
|
MooTransformPropFunc transform,
|
|
gpointer transform_data,
|
|
GDestroyNotify destroy_notify)
|
|
{
|
|
PropWatcher *watcher;
|
|
GObjectClass *target_class, *source_class;
|
|
char *signal_name;
|
|
|
|
g_return_val_if_fail (G_IS_OBJECT (target), NULL);
|
|
g_return_val_if_fail (G_IS_OBJECT (source), NULL);
|
|
g_return_val_if_fail (target_prop != NULL, NULL);
|
|
g_return_val_if_fail (source_prop != NULL, NULL);
|
|
g_return_val_if_fail (transform != NULL, NULL);
|
|
|
|
target_class = g_type_class_peek (G_OBJECT_TYPE (target));
|
|
source_class = g_type_class_peek (G_OBJECT_TYPE (source));
|
|
|
|
watcher = g_new0 (PropWatcher, 1);
|
|
|
|
watcher->source = source;
|
|
watcher->target = target;
|
|
|
|
watcher->source_prop = g_object_class_find_property (source_class, source_prop);
|
|
if (!watcher->source_prop)
|
|
{
|
|
g_warning ("%s: could not find property '%s' in class '%s'",
|
|
G_STRLOC, source_prop, g_type_name (G_OBJECT_TYPE (source)));
|
|
g_free (watcher);
|
|
return NULL;
|
|
}
|
|
|
|
watcher->target_prop = g_object_class_find_property (target_class, target_prop);
|
|
if (!watcher->target_prop)
|
|
{
|
|
g_warning ("%s: could not find property '%s' in class '%s'",
|
|
G_STRLOC, target_prop, g_type_name (G_OBJECT_TYPE (target)));
|
|
g_free (watcher);
|
|
return NULL;
|
|
}
|
|
|
|
watcher->alive = TRUE;
|
|
|
|
watcher->transform = transform;
|
|
watcher->data = transform_data;
|
|
watcher->destroy_notify = destroy_notify;
|
|
|
|
signal_name = g_strdup_printf ("notify::%s", source_prop);
|
|
g_signal_connect_swapped (source, signal_name,
|
|
G_CALLBACK (watcher_check), watcher);
|
|
g_object_weak_ref (source, (GWeakNotify) source_died, watcher);
|
|
g_object_weak_ref (target, (GWeakNotify) target_died, watcher);
|
|
|
|
g_free (signal_name);
|
|
return watcher;
|
|
}
|
|
|
|
|
|
static void
|
|
watcher_die (PropWatcher *watcher)
|
|
{
|
|
if (watcher->source)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (watcher->source,
|
|
(gpointer) watcher_check,
|
|
watcher);
|
|
g_object_weak_unref (watcher->source, (GWeakNotify) source_died, watcher);
|
|
}
|
|
|
|
if (watcher->target)
|
|
g_object_weak_unref (watcher->target, (GWeakNotify) target_died, watcher);
|
|
|
|
if (watcher->destroy_notify)
|
|
watcher->destroy_notify (watcher->data);
|
|
|
|
g_free (watcher);
|
|
}
|
|
|
|
|
|
void
|
|
moo_bind_property_full (GObject *target,
|
|
const char *target_prop,
|
|
GObject *source,
|
|
const char *source_prop,
|
|
MooTransformPropFunc transform,
|
|
gpointer transform_data,
|
|
GDestroyNotify destroy_notify)
|
|
{
|
|
PropWatcher *watcher;
|
|
|
|
g_return_if_fail (G_IS_OBJECT (target));
|
|
g_return_if_fail (G_IS_OBJECT (source));
|
|
g_return_if_fail (target_prop != NULL);
|
|
g_return_if_fail (source_prop != NULL);
|
|
g_return_if_fail (transform != NULL);
|
|
|
|
watcher = prop_watcher_new (target, target_prop,
|
|
source, source_prop,
|
|
transform, transform_data,
|
|
destroy_notify);
|
|
|
|
if (!watcher)
|
|
return;
|
|
|
|
watcher_check (watcher);
|
|
}
|
|
|
|
|
|
static void
|
|
source_died (PropWatcher *watcher)
|
|
{
|
|
watcher->source = NULL;
|
|
watcher_die (watcher);
|
|
}
|
|
|
|
|
|
static void
|
|
target_died (PropWatcher *watcher)
|
|
{
|
|
watcher->target = NULL;
|
|
watcher_die (watcher);
|
|
}
|
|
|
|
|
|
static void
|
|
watcher_check (PropWatcher *watcher)
|
|
{
|
|
GValue source_val, target_val;
|
|
|
|
if (!G_IS_OBJECT (watcher->source) ||
|
|
!G_IS_OBJECT (watcher->target))
|
|
{
|
|
watcher_die (watcher);
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
source_val.g_type = 0;
|
|
target_val.g_type = 0;
|
|
|
|
g_value_init (&source_val, watcher->source_prop->value_type);
|
|
g_value_init (&target_val, watcher->target_prop->value_type);
|
|
|
|
g_object_get_property (watcher->source,
|
|
watcher->source_prop->name,
|
|
&source_val);
|
|
watcher->transform (&target_val, &source_val, watcher->data);
|
|
g_object_set_property (watcher->target,
|
|
watcher->target_prop->name,
|
|
&target_val);
|
|
|
|
g_value_unset (&source_val);
|
|
g_value_unset (&target_val);
|
|
}
|
|
|
|
|
|
static void
|
|
bool_transform (GValue *target,
|
|
const GValue *source,
|
|
gpointer invert)
|
|
{
|
|
if (invert)
|
|
g_value_set_boolean (target, !g_value_get_boolean (source));
|
|
else
|
|
g_value_set_boolean (target, g_value_get_boolean (source));
|
|
}
|
|
|
|
void
|
|
moo_bind_bool_property (GObject *target,
|
|
const char *target_prop,
|
|
GObject *source,
|
|
const char *source_prop,
|
|
gboolean invert)
|
|
{
|
|
moo_bind_property_full (target, target_prop, source, source_prop,
|
|
bool_transform, GINT_TO_POINTER (invert), NULL);
|
|
}
|
|
|
|
|
|
void
|
|
moo_bind_sensitive (GtkToggleButton *btn,
|
|
GtkWidget **dependent,
|
|
int num_dependent,
|
|
gboolean invert)
|
|
{
|
|
int i;
|
|
for (i = 0; i < num_dependent; ++i)
|
|
moo_bind_bool_property (G_OBJECT (dependent[i]), "sensitive",
|
|
G_OBJECT (btn), "active", invert);
|
|
}
|