
2000-10-18 Not Zed <NotZed@HelixCode.com> * camel-mime-part.c (add_header): No, we must not encode the headers here. These interfaces ARE raw interfaces as they are defined in camel_medium. Also removed a bogus/meaningless FIXME. (set_header): Likewise here, we must not. (process_header): Removed another bogus comment. * camel-object.c (shared_is_of_type): Comment out the spitting of a big warning when we're trying to determine types from code. * providers/mbox/camel-mbox-summary.c (message_info_new_from_parser): Only call ibex funcitons if we have an index. * providers/mh/camel-mh-summary.c (camel_mh_summary_add): Only call ibex functions if we have an index. (remove_summary): Likewise. (camel_mh_summary_check): Likewise. * providers/nntp/camel-nntp-store.c (nntp_store_get_folder): get_folder -> flags argument. * providers/vee/camel-vee-store.c (vee_get_folder): create->flags. * providers/pop3/camel-pop3-store.c (get_folder): Changed create -> flags. * providers/imap/camel-imap-store.c (get_folder): Added flags argument. * providers/mh/camel-mh-folder.c (camel_mh_folder_new): Added flags argument, and fixed code appropriately. * providers/mh/camel-mh-store.c (get_folder): Added flags argument. * camel-folder-search.c (message_body_contains): Perform a regex match on the contents of messages. This wont quite work yet as message contents are encoded when written to a stream. (build_match_regex): Converts a number of strings into a regex matching pattern, escaping special chars. (match_message): match a single message from a folder, by uid. Slow. (search_body_contains): Changed to support matching where no index is supplied. Matches are performed by retrieving message contents, etc. () WTF? camel should not be including any widget headers. * providers/mbox/camel-mbox-folder.c (camel_mbox_folder_new): Added flags argument. (mbox_refresh_info): Changed into a NOP, the refresh info code moved into the new function. (camel_mbox_folder_new): If we have an index requested, build one, otherwise, remove an old one, or just dont do anything. * providers/mbox/camel-mbox-store.c (get_folder): Changed create to flags, changed code to suit. * camel-store.c (camel_store_get_folder): Changed create to flags. (get_folder_internal): And here. (get_folder): And here too. * camel-store.h (camel_store_get_folder): Change the create argument to be a flags argument. svn path=/trunk/; revision=5978
974 lines
24 KiB
C
974 lines
24 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
/* camel-object.c: Base class for Camel */
|
|
|
|
/*
|
|
* Author:
|
|
* Dan Winship <danw@helixcode.com>
|
|
*
|
|
* Copyright 2000 Helix Code, Inc. (http://www.helixcode.com)
|
|
*
|
|
* 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 <config.h>
|
|
#include "camel-object.h"
|
|
|
|
/* I just mashed the keyboard for these... */
|
|
#define CAMEL_OBJECT_MAGIC_VALUE 0x77A344EF
|
|
#define CAMEL_OBJECT_CLASS_MAGIC_VALUE 0xEE26A990
|
|
#define CAMEL_OBJECT_FINALIZED_VALUE 0x84AC3656
|
|
#define CAMEL_OBJECT_CLASS_FINALIZED_VALUE 0x7621ABCD
|
|
|
|
#define DEFAULT_PREALLOCS 8
|
|
|
|
#define BAST_CASTARD 1 /* Define to return NULL when casts fail */
|
|
|
|
#define NULL_PREP_VALUE ((gpointer)make_global_classfuncs) /* See camel_object_class_declare_event */
|
|
|
|
/* ** Quickie type system ************************************************* */
|
|
|
|
typedef struct _CamelTypeInfo
|
|
{
|
|
CamelType self;
|
|
CamelType parent;
|
|
const gchar *name;
|
|
|
|
size_t instance_size;
|
|
GMemChunk *instance_chunk;
|
|
CamelObjectInitFunc instance_init;
|
|
CamelObjectFinalizeFunc instance_finalize;
|
|
GList *free_instances;
|
|
|
|
size_t classfuncs_size;
|
|
CamelObjectClassInitFunc class_init;
|
|
CamelObjectClassFinalizeFunc class_finalize;
|
|
CamelObjectClass *global_classfuncs;
|
|
}
|
|
CamelTypeInfo;
|
|
|
|
typedef struct _CamelHookPair
|
|
{
|
|
CamelObjectEventHookFunc func;
|
|
gpointer user_data;
|
|
}
|
|
CamelHookPair;
|
|
|
|
/* ************************************************************************ */
|
|
|
|
static void camel_type_lock_up (void);
|
|
static void camel_type_lock_down (void);
|
|
|
|
static void obj_init (CamelObject * obj);
|
|
static void obj_finalize (CamelObject * obj);
|
|
static void obj_class_init (CamelObjectClass * class);
|
|
static void obj_class_finalize (CamelObjectClass * class);
|
|
|
|
static gboolean shared_is_of_type (CamelObjectShared * sh, CamelType ctype,
|
|
gboolean is_obj);
|
|
static void make_global_classfuncs (CamelTypeInfo * type_info);
|
|
|
|
/* ************************************************************************ */
|
|
|
|
G_LOCK_DEFINE_STATIC (type_system);
|
|
G_LOCK_DEFINE_STATIC (type_system_level);
|
|
static GPrivate *type_system_locklevel = NULL;
|
|
|
|
G_LOCK_DEFINE_STATIC (refcount);
|
|
|
|
static gboolean type_system_initialized = FALSE;
|
|
static GHashTable *ctype_to_typeinfo = NULL;
|
|
static const CamelType camel_object_type = 1;
|
|
static CamelType cur_max_type = CAMEL_INVALID_TYPE;
|
|
|
|
/* ************************************************************************ */
|
|
|
|
#define LOCK_VAL (GPOINTER_TO_INT (g_private_get (type_system_locklevel)))
|
|
#define LOCK_SET( val ) g_private_set (type_system_locklevel, GINT_TO_POINTER (val))
|
|
|
|
static void
|
|
camel_type_lock_up (void)
|
|
{
|
|
G_LOCK (type_system_level);
|
|
|
|
if (type_system_locklevel == NULL)
|
|
type_system_locklevel = g_private_new (GINT_TO_POINTER (0));
|
|
|
|
if (LOCK_VAL == 0) {
|
|
G_UNLOCK (type_system_level);
|
|
G_LOCK (type_system);
|
|
G_LOCK (type_system_level);
|
|
}
|
|
|
|
LOCK_SET (LOCK_VAL + 1);
|
|
|
|
G_UNLOCK (type_system_level);
|
|
}
|
|
|
|
static void
|
|
camel_type_lock_down (void)
|
|
{
|
|
G_LOCK (type_system_level);
|
|
|
|
if (type_system_locklevel == NULL) {
|
|
g_warning
|
|
("camel_type_lock_down: lock down before a lock up?");
|
|
type_system_locklevel = g_private_new (GINT_TO_POINTER (0));
|
|
G_UNLOCK (type_system_level);
|
|
return;
|
|
}
|
|
|
|
LOCK_SET (LOCK_VAL - 1);
|
|
|
|
if (LOCK_VAL == 0)
|
|
G_UNLOCK (type_system);
|
|
|
|
G_UNLOCK (type_system_level);
|
|
}
|
|
|
|
void
|
|
camel_type_init (void)
|
|
{
|
|
CamelTypeInfo *obj_info;
|
|
|
|
camel_type_lock_up ();
|
|
|
|
if (type_system_initialized) {
|
|
g_warning
|
|
("camel_type_init: type system already initialized.");
|
|
camel_type_lock_down ();
|
|
return;
|
|
}
|
|
|
|
type_system_initialized = TRUE;
|
|
ctype_to_typeinfo = g_hash_table_new (g_direct_hash, g_direct_equal);
|
|
|
|
obj_info = g_new (CamelTypeInfo, 1);
|
|
obj_info->self = camel_object_type;
|
|
obj_info->parent = CAMEL_INVALID_TYPE;
|
|
obj_info->name = "CamelObject";
|
|
|
|
obj_info->instance_size = sizeof (CamelObject);
|
|
obj_info->instance_chunk =
|
|
g_mem_chunk_create (CamelObject, DEFAULT_PREALLOCS,
|
|
G_ALLOC_ONLY);
|
|
obj_info->instance_init = obj_init;
|
|
obj_info->instance_finalize = obj_finalize;
|
|
obj_info->free_instances = NULL;
|
|
|
|
obj_info->classfuncs_size = sizeof (CamelObjectClass);
|
|
obj_info->class_init = obj_class_init;
|
|
obj_info->class_finalize = obj_class_finalize;
|
|
|
|
g_hash_table_insert (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (CAMEL_INVALID_TYPE), NULL);
|
|
g_hash_table_insert (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (camel_object_type), obj_info);
|
|
|
|
/* Sigh. Ugly */
|
|
make_global_classfuncs (obj_info);
|
|
|
|
cur_max_type = camel_object_type;
|
|
|
|
camel_type_lock_down ();
|
|
}
|
|
|
|
CamelType
|
|
camel_type_register (CamelType parent, const gchar * name,
|
|
size_t instance_size, size_t classfuncs_size,
|
|
CamelObjectClassInitFunc class_init,
|
|
CamelObjectClassFinalizeFunc class_finalize,
|
|
CamelObjectInitFunc instance_init,
|
|
CamelObjectFinalizeFunc instance_finalize)
|
|
{
|
|
CamelTypeInfo *parent_info;
|
|
CamelTypeInfo *obj_info;
|
|
gchar *chunkname;
|
|
|
|
g_return_val_if_fail (parent != CAMEL_INVALID_TYPE,
|
|
CAMEL_INVALID_TYPE);
|
|
g_return_val_if_fail (name, CAMEL_INVALID_TYPE);
|
|
g_return_val_if_fail (instance_size, CAMEL_INVALID_TYPE);
|
|
g_return_val_if_fail (classfuncs_size, CAMEL_INVALID_TYPE);
|
|
|
|
camel_type_lock_up ();
|
|
|
|
if (type_system_initialized == FALSE) {
|
|
G_UNLOCK (type_system);
|
|
camel_type_init ();
|
|
G_LOCK (type_system);
|
|
}
|
|
|
|
parent_info =
|
|
g_hash_table_lookup (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (parent));
|
|
|
|
if (parent_info == NULL) {
|
|
g_warning
|
|
("camel_type_register: no such parent type %d of class `%s'",
|
|
parent, name);
|
|
camel_type_lock_down ();
|
|
return CAMEL_INVALID_TYPE;
|
|
}
|
|
|
|
if (parent_info->instance_size > instance_size) {
|
|
g_warning
|
|
("camel_type_register: instance of class `%s' would be smaller than parent `%s'",
|
|
name, parent_info->name);
|
|
camel_type_lock_down ();
|
|
return CAMEL_INVALID_TYPE;
|
|
}
|
|
|
|
if (parent_info->classfuncs_size > classfuncs_size) {
|
|
g_warning
|
|
("camel_type_register: classfuncs of class `%s' would be smaller than parent `%s'",
|
|
name, parent_info->name);
|
|
camel_type_lock_down ();
|
|
return CAMEL_INVALID_TYPE;
|
|
}
|
|
|
|
cur_max_type++;
|
|
|
|
obj_info = g_new (CamelTypeInfo, 1);
|
|
obj_info->self = cur_max_type;
|
|
obj_info->parent = parent;
|
|
obj_info->name = name;
|
|
|
|
obj_info->instance_size = instance_size;
|
|
chunkname =
|
|
g_strdup_printf ("chunk for instances of Camel type `%s'",
|
|
name);
|
|
obj_info->instance_chunk =
|
|
g_mem_chunk_new (chunkname, instance_size,
|
|
instance_size * DEFAULT_PREALLOCS,
|
|
G_ALLOC_ONLY);
|
|
g_free (chunkname);
|
|
obj_info->instance_init = instance_init;
|
|
obj_info->instance_finalize = instance_finalize;
|
|
obj_info->free_instances = NULL;
|
|
|
|
obj_info->classfuncs_size = classfuncs_size;
|
|
obj_info->class_init = class_init;
|
|
obj_info->class_finalize = class_finalize;
|
|
|
|
g_hash_table_insert (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (obj_info->self), obj_info);
|
|
|
|
/* Sigh. Ugly. */
|
|
make_global_classfuncs (obj_info);
|
|
|
|
camel_type_lock_down ();
|
|
return obj_info->self;
|
|
}
|
|
|
|
CamelObjectClass *
|
|
camel_type_get_global_classfuncs (CamelType type)
|
|
{
|
|
CamelTypeInfo *type_info;
|
|
|
|
g_return_val_if_fail (type != CAMEL_INVALID_TYPE, NULL);
|
|
|
|
camel_type_lock_up ();
|
|
type_info =
|
|
g_hash_table_lookup (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (type));
|
|
camel_type_lock_down ();
|
|
|
|
g_return_val_if_fail (type_info != NULL, NULL);
|
|
|
|
return type_info->global_classfuncs;
|
|
}
|
|
|
|
const gchar *
|
|
camel_type_to_name (CamelType type)
|
|
{
|
|
CamelTypeInfo *type_info;
|
|
|
|
g_return_val_if_fail (type != CAMEL_INVALID_TYPE,
|
|
"(the invalid type)");
|
|
|
|
camel_type_lock_up ();
|
|
type_info =
|
|
g_hash_table_lookup (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (type));
|
|
camel_type_lock_down ();
|
|
|
|
g_return_val_if_fail (type_info != NULL,
|
|
"(a bad type parameter was specified)");
|
|
|
|
return type_info->name;
|
|
}
|
|
|
|
/* ** The CamelObject ***************************************************** */
|
|
|
|
static void
|
|
obj_init (CamelObject * obj)
|
|
{
|
|
obj->s.magic = CAMEL_OBJECT_MAGIC_VALUE;
|
|
obj->ref_count = 1;
|
|
obj->event_to_hooklist = NULL;
|
|
obj->in_event = 0;
|
|
}
|
|
|
|
static void
|
|
obj_finalize (CamelObject * obj)
|
|
{
|
|
g_return_if_fail (obj->s.magic == CAMEL_OBJECT_MAGIC_VALUE);
|
|
g_return_if_fail (obj->ref_count == 0);
|
|
g_return_if_fail (obj->in_event == 0);
|
|
|
|
obj->s.magic = CAMEL_OBJECT_FINALIZED_VALUE;
|
|
|
|
if (obj->event_to_hooklist) {
|
|
g_hash_table_foreach (obj->event_to_hooklist, (GHFunc) g_free,
|
|
NULL);
|
|
g_hash_table_destroy (obj->event_to_hooklist);
|
|
obj->event_to_hooklist = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
obj_class_init (CamelObjectClass * class)
|
|
{
|
|
class->s.magic = CAMEL_OBJECT_CLASS_MAGIC_VALUE;
|
|
|
|
camel_object_class_declare_event (class, "finalize", NULL);
|
|
}
|
|
|
|
static void
|
|
obj_class_finalize (CamelObjectClass * class)
|
|
{
|
|
g_return_if_fail (class->s.magic == CAMEL_OBJECT_CLASS_MAGIC_VALUE);
|
|
|
|
class->s.magic = CAMEL_OBJECT_CLASS_FINALIZED_VALUE;
|
|
|
|
if (class->event_to_preplist) {
|
|
g_hash_table_foreach (class->event_to_preplist,
|
|
(GHFunc) g_free, NULL);
|
|
g_hash_table_destroy (class->event_to_preplist);
|
|
class->event_to_preplist = NULL;
|
|
}
|
|
}
|
|
|
|
CamelType
|
|
camel_object_get_type (void)
|
|
{
|
|
if (type_system_initialized == FALSE)
|
|
camel_type_init ();
|
|
|
|
return camel_object_type;
|
|
}
|
|
|
|
CamelObject *
|
|
camel_object_new (CamelType type)
|
|
{
|
|
CamelTypeInfo *type_info;
|
|
GSList *parents = NULL;
|
|
GSList *head = NULL;
|
|
CamelObject *instance;
|
|
|
|
g_return_val_if_fail (type != CAMEL_INVALID_TYPE, NULL);
|
|
|
|
/* Look up the type */
|
|
|
|
camel_type_lock_up ();
|
|
|
|
type_info =
|
|
g_hash_table_lookup (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (type));
|
|
|
|
if (type_info == NULL) {
|
|
g_warning
|
|
("camel_object_new: trying to create object of invalid type %d",
|
|
type);
|
|
camel_type_lock_down ();
|
|
return NULL;
|
|
}
|
|
|
|
/* Grab an instance out of the freed ones if possible, alloc otherwise */
|
|
|
|
if (type_info->free_instances) {
|
|
GList *first;
|
|
|
|
first = g_list_first (type_info->free_instances);
|
|
instance = first->data;
|
|
type_info->free_instances =
|
|
g_list_remove_link (type_info->free_instances, first);
|
|
g_list_free_1 (first);
|
|
} else {
|
|
instance = g_mem_chunk_alloc0 (type_info->instance_chunk);
|
|
}
|
|
|
|
/* Init the instance and classfuncs a bit */
|
|
|
|
instance->s.type = type;
|
|
instance->classfuncs = type_info->global_classfuncs;
|
|
|
|
/* Loop through the parents in simplest -> most complex order, initing the class and instance.
|
|
|
|
* When parent = CAMEL_INVALID_TYPE and we're at the end of the line, _lookup returns NULL
|
|
* because we inserted it as corresponding to CAMEL_INVALID_TYPE. Clever, eh?
|
|
*/
|
|
|
|
while (type_info) {
|
|
parents = g_slist_prepend (parents, type_info);
|
|
type_info =
|
|
g_hash_table_lookup (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (type_info->
|
|
parent));
|
|
}
|
|
|
|
head = parents;
|
|
|
|
for (; parents && parents->data; parents = parents->next) {
|
|
CamelTypeInfo *thisinfo;
|
|
|
|
thisinfo = parents->data;
|
|
if (thisinfo->instance_init)
|
|
(thisinfo->instance_init) (instance);
|
|
}
|
|
|
|
g_slist_free (head);
|
|
|
|
camel_type_lock_down ();
|
|
return instance;
|
|
}
|
|
|
|
void
|
|
camel_object_ref (CamelObject * obj)
|
|
{
|
|
g_return_if_fail (CAMEL_IS_OBJECT (obj));
|
|
|
|
G_LOCK (refcount);
|
|
obj->ref_count++;
|
|
G_UNLOCK (refcount);
|
|
}
|
|
|
|
void
|
|
camel_object_unref (CamelObject * obj)
|
|
{
|
|
CamelTypeInfo *type_info;
|
|
CamelTypeInfo *iter;
|
|
GSList *parents = NULL;
|
|
GSList *head = NULL;
|
|
|
|
g_return_if_fail (CAMEL_IS_OBJECT (obj));
|
|
|
|
G_LOCK (refcount);
|
|
obj->ref_count--;
|
|
|
|
if (obj->ref_count > 0) {
|
|
G_UNLOCK (refcount);
|
|
return;
|
|
}
|
|
|
|
G_UNLOCK (refcount);
|
|
|
|
/* Oh no! We want to emit a "finalized" event, but that function refs the object
|
|
* because it's not supposed to get finalized in an event, but it is being finalized
|
|
* right now, and AAUGH AAUGH AUGH AUGH!
|
|
*
|
|
* So we don't call camel_object_trigger_event. We do it ourselves. We even know
|
|
* that CamelObject doesn't provide a prep for the finalized event, so we plunge
|
|
* right in and call our hooks.
|
|
*
|
|
* And there was much rejoicing.
|
|
*/
|
|
|
|
#define hooklist parents /*cough */
|
|
|
|
if (obj->event_to_hooklist) {
|
|
CamelHookPair *pair;
|
|
|
|
hooklist =
|
|
g_hash_table_lookup (obj->event_to_hooklist,
|
|
"finalize");
|
|
|
|
while (hooklist && hooklist->data) {
|
|
pair = hooklist->data;
|
|
(pair->func) (obj, NULL, pair->user_data);
|
|
hooklist = hooklist->next;
|
|
}
|
|
}
|
|
|
|
hooklist = NULL; /* Don't mess with this line */
|
|
|
|
#undef hooklist
|
|
|
|
/* Destroy it! hahaha! */
|
|
|
|
camel_type_lock_up ();
|
|
|
|
type_info =
|
|
g_hash_table_lookup (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (obj->s.type));
|
|
|
|
if (type_info == NULL) {
|
|
g_warning
|
|
("camel_object_unref: seemingly valid object has a bad type %d",
|
|
obj->s.type);
|
|
camel_type_lock_down ();
|
|
return;
|
|
}
|
|
|
|
/* Loop through the parents in most complex -> simplest order, finalizing the class
|
|
* and instance.
|
|
*
|
|
* When parent = CAMEL_INVALID_TYPE and we're at the end of the line, _lookup returns NULL
|
|
* because we inserted it as corresponding to CAMEL_INVALID_TYPE. Clever, eh?
|
|
*
|
|
* Use iter to preserve type_info for free_{instance,classfunc}s
|
|
*/
|
|
|
|
iter = type_info;
|
|
|
|
while (iter) {
|
|
parents = g_slist_prepend (parents, iter);
|
|
iter =
|
|
g_hash_table_lookup (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (iter->parent));
|
|
}
|
|
|
|
parents = g_slist_reverse (parents);
|
|
head = parents;
|
|
|
|
for (; parents && parents->data; parents = parents->next) {
|
|
CamelTypeInfo *thisinfo;
|
|
|
|
thisinfo = parents->data;
|
|
if (thisinfo->instance_finalize)
|
|
(thisinfo->instance_finalize) (obj);
|
|
}
|
|
|
|
g_slist_free (head);
|
|
|
|
/* A little bit of cleaning up.
|
|
|
|
* Don't erase the type, so we can peek at it if a finalized object
|
|
* is check_cast'ed somewhere.
|
|
*/
|
|
|
|
memset (obj, 0, type_info->instance_size);
|
|
obj->s.type = type_info->self;
|
|
obj->s.magic = CAMEL_OBJECT_FINALIZED_VALUE;
|
|
|
|
/* Tuck away the pointer for use in a new object */
|
|
|
|
type_info->free_instances =
|
|
g_list_prepend (type_info->free_instances, obj);
|
|
|
|
camel_type_lock_down ();
|
|
}
|
|
|
|
gboolean
|
|
camel_object_is_of_type (CamelObject * obj, CamelType ctype)
|
|
{
|
|
return shared_is_of_type ((CamelObjectShared *) obj, ctype, TRUE);
|
|
}
|
|
|
|
gboolean
|
|
camel_object_class_is_of_type (CamelObjectClass * class, CamelType ctype)
|
|
{
|
|
return shared_is_of_type ((CamelObjectShared *) class, ctype, FALSE);
|
|
}
|
|
|
|
#ifdef BAST_CASTARD
|
|
#define ERRVAL NULL
|
|
#else
|
|
#define ERRVAL obj
|
|
#endif
|
|
|
|
CamelObject *
|
|
camel_object_check_cast (CamelObject * obj, CamelType ctype)
|
|
{
|
|
if (shared_is_of_type ((CamelObjectShared *) obj, ctype, TRUE))
|
|
return obj;
|
|
return ERRVAL;
|
|
}
|
|
|
|
CamelObjectClass *
|
|
camel_object_class_check_cast (CamelObjectClass * class, CamelType ctype)
|
|
{
|
|
if (shared_is_of_type ((CamelObjectShared *) class, ctype, FALSE))
|
|
return class;
|
|
return ERRVAL;
|
|
}
|
|
|
|
#undef ERRVAL
|
|
|
|
gchar *
|
|
camel_object_describe (CamelObject * obj)
|
|
{
|
|
if (obj == NULL)
|
|
return g_strdup ("a NULL pointer");
|
|
|
|
if (obj->s.magic == CAMEL_OBJECT_MAGIC_VALUE) {
|
|
return g_strdup_printf ("an instance of `%s' at %p",
|
|
camel_type_to_name (obj->s.type),
|
|
obj);
|
|
} else if (obj->s.magic == CAMEL_OBJECT_FINALIZED_VALUE) {
|
|
return g_strdup_printf ("a finalized instance of `%s' at %p",
|
|
camel_type_to_name (obj->s.type),
|
|
obj);
|
|
} else if (obj->s.magic == CAMEL_OBJECT_CLASS_MAGIC_VALUE) {
|
|
return g_strdup_printf ("the classfuncs of `%s' at %p",
|
|
camel_type_to_name (obj->s.type),
|
|
obj);
|
|
} else if (obj->s.magic == CAMEL_OBJECT_CLASS_FINALIZED_VALUE) {
|
|
return
|
|
g_strdup_printf
|
|
("the finalized classfuncs of `%s' at %p",
|
|
camel_type_to_name (obj->s.type), obj);
|
|
}
|
|
|
|
return g_strdup ("not a CamelObject");
|
|
}
|
|
|
|
/* This is likely to be called in the class_init callback,
|
|
* and the type will likely be somewhat uninitialized.
|
|
* Is this a problem? We'll see....
|
|
*/
|
|
void
|
|
camel_object_class_declare_event (CamelObjectClass * class,
|
|
const gchar * name,
|
|
CamelObjectEventPrepFunc prep)
|
|
{
|
|
g_return_if_fail (CAMEL_IS_OBJECT_CLASS (class));
|
|
g_return_if_fail (name);
|
|
|
|
if (class->event_to_preplist == NULL)
|
|
class->event_to_preplist =
|
|
g_hash_table_new (g_str_hash, g_str_equal);
|
|
else if (g_hash_table_lookup (class->event_to_preplist, name) != NULL) {
|
|
g_warning
|
|
("camel_object_class_declare_event: event `%s' already declared for `%s'",
|
|
name, camel_type_to_name (class->s.type));
|
|
return;
|
|
}
|
|
|
|
/* AIEEEEEEEEEEEEEEEEEEEEEE
|
|
|
|
* I feel so naughty. Since it's valid to declare an event and not
|
|
* provide a hook, it should be valid to insert a NULL value into
|
|
* the table. However, then our lookup in trigger_event would be
|
|
* ambiguous, not telling us whether the event is undefined or whether
|
|
* it merely has no hook.
|
|
*
|
|
* So we create an 'NULL prep' value that != NULL... specifically, it
|
|
* equals the address of one of our static functions , because that
|
|
* can't possibly be your hook.
|
|
*
|
|
* Just don't forget to check for the 'evil value' and it'll work,
|
|
* I promise.
|
|
*/
|
|
|
|
if (prep == NULL)
|
|
prep = NULL_PREP_VALUE;
|
|
|
|
g_hash_table_insert (class->event_to_preplist, g_strdup (name), prep);
|
|
}
|
|
|
|
void
|
|
camel_object_hook_event (CamelObject * obj, const gchar * name,
|
|
CamelObjectEventHookFunc hook, gpointer user_data)
|
|
{
|
|
GSList *hooklist;
|
|
CamelHookPair *pair;
|
|
|
|
g_return_if_fail (CAMEL_IS_OBJECT (obj));
|
|
g_return_if_fail (name);
|
|
g_return_if_fail (hook);
|
|
|
|
if (obj->event_to_hooklist == NULL)
|
|
obj->event_to_hooklist =
|
|
g_hash_table_new (g_str_hash, g_str_equal);
|
|
|
|
pair = g_new (CamelHookPair, 1);
|
|
pair->func = hook;
|
|
pair->user_data = user_data;
|
|
|
|
hooklist = g_hash_table_lookup (obj->event_to_hooklist, name);
|
|
hooklist = g_slist_prepend (hooklist, pair);
|
|
g_hash_table_insert (obj->event_to_hooklist, g_strdup (name),
|
|
hooklist);
|
|
}
|
|
|
|
void
|
|
camel_object_unhook_event (CamelObject * obj, const gchar * name,
|
|
CamelObjectEventHookFunc hook, gpointer user_data)
|
|
{
|
|
GSList *hooklist;
|
|
GSList *head;
|
|
|
|
g_return_if_fail (CAMEL_IS_OBJECT (obj));
|
|
g_return_if_fail (name);
|
|
g_return_if_fail (hook);
|
|
|
|
if (obj->event_to_hooklist == NULL) {
|
|
g_warning
|
|
("camel_object_unhook_event: trying to unhook `%s' from an instance "
|
|
"of `%s' with no hooks attached", name,
|
|
camel_type_to_name (obj->s.type));
|
|
return;
|
|
}
|
|
|
|
hooklist = g_hash_table_lookup (obj->event_to_hooklist, name);
|
|
|
|
if (hooklist == NULL) {
|
|
g_warning
|
|
("camel_object_unhook_event: trying to unhook `%s' from an instance "
|
|
"of `%s' with no hooks attached to that event.",
|
|
name, camel_type_to_name (obj->s.type));
|
|
return;
|
|
}
|
|
|
|
head = hooklist;
|
|
|
|
while (hooklist) {
|
|
CamelHookPair *pair = (CamelHookPair *) hooklist->data;
|
|
|
|
if (pair->func == hook && pair->user_data == user_data) {
|
|
g_free (hooklist->data);
|
|
head = g_slist_remove_link (head, hooklist);
|
|
g_slist_free_1 (hooklist);
|
|
g_hash_table_insert (obj->event_to_hooklist, (char *) name,
|
|
head);
|
|
return;
|
|
}
|
|
|
|
hooklist = hooklist->next;
|
|
}
|
|
|
|
g_warning
|
|
("camel_object_unhook_event: cannot find hook/data pair %p/%p in an "
|
|
"instance of `%s' attached to `%s'", hook, user_data,
|
|
camel_type_to_name (obj->s.type), name);
|
|
}
|
|
|
|
void
|
|
camel_object_trigger_event (CamelObject * obj, const gchar * name,
|
|
gpointer event_data)
|
|
{
|
|
GSList *hooklist;
|
|
CamelHookPair *pair;
|
|
CamelObjectEventPrepFunc prep;
|
|
|
|
g_return_if_fail (CAMEL_IS_OBJECT (obj));
|
|
g_return_if_fail (name);
|
|
|
|
if (obj->in_event) {
|
|
g_warning
|
|
("camel_object_trigger_event: trying to trigger `%s' in class "
|
|
"`%s' while already triggering another event", name,
|
|
camel_type_to_name (obj->s.type));
|
|
return;
|
|
}
|
|
|
|
if (obj->classfuncs->event_to_preplist == NULL) {
|
|
g_warning
|
|
("camel_object_trigger_event: trying to trigger `%s' in class "
|
|
"`%s' with no defined events.", name,
|
|
camel_type_to_name (obj->s.type));
|
|
return;
|
|
}
|
|
|
|
prep = g_hash_table_lookup (obj->classfuncs->event_to_preplist, name);
|
|
|
|
if (prep == NULL) {
|
|
g_warning
|
|
("camel_object_trigger_event: trying to trigger undefined "
|
|
"event `%s' in class `%s'.", name,
|
|
camel_type_to_name (obj->s.type));
|
|
return;
|
|
}
|
|
|
|
/* Ref so that it can't get destroyed in the event, which would
|
|
* be Bad. And it's a valid ref anyway...
|
|
*/
|
|
|
|
camel_object_ref (obj);
|
|
obj->in_event = 1;
|
|
|
|
if ((prep != NULL_PREP_VALUE && !prep (obj, event_data))
|
|
|| obj->event_to_hooklist == NULL) {
|
|
obj->in_event = 0;
|
|
camel_object_unref (obj);
|
|
return;
|
|
}
|
|
|
|
hooklist = g_hash_table_lookup (obj->event_to_hooklist, name);
|
|
|
|
while (hooklist && hooklist->data) {
|
|
pair = hooklist->data;
|
|
(pair->func) (obj, event_data, pair->user_data);
|
|
hooklist = hooklist->next;
|
|
}
|
|
|
|
obj->in_event = 0;
|
|
camel_object_unref (obj);
|
|
}
|
|
|
|
/* ** Static helpers ****************************************************** */
|
|
|
|
static gboolean
|
|
shared_is_of_type (CamelObjectShared * sh, CamelType ctype, gboolean is_obj)
|
|
{
|
|
CamelTypeInfo *type_info;
|
|
gchar *targtype;
|
|
|
|
if (is_obj)
|
|
targtype = "instance";
|
|
else
|
|
targtype = "classdata";
|
|
|
|
if (ctype == CAMEL_INVALID_TYPE) {
|
|
g_warning
|
|
("shared_is_of_type: trying to cast to CAMEL_INVALID_TYPE");
|
|
return FALSE;
|
|
}
|
|
|
|
if (sh == NULL) {
|
|
g_warning
|
|
("shared_is_of_type: trying to cast NULL to %s of `%s'",
|
|
targtype, camel_type_to_name (ctype));
|
|
return FALSE;
|
|
}
|
|
|
|
if (sh->magic == CAMEL_OBJECT_FINALIZED_VALUE) {
|
|
g_warning
|
|
("shared_is_of_type: trying to cast finalized instance "
|
|
"of `%s' into %s of `%s'",
|
|
camel_type_to_name (sh->type), targtype,
|
|
camel_type_to_name (ctype));
|
|
return FALSE;
|
|
}
|
|
|
|
if (sh->magic == CAMEL_OBJECT_CLASS_FINALIZED_VALUE) {
|
|
g_warning
|
|
("shared_is_of_type: trying to cast finalized classdata "
|
|
"of `%s' into %s of `%s'",
|
|
camel_type_to_name (sh->type), targtype,
|
|
camel_type_to_name (ctype));
|
|
return FALSE;
|
|
}
|
|
|
|
if (is_obj) {
|
|
if (sh->magic == CAMEL_OBJECT_CLASS_MAGIC_VALUE) {
|
|
g_warning
|
|
("shared_is_of_type: trying to cast classdata "
|
|
"of `%s' into instance of `%s'",
|
|
camel_type_to_name (sh->type),
|
|
camel_type_to_name (ctype));
|
|
return FALSE;
|
|
}
|
|
|
|
if (sh->magic != CAMEL_OBJECT_MAGIC_VALUE) {
|
|
g_warning
|
|
("shared_is_of_type: trying to cast junk data "
|
|
"into instance of `%s'",
|
|
camel_type_to_name (ctype));
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
if (sh->magic == CAMEL_OBJECT_MAGIC_VALUE) {
|
|
g_warning
|
|
("shared_is_of_type: trying to cast instance "
|
|
"of `%s' into classdata of `%s'",
|
|
camel_type_to_name (sh->type),
|
|
camel_type_to_name (ctype));
|
|
return FALSE;
|
|
}
|
|
|
|
if (sh->magic != CAMEL_OBJECT_CLASS_MAGIC_VALUE) {
|
|
g_warning
|
|
("shared_is_of_type: trying to cast junk data "
|
|
"into classdata of `%s'",
|
|
camel_type_to_name (ctype));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
camel_type_lock_up ();
|
|
|
|
type_info =
|
|
g_hash_table_lookup (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (sh->type));
|
|
|
|
if (type_info == NULL) {
|
|
g_warning ("shared_is_of_type: seemingly valid %s has "
|
|
"bad type %d.", targtype, sh->type);
|
|
camel_type_lock_down ();
|
|
return FALSE;
|
|
}
|
|
|
|
while (type_info) {
|
|
if (type_info->self == ctype) {
|
|
camel_type_lock_down ();
|
|
return TRUE;
|
|
}
|
|
|
|
type_info =
|
|
g_hash_table_lookup (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (type_info->
|
|
parent));
|
|
}
|
|
|
|
/* this isn't an error, e.g. CAMEL_IS_FOLDER(folder), its upto the
|
|
caller to handle the false case */
|
|
/*g_warning
|
|
("shared_is_of_type: %s of `%s' (@%p) is not also %s of `%s'",
|
|
targtype, camel_type_to_name (sh->type), sh, targtype,
|
|
camel_type_to_name (ctype));*/
|
|
|
|
camel_type_lock_down ();
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
make_global_classfuncs (CamelTypeInfo * type_info)
|
|
{
|
|
CamelObjectClass *funcs;
|
|
GSList *parents;
|
|
GSList *head;
|
|
|
|
g_assert (type_info);
|
|
|
|
funcs = g_malloc0 (type_info->classfuncs_size);
|
|
funcs->s.type = type_info->self;
|
|
|
|
type_info->global_classfuncs = funcs;
|
|
|
|
parents = NULL;
|
|
while (type_info) {
|
|
parents = g_slist_prepend (parents, type_info);
|
|
type_info =
|
|
g_hash_table_lookup (ctype_to_typeinfo,
|
|
GINT_TO_POINTER (type_info->
|
|
parent));
|
|
}
|
|
|
|
head = parents;
|
|
|
|
for (; parents && parents->data; parents = parents->next) {
|
|
CamelTypeInfo *thisinfo;
|
|
|
|
thisinfo = parents->data;
|
|
if (thisinfo->class_init)
|
|
(thisinfo->class_init) (funcs);
|
|
}
|
|
|
|
g_slist_free (head);
|
|
}
|