From 92f0631a74a1e128e0936b30336ca314fd6fec4c Mon Sep 17 00:00:00 2001 From: Christopher James Lahey Date: Thu, 23 Mar 2000 23:37:48 +0000 Subject: Added parsing and testing for name, full name, birthday, telephone, email, 2000-03-23 Christopher James Lahey * addressbook/backend/ebook/e-card.c, addressbook/backend/ebook/e-card.h, addressbook/backend/ebook/e-card-types.h, addressbook/backend/ebook/e-card-pairs.h, addressbook/backend/ebook/test-card.c: Added parsing and testing for name, full name, birthday, telephone, email, and street address properties. svn path=/trunk/; revision=2157 --- addressbook/backend/ebook/e-card-pairs.h | 20 +- addressbook/backend/ebook/e-card-types.h | 48 ++-- addressbook/backend/ebook/e-card.c | 441 ++++++++++++++++++++++++------- addressbook/backend/ebook/e-card.h | 13 +- addressbook/backend/ebook/test-card.c | 68 ++++- 5 files changed, 454 insertions(+), 136 deletions(-) (limited to 'addressbook/backend') diff --git a/addressbook/backend/ebook/e-card-pairs.h b/addressbook/backend/ebook/e-card-pairs.h index baabdc3dca..e9a7f089f7 100644 --- a/addressbook/backend/ebook/e-card-pairs.h +++ b/addressbook/backend/ebook/e-card-pairs.h @@ -26,8 +26,8 @@ #include "libversit/vcc.h" #include -#if 0 +#if 0 struct pair { char *str; @@ -96,22 +96,6 @@ struct pair photo_pairs[] = { { VCQuickTimeProp, PHOTO_QTIME }, { NULL, 0 } }; -struct pair phone_pairs[] = { - { VCPreferredProp, PHONE_PREF }, - { VCWorkProp, PHONE_WORK }, - { VCHomeProp, PHONE_HOME }, - { VCVoiceProp, PHONE_VOICE }, - { VCFaxProp, PHONE_FAX }, - { VCMessageProp, PHONE_MSG }, - { VCCellularProp, PHONE_CELL }, - { VCPagerProp, PHONE_PAGER }, - { VCBBSProp, PHONE_BBS }, - { VCModemProp, PHONE_MODEM }, - { VCCarProp, PHONE_CAR }, - { VCISDNProp, PHONE_ISDN }, - { VCVideoProp, PHONE_VIDEO }, - { NULL, 0 } }; -#if 0 struct pair email_pairs[] = { { VCAOLProp, EMAIL_AOL }, { VCAppleLinkProp, EMAIL_APPLE_LINK }, @@ -126,7 +110,7 @@ struct pair email_pairs[] = { { VCTLXProp, EMAIL_TLX }, { VCX400Prop, EMAIL_X400 }, { NULL, 0 } }; -#endif + struct pair sound_pairs[] = { { VCAIFFProp, SOUND_AIFF }, { VCPCMProp, SOUND_PCM }, diff --git a/addressbook/backend/ebook/e-card-types.h b/addressbook/backend/ebook/e-card-types.h index 6e6a6a73be..6d98ccaa72 100644 --- a/addressbook/backend/ebook/e-card-types.h +++ b/addressbook/backend/ebook/e-card-types.h @@ -108,6 +108,8 @@ typedef struct { GList *l; } ECardList; +#endif + /* IDENTIFICATION PROPERTIES */ @@ -119,6 +121,7 @@ typedef struct { char *suffix; /* Esq. */ } ECardName; +#if 0 typedef struct { CardProperty prop; @@ -127,6 +130,7 @@ typedef struct { char *data; } ECardPhoto; +#endif typedef struct { int year; @@ -134,30 +138,31 @@ typedef struct { int day; } ECardDate; - /* TELECOMMUNICATIONS ADDRESSING PROPERTIES */ typedef enum { - PHONE_PREF = 1 << 0, - PHONE_WORK = 1 << 1, - PHONE_HOME = 1 << 2, - PHONE_VOICE = 1 << 3, - PHONE_FAX = 1 << 4, - PHONE_MSG = 1 << 5, - PHONE_CELL = 1 << 6, - PHONE_PAGER = 1 << 7, - PHONE_BBS = 1 << 8, - PHONE_MODEM = 1 << 9, - PHONE_CAR = 1 << 10, - PHONE_ISDN = 1 << 11, - PHONE_VIDEO = 1 << 12 + E_CARD_PHONE_PREF = 1 << 0, + E_CARD_PHONE_WORK = 1 << 1, + E_CARD_PHONE_HOME = 1 << 2, + E_CARD_PHONE_VOICE = 1 << 3, + E_CARD_PHONE_FAX = 1 << 4, + E_CARD_PHONE_MSG = 1 << 5, + E_CARD_PHONE_CELL = 1 << 6, + E_CARD_PHONE_PAGER = 1 << 7, + E_CARD_PHONE_BBS = 1 << 8, + E_CARD_PHONE_MODEM = 1 << 9, + E_CARD_PHONE_CAR = 1 << 10, + E_CARD_PHONE_ISDN = 1 << 11, + E_CARD_PHONE_VIDEO = 1 << 12 } ECardPhoneFlags; typedef struct { ECardPhoneFlags flags; - char *data; + char *number; } ECardPhone; +#if 0 + typedef struct { int sign; /* 1 or -1 */ int hours; /* Mexico General is at -6:00 UTC */ @@ -171,7 +176,7 @@ typedef struct { float lat; } ECardGeoPos; - +#endif /* DELIVERY ADDRESSING PROPERTIES */ typedef enum { @@ -181,12 +186,12 @@ typedef enum { ADDR_PARCEL = 1 << 3, ADDR_DOM = 1 << 4, ADDR_INTL = 1 << 5 -} ECardAddrFlags; +} ECardAddressFlags; typedef struct { - ECardAddrFlags flags; + ECardAddressFlags flags; - char *pobox; + char *po; char *ext; char *street; char *city; @@ -195,10 +200,11 @@ typedef struct { char *country; char *description; -} ECardAddr; +} ECardDeliveryAddress; +#if 0 typedef struct { - ECardAddrFlags flags; + ECardAddressFlags flags; char *data; } ECardAddrLabel; diff --git a/addressbook/backend/ebook/e-card.c b/addressbook/backend/ebook/e-card.c index 0029694d35..f6218804f1 100644 --- a/addressbook/backend/ebook/e-card.c +++ b/addressbook/backend/ebook/e-card.c @@ -18,13 +18,49 @@ #include #include -#define is_a_prop_of(obj,prop) isAPropertyOf (obj,prop) -#define str_val(obj) the_str = (vObjectValueType (obj))? fakeCString (vObjectUStringZValue (obj)) : calloc (1, 1) -#define has(obj,prop) (vo = isAPropertyOf (obj, prop)) +#define is_a_prop_of(obj,prop) (isAPropertyOf ((obj),(prop))) +#define str_val(obj) (the_str = (vObjectValueType (obj))? fakeCString (vObjectUStringZValue (obj)) : calloc (1, 1)) +#define has(obj,prop) (vo = isAPropertyOf ((obj), (prop))) #if 0 static VObject *card_convert_to_vobject (ECard *crd); #endif +static void parse(ECard *card, VObject *vobj); +static void e_card_init (ECard *card); +static void e_card_class_init (ECardClass *klass); + +static void e_card_destroy (GtkObject *object); + +static void assign_string(VObject *vobj, char **string); + +static void e_card_name_free(ECardName *name); +char *e_v_object_get_child_value(VObject *vobj, char *name); +static ECardDate e_card_date_from_string (char *str); + +static void parse_bday(ECard *card, VObject *object); +static void parse_full_name(ECard *card, VObject *object); +static void parse_name(ECard *card, VObject *object); +static void parse_email(ECard *card, VObject *object); +static void parse_phone(ECard *card, VObject *object); +static void parse_address(ECard *card, VObject *object); + +static ECardPhoneFlags get_phone_flags (VObject *vobj); +static ECardAddressFlags get_address_flags (VObject *vobj); + +typedef void (* ParsePropertyFunc) (ECard *card, VObject *object); + +struct { + char *key; + ParsePropertyFunc function; +} attribute_jump_array[] = +{ + { VCFullNameProp, parse_full_name }, + { VCNameProp, parse_name }, + { VCBirthDateProp, parse_bday }, + { VCEmailAddressProp, parse_email }, + { VCTelephoneProp, parse_phone }, + { VCAdrProp, parse_address } +}; /** * e_card_get_type: @@ -45,12 +81,8 @@ e_card_get_type (void) "ECard", sizeof (ECard), sizeof (ECardClass), - NULL, - NULL, -#if 0 (GtkClassInitFunc) e_card_class_init, (GtkObjectInitFunc) e_card_init, -#endif NULL, /* reserved_1 */ NULL, /* reserved_2 */ (GtkClassInitFunc) NULL @@ -64,7 +96,16 @@ e_card_get_type (void) ECard *e_card_new (char *vcard) { - return E_CARD(gtk_type_new(e_card_get_type())); + ECard *card = E_CARD(gtk_type_new(e_card_get_type())); + VObject *vobj = Parse_MIME(vcard, strlen(vcard)); + while(vobj) { + VObject *next; + parse(card, vobj); + next = nextVObjectInList(vobj); + cleanVObject(vobj); + vobj = next; + } + return card; } char *e_card_get_id (ECard *card) @@ -77,21 +118,183 @@ char *e_card_get_vcard (ECard *card) return NULL; } +static void +parse_name(ECard *card, VObject *vobj) +{ + if ( card->name ) { + e_card_name_free(card->name); + } + card->name = g_new(ECardName, 1); -#if 0 + card->name->family = e_v_object_get_child_value (vobj, VCFamilyNameProp); + card->name->given = e_v_object_get_child_value (vobj, VCGivenNameProp); + card->name->additional = e_v_object_get_child_value (vobj, VCAdditionalNamesProp); + card->name->prefix = e_v_object_get_child_value (vobj, VCNamePrefixesProp); + card->name->suffix = e_v_object_get_child_value (vobj, VCNameSuffixesProp); +} + +static void +parse_full_name(ECard *card, VObject *vobj) +{ + if ( card->fname ) + g_free(card->fname); + assign_string(vobj, &(card->fname)); +} + +static void +parse_email(ECard *card, VObject *vobj) +{ + char *next_email; + assign_string(vobj, &next_email); + card->email = g_list_append(card->email, next_email); +} + +static void +parse_bday(ECard *card, VObject *vobj) +{ + if ( vObjectValueType (vobj) ) { + char *str = fakeCString (vObjectUStringZValue (vobj)); + if ( card->bday ) + g_free(card->bday); + card->bday = g_new(ECardDate, 1); + *(card->bday) = e_card_date_from_string(str); + free(str); + } +} + +static void +parse_phone(ECard *card, VObject *vobj) +{ + ECardPhone *next_phone = g_new(ECardPhone, 1); + assign_string(vobj, &(next_phone->number)); + next_phone->flags = get_phone_flags(vobj); + card->phone = g_list_append(card->phone, next_phone); +} + +static void +parse_address(ECard *card, VObject *vobj) +{ + ECardDeliveryAddress *next_addr = g_new(ECardDeliveryAddress, 1); + + next_addr->flags = get_address_flags (vobj); + next_addr->po = e_v_object_get_child_value (vobj, VCPostalBoxProp); + next_addr->ext = e_v_object_get_child_value (vobj, VCExtAddressProp); + next_addr->street = e_v_object_get_child_value (vobj, VCStreetAddressProp); + next_addr->city = e_v_object_get_child_value (vobj, VCCityProp); + next_addr->region = e_v_object_get_child_value (vobj, VCRegionProp); + next_addr->code = e_v_object_get_child_value (vobj, VCPostalCodeProp); + next_addr->country = e_v_object_get_child_value (vobj, VCCountryNameProp); + next_addr->description = e_v_object_get_child_value (vobj, VCDescriptionProp); + + card->address = g_list_append(card->address, next_addr); +} + +static void +parse_attribute(ECard *card, VObject *vobj) +{ + ParsePropertyFunc function = g_hash_table_lookup(E_CARD_CLASS(GTK_OBJECT(card)->klass)->attribute_jump_table, vObjectName(vobj)); + if ( function ) + function(card, vobj); +} + +static void +parse(ECard *card, VObject *vobj) +{ + VObjectIterator iterator; + initPropIterator(&iterator, vobj); + while(moreIteration (&iterator)) { + parse_attribute(card, nextVObject(&iterator)); + } +} + +static void +e_card_class_init (ECardClass *klass) +{ + int i; + GtkObjectClass *object_class; + + object_class = GTK_OBJECT_CLASS(klass); + + klass->attribute_jump_table = g_hash_table_new(g_str_hash, g_str_equal); + + for ( i = 0; i < sizeof(attribute_jump_array) / sizeof(attribute_jump_array[0]); i++ ) { + g_hash_table_insert(klass->attribute_jump_table, attribute_jump_array[i].key, attribute_jump_array[i].function); + } + + object_class->destroy = e_card_destroy; +} + +static void +e_card_phone_free (ECardPhone *phone) +{ + if ( phone ) { + if ( phone->number ) + g_free(phone->number); + g_free(phone); + } +} + +static void +e_card_delivery_address_free (ECardDeliveryAddress *addr) +{ + if ( addr ) { + if ( addr->po ) + g_free(addr->po); + if ( addr->ext ) + g_free(addr->ext); + if ( addr->street ) + g_free(addr->street); + if ( addr->city ) + g_free(addr->city); + if ( addr->region ) + g_free(addr->region); + if ( addr->code ) + g_free(addr->code); + if ( addr->country ) + g_free(addr->country); + if ( addr->description ) + g_free(addr->description); + g_free(addr); + } +} /* * ECard lifecycle management and vCard loading/saving. */ +static void +e_card_destroy (GtkObject *object) +{ + ECard *card = E_CARD(object); + if ( card->fname ) + g_free(card->fname); + if ( card->name ) + e_card_name_free(card->name); + if ( card->bday ) + g_free(card->bday); + g_list_foreach(card->email, (GFunc)g_free, NULL); + g_list_free(card->email); + g_list_foreach(card->phone, (GFunc)e_card_phone_free, NULL); + g_list_free(card->phone); + g_list_foreach(card->address, (GFunc)e_card_delivery_address_free, NULL); + g_list_free(card->address); +} + /** - * e_card_new: + * e_card_init: */ -ECard * -e_card_new (void) +static void +e_card_init (ECard *card) { - ECard *c; - + + card->fname = NULL; + card->name = NULL; + card->bday = NULL; + card->email = NULL; + card->phone = NULL; + card->address = NULL; +#if 0 + c = g_new0 (ECard, 1); c->fname = @@ -144,8 +347,18 @@ e_card_new (void) c->key.prop.type = PROP_KEY; return c; +#endif } +static void +assign_string(VObject *vobj, char **string) +{ + char *str = (vObjectValueType (vobj) ? fakeCString (vObjectUStringZValue (vobj)) : calloc(1, 1)); + *string = g_strdup(str); + free(str); +} + +#if 0 static void e_card_str_free (CardStrProperty *sp) { @@ -436,20 +649,6 @@ get_addr_type (VObject *o) return ret; } -static int -get_phone_type (VObject *o) -{ - VObject *vo; - int ret = 0; - int i; - - for (i = 0; phone_pairs[i].str; i++) - if (has (o, phone_pairs[i].str)) - ret |= phone_pairs[i].id; - - return ret; -} - static enum EMailType get_email_type (VObject *o) { @@ -609,69 +808,6 @@ e_card_get_name (VObject *o) return name; } -static CardBDay -strtoCardBDay (char *str) -{ - char *s; - int i; - CardBDay bday; - - bday.year = 0; - bday.month = 0; - bday.day = 0; - - if (strchr (str, '-')) { - for (s = strtok (str, "-"), i = 0; s; - s = strtok (NULL, "-"), i++) - switch (i) { - case 0: - bday.year = atoi (s); - break; - case 1: - bday.month = atoi (s); - break; - case 2: - bday.day = atoi (s); - break; - default: - g_warning ("? < Too many values for BDay property."); - } - - if (i < 2) - g_warning ("? < Too few values for BDay property."); - } else { - if (strlen (str) >= 8) { - bday.day = atoi (str + 6); - str[6] = 0; - bday.month = atoi (str + 4); - str[4] = 0; - bday.year = atoi (str); - } else - g_warning ("? < Bad format for BDay property."); - } - - return bday; -} - -static ECardDelAddr * -e_card_get_del_addr (VObject *o) -{ - ECardDelAddr *addr; - - addr = g_new0 (ECardDelAddr, 1); - - addr->type = get_addr_type (o); - addr->po = e_card_prop_get_substr (o, VCPostalBoxProp); - addr->ext = e_card_prop_get_substr (o, VCExtAddressProp); - addr->street = e_card_prop_get_substr (o, VCStreetAddressProp); - addr->city = e_card_prop_get_substr (o, VCCityProp); - addr->region = e_card_prop_get_substr (o, VCRegionProp); - addr->code = e_card_prop_get_substr (o, VCPostalBoxProp); - addr->country = e_card_prop_get_substr (o, VCCountryNameProp); - - return addr; -} - static CardDelLabel * get_CardDelLabel (VObject *o) { @@ -1912,3 +2048,126 @@ card_save (Card *crd, FILE *fp) cleanVObject (object); } #endif + +static ECardDate +e_card_date_from_string (char *str) +{ + ECardDate date; + int length; + + date.year = 0; + date.month = 0; + date.day = 0; + + length = strlen(str); + + if (length == 10 ) { + date.year = str[0] * 1000 + str[1] * 100 + str[2] * 10 + str[3] - '0' * 1111; + date.month = str[5] * 10 + str[6] - '0' * 11; + date.day = str[8] * 10 + str[9] - '0' * 11; + } else if ( length == 8 ) { + date.year = str[0] * 1000 + str[1] * 100 + str[2] * 10 + str[3] - '0' * 1111; + date.month = str[4] * 10 + str[5] - '0' * 11; + date.day = str[6] * 10 + str[7] - '0' * 11; + } + + return date; +} + +static void +e_card_name_free(ECardName *name) +{ + if ( name ) { + if ( name->prefix ) + g_free(name->prefix); + if ( name->given ) + g_free(name->given); + if ( name->additional ) + g_free(name->additional); + if ( name->family ) + g_free(name->family); + if ( name->suffix ) + g_free(name->suffix); + g_free ( name ); + } +} + +char * +e_v_object_get_child_value(VObject *vobj, char *name) +{ + char *ret_val; + VObjectIterator iterator; + initPropIterator(&iterator, vobj); + while(moreIteration (&iterator)) { + VObject *attribute = nextVObject(&iterator); + const char *id = vObjectName(attribute); + if ( ! strcmp(id, name) ) { + assign_string(attribute, &ret_val); + return ret_val; + } + } + ret_val = g_new(char, 1); + *ret_val = 0; + return ret_val; +} + +static ECardPhoneFlags +get_phone_flags (VObject *vobj) +{ + ECardPhoneFlags ret = 0; + int i; + + struct { + char *id; + ECardPhoneFlags flag; + } phone_pairs[] = { + { VCPreferredProp, E_CARD_PHONE_PREF }, + { VCWorkProp, E_CARD_PHONE_WORK }, + { VCHomeProp, E_CARD_PHONE_HOME }, + { VCVoiceProp, E_CARD_PHONE_VOICE }, + { VCFaxProp, E_CARD_PHONE_FAX }, + { VCMessageProp, E_CARD_PHONE_MSG }, + { VCCellularProp, E_CARD_PHONE_CELL }, + { VCPagerProp, E_CARD_PHONE_PAGER }, + { VCBBSProp, E_CARD_PHONE_BBS }, + { VCModemProp, E_CARD_PHONE_MODEM }, + { VCCarProp, E_CARD_PHONE_CAR }, + { VCISDNProp, E_CARD_PHONE_ISDN }, + { VCVideoProp, E_CARD_PHONE_VIDEO }, + }; + + for (i = 0; i < sizeof(phone_pairs) / sizeof(phone_pairs[0]); i++) { + if (isAPropertyOf (vobj, phone_pairs[i].id)) { + ret |= phone_pairs[i].flag; + } + } + + return ret; +} + +static ECardAddressFlags +get_address_flags (VObject *vobj) +{ + ECardAddressFlags ret = 0; + int i; + + struct { + char *id; + ECardAddressFlags flag; + } addr_pairs[] = { + { VCDomesticProp, ADDR_DOM }, + { VCInternationalProp, ADDR_INTL }, + { VCPostalProp, ADDR_POSTAL }, + { VCParcelProp, ADDR_PARCEL }, + { VCHomeProp, ADDR_HOME }, + { VCWorkProp, ADDR_WORK }, + }; + + for (i = 0; i < sizeof(addr_pairs) / sizeof(addr_pairs[0]); i++) { + if (isAPropertyOf (vobj, addr_pairs[i].id)) { + ret |= addr_pairs[i].flag; + } + } + + return ret; +} diff --git a/addressbook/backend/ebook/e-card.h b/addressbook/backend/ebook/e-card.h index 4b04f34c4d..04c3cd2a96 100644 --- a/addressbook/backend/ebook/e-card.h +++ b/addressbook/backend/ebook/e-card.h @@ -28,18 +28,22 @@ typedef struct _ECardClass ECardClass; struct _ECard { GtkObject object; -#if 0 + char *fname; /* The full name. */ ECardName *name; /* The structured name. */ - - GList *del_addrs; /* Delivery addresses (ECardAddr *) */ + GList *address; /* Delivery addresses (ECardDeliveryAddress *) */ +#if 0 GList *del_labels; /* Delivery address labels * (ECardAddrLabel *) */ +#endif GList *phone; /* Phone numbers (ECardPhone *) */ GList *email; /* Email addresses (char *) */ +#if 0 char *url; /* The person's web page. */ - + +#endif ECardDate *bday; /* The person's birthday. */ +#if 0 ECardOrg *org; /* The person's organization. */ char *title; /* The person's title w/in his org */ @@ -75,6 +79,7 @@ struct _ECard { struct _ECardClass { GtkObjectClass parent_class; + GHashTable *attribute_jump_table; }; diff --git a/addressbook/backend/ebook/test-card.c b/addressbook/backend/ebook/test-card.c index 2576c87e6b..cf068a5498 100644 --- a/addressbook/backend/ebook/test-card.c +++ b/addressbook/backend/ebook/test-card.c @@ -18,6 +18,8 @@ " \ "EMAIL;INTERNET:nat@helixcode.com " \ +"ADR;WORK;POSTAL:P.O. Box 101;;;Any Town;CA;91921-1234; +" \ "END:VCARD " \ " @@ -62,9 +64,71 @@ main (int argc, char **argv) if (cardstr == NULL) cardstr = TEST_VCARD; - +#if 0 + { + int i; + for ( i = 0; i < 100000; i++ ) { + card = e_card_new (cardstr); + + gtk_object_unref (GTK_OBJECT (card)); + } + } +#endif card = e_card_new (cardstr); - + if ( card->fname ) + printf("Name : %s\n", card->fname); + if ( card->name ) { + printf("Full Name:\n"); + if ( card->name->prefix ) + printf(" prefix : %s\n", card->name->prefix); + if ( card->name->given ) + printf(" given : %s\n", card->name->given); + if ( card->name->additional ) + printf(" additional : %s\n", card->name->additional); + if ( card->name->family ) + printf(" family : %s\n", card->name->family); + if ( card->name->suffix ) + printf(" suffix : %s\n", card->name->suffix); + } + if ( card->bday ) { + printf("BDay : %4d-%02d-%02d\n", card->bday->year, card->bday->month, card->bday->day); + } + if ( card->email ) { + GList *email = card->email; + for ( ; email; email = email->next ) { + printf("Email : %s\n", (char *) email->data); + } + } + if ( card->phone ) { + GList *phone = card->phone; + for ( ; phone; phone = phone->next ) { + ECardPhone *e_card_phone = (ECardPhone *) phone->data; + printf("Phone ; %d : %s\n", e_card_phone->flags, e_card_phone->number); + } + } + if ( card->address ) { + GList *address = card->address; + for ( ; address; address = address->next ) { + ECardDeliveryAddress *del_address = (ECardDeliveryAddress *) address->data; + printf("Address ; %d:\n", del_address->flags); + if ( del_address->po ) + printf(" Po : %s\n", del_address->po); + if ( del_address->ext ) + printf(" Ext : %s\n", del_address->ext); + if ( del_address->street ) + printf(" Street : %s\n", del_address->street); + if ( del_address->city ) + printf(" City : %s\n", del_address->city); + if ( del_address->region ) + printf(" Region : %s\n", del_address->region); + if ( del_address->code ) + printf(" Code : %s\n", del_address->code); + if ( del_address->country ) + printf(" Country : %s\n", del_address->country); + if ( del_address->description ) + printf(" Description : %s\n", del_address->description); + } + } gtk_object_unref (GTK_OBJECT (card)); return 0; -- cgit v1.2.3