gimp/app/gimpset.c

403 lines
8.7 KiB
C

/* The GIMP -- an image manipulation program
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
*
* 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 "gimpsignal.h"
#include "gimpsetP.h"
/* Yep, this can be optimized quite a lot */
typedef struct _GimpSetHandler
{
const gchar *signame;
GtkSignalFunc func;
gpointer user_data;
} GimpSetHandler;
typedef struct
{
gpointer object;
GArray *handlers;
guint destroy_handler;
} Node;
enum
{
ADD,
REMOVE,
ACTIVE_CHANGED,
LAST_SIGNAL
};
static Node * gimp_set_find_node (GimpSet *set,
gpointer object);
static Node * gimp_set_node_new (GimpSet *set,
gpointer object);
static void gimp_set_node_free (GimpSet *set,
Node *node);
static guint gimp_set_signals[LAST_SIGNAL] = { 0 };
static GimpObjectClass *parent_class = NULL;
static void
gimp_set_destroy (GtkObject *object)
{
GimpSet *set = GIMP_SET (object);
GSList *list;
for (list = set->list; list; list = list->next)
gimp_set_node_free (set, list->data);
g_slist_free (set->list);
g_array_free (set->handlers, TRUE);
if (GTK_OBJECT_CLASS (parent_class)->destroy)
GTK_OBJECT_CLASS (parent_class)->destroy (object);
}
static void
gimp_set_init (GimpSet *set)
{
set->list = NULL;
set->type = GTK_TYPE_OBJECT;
set->handlers = g_array_new (FALSE, FALSE, sizeof (GimpSetHandler));
set->active_element = NULL;
}
static void
gimp_set_class_init (GimpSetClass* klass)
{
GtkObjectClass *object_class;
object_class = GTK_OBJECT_CLASS (klass);
parent_class = gtk_type_class (gimp_object_get_type ());
gimp_set_signals[ADD]=
gimp_signal_new ("add",
GTK_RUN_FIRST,
object_class->type,
GTK_SIGNAL_OFFSET (GimpSetClass,
add),
gimp_sigtype_pointer);
gimp_set_signals[REMOVE]=
gimp_signal_new ("remove",
GTK_RUN_FIRST,
object_class->type,
GTK_SIGNAL_OFFSET (GimpSetClass,
remove),
gimp_sigtype_pointer);
gimp_set_signals[ACTIVE_CHANGED]=
gimp_signal_new ("active_changed",
GTK_RUN_FIRST,
object_class->type,
GTK_SIGNAL_OFFSET (GimpSetClass,
active_changed),
gimp_sigtype_pointer);
gtk_object_class_add_signals (object_class, gimp_set_signals, LAST_SIGNAL);
object_class->destroy = gimp_set_destroy;
klass->add = NULL;
klass->remove = NULL;
klass->active_changed = NULL;
}
GtkType
gimp_set_get_type (void)
{
static GtkType gimpset_type = 0;
GIMP_TYPE_INIT (gimpset_type,
GimpSet,
GimpSetClass,
gimp_set_init,
gimp_set_class_init,
GIMP_TYPE_OBJECT);
return gimpset_type;
}
GimpSet *
gimp_set_new (GtkType type,
gboolean weak)
{
GimpSet *set;
/* untyped sets must not be weak, since we can't attach a
* destroy handler
*/
g_assert (!(type == GTK_TYPE_NONE && weak == TRUE));
set = gtk_type_new (gimp_set_get_type ());
set->type = type;
set->weak = weak;
return set;
}
static void
gimp_set_destroy_cb (GtkObject *object,
gpointer data)
{
GimpSet *set = GIMP_SET (data);
gimp_set_remove (set, object);
}
static Node *
gimp_set_find_node (GimpSet *set,
gpointer object)
{
GSList *list = set->list;
for (list = set->list; list; list = list->next)
{
Node *node = list->data;
if (node->object == object)
return node;
}
return NULL;
}
static Node *
gimp_set_node_new (GimpSet *set,
gpointer object)
{
gint i;
Node *node = g_new (Node, 1);
node->object = object;
node->handlers = g_array_new (FALSE, FALSE, sizeof (guint));
g_array_set_size (node->handlers, set->handlers->len);
for (i = 0; i < node->handlers->len; i++)
{
GimpSetHandler *handler =
&g_array_index (set->handlers, GimpSetHandler, i);
if (handler->signame)
g_array_index (node->handlers, guint, i)
= gtk_signal_connect (GTK_OBJECT (object),
handler->signame,
handler->func,
handler->user_data);
}
if (set->weak)
node->destroy_handler =
gtk_signal_connect (GTK_OBJECT (object), "destroy",
GTK_SIGNAL_FUNC (gimp_set_destroy_cb),
set);
return node;
}
static void
gimp_set_node_free (GimpSet *set,
Node *node)
{
gint i;
GimpObject *object = node->object;
for (i = 0; i < set->handlers->len; i++)
{
GimpSetHandler *handler =
&g_array_index (set->handlers, GimpSetHandler, i);
if (handler->signame)
gtk_signal_disconnect (GTK_OBJECT (object),
g_array_index (node->handlers, guint, i));
}
if (set->weak)
gtk_signal_disconnect (GTK_OBJECT (object),
node->destroy_handler);
g_array_free (node->handlers, TRUE);
g_free (node);
}
gboolean
gimp_set_add (GimpSet *set,
gpointer object)
{
g_return_val_if_fail (set, FALSE);
if (set->type != GTK_TYPE_NONE)
g_return_val_if_fail (GTK_CHECK_TYPE (object, set->type), FALSE);
if (gimp_set_find_node (set, object))
return FALSE;
set->list = g_slist_prepend (set->list, gimp_set_node_new (set, object));
gtk_signal_emit (GTK_OBJECT (set), gimp_set_signals[ADD], object);
return TRUE;
}
gboolean
gimp_set_remove (GimpSet *set,
gpointer object)
{
Node *node;
g_return_val_if_fail (set, FALSE);
node = gimp_set_find_node (set, object);
g_return_val_if_fail (node, FALSE);
gimp_set_node_free (set, node);
set->list = g_slist_remove (set->list, node);
gtk_signal_emit (GTK_OBJECT (set), gimp_set_signals[REMOVE], object);
return TRUE;
}
gboolean
gimp_set_have (GimpSet *set,
gpointer object)
{
return !!gimp_set_find_node (set, object);
}
void
gimp_set_foreach (GimpSet *set,
GFunc func,
gpointer user_data)
{
GSList *list;
for (list = set->list; list; list = list->next)
{
Node *node = list->data;
func (node->object, user_data);
}
}
GtkType
gimp_set_type (GimpSet* set)
{
return set->type;
}
void
gimp_set_set_active (GimpSet *set,
gpointer object)
{
if (object != set->active_element && gimp_set_have (set, object))
{
set->active_element = object;
gtk_signal_emit (GTK_OBJECT (set),
gimp_set_signals[ACTIVE_CHANGED],
object);
}
}
gpointer
gimp_set_get_active (GimpSet *set)
{
if (gimp_set_have (set, set->active_element))
return set->active_element;
return NULL;
}
GimpSetHandlerId
gimp_set_add_handler (GimpSet *set,
const gchar *signame,
GtkSignalFunc handler,
gpointer user_data)
{
GimpSetHandler set_handler;
GSList *list;
guint a;
g_assert (signame);
/* you can't set a handler on something that's not a GTK object */
g_assert (set->type != GTK_TYPE_NONE);
set_handler.signame = signame;
set_handler.func = handler;
set_handler.user_data = user_data;
for (a = 0; a < set->handlers->len; a++)
if (! g_array_index (set->handlers, GimpSetHandler, a).signame)
break;
if (a < set->handlers->len)
{
g_array_index (set->handlers, GimpSetHandler, a) = set_handler;
for (list = set->list; list; list = list->next)
{
Node *node = list->data;
guint i = gtk_signal_connect (GTK_OBJECT (node->object), signame,
handler,
user_data);
g_array_index (node->handlers, guint, a) = i;
}
}
else
{
g_array_append_val (set->handlers, set_handler);
for (list = set->list; list; list = list->next)
{
Node *node = list->data;
guint i = gtk_signal_connect (GTK_OBJECT(node->object), signame,
handler,
user_data);
g_array_append_val (node->handlers, i);
}
}
return a;
}
void
gimp_set_remove_handler (GimpSet *set,
GimpSetHandlerId id)
{
GSList *list;
/* you can't remove a signal handler on something that's not a GTK object */
g_return_if_fail (set->type != GTK_TYPE_NONE);
for (list = set->list; list; list = list->next)
{
Node *node = list->data;
gtk_signal_disconnect (GTK_OBJECT (node->object),
g_array_index (node->handlers, guint, id));
}
g_array_index (set->handlers, GimpSetHandler, id).signame = NULL;
}