/* This is -*- C -*- */ /* vim: set sw=2: */ /* $Id: guppi-data-socket.c,v 1.5 2002/01/14 05:01:17 trow Exp $ */ /* * guppi-data-socket.c * * Copyright (C) 2001 The Free Software Foundation * * Developed by Jon Trowbridge * * 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. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #include #include "guppi-data-socket.h" #include #include static GtkObjectClass *parent_class = NULL; enum { CHANGED, PLUGGED, LAST_SIGNAL }; static guint guppi_data_socket_signals[LAST_SIGNAL] = { 0 }; static guint next_group_id = 100; static GHashTable *socket_hash = NULL; static GHashTable * get_socket_hash (void) { if (socket_hash == NULL) { socket_hash = g_hash_table_new (NULL, NULL); } return socket_hash; } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ typedef struct _GuppiDataSocketPrivate GuppiDataSocketPrivate; struct _GuppiDataSocketPrivate { guint group_id; GuppiData *data; guint changed_handler; GuppiDataSocketTypeFn type_fn; GuppiDataSocketDestroyFn destroy_fn; GuppiDataSocketDupFn dup_fn; gpointer closure; }; static void guppi_data_socket_finalize (GtkObject *obj) { GuppiDataSocket *x = GUPPI_DATA_SOCKET(obj); g_hash_table_remove (get_socket_hash (), x); if (x->priv->destroy_fn) x->priv->destroy_fn (x->priv->closure); if (x->priv->changed_handler) { g_assert (x->priv->data != NULL); gtk_signal_disconnect (GTK_OBJECT (x->priv->data), x->priv->changed_handler); x->priv->changed_handler = 0; } guppi_unref0 (x->priv->data); g_free (x->priv); x->priv = NULL; guppi_finalized (obj); if (parent_class->finalize) parent_class->finalize (obj); } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ static void plug_cb (gpointer key, gpointer val, gpointer closure) { GuppiDataSocket *sock = key; GuppiDataSocket *plugged_sock = closure; if (plugged_sock != sock && plugged_sock->priv->group_id == sock->priv->group_id) { guppi_data_socket_set_data (sock, plugged_sock->priv->data); } } static void plugged (GuppiDataSocket *sock, GuppiData *new_data, GuppiData *old_data) { static gboolean locked = FALSE; if (locked) return; locked = TRUE; if (sock->priv->group_id != 0) { g_hash_table_foreach (get_socket_hash (), plug_cb, sock); } locked = FALSE; } static void guppi_data_socket_class_init (GuppiDataSocketClass *klass) { GtkObjectClass *object_class = (GtkObjectClass *)klass; parent_class = gtk_type_class (GTK_TYPE_OBJECT); klass->plugged = plugged; object_class->finalize = guppi_data_socket_finalize; guppi_data_socket_signals[CHANGED] = gtk_signal_new ("changed", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GuppiDataSocketClass, changed), gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); guppi_data_socket_signals[PLUGGED] = gtk_signal_new ("plugged", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GuppiDataSocketClass, plugged), gtk_marshal_NONE__POINTER_POINTER, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER, GTK_TYPE_POINTER); gtk_object_class_add_signals (object_class, guppi_data_socket_signals, LAST_SIGNAL); } static void guppi_data_socket_init (GuppiDataSocket *obj) { obj->priv = g_new0 (GuppiDataSocketPrivate, 1); } GtkType guppi_data_socket_get_type (void) { static GtkType guppi_data_socket_type = 0; if (!guppi_data_socket_type) { static const GtkTypeInfo guppi_data_socket_info = { "GuppiDataSocket", sizeof (GuppiDataSocket), sizeof (GuppiDataSocketClass), (GtkClassInitFunc)guppi_data_socket_class_init, (GtkObjectInitFunc)guppi_data_socket_init, NULL, NULL, (GtkClassInitFunc)NULL }; guppi_data_socket_type = gtk_type_unique (GTK_TYPE_OBJECT, &guppi_data_socket_info); } return guppi_data_socket_type; } void guppi_data_socket_construct (GuppiDataSocket *sock, GuppiDataSocketTypeFn type_fn, GuppiDataSocketDestroyFn destroy_fn, GuppiDataSocketDupFn dup_fn, gpointer closure) { g_return_if_fail (GUPPI_IS_DATA_SOCKET (sock)); g_return_if_fail (sock->priv->group_id == 0); sock->priv->group_id = next_group_id; ++next_group_id; sock->priv->type_fn = type_fn; sock->priv->destroy_fn = destroy_fn; sock->priv->dup_fn = dup_fn; sock->priv->closure = closure; g_hash_table_insert (get_socket_hash (), sock, sock); } GuppiDataSocket * guppi_data_socket_new (void) { GuppiDataSocket *sock; sock = GUPPI_DATA_SOCKET (guppi_type_new (guppi_data_socket_get_type ())); guppi_data_socket_construct (sock, NULL, NULL, NULL, NULL); return sock; } static gboolean our_type_check_fn (GuppiDataSocket *sock, GuppiData *data, gpointer closure) { GtkType accepted_type, data_type; if (data == NULL) return TRUE; accepted_type = (GtkType) GPOINTER_TO_UINT (closure); data_type = GTK_OBJECT_TYPE (data); return gtk_type_is_a (data_type, accepted_type); } GuppiDataSocket * guppi_data_socket_new_by_type (GtkType type) { GuppiDataSocket *sock; g_return_val_if_fail (type != 0, NULL); sock = GUPPI_DATA_SOCKET (guppi_type_new (guppi_data_socket_get_type ())); guppi_data_socket_construct (sock, our_type_check_fn, NULL, NULL, GUINT_TO_POINTER (type)); return sock; } GuppiDataSocket * guppi_data_socket_new_full (GuppiDataSocketTypeFn type_fn, GuppiDataSocketDestroyFn destroy_fn, GuppiDataSocketDupFn dup_fn, gpointer closure) { GuppiDataSocket *sock; sock = GUPPI_DATA_SOCKET (guppi_type_new (guppi_data_socket_get_type ())); guppi_data_socket_construct (sock, type_fn, destroy_fn, dup_fn, closure); return sock; } GuppiData * guppi_data_socket_get_data (GuppiDataSocket *sock) { g_return_val_if_fail (GUPPI_IS_DATA_SOCKET (sock), NULL); return sock->priv->data; } gboolean guppi_data_socket_try_data (GuppiDataSocket *sock, GuppiData *data) { g_return_val_if_fail (GUPPI_IS_DATA_SOCKET (sock), FALSE); g_return_val_if_fail (data == NULL || GUPPI_IS_DATA (data), FALSE); if (sock->priv->type_fn) return sock->priv->type_fn (sock, data, sock->priv->closure); return TRUE; } static void changed_cb (GuppiData *d, gpointer sock) { gtk_signal_emit (GTK_OBJECT (sock), guppi_data_socket_signals[CHANGED]); } void guppi_data_socket_set_data (GuppiDataSocket *sock, GuppiData *data) { g_return_if_fail (GUPPI_IS_DATA_SOCKET (sock)); g_return_if_fail (data == NULL || GUPPI_IS_DATA (data)); if (sock->priv->type_fn && !sock->priv->type_fn (sock, data, sock->priv->closure)) { return; } if (data != sock->priv->data) { GuppiData *old_data = sock->priv->data; if (sock->priv->data) { if (sock->priv->changed_handler) { gtk_signal_disconnect (GTK_OBJECT (sock->priv->data), sock->priv->changed_handler); sock->priv->changed_handler = 0; } } sock->priv->data = data; guppi_ref (sock->priv->data); if (sock->priv->data) { sock->priv->changed_handler = gtk_signal_connect (GTK_OBJECT (sock->priv->data), "changed", GTK_SIGNAL_FUNC (changed_cb), sock); } gtk_signal_emit (GTK_OBJECT (sock), guppi_data_socket_signals[PLUGGED], data, old_data); guppi_unref (old_data); } } void guppi_data_socket_connect (GuppiDataSocket *master, GuppiDataSocket *slave) { g_return_if_fail (GUPPI_IS_DATA_SOCKET (master)); g_return_if_fail (GUPPI_IS_DATA_SOCKET (slave)); if (master->priv->group_id == slave->priv->group_id) return; if (slave->priv->destroy_fn) slave->priv->destroy_fn (slave->priv->closure); slave->priv->type_fn = master->priv->type_fn; slave->priv->destroy_fn = master->priv->destroy_fn; slave->priv->dup_fn = master->priv->dup_fn; if (master->priv->dup_fn) slave->priv->closure = master->priv->dup_fn (master->priv->closure); else slave->priv->closure = master->priv->closure; /* Set group id temporarily to zero, to insure that no other GuppiDataSocket gets a plugged signal when we set the data. */ slave->priv->group_id = 0; guppi_data_socket_set_data (slave, master->priv->data); /* Now that the master and the slave's data are in sync, we can give the slave the master's group id. */ slave->priv->group_id = master->priv->group_id; } xmlNodePtr guppi_data_socket_export_xml (GuppiDataSocket *sock, GuppiXMLDocument *doc) { g_return_val_if_fail (GUPPI_IS_DATA_SOCKET (sock), NULL); g_return_val_if_fail (doc != NULL, NULL); return xmlNewNode (doc->ns, "DataSocket"); } gboolean guppi_data_socket_import_xml (GuppiDataSocket *sock, GuppiXMLDocument *doc, xmlNodePtr node) { g_return_val_if_fail (GUPPI_IS_DATA_SOCKET (sock), FALSE); g_return_val_if_fail (doc != NULL, FALSE); g_return_val_if_fail (node != NULL, FALSE); guppi_FIXME (); return TRUE; }