Mercurial > pidgin
changeset 25800:8b7ae3775a0b
propagate from branch 'im.pidgin.pidgin' (head 7821a3549d7d99473e999dc067afc4218addcc1e)
to branch 'im.pidgin.pidgin.next.minor' (head f5288ef412711861bacd78dba56c4963826d86ed)
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Fri, 31 Oct 2008 17:46:39 +0000 |
parents | 18711b62ec27 (current diff) ede22bc33d56 (diff) |
children | 178efff39041 |
files | libpurple/blist.c libpurple/blist.h libpurple/connection.c libpurple/connection.h libpurple/protocols/gg/gg.c |
diffstat | 51 files changed, 5110 insertions(+), 635 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog.API Fri Oct 31 04:03:44 2008 +0000 +++ b/ChangeLog.API Fri Oct 31 17:46:39 2008 +0000 @@ -1,5 +1,13 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.6.0 (??/??/????): + libpurple: + Added: + * purple_buddy_get_protocol_data + * purple_buddy_set_protocol_data + * purple_connection_get_protocol_data + * purple_connection_set_protocol_data + version 2.5.0 (08/18/2008): libpurple: Added:
--- a/libpurple/Makefile.am Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/Makefile.am Fri Oct 31 17:46:39 2008 +0000 @@ -75,8 +75,13 @@ stringref.c \ stun.c \ sound.c \ + sound-theme-loader.c \ + sound-theme.c \ sslconn.c \ upnp.c \ + theme.c \ + theme-loader.c \ + theme-manager.c \ util.c \ value.c \ version.c \ @@ -128,8 +133,13 @@ stringref.h \ stun.h \ sound.h \ + sound-theme-loader.h \ + sound-theme.h \ sslconn.h \ upnp.h \ + theme.h \ + theme-loader.h \ + theme-manager.h \ util.h \ value.h \ xmlnode.h \
--- a/libpurple/blist.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/blist.c Fri Oct 31 17:46:39 2008 +0000 @@ -1232,6 +1232,23 @@ return buddy->icon; } +gpointer +purple_buddy_get_protocol_data(const PurpleBuddy *buddy) +{ + g_return_val_if_fail(buddy != NULL, NULL); + + return buddy->proto_data; +} + +void +purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data) +{ + g_return_if_fail(buddy != NULL); + + buddy->proto_data = data; +} + + void purple_blist_add_chat(PurpleChat *chat, PurpleGroup *group, PurpleBlistNode *node) { PurpleBlistNode *cnode = (PurpleBlistNode*)chat;
--- a/libpurple/blist.h Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/blist.h Fri Oct 31 17:46:39 2008 +0000 @@ -470,6 +470,32 @@ PurpleBuddyIcon *purple_buddy_get_icon(const PurpleBuddy *buddy); /** + * Returns a buddy's protocol-specific data. + * + * This should only be called from the associated prpl. + * + * @param buddy The buddy. + * @return The protocol data. + * + * @see purple_buddy_set_protocol_data() + * @since 2.6.0 + */ +gpointer purple_buddy_get_protocol_data(const PurpleBuddy *buddy); + +/** + * Sets a buddy's protocol-specific data. + * + * This should only be called from the associated prpl. + * + * @param buddy The buddy. + * @param data The data. + * + * @see purple_buddy_get_protocol_data() + * @since 2.6.0 + */ +void purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data); + +/** * Returns a buddy's contact. * * @param buddy The buddy.
--- a/libpurple/connection.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/connection.c Fri Oct 31 17:46:39 2008 +0000 @@ -285,7 +285,7 @@ buddies = purple_find_buddies(account, NULL); while (buddies != NULL) { PurpleBuddy *buddy = buddies->data; - buddy->proto_data = NULL; + purple_buddy_set_protocol_data(buddy, NULL); buddies = g_slist_delete_link(buddies, buddies); } @@ -427,6 +427,13 @@ gc->display_name = g_strdup(name); } +void +purple_connection_set_protocol_data(PurpleConnection *connection, void *proto_data) { + g_return_if_fail(connection != NULL); + + connection->proto_data = proto_data; +} + PurpleConnectionState purple_connection_get_state(const PurpleConnection *gc) { @@ -467,6 +474,13 @@ return gc->display_name; } +void * +purple_connection_get_protocol_data(const PurpleConnection *connection) { + g_return_val_if_fail(connection != NULL, NULL); + + return connection->proto_data; +} + void purple_connection_update_progress(PurpleConnection *gc, const char *text, size_t step, size_t count)
--- a/libpurple/connection.h Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/connection.h Fri Oct 31 17:46:39 2008 +0000 @@ -354,6 +354,15 @@ void purple_connection_set_display_name(PurpleConnection *gc, const char *name); /** + * Sets the protocol data for a connection. + * + * @param connection The PurpleConnection. + * @param proto_data The protocol data to set for the connection. + * @since 2.6.0 + */ +void purple_connection_set_protocol_data(PurpleConnection *connection, void *proto_data); + +/** * Returns the connection state. * * @param gc The connection. @@ -408,6 +417,16 @@ const char *purple_connection_get_display_name(const PurpleConnection *gc); /** + * Gets the protocol data from a connection. + * + * @param connection The PurpleConnection. + * + * @return The protocol data for the connection. + * @since 2.6.0 + */ +void *purple_connection_get_protocol_data(const PurpleConnection *connection); + +/** * Updates the connection progress. * * @param gc The connection.
--- a/libpurple/core.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/core.c Fri Oct 31 17:46:39 2008 +0000 @@ -46,10 +46,12 @@ #include "signals.h" #include "smiley.h" #include "sound.h" +#include "sound-theme-loader.h" #include "sslconn.h" #include "status.h" #include "stun.h" #include "util.h" +#include "theme-manager.h" #ifdef HAVE_DBUS # ifndef DBUS_API_SUBJECT_TO_CHANGE @@ -143,6 +145,7 @@ purple_plugins_probe(G_MODULE_SUFFIX); + purple_theme_manager_init(); /* The buddy icon code uses the imgstore, so init it early. */ purple_imgstore_init(); @@ -171,7 +174,6 @@ purple_xfers_init(); purple_idle_init(); purple_smileys_init(); - /* * Call this early on to try to auto-detect our IP address and * hopefully save some time later. @@ -180,6 +182,8 @@ if (ops != NULL && ops->ui_init != NULL) ops->ui_init(); + + purple_theme_manager_refresh(); return TRUE; } @@ -215,6 +219,7 @@ purple_status_uninit(); purple_prefs_uninit(); purple_sound_uninit(); + purple_theme_manager_uninit(); purple_xfers_uninit(); purple_proxy_uninit(); purple_dnsquery_uninit();
--- a/libpurple/protocols/bonjour/bonjour.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/protocols/bonjour/bonjour.c Fri Oct 31 17:46:39 2008 +0000 @@ -261,9 +261,10 @@ static void bonjour_remove_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) { - if (buddy->proto_data) { - bonjour_buddy_delete(buddy->proto_data); - buddy->proto_data = NULL; + BonjourBuddy *bb = purple_buddy_get_protocol_data(buddy); + if (bb) { + bonjour_buddy_delete(bb); + purple_buddy_set_protocol_data(buddy, NULL); } } @@ -303,7 +304,7 @@ PurpleBuddy *buddy = purple_find_buddy(connection->account, who); BonjourBuddy *bb; - if (buddy == NULL || buddy->proto_data == NULL) + if (buddy == NULL || (bb = purple_buddy_get_protocol_data(buddy)) == NULL) { /* * This buddy is not in our buddy list, and therefore does not really @@ -312,7 +313,6 @@ return; } - bb = buddy->proto_data; bonjour_jabber_close_conversation(bb->conversation); bb->conversation = NULL; } @@ -351,7 +351,7 @@ { PurplePresence *presence; PurpleStatus *status; - BonjourBuddy *bb = buddy->proto_data; + BonjourBuddy *bb = purple_buddy_get_protocol_data(buddy); const char *status_description; const char *message; @@ -417,8 +417,7 @@ { PurpleBuddy *buddy = purple_find_buddy(connection->account, who); - return (buddy != NULL && buddy->proto_data != NULL); - + return (buddy != NULL && purple_buddy_get_protocol_data(buddy) != NULL); } static gboolean
--- a/libpurple/protocols/bonjour/bonjour_ft.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.c Fri Oct 31 17:46:39 2008 +0000 @@ -386,10 +386,9 @@ buddy = purple_find_buddy(xfer->account, xfer->who); /* this buddy is offline. */ - if (buddy == NULL || buddy->proto_data == NULL) + if (buddy == NULL || (bb = purple_buddy_get_protocol_data(buddy)) == NULL) return; - bb = (BonjourBuddy *)buddy->proto_data; /* Assume it is the first IP. We could do something like keep track of which one is in use or something. */ if (bb->ips) xf->buddy_ip = g_strdup(bb->ips->data); @@ -410,6 +409,7 @@ const char *type, *id; BonjourData *bd; PurpleXfer *xfer; + const gchar *name = NULL; if(pc == NULL || packet == NULL || pb == NULL) return; @@ -419,6 +419,8 @@ purple_debug_info("bonjour", "xep-si-parse.\n"); + name = purple_buddy_get_name(pb); + type = xmlnode_get_attrib(packet, "type"); id = xmlnode_get_attrib(packet, "id"); if(type) { @@ -446,31 +448,34 @@ /* TODO: Make sure that it is advertising a bytestreams transfer */ - bonjour_xfer_receive(pc, id, sid, pb->name, filesize, filename, XEP_BYTESTREAMS); + bonjour_xfer_receive(pc, id, sid, name, filesize, filename, XEP_BYTESTREAMS); parsed_receive = TRUE; } if (!parsed_receive) { + BonjourData *bd = purple_connection_get_protocol_data(pc); + purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n"); - xep_ft_si_reject((BonjourData *)pc->proto_data, id, pb->name, "403", "cancel"); + xep_ft_si_reject(bd, id, name, "403", "cancel"); /*TODO: Send Cancel (501) */ } } else if(!strcmp(type, "result")) { purple_debug_info("bonjour", "si offer Message type - RESULT.\n"); - xfer = bonjour_si_xfer_find(bd, id, pb->name); + xfer = bonjour_si_xfer_find(bd, id, name); if(xfer == NULL) { + BonjourData *bd = purple_connection_get_protocol_data(pc); purple_debug_info("bonjour", "xfer find fail.\n"); - xep_ft_si_reject((BonjourData *)pc->proto_data, id, pb->name, "403", "cancel"); + xep_ft_si_reject(bd, id, name, "403", "cancel"); } else bonjour_bytestreams_init(xfer); } else if(!strcmp(type, "error")) { purple_debug_info("bonjour", "si offer Message type - ERROR.\n"); - xfer = bonjour_si_xfer_find(bd, id, pb->name); + xfer = bonjour_si_xfer_find(bd, id, name); if(xfer == NULL) purple_debug_info("bonjour", "xfer find fail.\n"); @@ -498,7 +503,7 @@ purple_debug_info("bonjour", "xep-bytestreams-parse.\n"); type = xmlnode_get_attrib(packet, "type"); - from = pb->name; + from = purple_buddy_get_name(pb); query = xmlnode_get_child(packet,"query"); if(type) { if(!strcmp(type, "set")) { @@ -838,8 +843,10 @@ static void bonjour_bytestreams_connect(PurpleXfer *xfer, PurpleBuddy *pb) { + PurpleAccount *account = NULL; XepXfer *xf; char dstaddr[41]; + const gchar *name = NULL; unsigned char hashval[20]; char *p; int i; @@ -853,7 +860,10 @@ if(!xf) return; - p = g_strdup_printf("%s%s%s", xf->sid, pb->name, purple_account_get_username(pb->account)); + name = purple_buddy_get_name(pb); + account = purple_buddy_get_account(pb); + + p = g_strdup_printf("%s%s%s", xf->sid, name, purple_account_get_username(account)); purple_cipher_digest_region("sha1", (guchar *)p, strlen(p), sizeof(hashval), hashval, NULL); g_free(p);
--- a/libpurple/protocols/bonjour/buddy.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/protocols/bonjour/buddy.c Fri Oct 31 17:46:39 2008 +0000 @@ -157,7 +157,7 @@ purple_blist_add_buddy(buddy, NULL, group, NULL); } - buddy->proto_data = bonjour_buddy; + purple_buddy_set_protocol_data(buddy, bonjour_buddy); name = purple_buddy_get_name(buddy); /* Create the alias for the buddy using the first and the last name */ @@ -210,8 +210,8 @@ if (PURPLE_BLIST_NODE_SHOULD_SAVE(pb)) { purple_prpl_got_user_status(purple_buddy_get_account(pb), purple_buddy_get_name(pb), "offline", NULL); - bonjour_buddy_delete(pb->proto_data); - pb->proto_data = NULL; + bonjour_buddy_delete(purple_buddy_get_protocol_data(pb)); + purple_buddy_set_protocol_data(pb, NULL); } else { purple_account_remove_buddy(purple_buddy_get_account(pb), pb, NULL); purple_blist_remove_buddy(pb);
--- a/libpurple/protocols/bonjour/jabber.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Fri Oct 31 17:46:39 2008 +0000 @@ -240,17 +240,21 @@ _match_buddies_by_address(gpointer key, gpointer value, gpointer data) { PurpleBuddy *pb = value; + PurpleAccount *account = NULL; + BonjourBuddy *bb = NULL; struct _match_buddies_by_address_t *mbba = data; + account = purple_buddy_get_account(pb); + bb = purple_buddy_get_protocol_data(pb); + /* * If the current PurpleBuddy's data is not null and the PurpleBuddy's account * is the same as the account requesting the check then continue to determine * whether one of the buddies IPs matches the target IP. */ - if (mbba->jdata->account == pb->account && pb->proto_data != NULL) + if (mbba->jdata->account == account && bb != NULL) { const char *ip; - BonjourBuddy *bb = pb->proto_data; GSList *tmp = bb->ips; while(tmp) { @@ -268,7 +272,7 @@ _send_data_write_cb(gpointer data, gint source, PurpleInputCondition cond) { PurpleBuddy *pb = data; - BonjourBuddy *bb = pb->proto_data; + BonjourBuddy *bb = purple_buddy_get_protocol_data(pb); BonjourJabberConversation *bconv = bb->conversation; int ret, writelen; @@ -285,13 +289,16 @@ if (ret < 0 && errno == EAGAIN) return; else if (ret <= 0) { - PurpleConversation *conv; + PurpleConversation *conv = NULL; + PurpleAccount *account = NULL; const char *error = g_strerror(errno); purple_debug_error("bonjour", "Error sending message to buddy %s error: %s\n", purple_buddy_get_name(pb), error ? error : "(null)"); - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account); + account = purple_buddy_get_account(pb); + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account); if (conv != NULL) purple_conversation_write(conv, NULL, _("Unable to send message."), @@ -310,7 +317,7 @@ { gint ret; int len = strlen(message); - BonjourBuddy *bb = pb->proto_data; + BonjourBuddy *bb = purple_buddy_get_protocol_data(pb); BonjourJabberConversation *bconv = bb->conversation; /* If we're not ready to actually send, append it to the buffer */ @@ -328,13 +335,16 @@ if (ret == -1 && errno == EAGAIN) ret = 0; else if (ret <= 0) { - PurpleConversation *conv; + PurpleConversation *conv = NULL; + PurpleAccount *account = NULL; const char *error = g_strerror(errno); purple_debug_error("bonjour", "Error sending message to buddy %s error: %s\n", purple_buddy_get_name(pb), error ? error : "(null)"); - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account); + account = purple_buddy_get_account(pb); + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account); if (conv != NULL) purple_conversation_write(conv, NULL, _("Unable to send message."), @@ -386,9 +396,11 @@ purple_debug_warning("bonjour", "receive error: %s\n", err ? err : "(null)"); bonjour_jabber_close_conversation(bconv); - if (bconv->pb != NULL && bconv->pb->proto_data != NULL) { - BonjourBuddy *bb = bconv->pb->proto_data; - bb->conversation = NULL; + if (bconv->pb != NULL) { + BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb); + + if(bb != NULL) + bb->conversation = NULL; } /* I guess we really don't need to notify the user. @@ -396,7 +408,8 @@ } return; } else if (len == 0) { /* The other end has closed the socket */ - purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (bconv->pb && bconv->pb->name) ? bconv->pb->name : "(unknown)"); + const gchar *name = purple_buddy_get_name(bconv->pb); + purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (name) ? name : "(unknown)"); bonjour_jabber_stream_ended(bconv); return; } else { @@ -415,15 +428,19 @@ } void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) { + const gchar *name = NULL; + + if(bconv->pb != NULL) + name = purple_buddy_get_name(bconv->pb); - purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", bconv->pb ? bconv->pb->name : "(unknown)"); + purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", name ? name : "(unknown)"); /* Inform the user that the conversation has been closed */ if (bconv != NULL) { BonjourBuddy *bb = NULL; if(bconv->pb != NULL) - bb = bconv->pb->proto_data; + bb = purple_buddy_get_protocol_data(bconv->pb); #if 0 if(bconv->pb != NULL) { PurpleConversation *conv; @@ -469,7 +486,7 @@ BonjourBuddy *bb = NULL; if(bconv->pb) { - bb = bconv->pb->proto_data; + bb = purple_buddy_get_protocol_data(bconv->pb); bname = purple_buddy_get_name(bconv->pb); } @@ -747,17 +764,20 @@ _connected_to_buddy(gpointer data, gint source, const gchar *error) { PurpleBuddy *pb = data; - BonjourBuddy *bb = pb->proto_data; + BonjourBuddy *bb = purple_buddy_get_protocol_data(pb); bb->conversation->connect_data = NULL; if (source < 0) { - PurpleConversation *conv; + PurpleConversation *conv = NULL; + PurpleAccount *account = NULL; purple_debug_error("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n", purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, error ? error : "(null)"); - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account); + account = purple_buddy_get_account(pb); + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account); if (conv != NULL) purple_conversation_write(conv, NULL, _("Unable to send the message, the conversation couldn't be started."), @@ -770,12 +790,15 @@ if (!bonjour_jabber_send_stream_init(bb->conversation, source)) { const char *err = g_strerror(errno); - PurpleConversation *conv; + PurpleConversation *conv = NULL; + PurpleAccount *account = NULL; purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n", purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, err ? err : "(null)"); - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account); + account = purple_buddy_get_account(pb); + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account); if (conv != NULL) purple_conversation_write(conv, NULL, _("Unable to send the message, the conversation couldn't be started."), @@ -795,14 +818,14 @@ void bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv) { - PurpleBuddy *pb; + PurpleBuddy *pb = NULL; + BonjourBuddy *bb = NULL; g_return_if_fail(bconv->ip != NULL); g_return_if_fail(bconv->pb == NULL); pb = purple_find_buddy(bconv->account, bconv->buddy_name); - if (pb && pb->proto_data) { - BonjourBuddy *bb = pb->proto_data; + if (pb && (bb = purple_buddy_get_protocol_data(pb))) { const char *ip; GSList *tmp = bb->ips; @@ -860,7 +883,7 @@ purple_debug_error("bonjour", "More than one buddy matched for ip %s.\n", bconv->ip); else { PurpleBuddy *pb = mbba->matched_buddies->data; - BonjourBuddy *bb = pb->proto_data; + BonjourBuddy *bb = purple_buddy_get_protocol_data(pb); purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)\n", purple_buddy_get_name(pb), bconv->ip); @@ -900,12 +923,10 @@ g_return_val_if_fail(to != NULL, NULL); pb = purple_find_buddy(jdata->account, to); - if (pb == NULL || pb->proto_data == NULL) + if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL) /* You can not send a message to an offline buddy */ return NULL; - bb = (BonjourBuddy *) pb->proto_data; - /* Check if there is a previously open conversation */ if (bb->conversation == NULL) { @@ -952,7 +973,7 @@ int ret; pb = _find_or_start_conversation(jdata, to); - if (pb == NULL || pb->proto_data == NULL) { + if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL) { purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to); /* You can not send a message to an offline buddy */ return -10000; @@ -960,8 +981,6 @@ purple_markup_html_to_xhtml(body, &xhtml, &message); - bb = pb->proto_data; - message_node = xmlnode_new("message"); xmlnode_set_attrib(message_node, "to", bb->name); xmlnode_set_attrib(message_node, "from", purple_account_get_username(jdata->account)); @@ -1011,7 +1030,7 @@ /* Disconnect this conv. from the buddy here so it can't be disposed of twice.*/ if(bconv->pb != NULL) { - BonjourBuddy *bb = bconv->pb->proto_data; + BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb); if (bb->conversation == bconv) bb->conversation = NULL; } @@ -1040,7 +1059,7 @@ tmp_next = xfers->next; /* We only need to cancel this if it hasn't actually started transferring. */ /* This will change if we ever support IBB transfers. */ - if (strcmp(xfer->who, bconv->pb->name) == 0 + if (strcmp(xfer->who, purple_buddy_get_name(bconv->pb)) == 0 && (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED || purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) { purple_xfer_cancel_remote(xfer); @@ -1099,7 +1118,7 @@ buddies = purple_find_buddies(jdata->account, NULL); for (l = buddies; l; l = l->next) { - BonjourBuddy *bb = ((PurpleBuddy*) l->data)->proto_data; + BonjourBuddy *bb = purple_buddy_get_protocol_data((PurpleBuddy*) l->data); if (bb != NULL) { bonjour_jabber_close_conversation(bb->conversation); bb->conversation = NULL; @@ -1168,11 +1187,14 @@ if(pb == NULL) return FALSE; - acc = pb->account; + acc = purple_buddy_get_account(pb); for(l = acc->deny; l != NULL; l = l->next) { - if(!purple_utf8_strcasecmp(pb->name, (char *)l->data)) { - purple_debug_info("bonjour", "%s has been blocked by %s.\n", pb->name, acc->username); + const gchar *name = purple_buddy_get_name(pb); + const gchar *username = purple_account_get_username(acc); + + if(!purple_utf8_strcasecmp(name, (char *)l->data)) { + purple_debug_info("bonjour", "%s has been blocked by %s.\n", name, username); blocked = TRUE; break; } @@ -1189,8 +1211,10 @@ return; if(connection == NULL) { - if(pb->account != NULL) - connection = (pb->account)->gc; + PurpleAccount *account = purple_buddy_get_account(pb); + + if(account != NULL) + connection = purple_account_get_connection(account); } if(check_if_blocked(pb))
--- a/libpurple/protocols/bonjour/mdns_avahi.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/protocols/bonjour/mdns_avahi.c Fri Oct 31 17:46:39 2008 +0000 @@ -124,7 +124,7 @@ g_return_if_fail(r != NULL); pb = purple_find_buddy(account, name); - bb = (pb != NULL) ? pb->proto_data : NULL; + bb = (pb != NULL) ? purple_buddy_get_protocol_data(pb) : NULL; switch (event) { case AVAHI_RESOLVER_FAILURE: @@ -252,7 +252,7 @@ purple_debug_info("bonjour", "_browser_callback - Remove service\n"); pb = purple_find_buddy(account, name); if (pb != NULL) { - BonjourBuddy *bb = pb->proto_data; + BonjourBuddy *bb = purple_buddy_get_protocol_data(pb); AvahiBuddyImplData *b_impl; GSList *l; AvahiSvcResolverData *rd_search;
--- a/libpurple/protocols/gg/buddylist.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/protocols/gg/buddylist.c Fri Oct 31 17:46:39 2008 +0000 @@ -41,37 +41,47 @@ GGPInfo *info = gc->proto_data; PurpleAccount *account = purple_connection_get_account(gc); - PurpleBuddyList *blist; PurpleBlistNode *gnode, *cnode, *bnode; PurpleBuddy *buddy; uin_t *userlist = NULL; gchar *types = NULL; int size = 0; - if ((blist = purple_get_blist()) == NULL) - return; - - for (gnode = blist->root; gnode != NULL; gnode = gnode->next) { + for (gnode = purple_blist_get_root(); + gnode != NULL; + gnode = purple_blist_node_get_sibling_next(gnode)) + { if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue; - for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) { + for (cnode = purple_blist_node_get_first_child(gnode); + cnode != NULL; + cnode = purple_blist_node_get_sibling_next(cnode)) + { if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue; - for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) { + for (bnode = purple_blist_node_get_first_child(cnode); + bnode != NULL; + bnode = purple_blist_node_get_sibling_next(bnode)) + { + PurpleAccount *ba = NULL; + const gchar *name = NULL; + if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue; buddy = (PurpleBuddy *)bnode; - if (buddy->account != account) + if ((ba = purple_buddy_get_account(buddy)) != account) continue; + name = purple_buddy_get_name(buddy); + size++; userlist = (uin_t *) g_renew(uin_t, userlist, size); types = (gchar *) g_renew(gchar, types, size); - userlist[size - 1] = ggp_str_to_uin(buddy->name); + userlist[size - 1] = ggp_str_to_uin(name); types[size - 1] = GG_USER_NORMAL; purple_debug_info("gg", "ggp_buddylist_send: adding %d\n", userlist[size - 1]); @@ -173,36 +183,45 @@ void ggp_buddylist_offline(PurpleConnection *gc) { PurpleAccount *account = purple_connection_get_account(gc); - PurpleBuddyList *blist; PurpleBlistNode *gnode, *cnode, *bnode; PurpleBuddy *buddy; - if ((blist = purple_get_blist()) == NULL) - return; - - for (gnode = blist->root; gnode != NULL; gnode = gnode->next) { + for (gnode = purple_blist_get_root(); + gnode != NULL; + gnode = purple_blist_node_get_sibling_next(gnode)) + { if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue; - for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) { + for (cnode = purple_blist_node_get_first_child(gnode); + cnode != NULL; + cnode = purple_blist_node_get_sibling_next(cnode)) + { if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue; - for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) { + for (bnode = purple_blist_node_get_first_child(cnode); + bnode != NULL; + bnode = purple_blist_node_get_sibling_next(bnode)) + { + const gchar *name = NULL; + if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue; buddy = (PurpleBuddy *)bnode; + + name = purple_buddy_get_name(buddy); - if (buddy->account != account) + if (purple_buddy_get_account(buddy) != account) continue; purple_prpl_got_user_status( - account, buddy->name, "offline", NULL); + account, name, "offline", NULL); purple_debug_info("gg", "ggp_buddylist_offline: gone: %s\n", - buddy->name); + name); } } } @@ -212,7 +231,6 @@ /* char *ggp_buddylist_dump(PurpleAccount *account) {{{ */ char *ggp_buddylist_dump(PurpleAccount *account) { - PurpleBuddyList *blist; PurpleBlistNode *gnode, *cnode, *bnode; PurpleGroup *group; PurpleBuddy *buddy; @@ -220,33 +238,42 @@ char *buddylist = g_strdup(""); char *ptr; - if ((blist = purple_get_blist()) == NULL) - return NULL; - - for (gnode = blist->root; gnode != NULL; gnode = gnode->next) { + for (gnode = purple_blist_get_root(); + gnode != NULL; + gnode = purple_blist_node_get_sibling_next(gnode)) + { if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue; group = (PurpleGroup *)gnode; - for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) { + for (cnode = purple_blist_node_get_first_child(gnode); + cnode != NULL; + cnode = purple_blist_node_get_sibling_next(cnode)) + { if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue; - for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) { - gchar *newdata, *name, *alias, *gname; + for (bnode = purple_blist_node_get_first_child(cnode); + bnode != NULL; + bnode = purple_blist_node_get_sibling_next(bnode)) + { + gchar *newdata; + const gchar *name, *alias, *gname; gchar *cp_alias, *cp_gname; if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue; buddy = (PurpleBuddy *)bnode; - if (buddy->account != account) + if (purple_buddy_get_account(buddy) != account) continue; - name = buddy->name; - alias = buddy->alias ? buddy->alias : buddy->name; - gname = group->name; + name = purple_buddy_get_name(buddy); + alias = purple_buddy_get_alias(buddy); + if(alias == NULL) + alias = name; + gname = purple_group_get_name(group); cp_gname = charset_convert(gname, "UTF-8", "CP1250"); cp_alias = charset_convert(alias, "UTF-8", "CP1250");
--- a/libpurple/protocols/gg/gg.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/protocols/gg/gg.c Fri Oct 31 17:46:39 2008 +0000 @@ -1907,11 +1907,12 @@ { PurpleAccount *account; GGPInfo *info = gc->proto_data; + const gchar *name = purple_buddy_get_name(buddy); - gg_add_notify(info->session, ggp_str_to_uin(buddy->name)); + gg_add_notify(info->session, ggp_str_to_uin(name)); account = purple_connection_get_account(gc); - if (strcmp(purple_account_get_username(account), buddy->name) == 0) { + if (strcmp(purple_account_get_username(account), name) == 0) { ggp_status_fake_to_self(account); } } @@ -1923,7 +1924,7 @@ { GGPInfo *info = gc->proto_data; - gg_remove_notify(info->session, ggp_str_to_uin(buddy->name)); + gg_remove_notify(info->session, ggp_str_to_uin(purple_buddy_get_name(buddy))); } /* }}} */
--- a/libpurple/protocols/irc/irc.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/protocols/irc/irc.c Fri Oct 31 17:46:39 2008 +0000 @@ -569,7 +569,7 @@ { struct irc_conn *irc = (struct irc_conn *)gc->proto_data; struct irc_buddy *ib = g_new0(struct irc_buddy, 1); - ib->name = g_strdup(buddy->name); + ib->name = g_strdup(purple_buddy_get_name(buddy)); g_hash_table_insert(irc->buddies, ib->name, ib); /* if the timer isn't set, this is during signon, so we don't want to flood @@ -582,7 +582,7 @@ static void irc_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) { struct irc_conn *irc = (struct irc_conn *)gc->proto_data; - g_hash_table_remove(irc->buddies, buddy->name); + g_hash_table_remove(irc->buddies, purple_buddy_get_name(buddy)); } static void read_input(struct irc_conn *irc, int len)
--- a/libpurple/protocols/irc/msgs.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/protocols/irc/msgs.c Fri Oct 31 17:46:39 2008 +0000 @@ -95,20 +95,29 @@ } /* this used to be in the core, but it's not now */ - for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) { + for (gnode = purple_blist_get_root(); + gnode; + gnode = purple_blist_node_get_sibling_next(gnode)) + { if(!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue; - for(cnode = gnode->child; cnode; cnode = cnode->next) { + for(cnode = purple_blist_node_get_first_child(gnode); + cnode; + cnode = purple_blist_node_get_sibling_next(cnode)) + { if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue; - for(bnode = cnode->child; bnode; bnode = bnode->next) { + for(bnode = purple_blist_node_get_first_child(cnode); + bnode; + bnode = purple_blist_node_get_sibling_next(bnode)) + { PurpleBuddy *b; if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue; b = (PurpleBuddy *)bnode; - if(b->account == gc->account) { + if(purple_buddy_get_account(b) == gc->account) { struct irc_buddy *ib = g_new0(struct irc_buddy, 1); - ib->name = g_strdup(b->name); + ib->name = g_strdup(purple_buddy_get_name(b)); g_hash_table_insert(irc->buddies, ib->name, ib); } }
--- a/libpurple/protocols/jabber/buddy.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/protocols/jabber/buddy.c Fri Oct 31 17:46:39 2008 +0000 @@ -419,7 +419,7 @@ { PurpleStoredImage *img; JabberIq *iq; - JabberStream *js = gc->proto_data; + JabberStream *js = purple_connection_get_protocol_data(gc); xmlnode *vc_node; const struct tag_attr *tag_attr; @@ -496,7 +496,7 @@ PurplePresence *gpresence; PurpleStatus *status; - if(((JabberStream*)gc->proto_data)->pep) { + if(((JabberStream*)purple_connection_get_protocol_data(gc))->pep) { /* XEP-0084: User Avatars */ if(img) { /* @@ -568,7 +568,7 @@ g_free(base64avatar); /* publish the avatar itself */ - jabber_pep_publish((JabberStream*)gc->proto_data, publish); + jabber_pep_publish((JabberStream*)purple_connection_get_protocol_data(gc), publish); /* next step: publish the metadata */ publish = xmlnode_new("publish"); @@ -594,7 +594,7 @@ g_free(heightstring); /* publish the metadata */ - jabber_pep_publish((JabberStream*)gc->proto_data, publish); + jabber_pep_publish((JabberStream*)purple_connection_get_protocol_data(gc), publish); g_free(hash); } else { /* if(img) */ @@ -611,7 +611,7 @@ xmlnode_new_child(metadata, "stop"); /* publish the metadata */ - jabber_pep_publish((JabberStream*)gc->proto_data, publish); + jabber_pep_publish((JabberStream*)purple_connection_get_protocol_data(gc), publish); } } else { purple_debug(PURPLE_DEBUG_ERROR, "jabber", @@ -1797,7 +1797,7 @@ void jabber_buddy_get_info(PurpleConnection *gc, const char *who) { - JabberStream *js = gc->proto_data; + JabberStream *js = purple_connection_get_protocol_data(gc); char *bare_jid = jabber_get_bare_jid(who); if(bare_jid) { @@ -1848,10 +1848,10 @@ g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(buddy->account); - js = gc->proto_data; + gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + js = purple_connection_get_protocol_data(gc); - jabber_buddy_set_invisibility(js, buddy->name, TRUE); + jabber_buddy_set_invisibility(js, purple_buddy_get_name(buddy), TRUE); } static void jabber_buddy_make_visible(PurpleBlistNode *node, gpointer data) @@ -1863,10 +1863,10 @@ g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(buddy->account); - js = gc->proto_data; + gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + js = purple_connection_get_protocol_data(gc); - jabber_buddy_set_invisibility(js, buddy->name, FALSE); + jabber_buddy_set_invisibility(js, purple_buddy_get_name(buddy), FALSE); } static void jabber_buddy_cancel_presence_notification(PurpleBlistNode *node, @@ -1879,11 +1879,11 @@ g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(buddy->account); - js = gc->proto_data; + gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + js = purple_connection_get_protocol_data(gc); /* I wonder if we should prompt the user before doing this */ - jabber_presence_subscription_set(js, buddy->name, "unsubscribed"); + jabber_presence_subscription_set(js, purple_buddy_get_name(buddy), "unsubscribed"); } static void jabber_buddy_rerequest_auth(PurpleBlistNode *node, gpointer data) @@ -1895,10 +1895,10 @@ g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(buddy->account); - js = gc->proto_data; + gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + js = purple_connection_get_protocol_data(gc); - jabber_presence_subscription_set(js, buddy->name, "subscribe"); + jabber_presence_subscription_set(js, purple_buddy_get_name(buddy), "subscribe"); } @@ -1911,18 +1911,18 @@ g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(buddy->account); - js = gc->proto_data; + gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + js = purple_connection_get_protocol_data(gc); - jabber_presence_subscription_set(js, buddy->name, "unsubscribe"); + jabber_presence_subscription_set(js, purple_buddy_get_name(buddy), "unsubscribe"); } static void jabber_buddy_login(PurpleBlistNode *node, gpointer data) { if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { /* simply create a directed presence of the current status */ PurpleBuddy *buddy = (PurpleBuddy *) node; - PurpleConnection *gc = purple_account_get_connection(buddy->account); - JabberStream *js = gc->proto_data; + PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + JabberStream *js = purple_connection_get_protocol_data(gc); PurpleAccount *account = purple_connection_get_account(gc); PurplePresence *gpresence = purple_account_get_presence(account); PurpleStatus *status = purple_presence_get_active_status(gpresence); @@ -1936,7 +1936,7 @@ g_free(msg); - xmlnode_set_attrib(presence, "to", buddy->name); + xmlnode_set_attrib(presence, "to", purple_buddy_get_name(buddy)); jabber_send(js, presence); xmlnode_free(presence); @@ -1947,12 +1947,13 @@ if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { /* simply create a directed unavailable presence */ PurpleBuddy *buddy = (PurpleBuddy *) node; - JabberStream *js = purple_account_get_connection(buddy->account)->proto_data; + PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + JabberStream *js = purple_connection_get_protocol_data(gc); xmlnode *presence; presence = jabber_presence_create_js(js, JABBER_BUDDY_STATE_UNAVAILABLE, NULL, 0); - xmlnode_set_attrib(presence, "to", buddy->name); + xmlnode_set_attrib(presence, "to", purple_buddy_get_name(buddy)); jabber_send(js, presence); xmlnode_free(presence); @@ -1961,9 +1962,10 @@ static GList *jabber_buddy_menu(PurpleBuddy *buddy) { - PurpleConnection *gc = purple_account_get_connection(buddy->account); - JabberStream *js = gc->proto_data; - JabberBuddy *jb = jabber_buddy_find(js, buddy->name, TRUE); + PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + JabberStream *js = purple_connection_get_protocol_data(gc); + const char *name = purple_buddy_get_name(buddy); + JabberBuddy *jb = jabber_buddy_find(js, name, TRUE); GList *jbrs; GList *m = NULL; @@ -2018,7 +2020,7 @@ * that gateways on the roster can be identified by having no '@' in their jid. This is a faily safe assumption, since * people don't tend to have a server or other service there. */ - if (g_utf8_strchr(buddy->name, -1, '@') == NULL) { + if (g_utf8_strchr(name, -1, '@') == NULL) { act = purple_menu_action_new(_("Log In"), PURPLE_CALLBACK(jabber_buddy_login), NULL, NULL); @@ -2486,7 +2488,7 @@ void jabber_user_search_begin(PurplePluginAction *action) { PurpleConnection *gc = (PurpleConnection *) action->context; - JabberStream *js = gc->proto_data; + JabberStream *js = purple_connection_get_protocol_data(gc); purple_request_input(gc, _("Enter a User Directory"), _("Enter a User Directory"), _("Select a user directory to search"),
--- a/libpurple/protocols/jabber/google.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/protocols/jabber/google.c Fri Oct 31 17:46:39 2008 +0000 @@ -309,7 +309,7 @@ g = purple_buddy_get_group(b); group = xmlnode_new_child(item, "group"); - xmlnode_insert_data(group, g->name, -1); + xmlnode_insert_data(group, purple_group_get_name(g), -1); buddies = buddies->next; } @@ -357,7 +357,7 @@ if (!js || !js->server_caps & JABBER_CAP_GOOGLE_ROSTER) return; - buddies = purple_find_buddies(js->gc->account, who); + buddies = purple_find_buddies(purple_connection_get_account(js->gc), who); if(!buddies) return; @@ -375,7 +375,7 @@ g = purple_buddy_get_group(b); group = xmlnode_new_child(item, "group"); - xmlnode_insert_data(group, g->name, -1); + xmlnode_insert_data(group, purple_buddy_get_group(g), -1); buddies = buddies->next; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/sound-theme-loader.c Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,115 @@ +/* + * SoundThemeLoader for LibPurple + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "sound-theme-loader.h" +#include "sound-theme.h" +#include "util.h" +#include "xmlnode.h" + +/***************************************************************************** + * Sound Theme Builder + *****************************************************************************/ + +static PurpleTheme * +purple_sound_loader_build(const gchar *dir) +{ + xmlnode *root_node = NULL, *sub_node; + gchar *filename_full, *data; + PurpleSoundTheme *theme = NULL; + + /* Find the theme file */ + g_return_val_if_fail(dir != NULL, NULL); + filename_full = g_build_filename(dir, "theme.xml", NULL); + + if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR)) + root_node = xmlnode_from_file(dir, "theme.xml", "sound themes", "sound-loader"); + + g_free(filename_full); + g_return_val_if_fail(root_node != NULL, NULL); + + /* Parse the tree */ + sub_node = xmlnode_get_child(root_node, "description"); + data = xmlnode_get_data(sub_node); + + if (xmlnode_get_attrib(root_node, "name") != NULL) { + theme = g_object_new(PURPLE_TYPE_SOUND_THEME, + "type", "sound", + "name", xmlnode_get_attrib(root_node, "name"), + "author", xmlnode_get_attrib(root_node, "author"), + "image", xmlnode_get_attrib(root_node, "image"), + "directory", dir, + "description", data, NULL); + + sub_node = xmlnode_get_child(root_node, "event"); + + while (sub_node) { + purple_sound_theme_set_file(theme, + xmlnode_get_attrib(sub_node, "name"), + xmlnode_get_attrib(sub_node, "file")); + sub_node = xmlnode_get_next_twin(sub_node); + } + } + + xmlnode_free(root_node); + g_free(data); + return PURPLE_THEME(theme); +} + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +purple_sound_theme_loader_class_init(PurpleSoundThemeLoaderClass *klass) +{ + PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass); + + loader_klass->purple_theme_loader_build = purple_sound_loader_build; +} + + +GType +purple_sound_theme_loader_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PurpleSoundThemeLoaderClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)purple_sound_theme_loader_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PurpleSoundThemeLoader), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static(PURPLE_TYPE_THEME_LOADER, + "PurpleSoundThemeLoader", + &info, 0); + } + return type; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/sound-theme-loader.h Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,71 @@ +/** + * @file sound-loader.h Purple Sound Theme Loader Class API + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PURPLE_SOUND_THEME_LOADER_H_ +#define _PURPLE_SOUND_THEME_LOADER_H_ + +#include <glib.h> +#include <glib-object.h> +#include "theme-loader.h" + +/** + * A purple sound theme loader. extends PurpleThemeLoader (theme-loader.h) + * This is a class designed to build sound themes + * + * PurpleSoundThemeLoader is a GObject. + */ +typedef struct _PurpleSoundThemeLoader PurpleSoundThemeLoader; +typedef struct _PurpleSoundThemeLoaderClass PurpleSoundThemeLoaderClass; + +#define PURPLE_TYPE_SOUND_THEME_LOADER (purple_sound_theme_loader_get_type ()) +#define PURPLE_SOUND_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PURPLE_TYPE_SOUND_THEME_LOADER, PurpleSoundThemeLoader)) +#define PURPLE_SOUND_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_SOUND_THEME_LOADER, PurpleSoundThemeLoaderClass)) +#define PURPLE_IS_SOUND_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PURPLE_TYPE_SOUND_THEME_LOADER)) +#define PURPLE_IS_SOUND_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_SOUND_THEME_LOADER)) +#define PURPLE_SOUND_THEME_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_SOUND_THEME_LOADER, PurpleSoundThemeLoaderClass)) + +struct _PurpleSoundThemeLoader +{ + PurpleThemeLoader parent; +}; + +struct _PurpleSoundThemeLoaderClass +{ + PurpleThemeLoaderClass parent_class; +}; + +/**************************************************************************/ +/** @name Purple Theme-Loader API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType purple_sound_theme_loader_get_type(void); + +G_END_DECLS +#endif /* _PURPLE_SOUND_THEME_LOADER_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/sound-theme.c Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,163 @@ +/* + * Sound Themes for LibPurple + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "sound-theme.h" + +#define PURPLE_SOUND_THEME_GET_PRIVATE(Gobject) \ + ((PurpleSoundThemePrivate *) ((PURPLE_SOUND_THEME(Gobject))->priv)) + + +/****************************************************************************** + * Structs + *****************************************************************************/ +typedef struct { + /* used to store filenames of diffrent sounds */ + GHashTable *sound_files; +} PurpleSoundThemePrivate; + +/****************************************************************************** + * Globals + *****************************************************************************/ + +static GObjectClass *parent_class = NULL; + +/****************************************************************************** + * Enums + *****************************************************************************/ + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +purple_sound_theme_init(GTypeInstance *instance, + gpointer klass) +{ + PurpleSoundThemePrivate *priv; + + (PURPLE_SOUND_THEME(instance))->priv = g_new0(PurpleSoundThemePrivate, 1); + + priv = PURPLE_SOUND_THEME_GET_PRIVATE(instance); + + priv->sound_files = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_free); +} + +static void +purple_sound_theme_finalize (GObject *obj) +{ + PurpleSoundThemePrivate *priv; + + priv = PURPLE_SOUND_THEME_GET_PRIVATE(obj); + + g_hash_table_destroy(priv->sound_files); + + parent_class->finalize (obj); +} + +static void +purple_sound_theme_class_init (PurpleSoundThemeClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_peek_parent (klass); + + obj_class->finalize = purple_sound_theme_finalize; +} + +GType +purple_sound_theme_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PurpleSoundThemeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)purple_sound_theme_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PurpleSoundTheme), + 0, /* n_preallocs */ + purple_sound_theme_init, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static (PURPLE_TYPE_THEME, + "PurpleSoundTheme", + &info, 0); + } + return type; +} + + +/***************************************************************************** + * Public API functions + *****************************************************************************/ + +const gchar * +purple_sound_theme_get_file(PurpleSoundTheme *theme, + const gchar *event) +{ + PurpleSoundThemePrivate *priv; + + g_return_val_if_fail(PURPLE_IS_SOUND_THEME(theme), NULL); + + priv = PURPLE_SOUND_THEME_GET_PRIVATE(theme); + + return g_hash_table_lookup(priv->sound_files, event); +} + +gchar * +purple_sound_theme_get_file_full(PurpleSoundTheme *theme, + const gchar *event) +{ + const gchar *filename; + + g_return_val_if_fail(PURPLE_IS_SOUND_THEME(theme), NULL); + + filename = purple_sound_theme_get_file(theme, event); + + g_return_val_if_fail(filename, NULL); + + return g_build_filename(purple_theme_get_dir(PURPLE_THEME(theme)), filename, NULL); +} + +void +purple_sound_theme_set_file(PurpleSoundTheme *theme, + const gchar *event, + const gchar *filename) +{ + PurpleSoundThemePrivate *priv; + g_return_if_fail(PURPLE_IS_SOUND_THEME(theme)); + + priv = PURPLE_SOUND_THEME_GET_PRIVATE(theme); + + if (filename != NULL)g_hash_table_replace(priv->sound_files, + g_strdup(event), + g_strdup(filename)); + else g_hash_table_remove(priv->sound_files, event); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/sound-theme.h Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,102 @@ +/** + * @file sound-theme.h Purple Sound Theme Abstact Class API + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PURPLE_SOUND_THEME_H_ +#define _PURPLE_SOUND_THEME_H_ + +#include <glib.h> +#include <glib-object.h> +#include "theme.h" +#include "sound.h" + +/** + * extends PurpleTheme (theme.h) + * A purple sound theme. + * This is an object for Purple to represent a sound theme. + * + * PurpleSoundTheme is a PurpleTheme Object. + */ +typedef struct _PurpleSoundTheme PurpleSoundTheme; +typedef struct _PurpleSoundThemeClass PurpleSoundThemeClass; + +#define PURPLE_TYPE_SOUND_THEME (purple_sound_theme_get_type ()) +#define PURPLE_SOUND_THEME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PURPLE_TYPE_SOUND_THEME, PurpleSoundTheme)) +#define PURPLE_SOUND_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_SOUND_THEME, PurpleSoundThemeClass)) +#define PURPLE_IS_SOUND_THEME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PURPLE_TYPE_SOUND_THEME)) +#define PURPLE_IS_SOUND_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_SOUND_THEME)) +#define PURPLE_SOUND_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_SOUND_THEME, PurpleSoundThemeClass)) + +struct _PurpleSoundTheme +{ + PurpleTheme parent; + gpointer priv; +}; + +struct _PurpleSoundThemeClass +{ + PurpleThemeClass parent_class; +}; + +/**************************************************************************/ +/** @name Purple Sound Theme API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType purple_sound_theme_get_type(void); + +/** + * Returns a copy of the filename for the sound event + * + * @param event the purple sound event to look up + * + * @returns the filename of the sound event + */ +const gchar *purple_sound_theme_get_file(PurpleSoundTheme *theme, + const gchar *event); +/** + * Returns a copy of the directory and filename for the sound event + * + * @param event the purple sound event to look up + * + * @returns the directory + '/' + filename of the sound event + */ +gchar *purple_sound_theme_get_file_full(PurpleSoundTheme *theme, + const gchar *event); +/** + * Sets the filename for a given sound event + * + * @param event the purple sound event to look up + * @param filename the name of the file to be used for the event + */ +void purple_sound_theme_set_file(PurpleSoundTheme *theme, + const gchar *event, + const gchar *filename); + +G_END_DECLS +#endif /* _PURPLE_SOUND_THEME_H_ */
--- a/libpurple/sound.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/sound.c Fri Oct 31 17:46:39 2008 +0000 @@ -25,6 +25,8 @@ #include "blist.h" #include "prefs.h" #include "sound.h" +#include "sound-theme-loader.h" +#include "theme-manager.h" static PurpleSoundUiOps *sound_ui_ops = NULL; @@ -134,6 +136,8 @@ purple_prefs_add_none("/purple/sound"); purple_prefs_add_int("/purple/sound/while_status", STATUS_AVAILABLE); memset(last_played, 0, sizeof(last_played)); + + purple_theme_manager_register_type(g_object_new(PURPLE_TYPE_SOUND_THEME_LOADER, "type", "sound", NULL)); } void
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/theme-loader.c Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,186 @@ +/* + * ThemeLoaders for LibPurple + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "theme-loader.h" + +#define PURPLE_THEME_LOADER_GET_PRIVATE(PurpleThemeLoader) \ + ((PurpleThemeLoaderPrivate *) ((PurpleThemeLoader)->priv)) + +void purple_theme_loader_set_type_string(PurpleThemeLoader *loader, const gchar *type); +/****************************************************************************** + * Structs + *****************************************************************************/ +typedef struct { + gchar *type; +} PurpleThemeLoaderPrivate; + +/****************************************************************************** + * Globals + *****************************************************************************/ + +static GObjectClass *parent_class = NULL; + +/****************************************************************************** + * Enums + *****************************************************************************/ + +enum { + PROP_ZERO = 0, + PROP_TYPE, +}; + +/****************************************************************************** + * GObject Stuff * + *****************************************************************************/ + +static void +purple_theme_loader_get_property(GObject *obj, guint param_id, GValue *value, + GParamSpec *psec) +{ + PurpleThemeLoader *theme_loader = PURPLE_THEME_LOADER(obj); + + switch(param_id) { + case PROP_TYPE: + g_value_set_string(value, purple_theme_loader_get_type_string(theme_loader)); + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec); + break; + } +} + +static void +purple_theme_loader_set_property(GObject *obj, guint param_id, const GValue *value, + GParamSpec *psec) +{ + PurpleThemeLoader *loader = PURPLE_THEME_LOADER(obj); + + switch(param_id) { + case PROP_TYPE: + purple_theme_loader_set_type_string(loader, g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec); + break; + } +} + +static void +purple_theme_loader_init(GTypeInstance *instance, + gpointer klass) +{ + PurpleThemeLoader *loader = PURPLE_THEME_LOADER(instance); + loader->priv = g_new0(PurpleThemeLoaderPrivate, 1); +} + +static void +purple_theme_loader_finalize(GObject *obj) +{ + PurpleThemeLoader *loader = PURPLE_THEME_LOADER(obj); + PurpleThemeLoaderPrivate *priv = PURPLE_THEME_LOADER_GET_PRIVATE(loader); + + g_free(priv->type); + + parent_class->finalize (obj); +} + +static void +purple_theme_loader_class_init (PurpleThemeLoaderClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + parent_class = g_type_class_peek_parent (klass); + + obj_class->get_property = purple_theme_loader_get_property; + obj_class->set_property = purple_theme_loader_set_property; + obj_class->finalize = purple_theme_loader_finalize; + + /* TYPE STRING (read only) */ + pspec = g_param_spec_string("type", "Type", + "The string represtenting the type of the theme", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property(obj_class, PROP_TYPE, pspec); +} + + +GType +purple_theme_loader_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PurpleThemeLoaderClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)purple_theme_loader_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PurpleThemeLoader), + 0, /* n_preallocs */ + purple_theme_loader_init, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static (G_TYPE_OBJECT, + "PurpleThemeLoader", + &info, G_TYPE_FLAG_ABSTRACT); + } + return type; +} + + +/***************************************************************************** + * Public API functions + *****************************************************************************/ + + +const gchar * +purple_theme_loader_get_type_string (PurpleThemeLoader *theme_loader) +{ + PurpleThemeLoaderPrivate *priv = NULL; + + g_return_val_if_fail(PURPLE_IS_THEME_LOADER(theme_loader), NULL); + + priv = PURPLE_THEME_LOADER_GET_PRIVATE(theme_loader); + return priv->type; +} + +/* < private > */ +void +purple_theme_loader_set_type_string(PurpleThemeLoader *loader, const gchar *type) +{ + PurpleThemeLoaderPrivate *priv; + + g_return_if_fail(PURPLE_IS_THEME_LOADER(loader)); + + priv = PURPLE_THEME_LOADER_GET_PRIVATE(loader); + + g_free(priv->type); + priv->type = g_strdup(type); +} + +PurpleTheme * +purple_theme_loader_build(PurpleThemeLoader *loader, const gchar *dir) +{ + return PURPLE_THEME_LOADER_GET_CLASS(loader)->purple_theme_loader_build(dir); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/theme-loader.h Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,92 @@ +/** + * @file theme-loader.h Purple Theme Loader Abstact Class API + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PURPLE_THEME_LOADER_H_ +#define _PURPLE_THEME_LOADER_H_ + +#include <glib.h> +#include <glib-object.h> +#include "theme.h" + +/** + * A purple theme loader. + * This is an abstract class for Purple to use with the Purple theme manager. + * The loader is responsible for building each type of theme + * + * PurpleThemeLoader is a GObject. + */ +typedef struct _PurpleThemeLoader PurpleThemeLoader; +typedef struct _PurpleThemeLoaderClass PurpleThemeLoaderClass; + +#define PURPLE_TYPE_THEME_LOADER (purple_theme_loader_get_type ()) +#define PURPLE_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PURPLE_TYPE_THEME_LOADER, PurpleThemeLoader)) +#define PURPLE_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_THEME_LOADER, PurpleThemeLoaderClass)) +#define PURPLE_IS_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PURPLE_TYPE_THEME_LOADER)) +#define PURPLE_IS_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_THEME_LOADER)) +#define PURPLE_THEME_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_THEME_LOADER, PurpleThemeLoaderClass)) + +struct _PurpleThemeLoader +{ + GObject parent; + gpointer priv; +}; + +struct _PurpleThemeLoaderClass +{ + GObjectClass parent_class; + PurpleTheme *((*purple_theme_loader_build)(const gchar*)); +}; + +/**************************************************************************/ +/** @name Purple Theme-Loader API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType purple_theme_loader_get_type(void); + +/** + * Returns the string represtenting the type of the theme loader + * + * @param self the theme loader + * + * @returns the string represting this type + */ +const gchar *purple_theme_loader_get_type_string(PurpleThemeLoader *self); + +/** + * Creates a new PurpleTheme + * + * @param dir the directory containing the theme + * + * @returns PurpleTheme containing the information from the directory + */ +PurpleTheme *purple_theme_loader_build(PurpleThemeLoader *loader, const gchar *dir); + +G_END_DECLS +#endif /* _PURPLE_THEME_LOADER_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/theme-manager.c Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,302 @@ +/* + * Themes for LibPurple + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include <string.h> + +#include "theme-manager.h" +#include "util.h" + +/****************************************************************************** + * Globals + *****************************************************************************/ + +static GHashTable *theme_table = NULL; + +/***************************************************************************** + * GObject Stuff + ****************************************************************************/ + +GType +purple_theme_manager_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PurpleThemeManagerClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PurpleThemeManager), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* Value Table */ + }; + type = g_type_register_static(G_TYPE_OBJECT, + "PurpleThemeManager", + &info, 0); + } + return type; +} + +/****************************************************************************** + * Helpers + *****************************************************************************/ +/* makes a key of <type> + '/' + <name> */ +static gchar * +purple_theme_manager_make_key(const gchar *name, const gchar *type) +{ + g_return_val_if_fail(name && strlen(name), NULL); + g_return_val_if_fail(type && strlen(type), NULL); + return g_strconcat(type, "/", name, NULL); +} + +/* returns TRUE if theme is of type "user_data" */ +static gboolean +purple_theme_manager_is_theme_type(gchar *key, + gpointer value, + gchar *user_data) +{ + return g_str_has_prefix(key, g_strconcat(user_data, "/", NULL)); +} + +static gboolean +purple_theme_manager_is_theme(gchar *key, + gpointer value, + gchar *user_data) +{ + return PURPLE_IS_THEME(value); +} + +static void +purple_theme_manager_function_wrapper(gchar *key, + gpointer value, + PTFunc user_data) +{ + if (PURPLE_IS_THEME(value)) + (* user_data)(value); +} + +static void +purple_theme_manager_build_dir(const gchar *root) +{ + + gchar *purple_dir, *theme_dir; + const gchar *name = NULL, *type = NULL; + GDir *rdir, *tdir; + PurpleThemeLoader *loader; + + rdir = g_dir_open(root, 0, NULL); + + g_return_if_fail(rdir); + + /* Parses directory by root/name/purple/type */ + while((name = g_dir_read_name(rdir))) { + purple_dir = g_build_filename(root, name, "purple", NULL); + tdir = g_dir_open(purple_dir, 0, NULL); + + if(!tdir) { + g_free(purple_dir); + + continue; + } + + while((type = g_dir_read_name(tdir))) { + if((loader = g_hash_table_lookup(theme_table, type))) { + PurpleTheme *theme = NULL; + + theme_dir = g_build_filename(purple_dir, type, NULL); + + theme = purple_theme_loader_build(loader, theme_dir); + + if(PURPLE_IS_THEME(theme)) + purple_theme_manager_add_theme(theme); + } + } + + g_dir_close(tdir); + g_free(purple_dir); + } + + g_dir_close(rdir); +} + +/***************************************************************************** + * Public API functions * + *****************************************************************************/ + +void +purple_theme_manager_init(void) +{ + theme_table = g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + g_object_unref); +} + +void +purple_theme_manager_refresh() +{ + gchar *path = NULL; + const gchar *xdg = NULL; + gint i = 0; + + g_hash_table_foreach_remove(theme_table, + (GHRFunc) purple_theme_manager_is_theme, + NULL); + + /* Add themes from ~/.purple */ + path = g_build_filename(purple_user_dir(), "themes", NULL); + purple_theme_manager_build_dir(path); + g_free(path); + + /* look for XDG_DATA_HOME. If we don't have it use ~/.local, and add it */ + if((xdg = g_getenv("XDG_DATA_HOME")) != NULL) + path = g_build_filename(xdg, "themes", NULL); + else + path = g_build_filename(purple_home_dir(), ".local", "themes", NULL); + + purple_theme_manager_build_dir(path); + g_free(path); + + /* now dig through XDG_DATA_DIRS and add those too */ + xdg = g_getenv("XDG_DATA_DIRS"); + if(xdg) { + gchar **xdg_dirs = g_strsplit(xdg, G_SEARCHPATH_SEPARATOR_S, 0); + + for(i = 0; xdg_dirs[i]; i++) { + path = g_build_filename(xdg_dirs[i], "themes", NULL); + purple_theme_manager_build_dir(path); + g_free(path); + } + + g_strfreev(xdg_dirs); + } +} + +void +purple_theme_manager_uninit() +{ + g_hash_table_destroy(theme_table); +} + + +void +purple_theme_manager_register_type(PurpleThemeLoader *loader) +{ + gchar *type; + + g_return_if_fail(PURPLE_IS_THEME_LOADER(loader)); + + type = g_strdup(purple_theme_loader_get_type_string(loader)); + g_return_if_fail(type); + + /* if something is already there do nothing */ + if (! g_hash_table_lookup(theme_table, type)) + g_hash_table_insert(theme_table, type, loader); +} + +void +purple_theme_manager_unregister_type(PurpleThemeLoader *loader) +{ + const gchar *type; + + g_return_if_fail(PURPLE_IS_THEME_LOADER(loader)); + + type = purple_theme_loader_get_type_string(loader); + g_return_if_fail(type); + + if (g_hash_table_lookup(theme_table, type) == loader){ + + g_hash_table_remove(theme_table, type); + + g_hash_table_foreach_remove(theme_table, + (GHRFunc)purple_theme_manager_is_theme_type, + (gpointer)type); + }/* only free if given registered loader */ +} + +PurpleTheme * +purple_theme_manager_find_theme(const gchar *name, + const gchar *type) +{ + gchar *key; + PurpleTheme *theme; + + key = purple_theme_manager_make_key(name, type); + + g_return_val_if_fail(key, NULL); + + theme = g_hash_table_lookup(theme_table, key); + + g_free(key); + + return theme; +} + + +void +purple_theme_manager_add_theme(PurpleTheme *theme) +{ + gchar *key; + + g_return_if_fail(PURPLE_IS_THEME(theme)); + + key = purple_theme_manager_make_key(purple_theme_get_name(theme), + purple_theme_get_type_string(theme)); + + g_return_if_fail(key); + + /* if something is already there do nothing */ + if (g_hash_table_lookup(theme_table, key) == NULL) + g_hash_table_insert(theme_table, key, theme); +} + +void +purple_theme_manager_remove_theme(PurpleTheme *theme) +{ + gchar *key; + + g_return_if_fail(PURPLE_IS_THEME(theme)); + + key = purple_theme_manager_make_key(purple_theme_get_name(theme), + purple_theme_get_type_string(theme)); + + g_return_if_fail(key); + + g_hash_table_remove(theme_table, key); + + g_free(key); +} + +void +purple_theme_manager_for_each_theme(PTFunc func) +{ + g_return_if_fail(func); + + g_hash_table_foreach(theme_table, + (GHFunc) purple_theme_manager_function_wrapper, + func); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/theme-manager.h Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,125 @@ +/** + * @file thememanager.h Theme Manager API + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef __PURPLE_THEME_MANAGER_H__ +#define __PURPLE_THEME_MANAGER_H__ + +#include <glib-object.h> +#include <glib.h> +#include "theme.h" +#include "theme-loader.h" + +typedef void (*PTFunc) (PurpleTheme *); + +typedef struct _PurpleThemeManager PurpleThemeManager; +typedef struct _PurpleThemeManagerClass PurpleThemeManagerClass; + +#define PURPLE_TYPE_THEME_MANAGER (purple_theme_manager_get_type ()) +#define PURPLE_THEME_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_THEME_MANAGER, PurpleThemeManager)) +#define PURPLE_THEME_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_THEME_MANAGER, PurpleThemeManagerClass)) +#define PURPLE_IS_THEME_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PURPLE_TYPE_THEME_MANAGER)) +#define PURPLE_IS_THEME_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_THEME_MANAGER)) +#define PURPLE_GET_THEME_MANAGER_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_THEME_MANAGER, PurpleThemeManagerClass)) + +struct _PurpleThemeManager { + GObject parent; +}; + +struct _PurpleThemeManagerClass { + GObjectClass parent_class; +}; + +/**************************************************************************/ +/** @name Purple Theme Manager API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType purple_theme_manager_get_type (void); + +/** + * Initalizes the theme manager + */ +void purple_theme_manager_init (void); + +/** + * Uninitalizes the manager then frees all the themes an loaders it is responsible for + */ +void purple_theme_manager_uninit (void); + +/** + * Rebuilds all the themes in the theme manager + * (removes all current themes but keeps the added loaders) + */ +void purple_theme_manager_refresh(void); + +/** + * Finds the PurpleTheme object stored by the theme manager + * + * @param name the name of the PurpleTheme + * @param type the type of the PurpleTheme + * + * @returns The PurpleTheme or NULL if it wasn't found + */ +PurpleTheme *purple_theme_manager_find_theme(const gchar *name, const gchar *type); + +/** + * Adds a PurpleTheme to the theme manager, if the theme already exits it does nothing + * + * @param theme the PurpleTheme to add to the manager + */ +void purple_theme_manager_add_theme(PurpleTheme *theme); + +/** + * Removes a PurpleTheme from the theme manager, and frees the theme + * @param theme the PurpleTheme to remove from the manager + */ +void purple_theme_manager_remove_theme(PurpleTheme *theme); + +/** + * Addes a Loader to the theme manager so it knows how to build themes + * @param loader the PurpleThemeLoader to add + */ +void purple_theme_manager_register_type(PurpleThemeLoader *loader); + +/** + * Removes the loader and all themes of the same type from the loader + * @param loader the PurpleThemeLoader to be removed + */ +void purple_theme_manager_unregister_type(PurpleThemeLoader *loader); + +/** + * Calles the given function on each purple theme + * + * @param func the PTFunc to be applied to each theme + */ +void purple_theme_manager_for_each_theme(PTFunc func); + +G_END_DECLS +#endif /* __PURPLE_THEME_MANAGER_H__ */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/theme.c Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,407 @@ +/* + * Themes for LibPurple + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "theme.h" +#include "util.h" + +#include <string.h> + +#define PURPLE_THEME_GET_PRIVATE(PurpleTheme) \ + ((PurpleThemePrivate *) ((PurpleTheme)->priv)) + +void purple_theme_set_type_string(PurpleTheme *theme, const gchar *type); + +/****************************************************************************** + * Structs + *****************************************************************************/ +typedef struct { + gchar *name; + gchar *description; + gchar *author; + gchar *type; + gchar *dir; + gchar *img; +} PurpleThemePrivate; + +/****************************************************************************** + * Globals + *****************************************************************************/ + +static GObjectClass *parent_class = NULL; + +/****************************************************************************** + * Enums + *****************************************************************************/ + +enum { + PROP_ZERO = 0, + PROP_NAME, + PROP_DESCRIPTION, + PROP_AUTHOR, + PROP_TYPE, + PROP_DIR, + PROP_IMAGE +}; + +/****************************************************************************** + * GObject Stuff * + *****************************************************************************/ + +static void +purple_theme_get_property(GObject *obj, guint param_id, GValue *value, + GParamSpec *psec) +{ + PurpleTheme *theme = PURPLE_THEME(obj); + + switch(param_id) { + case PROP_NAME: + g_value_set_string(value, purple_theme_get_name(theme)); + break; + case PROP_DESCRIPTION: + g_value_set_string(value, purple_theme_get_description(theme)); + break; + case PROP_AUTHOR: + g_value_set_string(value, purple_theme_get_author(theme)); + break; + case PROP_TYPE: + g_value_set_string(value, purple_theme_get_type_string(theme)); + break; + case PROP_DIR: + g_value_set_string(value, purple_theme_get_dir(theme)); + break; + case PROP_IMAGE: + g_value_set_string(value, purple_theme_get_image(theme)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec); + break; + } +} + +static void +purple_theme_set_property(GObject *obj, guint param_id, const GValue *value, + GParamSpec *psec) +{ + PurpleTheme *theme = PURPLE_THEME(obj); + + switch(param_id) { + case PROP_NAME: + purple_theme_set_name(theme, g_value_get_string(value)); + break; + case PROP_DESCRIPTION: + purple_theme_set_description(theme, g_value_get_string(value)); + break; + case PROP_AUTHOR: + purple_theme_set_author(theme, g_value_get_string(value)); + break; + case PROP_TYPE: + purple_theme_set_type_string(theme, g_value_get_string(value)); + break; + case PROP_DIR: + purple_theme_set_dir(theme, g_value_get_string(value)); + break; + case PROP_IMAGE: + purple_theme_set_image(theme, g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec); + break; + } +} + +static void +purple_theme_init(GTypeInstance *instance, + gpointer klass) +{ + PurpleTheme *theme = PURPLE_THEME(instance); + theme->priv = g_new0(PurpleThemePrivate, 1); +} + +static void +purple_theme_finalize(GObject *obj) +{ + PurpleTheme *theme = PURPLE_THEME(obj); + PurpleThemePrivate *priv = PURPLE_THEME_GET_PRIVATE(theme); + + g_free(priv->name); + g_free(priv->description); + g_free(priv->author); + g_free(priv->type); + g_free(priv->dir); + g_free(priv->img); + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +purple_theme_class_init (PurpleThemeClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + parent_class = g_type_class_peek_parent(klass); + + obj_class->get_property = purple_theme_get_property; + obj_class->set_property = purple_theme_set_property; + obj_class->finalize = purple_theme_finalize; + + /* NAME */ + pspec = g_param_spec_string("name", "Name", + "The name of the theme", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_property(obj_class, PROP_NAME, pspec); + + /* DESCRIPTION */ + pspec = g_param_spec_string("description", "Description", + "The description of the theme", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_property(obj_class, PROP_DESCRIPTION, pspec); + + /* AUTHOR */ + pspec = g_param_spec_string("author", "Author", + "The author of the theme", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_property(obj_class, PROP_AUTHOR, pspec); + + /* TYPE STRING (read only) */ + pspec = g_param_spec_string("type", "Type", + "The string represtenting the type of the theme", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property(obj_class, PROP_TYPE, pspec); + + /* DIRECTORY */ + pspec = g_param_spec_string("directory", "Directory", + "The directory that contains the theme and all its files", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_property(obj_class, PROP_DIR, pspec); + + /* PREVIEW IMAGE */ + pspec = g_param_spec_string("image", "Image", + "A preview image of the theme", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_IMAGE, pspec); +} + + +GType +purple_theme_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PurpleThemeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)purple_theme_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PurpleTheme), + 0, /* n_preallocs */ + purple_theme_init, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static (G_TYPE_OBJECT, + "PurpleTheme", + &info, G_TYPE_FLAG_ABSTRACT); + } + return type; +} + +/****************************************************************************** + * Helper Functions + *****************************************************************************/ + +static gchar* +theme_clean_text(const gchar *text) +{ + gchar *clean_text = g_markup_escape_text(text, strlen(text)); + g_strdelimit(clean_text, "\n", ' '); + purple_str_strip_char(clean_text, '\r'); + return clean_text; +} + +/***************************************************************************** + * Public API functions + *****************************************************************************/ + +const gchar * +purple_theme_get_name(PurpleTheme *theme) +{ + PurpleThemePrivate *priv; + + g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL); + + priv = PURPLE_THEME_GET_PRIVATE(theme); + return priv->name; +} + +void +purple_theme_set_name(PurpleTheme *theme, const gchar *name) +{ + PurpleThemePrivate *priv; + + g_return_if_fail(PURPLE_IS_THEME(theme)); + + priv = PURPLE_THEME_GET_PRIVATE(theme); + + g_free(priv->name); + priv->name = theme_clean_text(name); +} + +const gchar * +purple_theme_get_description(PurpleTheme *theme) +{ + PurpleThemePrivate *priv; + + g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL); + + priv = PURPLE_THEME_GET_PRIVATE(theme); + return priv->description; +} + +void +purple_theme_set_description(PurpleTheme *theme, const gchar *description) +{ + PurpleThemePrivate *priv; + + g_return_if_fail(PURPLE_IS_THEME(theme)); + + priv = PURPLE_THEME_GET_PRIVATE(theme); + + g_free(priv->description); + priv->description = theme_clean_text(description); +} + +const gchar * +purple_theme_get_author(PurpleTheme *theme) +{ + PurpleThemePrivate *priv; + + g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL); + + priv = PURPLE_THEME_GET_PRIVATE(theme); + return priv->author; +} + +void +purple_theme_set_author(PurpleTheme *theme, const gchar *author) +{ + PurpleThemePrivate *priv; + + g_return_if_fail(PURPLE_IS_THEME(theme)); + + priv = PURPLE_THEME_GET_PRIVATE(theme); + + g_free(priv->author); + priv->author = theme_clean_text(author); +} + +const gchar * +purple_theme_get_type_string(PurpleTheme *theme) +{ + PurpleThemePrivate *priv; + + g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL); + + priv = PURPLE_THEME_GET_PRIVATE(theme); + return priv->type; +} + +/* < private > */ +void +purple_theme_set_type_string(PurpleTheme *theme, const gchar *type) +{ + PurpleThemePrivate *priv; + + g_return_if_fail(PURPLE_IS_THEME(theme)); + + priv = PURPLE_THEME_GET_PRIVATE(theme); + + g_free(priv->type); + priv->type = g_strdup(type); +} + +const gchar * +purple_theme_get_dir(PurpleTheme *theme) +{ + PurpleThemePrivate *priv; + + g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL); + + priv = PURPLE_THEME_GET_PRIVATE(theme); + return priv->dir; +} + +void +purple_theme_set_dir(PurpleTheme *theme, const gchar *dir) +{ + PurpleThemePrivate *priv; + + g_return_if_fail(PURPLE_IS_THEME(theme)); + + priv = PURPLE_THEME_GET_PRIVATE(theme); + + g_free(priv->dir); + priv->dir = g_strdup(dir); +} + +const gchar * +purple_theme_get_image(PurpleTheme *theme) +{ + PurpleThemePrivate *priv; + + g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL); + + priv = PURPLE_THEME_GET_PRIVATE(theme); + + return priv->img; +} + +gchar * +purple_theme_get_image_full(PurpleTheme *theme) +{ + const gchar *filename = purple_theme_get_image(theme); + + g_return_val_if_fail(filename, NULL); + + return g_build_filename(purple_theme_get_dir(PURPLE_THEME(theme)), filename, NULL); +} + +void +purple_theme_set_image(PurpleTheme *theme, const gchar *img) +{ + PurpleThemePrivate *priv; + + g_return_if_fail(PURPLE_IS_THEME(theme)); + + priv = PURPLE_THEME_GET_PRIVATE(theme); + + g_free(priv->img); + priv->img = g_strdup(img); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/theme.h Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,175 @@ +/** + * @file theme.h Purple Theme Abstact Class API + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PURPLE_THEME_H_ +#define _PURPLE_THEME_H_ + +#include <glib.h> +#include <glib-object.h> +#include "imgstore.h" + +/** + * A purple theme. + * This is an abstract class for Purple to use with the Purple theme manager. + * + * PurpleTheme is a GObject. + */ +typedef struct _PurpleTheme PurpleTheme; +typedef struct _PurpleThemeClass PurpleThemeClass; + +#define PURPLE_TYPE_THEME (purple_theme_get_type ()) +#define PURPLE_THEME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PURPLE_TYPE_THEME, PurpleTheme)) +#define PURPLE_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_THEME, PurpleThemeClass)) +#define PURPLE_IS_THEME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PURPLE_TYPE_THEME)) +#define PURPLE_IS_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_THEME)) +#define PURPLE_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_THEME, PurpleThemeClass)) + +struct _PurpleTheme +{ + GObject parent; + gpointer priv; +}; + +struct _PurpleThemeClass +{ + GObjectClass parent_class; +}; + +/**************************************************************************/ +/** @name Purple Theme API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType purple_theme_get_type(void); + +/** + * Returns the name of the PurpleTheme object + * + * @param theme the purple theme + * + * @return The string representating the name of the theme + */ +const gchar *purple_theme_get_name(PurpleTheme *theme); + +/** + * Sets the name of the PurpleTheme object + * + * @param theme the purple theme + * @param name the name of the PurpleTheme object + */ +void purple_theme_set_name(PurpleTheme *theme, const gchar *name); + +/** + * Returns the description of the PurpleTheme object + * + * @param theme the purple theme + * + * @return A short description of the theme + */ +const gchar *purple_theme_get_description(PurpleTheme *theme); + +/** + * Sets the description of the PurpleTheme object + * + * @param theme the purple theme + * @param description the description of the PurpleTheme object + */ +void purple_theme_set_description(PurpleTheme *theme, const gchar *description); + +/** + * Returns the author of the PurpleTheme object + * + * @param theme the purple theme + * + * @return The author of the theme + */ +const gchar *purple_theme_get_author(PurpleTheme *theme); + +/** + * Sets the author of the PurpleTheme object + * + * @param theme the purple theme + * @param author the author of the PurpleTheme object + */ +void purple_theme_set_author(PurpleTheme *theme, const gchar *author); + +/** + * Returns the type (string) of the PurpleTheme object + * + * @param theme the purple theme + * + * @return The string represtenting the type + */ +const gchar *purple_theme_get_type_string(PurpleTheme *theme); + +/** + * Returns the directory of the PurpleTheme object + * + * @param theme the purple theme + * + * @return The string represtenting the theme directory + */ +const gchar *purple_theme_get_dir(PurpleTheme *theme); + +/** + * Sets the directory of the PurpleTheme object + * + * @param theme the purple theme + * @param dir the directory of the PurpleTheme object + */ +void purple_theme_set_dir(PurpleTheme *theme, const gchar *dir); + +/** + * Returns the image preview of the PurpleTheme object + * + * @param theme the purple theme + * + * @return The image preview of the PurpleTheme object + */ +const gchar *purple_theme_get_image(PurpleTheme *theme); + +/** + * Returns the image preview and directory of the PurpleTheme object + * + * @param theme the purple theme + * + * @return The image preview of the PurpleTheme object + */ +gchar *purple_theme_get_image_full(PurpleTheme *theme); + +/** + * Sets the directory of the PurpleTheme object + * + * @param theme the purple theme + * @param img the image preview of the PurpleTheme object + */ +void purple_theme_set_image(PurpleTheme *theme, const gchar *img); + +G_END_DECLS +#endif /* _PURPLE_THEME_H_ */
--- a/libpurple/util.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/util.c Fri Oct 31 17:46:39 2008 +0000 @@ -2771,70 +2771,7 @@ xmlnode * purple_util_read_xml_from_file(const char *filename, const char *description) { - const char *user_dir = purple_user_dir(); - gchar *filename_full; - GError *error = NULL; - gchar *contents = NULL; - gsize length; - xmlnode *node = NULL; - - g_return_val_if_fail(user_dir != NULL, NULL); - - purple_debug_info("util", "Reading file %s from directory %s\n", - filename, user_dir); - - filename_full = g_build_filename(user_dir, filename, NULL); - - if (!g_file_test(filename_full, G_FILE_TEST_EXISTS)) - { - purple_debug_info("util", "File %s does not exist (this is not " - "necessarily an error)\n", filename_full); - g_free(filename_full); - return NULL; - } - - if (!g_file_get_contents(filename_full, &contents, &length, &error)) - { - purple_debug_error("util", "Error reading file %s: %s\n", - filename_full, error->message); - g_error_free(error); - } - - if ((contents != NULL) && (length > 0)) - { - node = xmlnode_from_str(contents, length); - - /* If we were unable to parse the file then save its contents to a backup file */ - if (node == NULL) - { - gchar *filename_temp; - - filename_temp = g_strdup_printf("%s~", filename); - purple_debug_error("util", "Error parsing file %s. Renaming old " - "file to %s\n", filename_full, filename_temp); - purple_util_write_data_to_file(filename_temp, contents, length); - g_free(filename_temp); - } - - g_free(contents); - } - - /* If we could not parse the file then show the user an error message */ - if (node == NULL) - { - gchar *title, *msg; - title = g_strdup_printf(_("Error Reading %s"), filename); - msg = g_strdup_printf(_("An error was encountered reading your " - "%s. They have not been loaded, and the old file " - "has been renamed to %s~."), description, filename_full); - purple_notify_error(NULL, NULL, title, msg); - g_free(title); - g_free(msg); - } - - g_free(filename_full); - - return node; + return xmlnode_from_file(purple_user_dir(), filename, description, "util"); } /*
--- a/libpurple/xmlnode.c Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/xmlnode.c Fri Oct 31 17:46:39 2008 +0000 @@ -728,6 +728,78 @@ return ret; } +xmlnode * +xmlnode_from_file(const char *dir,const char *filename, const char *description, const char *process) +{ + gchar *filename_full; + GError *error = NULL; + gchar *contents = NULL; + gsize length; + xmlnode *node = NULL; + + g_return_val_if_fail(dir != NULL, NULL); + + purple_debug_info(process, "Reading file %s from directory %s\n", + filename, dir); + + filename_full = g_build_filename(dir, filename, NULL); + + if (!g_file_test(filename_full, G_FILE_TEST_EXISTS)) + { + purple_debug_info(process, "File %s does not exist (this is not " + "necessarily an error)\n", filename_full); + g_free(filename_full); + return NULL; + } + + if (!g_file_get_contents(filename_full, &contents, &length, &error)) + { + purple_debug_error(process, "Error reading file %s: %s\n", + filename_full, error->message); + g_error_free(error); + } + + if ((contents != NULL) && (length > 0)) + { + node = xmlnode_from_str(contents, length); + + /* If we were unable to parse the file then save its contents to a backup file */ + if (node == NULL) + { + gchar *filename_temp, *filename_temp_full; + + filename_temp = g_strdup_printf("%s~", filename); + filename_temp_full = g_build_filename(dir, filename_temp, NULL); + + purple_debug_error("util", "Error parsing file %s. Renaming old " + "file to %s\n", filename_full, filename_temp); + purple_util_write_data_to_file_absolute(filename_temp_full, contents, length); + + g_free(filename_temp_full); + g_free(filename_temp); + } + + g_free(contents); + } + + /* If we could not parse the file then show the user an error message */ + if (node == NULL) + { + gchar *title, *msg; + title = g_strdup_printf(_("Error Reading %s"), filename); + msg = g_strdup_printf(_("An error was encountered reading your " + "%s. The file has not been loaded, and the old file " + "has been renamed to %s~."), description, filename_full); + purple_notify_error(NULL, NULL, title, msg); + g_free(title); + g_free(msg); + } + + g_free(filename_full); + + return node; +} + static void xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data) {
--- a/libpurple/xmlnode.h Fri Oct 31 04:03:44 2008 +0000 +++ b/libpurple/xmlnode.h Fri Oct 31 17:46:39 2008 +0000 @@ -26,6 +26,8 @@ #ifndef _PURPLE_XMLNODE_H_ #define _PURPLE_XMLNODE_H_ +#include <glib.h> + #ifdef __cplusplus extern "C" { #endif @@ -297,6 +299,20 @@ */ void xmlnode_free(xmlnode *node); +/** + * Creates a node from a XML File. Calling this on the + * root node of an XML document will parse the entire document + * into a tree of nodes, and return the xmlnode of the root. + * + * @param str The string of xml. + * @param description The description of the file being parsed + * @process The utility that is calling xmlnode_from_file + * + * @return The new node. + */ +xmlnode *xmlnode_from_file(const char *dir, const char *filename, + const char *description, const char *process); + #ifdef __cplusplus } #endif
--- a/pidgin/Makefile.am Fri Oct 31 04:03:44 2008 +0000 +++ b/pidgin/Makefile.am Fri Oct 31 17:46:39 2008 +0000 @@ -79,6 +79,8 @@ pidginstock.c \ gtkaccount.c \ gtkblist.c \ + gtkblist-theme-loader.c \ + gtkblist-theme.c \ gtkcelllayout.c \ gtkcellrendererexpander.c \ gtkcellrendererprogress.c \ @@ -95,6 +97,8 @@ gtkeventloop.c \ gtkexpander.c \ gtkft.c \ + gtkicon-theme.c \ + gtkicon-theme-loader.c \ gtkidle.c \ gtkimhtml.c \ gtkimhtmltoolbar.c \ @@ -117,6 +121,7 @@ gtksourceiter.c \ gtksourceundomanager.c \ gtksourceview-marshal.c \ + gtkstatus-icon-theme.c \ gtkstatusbox.c \ gtkthemes.c \ gtkutils.c \ @@ -128,6 +133,8 @@ eggtrayicon.h \ gtkaccount.h \ gtkblist.h \ + gtkblist-theme-loader.h \ + gtkblist-theme.h \ gtkcelllayout.h \ gtkcellrendererexpander.h \ gtkcellrendererprogress.h \ @@ -147,6 +154,8 @@ gtkeventloop.h \ gtkexpander.h \ gtkft.h \ + gtkicon-theme.h \ + gtkicon-theme-loader.h \ gtkidle.h \ gtkgaim-compat.h \ gtkimhtml.h \ @@ -170,6 +179,7 @@ gtksourceiter.h \ gtksourceundomanager.h \ gtksourceview-marshal.h \ + gtkstatus-icon-theme.h \ gtkstatusbox.h \ pidginstock.h \ gtkthemes.h \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkblist-theme-loader.c Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,274 @@ +/* + * GTKBlistThemeLoader for Pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include <stdlib.h> + +#include "xmlnode.h" + +#include "gtkblist-theme-loader.h" +#include "gtkblist-theme.h" + +/****************************************************************************** + * Globals + *****************************************************************************/ +#define DEFAULT_TEXT_COLOR "black" +/***************************************************************************** + * Buddy List Theme Builder + *****************************************************************************/ + +static PurpleTheme * +pidgin_blist_loader_build(const gchar *dir) +{ + xmlnode *root_node = NULL, *sub_node, *sub_sub_node; + gchar *filename_full, *data; + const gchar *temp; + gboolean sucess = TRUE; + GdkColor *bgcolor, *expanded_bgcolor, *collapsed_bgcolor, *contact_color; + GdkColor color; + FontColorPair *expanded, *collapsed, *contact, *online, *away, *offline, *idle, *message, *status; + PidginBlistLayout *layout; + PidginBlistTheme *theme; + + /* Find the theme file */ + g_return_val_if_fail(dir != NULL, NULL); + filename_full = g_build_filename(dir, "theme.xml", NULL); + + if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR)) + root_node = xmlnode_from_file(dir, "theme.xml", "buddy list themes", "blist-loader"); + + g_free(filename_full); + g_return_val_if_fail(root_node != NULL, NULL); + + sub_node = xmlnode_get_child(root_node, "description"); + data = xmlnode_get_data(sub_node); + + /* init all structs and colors */ + bgcolor = g_new0(GdkColor, 1); + expanded_bgcolor = g_new0(GdkColor, 1); + collapsed_bgcolor = g_new0(GdkColor, 1); + + layout = g_new0(PidginBlistLayout, 1); + + contact_color = g_new0(GdkColor, 1); + + expanded = g_new0(FontColorPair, 1); + collapsed = g_new0(FontColorPair, 1); + contact = g_new0(FontColorPair, 1); + online = g_new0(FontColorPair, 1); + away = g_new0(FontColorPair, 1); + offline = g_new0(FontColorPair, 1); + idle = g_new0(FontColorPair, 1); + message = g_new0(FontColorPair, 1); + status = g_new0(FontColorPair, 1); + + /* <blist> */ + if ((sucess = (sub_node = xmlnode_get_child(root_node, "blist")) != NULL)) { + if ((temp = xmlnode_get_attrib(sub_node, "color")) != NULL && gdk_color_parse(temp, bgcolor)) + gdk_colormap_alloc_color(gdk_colormap_get_system(), bgcolor, FALSE, TRUE); + else { + g_free(bgcolor); + bgcolor = NULL; + } + } + + /* <groups> */ + if ((sucess = sucess && (sub_node = xmlnode_get_child(root_node, "groups")) != NULL + && (sub_sub_node = xmlnode_get_child(sub_node, "expanded")) != NULL)) { + + expanded->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + + if ((temp = xmlnode_get_attrib(sub_sub_node, "text_color")) != NULL && gdk_color_parse(temp, &color)) + expanded->color = g_strdup(temp); + else expanded->color = g_strdup(DEFAULT_TEXT_COLOR); + + + if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, expanded_bgcolor)) + gdk_colormap_alloc_color(gdk_colormap_get_system(), expanded_bgcolor, FALSE, TRUE); + else { + g_free(expanded_bgcolor); + expanded_bgcolor = NULL; + } + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "collapsed")) != NULL)) { + + collapsed->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + + if((temp = xmlnode_get_attrib(sub_sub_node, "text_color")) != NULL && gdk_color_parse(temp, &color)) + collapsed->color = g_strdup(temp); + else collapsed->color = g_strdup(DEFAULT_TEXT_COLOR); + + if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, collapsed_bgcolor)) + gdk_colormap_alloc_color(gdk_colormap_get_system(), collapsed_bgcolor, FALSE, TRUE); + else { + g_free(collapsed_bgcolor); + collapsed_bgcolor = NULL; + } + } + + /* <buddys> */ + if ((sucess = sucess && (sub_node = xmlnode_get_child(root_node, "buddys")) != NULL && + (sub_sub_node = xmlnode_get_child(sub_node, "placement")) != NULL)) { + + layout->status_icon = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) : 0; + layout->text = (temp = xmlnode_get_attrib(sub_sub_node, "name")) != NULL ? atoi(temp) : 1; + layout->emblem = (temp = xmlnode_get_attrib(sub_sub_node, "emblem")) != NULL ? atoi(temp) : 2; + layout->protocol_icon = (temp = xmlnode_get_attrib(sub_sub_node, "protocol_icon")) != NULL ? atoi(temp) : 3; + layout->buddy_icon = (temp = xmlnode_get_attrib(sub_sub_node, "buddy_icon")) != NULL ? atoi(temp) : 4; + layout->show_status = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) != 0 : 1; + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "background")) != NULL)) { + if(gdk_color_parse(xmlnode_get_attrib(sub_sub_node, "color"), contact_color)) + gdk_colormap_alloc_color(gdk_colormap_get_system(), contact_color, FALSE, TRUE); + else { + g_free(contact_color); + contact_color = NULL; + } + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "contact_text")) != NULL)) { + contact->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + contact->color = g_strdup(temp); + else contact->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "online_text")) != NULL)) { + online->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + online->color = g_strdup(temp); + else online->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "away_text")) != NULL)) { + away->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + away->color = g_strdup(temp); + else away->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "offline_text")) != NULL)) { + offline->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + online->color = g_strdup(temp); + else online->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "idle_text")) != NULL)) { + idle->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + idle->color = g_strdup(temp); + else online->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "message_text")) != NULL)) { + message->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + message->color = g_strdup(temp); + else message->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + if ((sucess = sucess && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "status_text")) != NULL)) { + status->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); + if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) + status->color = g_strdup(temp); + else status->color = g_strdup(DEFAULT_TEXT_COLOR); + } + + /* name is required for theme manager */ + sucess = sucess && xmlnode_get_attrib(root_node, "name") != NULL; + + /* the new theme */ + theme = g_object_new(PIDGIN_TYPE_BLIST_THEME, + "type", "blist", + "name", xmlnode_get_attrib(root_node, "name"), + "author", xmlnode_get_attrib(root_node, "author"), + "image", xmlnode_get_attrib(root_node, "image"), + "directory", dir, + "description", data, + "background-color", bgcolor, + "layout", layout, + "expanded-color", expanded_bgcolor, + "expanded-text", expanded, + "collapsed-color", collapsed_bgcolor, + "collapsed-text", collapsed, + "contact-color", contact_color, + "contact", contact, + "online", online, + "away", away, + "offline", offline, + "idle", idle, + "message", message, + "status", status, NULL); + + xmlnode_free(root_node); + g_free(data); + + /* malformed xml file - also frees all partial data*/ + if (!sucess) { + g_object_unref(theme); + theme = NULL; + } + + return PURPLE_THEME(theme); +} + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +pidgin_blist_theme_loader_class_init(PidginBlistThemeLoaderClass *klass) +{ + PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass); + + loader_klass->purple_theme_loader_build = pidgin_blist_loader_build; +} + + +GType +pidgin_blist_theme_loader_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PidginBlistThemeLoaderClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_blist_theme_loader_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PidginBlistThemeLoader), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static(PURPLE_TYPE_THEME_LOADER, + "PidginBlistThemeLoader", + &info, 0); + } + return type; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkblist-theme-loader.h Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,71 @@ +/** + * @file gtkblist-loader.h Pidgin Buddy List Theme Loader Class API + */ + +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PIDGIN_BLIST_THEME_LOADER_H_ +#define _PIDGIN_BLIST_THEME_LOADER_H_ + +#include <glib.h> +#include <glib-object.h> +#include "theme-loader.h" + +/** + * A pidgin buddy list theme loader. extends PurpleThemeLoader (theme-loader.h) + * This is a class designed to build sound themes + * + * PidginBlistThemeLoader is a GObject. + */ +typedef struct _PidginBlistThemeLoader PidginBlistThemeLoader; +typedef struct _PidginBlistThemeLoaderClass PidginBlistThemeLoaderClass; + +#define PIDGIN_TYPE_BLIST_THEME_LOADER (pidgin_blist_theme_loader_get_type ()) +#define PIDGIN_BLIST_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_BLIST_THEME_LOADER, PidginBlistThemeLoader)) +#define PIDGIN_BLIST_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_BLIST_THEME_LOADER, PidginBlistThemeLoaderClass)) +#define PIDGIN_IS_BLIST_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_BLIST_THEME_LOADER)) +#define PIDGIN_IS_BLIST_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_BLIST_THEME_LOADER)) +#define PIDGIN_BLIST_THEME_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_BLIST_THEME_LOADER, PidginBlistThemeLoaderClass)) + +struct _PidginBlistThemeLoader +{ + PurpleThemeLoader parent; +}; + +struct _PidginBlistThemeLoaderClass +{ + PurpleThemeLoaderClass parent_class; +}; + +/**************************************************************************/ +/** @name Buddy List Theme-Loader API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_blist_theme_loader_get_type(void); + +G_END_DECLS +#endif /* _PIDGIN_BLIST_THEME_LOADER_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkblist-theme.c Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,743 @@ +/* + * Buddy List Themes for Pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "gtkblist-theme.h" + +#define PIDGIN_BLIST_THEME_GET_PRIVATE(Gobject) \ + ((PidginBlistThemePrivate *) ((PIDGIN_BLIST_THEME(Gobject))->priv)) + +/****************************************************************************** + * Structs + *****************************************************************************/ +typedef struct { + /* Buddy list */ + gdouble opacity; + GdkColor *bgcolor; + PidginBlistLayout *layout; + + /* groups */ + GdkColor *expanded_color; + FontColorPair *expanded; + + GdkColor *collapsed_color; + FontColorPair *collapsed; + + /* buddy */ + GdkColor *contact_color; + + FontColorPair *contact; + + FontColorPair *online; + FontColorPair *away; + FontColorPair *offline; + FontColorPair *idle; + FontColorPair *message; + + FontColorPair *status; + +} PidginBlistThemePrivate; + +/****************************************************************************** + * Globals + *****************************************************************************/ + +static GObjectClass *parent_class = NULL; + +/****************************************************************************** + * Enums + *****************************************************************************/ +enum { + PROP_ZERO = 0, + PROP_BACKGROUND_COLOR, + PROP_OPACITY, + PROP_LAYOUT, + PROP_EXPANDED_COLOR, + PROP_EXPANDED_TEXT, + PROP_COLLAPSED_COLOR, + PROP_COLLAPSED_TEXT, + PROP_CONTACT_COLOR, + PROP_CONTACT, + PROP_ONLINE, + PROP_AWAY, + PROP_OFFLINE, + PROP_IDLE, + PROP_MESSAGE, + PROP_STATUS, +}; + +/****************************************************************************** + * Helpers + *****************************************************************************/ + +void +free_font_and_color(FontColorPair *pair) +{ + if(pair != NULL){ + if (pair->font) + g_free(pair->font); + if (pair->color) + g_free(pair->color); + g_free(pair); + } +} + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +pidgin_blist_theme_init(GTypeInstance *instance, + gpointer klass) +{ + (PIDGIN_BLIST_THEME(instance))->priv = g_new0(PidginBlistThemePrivate, 1); +} + +static void +pidgin_blist_theme_get_property(GObject *obj, guint param_id, GValue *value, + GParamSpec *psec) +{ + PidginBlistTheme *theme = PIDGIN_BLIST_THEME(obj); + + switch(param_id) { + case PROP_BACKGROUND_COLOR: + g_value_set_pointer(value, pidgin_blist_theme_get_background_color(theme)); + break; + case PROP_OPACITY: + g_value_set_double(value, pidgin_blist_theme_get_opacity(theme)); + break; + case PROP_LAYOUT: + g_value_set_pointer(value, pidgin_blist_theme_get_layout(theme)); + break; + case PROP_EXPANDED_COLOR: + g_value_set_pointer(value, pidgin_blist_theme_get_expanded_background_color(theme)); + break; + case PROP_EXPANDED_TEXT: + g_value_set_pointer(value, pidgin_blist_theme_get_expanded_text_info(theme)); + break; + case PROP_COLLAPSED_COLOR: + g_value_set_pointer(value, pidgin_blist_theme_get_collapsed_background_color(theme)); + break; + case PROP_COLLAPSED_TEXT: + g_value_set_pointer(value, pidgin_blist_theme_get_collapsed_text_info(theme)); + break; + case PROP_CONTACT_COLOR: + g_value_set_pointer(value, pidgin_blist_theme_get_contact_color(theme)); + break; + case PROP_CONTACT: + g_value_set_pointer(value, pidgin_blist_theme_get_contact_text_info(theme)); + break; + case PROP_ONLINE: + g_value_set_pointer(value, pidgin_blist_theme_get_online_text_info(theme)); + break; + case PROP_AWAY: + g_value_set_pointer(value, pidgin_blist_theme_get_away_text_info(theme)); + break; + case PROP_OFFLINE: + g_value_set_pointer(value, pidgin_blist_theme_get_offline_text_info(theme)); + break; + case PROP_IDLE: + g_value_set_pointer(value, pidgin_blist_theme_get_idle_text_info(theme)); + break; + case PROP_MESSAGE: + g_value_set_pointer(value, pidgin_blist_theme_get_unread_message_text_info(theme)); + break; + case PROP_STATUS: + g_value_set_pointer(value, pidgin_blist_theme_get_status_text_info(theme)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec); + break; + } +} + +static void +pidgin_blist_theme_set_property(GObject *obj, guint param_id, const GValue *value, + GParamSpec *psec) +{ + PidginBlistTheme *theme = PIDGIN_BLIST_THEME(obj); + + switch(param_id) { + case PROP_BACKGROUND_COLOR: + pidgin_blist_theme_set_background_color(theme, g_value_get_pointer(value)); + break; + case PROP_OPACITY: + pidgin_blist_theme_set_opacity(theme, g_value_get_double(value)); + break; + case PROP_LAYOUT: + pidgin_blist_theme_set_layout(theme, g_value_get_pointer(value)); + break; + case PROP_EXPANDED_COLOR: + pidgin_blist_theme_set_expanded_background_color(theme, g_value_get_pointer(value)); + break; + case PROP_EXPANDED_TEXT: + pidgin_blist_theme_set_expanded_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_COLLAPSED_COLOR: + pidgin_blist_theme_set_collapsed_background_color(theme, g_value_get_pointer(value)); + break; + case PROP_COLLAPSED_TEXT: + pidgin_blist_theme_set_collapsed_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_CONTACT_COLOR: + pidgin_blist_theme_set_contact_color(theme, g_value_get_pointer(value)); + break; + case PROP_CONTACT: + pidgin_blist_theme_set_contact_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_ONLINE: + pidgin_blist_theme_set_online_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_AWAY: + pidgin_blist_theme_set_away_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_OFFLINE: + pidgin_blist_theme_set_offline_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_IDLE: + pidgin_blist_theme_set_idle_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_MESSAGE: + pidgin_blist_theme_set_unread_message_text_info(theme, g_value_get_pointer(value)); + break; + case PROP_STATUS: + pidgin_blist_theme_set_status_text_info(theme, g_value_get_pointer(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec); + break; + } +} +static void +pidgin_blist_theme_finalize (GObject *obj) +{ + PidginBlistThemePrivate *priv; + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(obj); + + /* Buddy List */ + g_free(priv->layout); + + /* Group */ + free_font_and_color(priv->expanded); + free_font_and_color(priv->collapsed); + + /* Buddy */ + free_font_and_color(priv->contact); + free_font_and_color(priv->online); + free_font_and_color(priv->away); + free_font_and_color(priv->offline); + free_font_and_color(priv->message); + free_font_and_color(priv->status); + + g_free(priv); + + parent_class->finalize (obj); +} + +static void +pidgin_blist_theme_class_init (PidginBlistThemeClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + parent_class = g_type_class_peek_parent (klass); + + obj_class->get_property = pidgin_blist_theme_get_property; + obj_class->set_property = pidgin_blist_theme_set_property; + obj_class->finalize = pidgin_blist_theme_finalize; + + /* Buddy List */ + pspec = g_param_spec_pointer("background-color", "Background Color", + "The background color for the buddy list", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_BACKGROUND_COLOR, pspec); + + pspec = g_param_spec_pointer("layout", "Layout", + "The layout of icons, name, and status of the blist", + G_PARAM_READWRITE); + + g_object_class_install_property(obj_class, PROP_LAYOUT, pspec); + + /* Group */ + pspec = g_param_spec_pointer("expanded-color", "Expanded Background Color", + "The background color of an expanded group", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_EXPANDED_COLOR, pspec); + + pspec = g_param_spec_pointer("expanded-text", "Expanded Text", + "The text information for when a group is expanded", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_EXPANDED_TEXT, pspec); + + pspec = g_param_spec_pointer("collapsed-color", "Collapsed Background Color", + "The background color of a collapsed group", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_COLLAPSED_COLOR, pspec); + + pspec = g_param_spec_pointer("collapsed-text", "Collapsed Text", + "The text information for when a group is collapsed", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_COLLAPSED_TEXT, pspec); + + /* Buddy */ + pspec = g_param_spec_pointer("contact-color", "Contact/Chat Background Color", + "The background color of a contact or chat", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_CONTACT_COLOR, pspec); + + pspec = g_param_spec_pointer("contact", "Contact Text", + "The text information for when a contact is expanded", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_CONTACT, pspec); + + pspec = g_param_spec_pointer("online", "On-line Text", + "The text information for when a buddy is online", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_ONLINE, pspec); + + pspec = g_param_spec_pointer("away", "Away Text", + "The text information for when a buddy is away", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_AWAY, pspec); + + pspec = g_param_spec_pointer("offline", "Off-line Text", + "The text information for when a buddy is off-line", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_OFFLINE, pspec); + + pspec = g_param_spec_pointer("idle", "Idle Text", + "The text information for when a buddy is idle", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_IDLE, pspec); + + pspec = g_param_spec_pointer("message", "Message Text", + "The text information for when a buddy is has an unread message", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_MESSAGE, pspec); + + pspec = g_param_spec_pointer("status", "Status Text", + "The text information for a buddy's status", + G_PARAM_READWRITE); + g_object_class_install_property(obj_class, PROP_STATUS, pspec); +} + +GType +pidgin_blist_theme_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static GTypeInfo info = { + sizeof (PidginBlistThemeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_blist_theme_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PidginBlistTheme), + 0, /* n_preallocs */ + pidgin_blist_theme_init, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static (PURPLE_TYPE_THEME, + "PidginBlistTheme", + &info, 0); + } + return type; +} + + +/***************************************************************************** + * Public API functions + *****************************************************************************/ + +/* get methods */ +GdkColor * +pidgin_blist_theme_get_background_color(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->bgcolor; +} + +gdouble +pidgin_blist_theme_get_opacity(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), 1.0); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->opacity; +} + +PidginBlistLayout * +pidgin_blist_theme_get_layout(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->layout; +} + +GdkColor * +pidgin_blist_theme_get_expanded_background_color(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->expanded_color; +} + +FontColorPair * +pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->expanded; +} + +GdkColor * +pidgin_blist_theme_get_collapsed_background_color(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->collapsed_color; +} + +FontColorPair * +pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->collapsed; +} + +GdkColor * +pidgin_blist_theme_get_contact_color(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->contact_color; +} + +FontColorPair * +pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->contact; +} + +FontColorPair * +pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->online; +} + +FontColorPair * +pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->away; +} + +FontColorPair * +pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->offline; +} + +FontColorPair * +pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->idle; +} + +FontColorPair * +pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->message; +} + +FontColorPair * +pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme) +{ + PidginBlistThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + return priv->status; +} + +/* Set Methods */ +void +pidgin_blist_theme_set_background_color(PidginBlistTheme *theme, GdkColor *color) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + priv->bgcolor = color; +} + +void +pidgin_blist_theme_set_opacity(PidginBlistTheme *theme, gdouble opacity) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme) || opacity < 0.0 || opacity > 1.0); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + priv->opacity = opacity; +} + +void +pidgin_blist_theme_set_layout(PidginBlistTheme *theme, PidginBlistLayout *layout) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + g_free(priv->layout); + priv->layout = layout; +} + +void +pidgin_blist_theme_set_expanded_background_color(PidginBlistTheme *theme, GdkColor *color) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + priv->expanded_color = color; +} + +void +pidgin_blist_theme_set_expanded_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->expanded); + priv->expanded = pair; +} + +void +pidgin_blist_theme_set_collapsed_background_color(PidginBlistTheme *theme, GdkColor *color) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + priv->collapsed_color = color; +} + +void +pidgin_blist_theme_set_collapsed_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->collapsed); + priv->collapsed = pair; +} + +void +pidgin_blist_theme_set_contact_color(PidginBlistTheme *theme, GdkColor *color) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + priv->contact_color = color; +} + +void +pidgin_blist_theme_set_contact_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->contact); + priv->contact = pair; +} + +void +pidgin_blist_theme_set_online_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->online); + priv->online = pair; +} + +void +pidgin_blist_theme_set_away_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->away); + priv->away = pair; +} + +void +pidgin_blist_theme_set_offline_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->offline); + priv->offline = pair; +} + +void +pidgin_blist_theme_set_idle_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->idle); + priv->idle = pair; +} + +void +pidgin_blist_theme_set_unread_message_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->message); + priv->message = pair; +} + +void +pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, FontColorPair *pair) +{ + PidginBlistThemePrivate *priv; + + g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme)); + + priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); + + free_font_and_color(priv->status); + priv->status = pair; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkblist-theme.h Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,317 @@ +/** + * @file gtkblist-theme.h GTK+ Buddy List Theme API + */ + +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PIDGIN_BLIST_THEME_H_ +#define _PIDGIN_BLIST_THEME_H_ + +#include <glib.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +#include "theme.h" + +/** + * extends PurpleTheme (theme.h) + * A pidgin buddy list theme. + * This is an object for Purple to represent a buddy list theme. + * + * PidginBlistTheme is a PurpleTheme Object. + */ +typedef struct _PidginBlistTheme PidginBlistTheme; +typedef struct _PidginBlistThemeClass PidginBlistThemeClass; + +#define PIDGIN_TYPE_BLIST_THEME (pidgin_blist_theme_get_type ()) +#define PIDGIN_BLIST_THEME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_BLIST_THEME, PidginBlistTheme)) +#define PIDGIN_BLIST_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_BLIST_THEME, PidginBlistThemeClass)) +#define PIDGIN_IS_BLIST_THEME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_BLIST_THEME)) +#define PIDGIN_IS_BLIST_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_BLIST_THEME)) +#define PIDGIN_BLIST_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_BLIST_THEME, PidginBlistThemeClass)) + +struct _PidginBlistTheme +{ + PurpleTheme parent; + gpointer priv; +}; + +struct _PidginBlistThemeClass +{ + PurpleThemeClass parent_class; +}; + +typedef struct +{ + gchar *font; + gchar *color; + +} FontColorPair; + +typedef struct +{ + gint status_icon; + gint text; + gint emblem; + gint protocol_icon; + gint buddy_icon; + gboolean show_status; + +} PidginBlistLayout; + +/**************************************************************************/ +/** @name FontColorPair API */ +/**************************************************************************/ + +/** + * Frees a font and color pair + */ +void free_font_and_color(FontColorPair *pair); + +/**************************************************************************/ +/** @name Purple Buddy List Theme API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_blist_theme_get_type(void); + +/* get methods */ + +/** + * Returns the background color of the buddy list + * + * @returns a gdk color + */ + GdkColor *pidgin_blist_theme_get_background_color(PidginBlistTheme *theme); + +/** + * Returns the opacity of the buddy list window + * (0.0 or clear to 1.0 fully Opaque) + * + * @returns the opacity + */ +gdouble pidgin_blist_theme_get_opacity(PidginBlistTheme *theme); + +/** + * Returns the layout to be used with the buddy list + * + * @returns the buddy list layout + */ + PidginBlistLayout *pidgin_blist_theme_get_layout(PidginBlistTheme *theme); + +/** + * Returns the background color to be used with expanded groups + * + * @returns a gdk color + */ + GdkColor *pidgin_blist_theme_get_expanded_background_color(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used with expanded groups + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme); + +/** + * Returns the background color to be used with collapsed groups + * + * @returns a gdk color + */ + GdkColor *pidgin_blist_theme_get_collapsed_background_color(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used with collapsed groups + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme); + +/** + * Returns the colors to be used for contacts and chats + * + * @returns a gdkcolor for contacts and chats + */ + GdkColor *pidgin_blist_theme_get_contact_color(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for expanded contacts + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for online buddies + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for away and idle buddies + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for offline buddies + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for idle buddies + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for buddies with unread messages + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme); + +/** + * Returns the text font and color to be used for a buddy's status message + * + * @returns a font and color pair + */ + FontColorPair *pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme); + +/* Set Methods */ + +/** + * Sets the background color to be used for this buddy list theme + * + * @param color the new background color + */ +void pidgin_blist_theme_set_background_color(PidginBlistTheme *theme, GdkColor *color); + +/** + * Sets the opacity to be used for this buddy list theme + * + * @param opacity the new opacity setting + */ +void pidgin_blist_theme_set_opacity(PidginBlistTheme *theme, gdouble opacity); + +/** + * Sets the buddy list layout to be used for this buddy list theme + * + * @param layout the new layout + */ +void pidgin_blist_theme_set_layout(PidginBlistTheme *theme, PidginBlistLayout *layout); + +/** + * Sets the background color to be used for expanded groups + * + * @param color the new background color + */ +void pidgin_blist_theme_set_expanded_background_color(PidginBlistTheme *theme, GdkColor *color); + +/** + * Sets the text color and font to be used for expanded groups + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_expanded_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the background color to be used for collapsed groups + * + * @param color the new background color + */ +void pidgin_blist_theme_set_collapsed_background_color(PidginBlistTheme *theme, GdkColor *color); + +/** + * Sets the text color and font to be used for expanded groups + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_collapsed_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the background color to be used for contacts and chats + * + * @param color the color to use for contacts and chats + */ +void pidgin_blist_theme_set_contact_color(PidginBlistTheme *theme, GdkColor *color); + +/** + * Sets the text color and font to be used for expanded contacts + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_contact_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the text color and font to be used for online buddies + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_online_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the text color and font to be used for away and idle buddies + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_away_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the text color and font to be used for offline buddies + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_offline_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the text color and font to be used for idle buddies + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_idle_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the text color and font to be used for buddies with an unread message + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_unread_message_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +/** + * Sets the text color and font to be used for buddy status messages + * + * @param pair the new text font at color pair + */ +void pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, FontColorPair *pair); + +G_END_DECLS +#endif /* _PIDGIN_BLIST_THEME_H_ */
--- a/pidgin/gtkblist.c Fri Oct 31 04:03:44 2008 +0000 +++ b/pidgin/gtkblist.c Fri Oct 31 17:46:39 2008 +0000 @@ -38,6 +38,8 @@ #include "request.h" #include "signals.h" #include "pidginstock.h" +#include "theme-loader.h" +#include "theme-manager.h" #include "util.h" #include "gtkaccount.h" @@ -58,6 +60,8 @@ #include "gtkstatusbox.h" #include "gtkscrollbook.h" #include "gtksmiley.h" +#include "gtkblist-theme-loader.h" +#include "gtkblist-theme.h" #include "gtkutils.h" #include "pidgin/minidialog.h" #include "pidgin/pidgintooltip.h" @@ -121,6 +125,9 @@ * is showing; @c NULL otherwise. */ PidginMiniDialog *signed_on_elsewhere; + + PidginBlistTheme *current_theme; + } PidginBuddyListPrivate; #define PIDGIN_BUDDY_LIST_GET_PRIVATE(list) \ @@ -179,17 +186,6 @@ } conv; } PidginBlistNode; -static char dim_grey_string[8] = ""; -static char *dim_grey(void) -{ - if (!gtkblist) - return "dim grey"; - if (!dim_grey_string[0]) { - snprintf(dim_grey_string, sizeof(dim_grey_string), "%s", pidgin_get_dim_grey_string(gtkblist->treeview)); - } - return dim_grey_string; -} - /*************************************************** * Callbacks * ***************************************************/ @@ -1790,7 +1786,8 @@ return handled; } -static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_data) +static gboolean +gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_data) { GtkTreePath *path; PurpleBlistNode *node; @@ -3776,19 +3773,22 @@ return ret; } -gchar *pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased) -{ - const char *name; - char *esc, *text = NULL; +gchar * +pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased) +{ + const char *name, *name_color, *name_font, *status_color, *status_font; + char *text = NULL; PurplePlugin *prpl; PurplePluginProtocolInfo *prpl_info = NULL; PurpleContact *contact; PurplePresence *presence; struct _pidgin_blist_node *gtkcontactnode = NULL; - char *idletime = NULL, *statustext = NULL; - time_t t; + char *idletime = NULL, *statustext = NULL, *nametext = NULL; PurpleConversation *conv = find_conversation_with_buddy(b); gboolean hidden_conv = FALSE; + gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); + FontColorPair *pair; + PidginBlistTheme *theme; if (conv != NULL) { PidginBlistNode *ui = b->node.ui_data; @@ -3806,174 +3806,164 @@ if(contact) gtkcontactnode = ((PurpleBlistNode*)contact)->ui_data; - if(gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias) + /* Name */ + if (gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias) name = contact->alias; else name = purple_buddy_get_alias(b); - esc = g_markup_escape_text(name, strlen(name)); + nametext = g_markup_escape_text(name, strlen(name)); presence = purple_buddy_get_presence(b); - if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons") && aliased) - { - if (!selected && purple_presence_is_idle(presence)) - { - text = g_strdup_printf("<span color='%s'>%s</span>", - dim_grey(), esc); - g_free(esc); - if (hidden_conv) { - char *tmp = text; - text = g_strdup_printf("<b>%s</b>", text); + /* Name is all that is needed */ + if (aliased && biglist) { + + /* Status Info */ + prpl = purple_find_prpl(purple_account_get_protocol_id(b->account)); + + if (prpl != NULL) + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + + if (prpl_info && prpl_info->status_text && b->account->gc) { + char *tmp = prpl_info->status_text(b); + const char *end; + + if(tmp && !g_utf8_validate(tmp, -1, &end)) { + char *new = g_strndup(tmp, + g_utf8_pointer_to_offset(tmp, end)); + g_free(tmp); + tmp = new; + } + /* add ... to messages that are too long, GTK 2.6+ does it automatically */ +#if !GTK_CHECK_VERSION(2,6,0) + if(tmp) { + char buf[32]; + char *c = tmp; + int length = 0, vis=0; + gboolean inside = FALSE; + g_strdelimit(tmp, "\n", ' '); + purple_str_strip_char(tmp, '\r'); + + while(*c && vis < 20) { + if(*c == '&') + inside = TRUE; + else if(*c == ';') + inside = FALSE; + if(!inside) + vis++; + c = g_utf8_next_char(c); /* this is fun */ + } + + length = c - tmp; + + if(vis == 20) + g_snprintf(buf, sizeof(buf), "%%.%ds...", length); + else + g_snprintf(buf, sizeof(buf), "%%s "); + + statustext = g_strdup_printf(buf, tmp);purple_presence_is_idle(presence) + g_free(tmp); } - return text; - } - else if (hidden_conv) - { - char *tmp = esc; - esc = g_strdup_printf("<b>%s</b>", esc); - g_free(tmp); - } - return esc; - } - - prpl = purple_find_prpl(purple_account_get_protocol_id(b->account)); - - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info && prpl_info->status_text && b->account->gc) { - char *tmp = prpl_info->status_text(b); - const char *end; - - if(tmp && !g_utf8_validate(tmp, -1, &end)) { - char *new = g_strndup(tmp, - g_utf8_pointer_to_offset(tmp, end)); - g_free(tmp); - tmp = new; +#else + if(tmp) { + g_strdelimit(tmp, "\n", ' '); + purple_str_strip_char(tmp, '\r'); + } + statustext = tmp; +#endif } - -#if !GTK_CHECK_VERSION(2,6,0) - if(tmp) { - char buf[32]; - char *c = tmp; - int length = 0, vis=0; - gboolean inside = FALSE; - g_strdelimit(tmp, "\n", ' '); - purple_str_strip_char(tmp, '\r'); - - while(*c && vis < 20) { - if(*c == '&') - inside = TRUE; - else if(*c == ';') - inside = FALSE; - if(!inside) - vis++; - c = g_utf8_next_char(c); /* this is fun */ - } - - length = c - tmp; - - if(vis == 20) - g_snprintf(buf, sizeof(buf), "%%.%ds...", length); - else - g_snprintf(buf, sizeof(buf), "%%s "); - - statustext = g_strdup_printf(buf, tmp); - - g_free(tmp); - } -#else - if(tmp) { - g_strdelimit(tmp, "\n", ' '); - purple_str_strip_char(tmp, '\r'); - } - statustext = tmp; -#endif - } - - if(!purple_presence_is_online(presence) && !statustext) - statustext = g_strdup(_("Offline")); - else if (!statustext) - text = g_strdup(esc); - - if (purple_presence_is_idle(presence)) { - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time")) { + + if(!purple_presence_is_online(presence) && !statustext) + statustext = g_strdup(_("Offline")); + + /* Idle Text */ + if (purple_presence_is_idle(presence) && purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time")) { time_t idle_secs = purple_presence_get_idle_time(presence); if (idle_secs > 0) { int iday, ihrs, imin; + time_t t; time(&t); iday = (t - idle_secs) / (24 * 60 * 60); ihrs = ((t - idle_secs) / 60 / 60) % 24; imin = ((t - idle_secs) / 60) % 60; - - if (iday) + + if (iday) idletime = g_strdup_printf(_("Idle %dd %dh %02dm"), iday, ihrs, imin); else if (ihrs) idletime = g_strdup_printf(_("Idle %dh %02dm"), ihrs, imin); else idletime = g_strdup_printf(_("Idle %dm"), imin); - } - else - idletime = g_strdup(_("Idle")); - - if (!selected) { - g_free(text); - text = g_strdup_printf("<span color='%s'>%s</span>\n" - "<span color='%s' size='smaller'>%s%s%s</span>", - dim_grey(), esc, dim_grey(), - idletime != NULL ? idletime : "", - (idletime != NULL && statustext != NULL) ? " - " : "", - statustext != NULL ? statustext : ""); - } - } - else if (!selected && !statustext) {/* We handle selected text later */ - g_free(text); - text = g_strdup_printf("<span color='%s'>%s</span>", dim_grey(), esc); - } else if (!selected && !text) { - g_free(text); - text = g_strdup_printf("<span color='%s'>%s</span>\n" - "<span color='%s' size='smaller'>%s</span>", - dim_grey(), esc, dim_grey(), - statustext != NULL ? statustext : ""); + + } else idletime = g_strdup(_("Idle")); } - } else if (!PURPLE_BUDDY_IS_ONLINE(b)) { - if (!selected && !statustext) {/* We handle selected text later */ - g_free(text); - text = g_strdup_printf("<span color='%s'>%s</span>", dim_grey(), esc); - } else if (!selected && !text) - text = g_strdup_printf("<span color='%s'>%s</span>\n" - "<span color='%s' size='smaller'>%s</span>", - dim_grey(), esc, dim_grey(), - statustext != NULL ? statustext : ""); - - } - /* Not idle and not selected */ - else if (!selected && !text) - { - text = g_strdup_printf("%s\n" - "<span color='%s' size='smaller'>%s</span>", - esc, dim_grey(), - statustext != NULL ? statustext : ""); - } - - /* It is selected. */ - if ((selected && !text) || (selected && idletime)) { - g_free(text); - text = g_strdup_printf("%s\n" - "<span size='smaller'>%s%s%s</span>", - esc, - idletime != NULL ? idletime : "", - (idletime != NULL && statustext != NULL) ? " - " : "", - statustext != NULL ? statustext : ""); - } - - g_free(idletime); - g_free(statustext); - g_free(esc); + } + + /* choose the colors of the text */ + theme = pidgin_blist_get_theme(); + + if (theme == NULL) { + status_color = name_color = "dim grey"; + status_font = name_font = ""; + + } else if (purple_presence_is_idle(presence)) { + pair = pidgin_blist_theme_get_idle_text_info(theme); + status_color = name_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey"; + status_font = name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + + } else if (!purple_presence_is_online(presence)) { + pair = pidgin_blist_theme_get_offline_text_info(theme); + name_color = (pair != NULL && pair->color != NULL) ? pair->color : "black"; + name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + + pair = pidgin_blist_theme_get_status_text_info(theme); + status_color = (pair != NULL && pair->color != NULL) ? g_strdup(pair->color) : "dim grey"; + status_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + + } else if (purple_presence_is_available(presence)) { + pair = pidgin_blist_theme_get_online_text_info(theme); + name_color = (pair != NULL && pair->color != NULL) ? g_strdup(pair->color) : "black"; + name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + + pair = pidgin_blist_theme_get_status_text_info(theme); + status_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey"; + status_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + + } else { + pair = pidgin_blist_theme_get_away_text_info(theme); + name_color = (pair != NULL && pair->color != NULL) ? pair->color : "black"; + name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + + pair = pidgin_blist_theme_get_status_text_info(theme); + status_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey"; + status_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; + } + + if (aliased && selected) { + name_color = "black"; + status_color = "black"; + } + + /* Put it all together */ + if (aliased && biglist && (statustext || idletime)) { + /* using <span size='smaller'> breaks the status, so it must be seperated into <small><span>*/ + text = g_strdup_printf("<span font_desc='%s' foreground='%s'>%s</span>\n" + "<small><span font_desc='%s' foreground='%s'>%s%s%s</span></small>", + name_font, name_color, nametext, status_font, status_color, + idletime != NULL ? idletime : "", + (idletime != NULL && statustext != NULL) ? " - " : "", + statustext != NULL ? statustext : ""); + + } else text = g_strdup_printf("<span font_desc='%s' color='%s'>%s</span>", name_font, name_color, nametext); + + g_free(nametext); + if (statustext) + g_free(statustext); + if (idletime) + g_free(idletime); if (hidden_conv) { char *tmp = text; @@ -5219,11 +5209,144 @@ } #endif +/* builds the blist layout according to to the current theme */ +static void +pidgin_blist_build_layout(PurpleBuddyList *list) +{ + GtkTreeViewColumn *column; + PidginBlistLayout *layout; + PidginBlistTheme *theme; + GtkCellRenderer *rend; + gint i, status_icon = 0, text = 1, emblem = 2, protocol_icon = 3, buddy_icon = 4; + + + column = gtkblist->text_column; + + if ((theme = pidgin_blist_get_theme()) != NULL && (layout = pidgin_blist_theme_get_layout(theme)) != NULL) { + status_icon = layout->status_icon ; + text = layout->text; + emblem = layout->emblem; + protocol_icon = layout->protocol_icon; + buddy_icon = layout->buddy_icon; + } + + gtk_tree_view_column_clear(column); + + /* group */ + rend = pidgin_cell_renderer_expander_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, + "visible", GROUP_EXPANDER_VISIBLE_COLUMN, + "expander-visible", GROUP_EXPANDER_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "sensitive", GROUP_EXPANDER_COLUMN, + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + NULL); + + /* contact */ + rend = pidgin_cell_renderer_expander_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, + "visible", CONTACT_EXPANDER_VISIBLE_COLUMN, + "expander-visible", CONTACT_EXPANDER_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "sensitive", CONTACT_EXPANDER_COLUMN, + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + NULL); + + for (i = 0; i < 5; i++) { + + if (status_icon == i) { + /* status icons */ + rend = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, + "pixbuf", STATUS_ICON_COLUMN, + "visible", STATUS_ICON_VISIBLE_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + NULL); + g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL); + + } else if (text == i) { + /* name */ + gtkblist->text_rend = rend = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, rend, TRUE); + gtk_tree_view_column_set_attributes(column, rend, +#if GTK_CHECK_VERSION(2,6,0) + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + "markup", NAME_COLUMN, + NULL); +#if GTK_CHECK_VERSION(2,6,0) + g_signal_connect(G_OBJECT(rend), "editing-started", G_CALLBACK(gtk_blist_renderer_editing_started_cb), NULL); + g_signal_connect(G_OBJECT(rend), "editing-canceled", G_CALLBACK(gtk_blist_renderer_editing_cancelled_cb), list); +#endif + g_signal_connect(G_OBJECT(rend), "edited", G_CALLBACK(gtk_blist_renderer_edited_cb), list); + g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); +#if GTK_CHECK_VERSION(2,6,0) + g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); +#endif + + /* idle */ + rend = gtk_cell_renderer_text_new(); + g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, + "markup", IDLE_COLUMN, + "visible", IDLE_VISIBLE_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + NULL); + } else if (emblem == i) { + /* emblem */ + rend = gtk_cell_renderer_pixbuf_new(); + g_object_set(rend, "xalign", 1.0, "yalign", 0.5, "ypad", 0, "xpad", 3, NULL); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, "pixbuf", EMBLEM_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + "visible", EMBLEM_VISIBLE_COLUMN, NULL); + + } else if (protocol_icon == i) { + /* protocol icon */ + rend = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, + "pixbuf", PROTOCOL_ICON_COLUMN, + "visible", PROTOCOL_ICON_VISIBLE_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + NULL); + g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL); + + } else if (buddy_icon == i) { + /* buddy icon */ + rend = gtk_cell_renderer_pixbuf_new(); + g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_set_attributes(column, rend, "pixbuf", BUDDY_ICON_COLUMN, +#if GTK_CHECK_VERSION(2,6,0) + "cell-background-gdk", BGCOLOR_COLUMN, +#endif + "visible", BUDDY_ICON_VISIBLE_COLUMN, + NULL); + } + + }/* end for loop */ + +} + static void pidgin_blist_show(PurpleBuddyList *list) { PidginBuddyListPrivate *priv; void *handle; - GtkCellRenderer *rend; GtkTreeViewColumn *column; GtkWidget *menu; GtkWidget *ebox; @@ -5249,6 +5372,8 @@ gtkblist = PIDGIN_BLIST(list); priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist); + priv->current_theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"), "blist")); + gtkblist->empty_avatar = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32); gdk_pixbuf_fill(gtkblist->empty_avatar, 0x00000000); @@ -5281,8 +5406,8 @@ gtk_item_factory_create_items(gtkblist->ift, sizeof(blist_menu) / sizeof(*blist_menu), blist_menu, NULL); pidgin_load_accels(); - g_signal_connect(G_OBJECT(accel_group), "accel-changed", - G_CALLBACK(pidgin_save_accels_cb), NULL); + g_signal_connect(G_OBJECT(accel_group), "accel-changed", G_CALLBACK(pidgin_save_accels_cb), NULL); + menu = gtk_item_factory_get_widget(gtkblist->ift, "<PurpleMain>"); gtkblist->menutray = pidgin_menu_tray_new(); gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtkblist->menutray); @@ -5432,105 +5557,16 @@ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); + /* expander columns */ column = gtk_tree_view_column_new(); gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column); gtk_tree_view_column_set_visible(column, FALSE); gtk_tree_view_set_expander_column(GTK_TREE_VIEW(gtkblist->treeview), column); - gtkblist->text_column = column = gtk_tree_view_column_new (); - rend = pidgin_cell_renderer_expander_new(); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "visible", GROUP_EXPANDER_VISIBLE_COLUMN, - "expander-visible", GROUP_EXPANDER_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "sensitive", GROUP_EXPANDER_COLUMN, - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - NULL); - - rend = pidgin_cell_renderer_expander_new(); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "expander-visible", CONTACT_EXPANDER_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "sensitive", CONTACT_EXPANDER_COLUMN, - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - "visible", CONTACT_EXPANDER_VISIBLE_COLUMN, - NULL); - - rend = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "pixbuf", STATUS_ICON_COLUMN, - "visible", STATUS_ICON_VISIBLE_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - NULL); - g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL); - - gtkblist->text_rend = rend = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start (column, rend, TRUE); - gtk_tree_view_column_set_attributes(column, rend, -#if GTK_CHECK_VERSION(2,6,0) - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - "markup", NAME_COLUMN, - NULL); -#if GTK_CHECK_VERSION(2,6,0) - g_signal_connect(G_OBJECT(rend), "editing-started", G_CALLBACK(gtk_blist_renderer_editing_started_cb), NULL); - g_signal_connect(G_OBJECT(rend), "editing-canceled", G_CALLBACK(gtk_blist_renderer_editing_cancelled_cb), list); -#endif - g_signal_connect(G_OBJECT(rend), "edited", G_CALLBACK(gtk_blist_renderer_edited_cb), list); - g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); -#if GTK_CHECK_VERSION(2,6,0) - g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); -#endif - gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column); - - rend = gtk_cell_renderer_text_new(); - g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "markup", IDLE_COLUMN, - "visible", IDLE_VISIBLE_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - NULL); - - rend = gtk_cell_renderer_pixbuf_new(); - g_object_set(rend, "xalign", 1.0, "yalign", 0.5, "ypad", 0, "xpad", 3, NULL); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, "pixbuf", EMBLEM_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - "visible", EMBLEM_VISIBLE_COLUMN, NULL); - - rend = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, - "pixbuf", PROTOCOL_ICON_COLUMN, - "visible", PROTOCOL_ICON_VISIBLE_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - NULL); - g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL); - - rend = gtk_cell_renderer_pixbuf_new(); - g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, "pixbuf", BUDDY_ICON_COLUMN, -#if GTK_CHECK_VERSION(2,6,0) - "cell-background-gdk", BGCOLOR_COLUMN, -#endif - "visible", BUDDY_ICON_VISIBLE_COLUMN, - NULL); - + /* everything else column */ + gtkblist->text_column = gtk_tree_view_column_new (); + gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->text_column); + pidgin_blist_build_layout(list); g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL); g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL); @@ -5957,13 +5993,18 @@ GtkTreeIter iter; GtkTreePath *path; gboolean expanded; - GdkColor bgcolor; + GdkColor *bgcolor = NULL; GdkPixbuf *avatar = NULL; + PidginBlistTheme *theme = NULL; if(!insert_node(list, gnode, &iter)) return; - bgcolor = gtkblist->treeview->style->bg[GTK_STATE_ACTIVE]; + if ((theme = pidgin_blist_get_theme()) == NULL) + bgcolor = NULL; + else if (purple_blist_node_get_bool(gnode, "collapsed") || count <= 0) + bgcolor = pidgin_blist_theme_get_collapsed_background_color(theme); + else bgcolor = pidgin_blist_theme_get_expanded_background_color(theme); path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(gtkblist->treeview), path); @@ -5981,7 +6022,7 @@ STATUS_ICON_COLUMN, NULL, NAME_COLUMN, title, NODE_COLUMN, gnode, - /* BGCOLOR_COLUMN, &bgcolor, */ + BGCOLOR_COLUMN, bgcolor, GROUP_EXPANDER_COLUMN, TRUE, GROUP_EXPANDER_VISIBLE_COLUMN, TRUE, CONTACT_EXPANDER_VISIBLE_COLUMN, FALSE, @@ -6004,6 +6045,9 @@ char *mark, *esc; PurpleBlistNode *selected_node = NULL; GtkTreeIter iter; + FontColorPair *pair; + gchar *text_color, *text_font; + PidginBlistTheme *theme; group = (PurpleGroup*)gnode; @@ -6019,8 +6063,20 @@ purple_blist_get_group_size(group, FALSE)); } + theme = pidgin_blist_get_theme(); + if (theme == NULL) + pair = NULL; + else if + (expanded) pair = pidgin_blist_theme_get_expanded_text_info(theme); + else pair = pidgin_blist_theme_get_collapsed_text_info(theme); + + + text_color = (selected || pair == NULL || pair->color == NULL) ? "black" : pair->color; + text_font = (pair == NULL || pair->font == NULL) ? "" : pair->font; + esc = g_markup_escape_text(group->name, -1); - mark = g_strdup_printf("<span weight='bold'>%s</span>%s", esc ? esc : "", group_count); + mark = g_strdup_printf("<span foreground='%s' font_desc='%s'><b>%s</b>%s</span>", + text_color, text_font, esc ? esc : "", group_count); g_free(esc); return mark; @@ -6028,14 +6084,15 @@ static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *node) { - PurplePresence *presence; + PurplePresence *presence = purple_buddy_get_presence(buddy); GdkPixbuf *status, *avatar, *emblem, *prpl_icon; + GdkColor *color = NULL; char *mark; char *idle = NULL; gboolean expanded = ((struct _pidgin_blist_node *)(node->parent->ui_data))->contact_expanded; gboolean selected = (gtkblist->selected_node == node); gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); - presence = purple_buddy_get_presence(buddy); + PidginBlistTheme *theme; if (editing_blist) return; @@ -6059,35 +6116,38 @@ emblem = pidgin_blist_get_emblem((PurpleBlistNode*) buddy); mark = pidgin_blist_get_name_markup(buddy, selected, TRUE); + theme = pidgin_blist_get_theme(); + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time") && - purple_presence_is_idle(presence) && - !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons")) + purple_presence_is_idle(presence) && !biglist) { time_t idle_secs = purple_presence_get_idle_time(presence); if (idle_secs > 0) { + FontColorPair *pair = NULL; + const gchar *textcolor; time_t t; int ihrs, imin; time(&t); + ihrs = (t - idle_secs) / 3600; imin = ((t - idle_secs) / 60) % 60; - idle = g_strdup_printf("%d:%02d", ihrs, imin); - } - } - - if (purple_presence_is_idle(presence)) - { - if (idle && !selected) { - char *i2 = g_strdup_printf("<span color='%s'>%s</span>", - dim_grey(), idle); - g_free(idle); - idle = i2; + + if (!selected && theme != NULL && (pair = pidgin_blist_theme_get_idle_text_info(theme)) != NULL && pair->color != NULL) + textcolor = pair->color; + else textcolor = "black"; + + idle = g_strdup_printf("<span color='%s' font_desc='%s'>%d:%02d</span>", textcolor, + (pair == NULL || pair->font == NULL) ? "" : pair->font, ihrs, imin); } } prpl_icon = pidgin_create_prpl_icon(buddy->account, PIDGIN_PRPL_ICON_SMALL); + if (theme != NULL) + color = pidgin_blist_theme_get_contact_color(theme); + gtk_tree_store_set(gtkblist->treemodel, iter, STATUS_ICON_COLUMN, status, STATUS_ICON_VISIBLE_COLUMN, TRUE, @@ -6100,7 +6160,7 @@ EMBLEM_VISIBLE_COLUMN, (emblem != NULL), PROTOCOL_ICON_COLUMN, prpl_icon, PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"), - BGCOLOR_COLUMN, NULL, + BGCOLOR_COLUMN, color, CONTACT_EXPANDER_COLUMN, NULL, CONTACT_EXPANDER_VISIBLE_COLUMN, expanded, GROUP_EXPANDER_VISIBLE_COLUMN, FALSE, @@ -6158,19 +6218,37 @@ if(gtknode->contact_expanded) { GdkPixbuf *status; - char *mark; + gchar *mark; + GdkColor *color = NULL; + PidginBlistTheme *theme = pidgin_blist_get_theme(); + gboolean selected = (gtkblist->selected_node == cnode); + + mark = g_markup_escape_text(purple_contact_get_alias(contact), -1); + + if (theme != NULL) { + FontColorPair *pair = pidgin_blist_theme_get_contact_text_info(theme); + color = pidgin_blist_theme_get_contact_color(theme); + + if (pair != NULL) { + gchar *temp = g_strdup_printf("<span foreground='%s' font_desc='%s'>%s</span>", + (selected || pair->color == NULL) ? "black" : pair->color, + (pair->font == NULL) ? "" : pair->font, mark); + + g_free(mark); + mark = temp; + } + } status = pidgin_blist_get_status_icon(cnode, biglist? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL); - - mark = g_markup_escape_text(purple_contact_get_alias(contact), -1); + gtk_tree_store_set(gtkblist->treemodel, &iter, STATUS_ICON_COLUMN, status, STATUS_ICON_VISIBLE_COLUMN, TRUE, NAME_COLUMN, mark, IDLE_COLUMN, NULL, IDLE_VISIBLE_COLUMN, FALSE, - BGCOLOR_COLUMN, NULL, + BGCOLOR_COLUMN, color, BUDDY_ICON_COLUMN, NULL, CONTACT_EXPANDER_COLUMN, TRUE, CONTACT_EXPANDER_VISIBLE_COLUMN, TRUE, @@ -6238,12 +6316,16 @@ if(purple_account_is_connected(chat->account)) { GtkTreeIter iter; GdkPixbuf *status, *avatar, *emblem, *prpl_icon; - char *mark; + gchar *mark, *color, *font, *tmp; gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); PidginBlistNode *ui; PurpleConversation *conv; gboolean hidden; + GdkColor *bgcolor = NULL; + FontColorPair *pair; + PidginBlistTheme *theme; + gboolean selected = (gtkblist->selected_node == node); if (!insert_node(list, node, &iter)) return; @@ -6263,14 +6345,29 @@ else avatar = NULL; - mark = g_markup_escape_text(purple_chat_get_name(chat), -1); - if (hidden) { - char *bold = g_strdup_printf("<b>%s</b>", mark); - g_free(mark); - mark = bold; - } + mark = g_markup_escape_text(purple_chat_get_name(chat), -1); + + theme = pidgin_blist_get_theme(); + + if (theme == NULL) + pair = NULL; + else if (hidden) + pair = pidgin_blist_theme_get_unread_message_text_info(theme); + else pair = pidgin_blist_theme_get_online_text_info(theme); + + font = (pair == NULL || pair->font == NULL) ? "" : g_strdup(pair->font); + color = (selected || pair == NULL || pair->color == NULL) ? "black" : g_strdup(pair->color); + + tmp = g_strdup_printf("<span font_desc='%s' color='%s' weight='%s'>%s</span>", + font, color, hidden ? "bold" : "normal", mark); + + g_free(mark); + mark = tmp; prpl_icon = pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL); + + if (theme != NULL) + bgcolor = pidgin_blist_theme_get_contact_color(theme); gtk_tree_store_set(gtkblist->treemodel, &iter, STATUS_ICON_COLUMN, status, @@ -6282,6 +6379,7 @@ PROTOCOL_ICON_COLUMN, prpl_icon, PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"), NAME_COLUMN, mark, + BGCOLOR_COLUMN, bgcolor, GROUP_EXPANDER_VISIBLE_COLUMN, FALSE, -1); @@ -6294,6 +6392,7 @@ g_object_unref(avatar); if(prpl_icon) g_object_unref(prpl_icon); + } else { pidgin_blist_hide_node(list, node, TRUE); } @@ -7153,6 +7252,33 @@ (GSourceFunc)buddy_signonoff_timeout_cb, buddy); } +void +pidgin_blist_set_theme(PidginBlistTheme *theme) +{ + PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist); + PurpleBuddyList *list = purple_get_blist(); + + if (theme != NULL) + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/theme", + purple_theme_get_name(PURPLE_THEME(theme))); + else purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/theme", ""); + + priv->current_theme = theme; + + pidgin_blist_build_layout(list); + + pidgin_blist_refresh(list); +} + + +PidginBlistTheme * +pidgin_blist_get_theme() +{ + PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist); + + return priv->current_theme; +} + void pidgin_blist_init(void) { void *gtk_blist_handle = pidgin_blist_get_handle(); @@ -7181,6 +7307,9 @@ /* This pref is used in pidgintooltip.c. */ purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay", 500); #endif + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/blist/theme", ""); + + purple_theme_manager_register_type(g_object_new(PIDGIN_TYPE_BLIST_THEME_LOADER, "type", "blist", NULL)); /* Register our signals */ purple_signal_register(gtk_blist_handle, "gtkblist-hiding",
--- a/pidgin/gtkblist.h Fri Oct 31 04:03:44 2008 +0000 +++ b/pidgin/gtkblist.h Fri Oct 31 17:46:39 2008 +0000 @@ -59,6 +59,7 @@ #include "pidgin.h" #include "blist.h" +#include "gtkblist-theme.h" /************************************************************************** * @name Structures @@ -250,6 +251,19 @@ */ void pidgin_blist_add_alert(GtkWidget *widget); +/** + * Sets the current theme for Pidgin to use + * + * @param theme the new theme to use + */ +void pidgin_blist_set_theme(PidginBlistTheme *theme); + +/** + * Gets Pidgin's current buddy list theme + * + * @returns the current theme + */ +PidginBlistTheme *pidgin_blist_get_theme(void); /************************************************************************** * @name GTK+ Buddy List sorting functions
--- a/pidgin/gtkcellrendererexpander.c Fri Oct 31 04:03:44 2008 +0000 +++ b/pidgin/gtkcellrendererexpander.c Fri Oct 31 17:46:39 2008 +0000 @@ -228,7 +228,7 @@ } -static void pidgin_cell_renderer_expander_render (GtkCellRenderer *cell, +static void pidgin_cell_renderer_expander_render(GtkCellRenderer *cell, GdkWindow *window, GtkWidget *widget, GdkRectangle *background_area, @@ -237,7 +237,7 @@ guint flags) { PidginCellRendererExpander *cellexpander = (PidginCellRendererExpander *) cell; - + gboolean set; gint width, height; GtkStateType state; @@ -270,7 +270,10 @@ cell_area->x + cell->xpad + (width / 2), cell_area->y + cell->ypad + (height / 2), cell->is_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED); - if (cell->is_expanded) + + /* only draw the line if the color isn't set - this prevents a bug where the hline appears only under the expander */ + g_object_get(cellexpander, "cell-background-set", &set, NULL); + if (cell->is_expanded && !set) gtk_paint_hline (widget->style, window, state, NULL, widget, NULL, 0, widget->allocation.width, cell_area->y + cell_area->height); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkicon-theme-loader.c Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,115 @@ +/* + * PidginIconThemeLoader for Pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "gtkicon-theme-loader.h" +#include "gtkstatus-icon-theme.h" + +#include "xmlnode.h" + +/***************************************************************************** + * Icon Theme Builder + *****************************************************************************/ + +static PurpleTheme * +pidgin_icon_loader_build(const gchar *dir) +{ + xmlnode *root_node = NULL, *sub_node; + gchar *filename_full, *data; + PidginIconTheme *theme = NULL; + + /* Find the theme file */ + g_return_val_if_fail(dir != NULL, NULL); + filename_full = g_build_filename(dir, "theme.xml", NULL); + + if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR)) + root_node = xmlnode_from_file(dir, "theme.xml", "sound themes", "sound-loader"); + + g_free(filename_full); + g_return_val_if_fail(root_node != NULL, NULL); + + /* Parse the tree */ + sub_node = xmlnode_get_child(root_node, "description"); + data = xmlnode_get_data(sub_node); + + if (xmlnode_get_attrib(root_node, "name") != NULL) { + theme = g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME, + "type", "status-icon", + "name", xmlnode_get_attrib(root_node, "name"), + "author", xmlnode_get_attrib(root_node, "author"), + "image", xmlnode_get_attrib(root_node, "image"), + "directory", dir, + "description", data, NULL); + + sub_node = xmlnode_get_child(root_node, "icon"); + + while (sub_node){ + pidgin_icon_theme_set_icon(theme, + xmlnode_get_attrib(sub_node, "id"), + xmlnode_get_attrib(sub_node, "file")); + sub_node = xmlnode_get_next_twin(sub_node); + } + } + + xmlnode_free(root_node); + g_free(data); + return PURPLE_THEME(theme); +} + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +pidgin_icon_theme_loader_class_init (PidginIconThemeLoaderClass *klass) +{ + PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass); + + loader_klass->purple_theme_loader_build = pidgin_icon_loader_build; +} + + +GType +pidgin_icon_theme_loader_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PidginIconThemeLoaderClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_icon_theme_loader_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PidginIconThemeLoader), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static (PURPLE_TYPE_THEME_LOADER, + "PidginIconThemeLoader", + &info, 0); + } + return type; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkicon-theme-loader.h Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,71 @@ +/** + * @file gtkicon-loader.h Pidgin Icon Theme Loader Class API + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PIDGIN_ICON_THEME_LOADER_H_ +#define _PIDGIN_ICON_THEME_LOADER_H_ + +#include <glib.h> +#include <glib-object.h> +#include "theme-loader.h" + +/** + * A pidgin icon theme loader. Extends PurpleThemeLoader (theme-loader.h) + * This is a class designed to build icon themes + * + * PidginIconThemeLoader is a GObject. + */ +typedef struct _PidginIconThemeLoader PidginIconThemeLoader; +typedef struct _PidginIconThemeLoaderClass PidginIconThemeLoaderClass; + +#define PIDGIN_TYPE_ICON_THEME_LOADER (pidgin_icon_theme_loader_get_type ()) +#define PIDGIN_ICON_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_ICON_THEME_LOADER, PidginIconThemeLoader)) +#define PIDGIN_ICON_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_ICON_THEME_LOADER, PidginIconThemeLoaderClass)) +#define PIDGIN_IS_ICON_THEME_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_ICON_THEME_LOADER)) +#define PIDGIN_IS_ICON_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_ICON_THEME_LOADER)) +#define PIDGIN_ICON_THEME_LOADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_ICON_THEME_LOADER, PidginIconThemeLoaderClass)) + +struct _PidginIconThemeLoader +{ + PurpleThemeLoader parent; +}; + +struct _PidginIconThemeLoaderClass +{ + PurpleThemeLoaderClass parent_class; +}; + +/**************************************************************************/ +/** @name Pidgin Icon Theme-Loader API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_icon_theme_loader_get_type(void); + +G_END_DECLS +#endif /* _PIDGIN_ICON_THEME_LOADER_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkicon-theme.c Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,147 @@ +/* + * Icon Themes for Pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "gtkicon-theme.h" +#include "pidginstock.h" + +#include <gtk/gtk.h> + +#define PIDGIN_ICON_THEME_GET_PRIVATE(Gobject) \ + ((PidginIconThemePrivate *) ((PIDGIN_ICON_THEME(Gobject))->priv)) + + +/****************************************************************************** + * Structs + *****************************************************************************/ +typedef struct { + /* used to store filenames of diffrent icons */ + GHashTable *icon_files; +} PidginIconThemePrivate; + +/****************************************************************************** + * Globals + *****************************************************************************/ + +static GObjectClass *parent_class = NULL; + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +pidgin_icon_theme_init(GTypeInstance *instance, + gpointer klass) +{ + PidginIconThemePrivate *priv; + + (PIDGIN_ICON_THEME(instance))->priv = g_new0(PidginIconThemePrivate, 1); + + priv = PIDGIN_ICON_THEME_GET_PRIVATE(instance); + + priv->icon_files = g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + g_free); +} + +static void +pidgin_icon_theme_finalize(GObject *obj) +{ + PidginIconThemePrivate *priv; + + priv = PIDGIN_ICON_THEME_GET_PRIVATE(obj); + + g_hash_table_destroy(priv->icon_files); + g_free(priv); + + parent_class->finalize(obj); +} + +static void +pidgin_icon_theme_class_init(PidginIconThemeClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); + + obj_class->finalize = pidgin_icon_theme_finalize; +} + +GType +pidgin_icon_theme_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PidginIconThemeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_icon_theme_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PidginIconTheme), + 0, /* n_preallocs */ + pidgin_icon_theme_init, /* instance_init */ + NULL, /* value table */ + }; + type = g_type_register_static(PURPLE_TYPE_THEME, + "PidginIconTheme", + &info, G_TYPE_FLAG_ABSTRACT); + } + return type; +} + + +/***************************************************************************** + * Public API functions + *****************************************************************************/ + +const gchar * +pidgin_icon_theme_get_icon(PidginIconTheme *theme, + const gchar *id) +{ + PidginIconThemePrivate *priv; + + g_return_val_if_fail(PIDGIN_IS_ICON_THEME(theme), NULL); + + priv = PIDGIN_ICON_THEME_GET_PRIVATE(theme); + + return g_hash_table_lookup(priv->icon_files, id); +} + +void +pidgin_icon_theme_set_icon(PidginIconTheme *theme, + const gchar *id, + const gchar *filename) +{ + PidginIconThemePrivate *priv; + g_return_if_fail(PIDGIN_IS_ICON_THEME(theme)); + + priv = PIDGIN_ICON_THEME_GET_PRIVATE(theme); + + if (filename != NULL) + g_hash_table_replace(priv->icon_files, + g_strdup(id), + g_strdup(filename)); + else g_hash_table_remove(priv->icon_files, id); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkicon-theme.h Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,92 @@ +/** + * @file icon-theme.h Pidgin Icon Theme Class API + */ + +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PIDGIN_ICON_THEME_H_ +#define _PIDGIN_ICON_THEME_H_ + +#include <glib.h> +#include <glib-object.h> +#include "theme.h" + +/** + * extends PurpleTheme (theme.h) + * A pidgin icon theme. + * This object represents a Pidgin icon theme. + * + * PidginIconTheme is a PurpleTheme Object. + */ +typedef struct _PidginIconTheme PidginIconTheme; +typedef struct _PidginIconThemeClass PidginIconThemeClass; + +#define PIDGIN_TYPE_ICON_THEME (pidgin_icon_theme_get_type ()) +#define PIDGIN_ICON_THEME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_ICON_THEME, PidginIconTheme)) +#define PIDGIN_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_ICON_THEME, PidginIconThemeClass)) +#define PIDGIN_IS_ICON_THEME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_ICON_THEME)) +#define PIDGIN_IS_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_ICON_THEME)) +#define PIDGIN_ICON_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_ICON_THEME, PidginIconThemeClass)) + +struct _PidginIconTheme +{ + PurpleTheme parent; + gpointer priv; +}; + +struct _PidginIconThemeClass +{ + PurpleThemeClass parent_class; +}; + +/**************************************************************************/ +/** @name Pidgin Icon Theme API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_icon_theme_get_type(void); + +/** + * Returns a copy of the filename for the icon event or NULL if it is not set + * + * @param event the pidgin icon event to look up + * + * @returns the filename of the icon event + */ +const gchar *pidgin_icon_theme_get_icon(PidginIconTheme *theme, + const gchar *event); +/** + * Sets the filename for a given icon id, setting the icon to NULL will remove the icon from the theme + * + * @param icon_id a string representing what the icon is to be used for + * @param filename the name of the file to be used for the given id + */ +void pidgin_icon_theme_set_icon(PidginIconTheme *theme, + const gchar *icon_id, + const gchar *filename); + +G_END_DECLS +#endif /* _PIDGIN_ICON_THEME_H_ */
--- a/pidgin/gtkprefs.c Fri Oct 31 04:03:44 2008 +0000 +++ b/pidgin/gtkprefs.c Fri Oct 31 17:46:39 2008 +0000 @@ -35,6 +35,8 @@ #include "request.h" #include "savedstatuses.h" #include "sound.h" +#include "sound-theme.h" +#include "theme-manager.h" #include "util.h" #include "network.h" @@ -47,6 +49,7 @@ #include "gtkprefs.h" #include "gtksavedstatuses.h" #include "gtksound.h" +#include "gtkstatus-icon-theme.h" #include "gtkthemes.h" #include "gtkutils.h" #include "pidginstock.h" @@ -56,6 +59,8 @@ #define PROXYUSER 2 #define PROXYPASS 3 +#define PREFS_OPTIMAL_ICON_SIZE 32 + static int sound_row_sel = 0; static GtkWidget *prefsnotebook; @@ -69,6 +74,12 @@ static int notebook_page = 0; static GtkTreeRowReference *previous_smiley_row = NULL; +static gboolean prefs_themes_unsorted = TRUE; +static GtkListStore *prefs_sound_themes; +static GtkListStore *prefs_blist_themes; +static GtkListStore *prefs_status_icon_themes; + + /* * PROTOTYPES */ @@ -546,6 +557,213 @@ gtk_drag_finish(dc, FALSE, FALSE, t); } +/* Rebuild the markup for the sound theme selection for "(Custom)" themes */ +static void +pref_sound_generate_markup() +{ + gboolean print_custom, customized; + const gchar *name, *author, *description, *current_theme; + gchar *markup; + PurpleSoundTheme *theme; + GtkTreeIter iter; + + customized = pidgin_sound_is_customized(); + current_theme = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"); + + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(prefs_sound_themes), &iter)) { + do { + gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &iter, 2, &name, -1); + + print_custom = customized && g_str_equal(current_theme, name); + + if (g_str_equal(name, "")) + markup = g_strdup_printf("<b>(Default)</b>%s%s - None\n<span foreground='dim grey'>The default Pidgin sound theme</span>", + print_custom ? " " : "", print_custom ? "(Custom)" : ""); + else { + theme = PURPLE_SOUND_THEME(purple_theme_manager_find_theme(name, "sound")); + author = purple_theme_get_author(PURPLE_THEME(theme)); + description = purple_theme_get_description(PURPLE_THEME(theme)); + + markup = g_strdup_printf("<b>%s</b>%s%s%s%s\n<span foreground='dim grey'>%s</span>", + name, print_custom ? " " : "", print_custom ? "(Custom)" : "", + author != NULL ? " - " : "", author != NULL ? author : "", description != NULL ? description : ""); + } + + gtk_list_store_set(prefs_sound_themes, &iter, 1, markup, -1); + + g_free(markup); + + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(prefs_sound_themes), &iter)); + } +} + +/* adds the themes to the theme list from the manager so they can be sisplayed in prefs */ +static void +prefs_themes_sort(PurpleTheme *theme) +{ + GdkPixbuf *pixbuf = NULL; + GtkTreeIter iter; + gchar *image_full = NULL, *markup; + const gchar *name, *author, *description; + + if (PURPLE_IS_SOUND_THEME(theme)){ + + image_full = purple_theme_get_image_full(theme); + if (image_full != NULL){ + pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL); + g_free(image_full); + } else pixbuf = NULL; + + gtk_list_store_append(prefs_sound_themes, &iter); + gtk_list_store_set(prefs_sound_themes, &iter, 0, pixbuf, 2, purple_theme_get_name(theme), -1); + + if (pixbuf != NULL) + gdk_pixbuf_unref(pixbuf); + + } else if (PIDGIN_IS_BLIST_THEME(theme) || PIDGIN_IS_STATUS_ICON_THEME(theme)){ + GtkListStore *store; + + if (PIDGIN_IS_BLIST_THEME(theme)) + store = prefs_blist_themes; + else store = prefs_status_icon_themes; + + image_full = purple_theme_get_image_full(theme); + if (image_full != NULL){ + pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL); + g_free(image_full); + } else pixbuf = NULL; + + name = purple_theme_get_name(theme); + author = purple_theme_get_author(theme); + description = purple_theme_get_description(theme); + + markup = g_strdup_printf("<b>%s</b>%s%s\n<span foreground='dim grey'>%s</span>", name, author != NULL ? " - " : "", + author != NULL ? author : "", description != NULL ? description : ""); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, pixbuf, 1, markup, 2, name, -1); + + g_free(markup); + if (pixbuf != NULL) + gdk_pixbuf_unref(pixbuf); + } + +} + +/* init all the theme variables so that the themes can be sorted later and used by pref pages */ +static void +prefs_themes_init() +{ + GdkPixbuf *pixbuf = NULL; + gchar *filename; + GtkTreeIter iter; + + filename = g_build_filename(DATADIR, "icons", "hicolor", "32x32", "apps", "pidgin.png", NULL); + pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL); + g_free(filename); + + /* sound themes */ + prefs_sound_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + gtk_list_store_append(prefs_sound_themes, &iter); + gtk_list_store_set(prefs_sound_themes, &iter, 0, pixbuf, 2, "", -1); + + /* blist themes */ + prefs_blist_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + gtk_list_store_append(prefs_blist_themes, &iter); + gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, "<b>(Default)</b> - None\n<span color='dim grey'>" + "The default Pidgin buddy list theme</span>", 2, "", -1); + + /* status icon themes */ + prefs_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + gtk_list_store_append(prefs_status_icon_themes, &iter); + gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, "<b>(Default)</b> - None\n<span color='dim grey'>" + "The default Pidgin status icon theme</span>", 2, "", -1); + + gdk_pixbuf_unref(pixbuf); +} + +/* builds a theme combo box from a list store with colums: icon preview, markup, theme name */ +static GtkWidget * +prefs_build_theme_combo_box(GtkListStore *store, const gchar *current_theme) +{ + GtkWidget *combo_box; + GtkCellRenderer *cell_rend; + GtkTreeIter iter; + gchar *theme = NULL; + gboolean unset = TRUE; + + g_return_val_if_fail(store != NULL && current_theme != NULL, NULL); + + combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); + + cell_rend = gtk_cell_renderer_pixbuf_new(); + gtk_cell_renderer_set_fixed_size(cell_rend, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "pixbuf", 0, NULL); + + cell_rend = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "markup", 1, NULL); +/*#if GTK_CHECK_VERSION(2,6,0) + g_object_set(cell_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); +#endif*/ + + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) { + do { + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 2, &theme, -1); + + if (g_str_equal(current_theme, theme)) { + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo_box), &iter); + unset = FALSE; + } + + g_free(theme); + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); + } + + if (unset) + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0); + + return combo_box; +} + +/* sets the current sound theme */ +static void +prefs_set_sound_theme_cb(GtkComboBox *combo_box, gpointer user_data) +{ + gint i; + gchar *pref; + gchar *new_theme; + gboolean sucess; + GtkTreeIter new_iter; + + + sucess = gtk_combo_box_get_active_iter(combo_box, &new_iter); + g_return_if_fail(sucess); + + gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &new_iter, 2, &new_theme, -1); + + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/sound/theme", new_theme); + + /* New theme removes all customization */ + for(i=0; i < PURPLE_NUM_SOUNDS; i++){ + pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s", + pidgin_sound_get_event_option(i)); + purple_prefs_set_path(pref, ""); + g_free(pref); + } + + /* gets rid of the "(Custom)" from the last selection */ + pref_sound_generate_markup(); + + gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)")); + + g_free(new_theme); +} + /* Does same as normal sort, except "none" is sorted first */ static gint pidgin_sort_smileys (GtkTreeModel *model, GtkTreeIter *a, @@ -922,6 +1140,40 @@ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0); } +/* sets the current buddy list theme */ +static void +prefs_set_blist_theme_cb(GtkComboBox *combo_box, gpointer user_data) +{ + PidginBlistTheme *theme; + GtkTreeIter iter; + gchar *name = NULL; + + g_return_if_fail(gtk_combo_box_get_active_iter(combo_box, &iter)); + gtk_tree_model_get(GTK_TREE_MODEL(prefs_blist_themes), &iter, 2, &name, -1); + + theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(name, "blist")); + g_free(name); + + pidgin_blist_set_theme(theme); +} + +/* sets the current icon theme */ +static void +prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data) +{ + PidginStatusIconTheme *theme; + GtkTreeIter iter; + gchar *name = NULL; + + g_return_if_fail(gtk_combo_box_get_active_iter(combo_box, &iter)); + gtk_tree_model_get(GTK_TREE_MODEL(prefs_status_icon_themes), &iter, 2, &name, -1); + + theme = PIDGIN_STATUS_ICON_THEME(purple_theme_manager_find_theme(name, "status-icon")); + g_free(name); + + pidgin_stock_load_status_icon_theme(theme); +} + static GtkWidget * interface_page(void) { @@ -929,6 +1181,7 @@ GtkWidget *vbox; GtkWidget *vbox2; GtkWidget *label; + GtkWidget *combo_box; GtkSizeGroup *sg; GList *names = NULL; @@ -937,6 +1190,19 @@ sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + /* Buddy List Themes */ + vbox = pidgin_make_frame(ret, _("Buddy List Theme")); + + combo_box = prefs_build_theme_combo_box(prefs_blist_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme")); + gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_blist_theme_cb, NULL); + + /* Status Icon Themes */ + combo_box = prefs_build_theme_combo_box(prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme")); + gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_status_icon_theme_cb, NULL); + + /* System Tray */ vbox = pidgin_make_frame(ret, _("System Tray Icon")); label = pidgin_prefs_dropdown(vbox, _("_Show system tray icon:"), PURPLE_PREF_STRING, PIDGIN_PREFS_ROOT "/docklet/show", @@ -1736,6 +2002,8 @@ g_free(pref); gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)")); + + pref_sound_generate_markup(); } static void @@ -1758,6 +2026,8 @@ */ if (sound == sound_row_sel) gtk_entry_set_text(GTK_ENTRY(sound_entry), filename); + + pref_sound_generate_markup(); } static void select_sound(GtkWidget *button, gpointer being_NULL_is_fun) @@ -1826,6 +2096,8 @@ if (sound_entry) gtk_entry_set_text(GTK_ENTRY(sound_entry), (file && *file != '\0') ? file : _("(default)")); g_value_unset (&val); + + pref_sound_generate_markup(); } @@ -1851,7 +2123,7 @@ sound_page(void) { GtkWidget *ret; - GtkWidget *vbox, *sw, *button; + GtkWidget *vbox, *sw, *button, *combo_box; GtkSizeGroup *sg; GtkTreeIter iter; GtkWidget *event_view; @@ -1945,7 +2217,6 @@ purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method", sound_changed2_cb, vbox); #endif - vbox = pidgin_make_frame(ret, _("Sound Events")); /* The following is an ugly hack to make the frame expand so the @@ -1957,6 +2228,14 @@ gtk_box_set_child_packing(GTK_BOX(vbox->parent->parent->parent), vbox->parent->parent, TRUE, TRUE, 0, GTK_PACK_START); + /* SOUND THEMES */ + combo_box = prefs_build_theme_combo_box(prefs_sound_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme")); + pref_sound_generate_markup(); + gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_sound_theme_cb, NULL); + + /* SOUND SELECTION */ sw = gtk_scrolled_window_new(NULL,NULL); gtk_widget_set_size_request(sw, -1, 100); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); @@ -2189,7 +2468,12 @@ gtk_window_present(GTK_WINDOW(prefs)); return; } - + + /* add everthing in the thmeme manager before the window is loaded */ + if (prefs_themes_unsorted) { + purple_theme_manager_for_each_theme(prefs_themes_sort); + prefs_themes_unsorted = FALSE; + } /* copy the preferences to tmp values... * I liked "take affect immediately" Oh well :-( */ /* (that should have been "effect," right?) */ @@ -2284,6 +2568,9 @@ purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder", ""); purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_icon_folder", ""); + /* Themes */ + prefs_themes_init(); + /* Smiley Themes */ purple_prefs_add_none(PIDGIN_PREFS_ROOT "/smileys"); purple_prefs_add_string(PIDGIN_PREFS_ROOT "/smileys/theme", "Default");
--- a/pidgin/gtksound.c Fri Oct 31 04:03:44 2008 +0000 +++ b/pidgin/gtksound.c Fri Oct 31 17:46:39 2008 +0000 @@ -40,6 +40,8 @@ #include "notify.h" #include "prefs.h" #include "sound.h" +#include "sound-theme.h" +#include "theme-manager.h" #include "util.h" #include "gtkconv.h" @@ -294,6 +296,7 @@ purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/nick_said", ""); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/pounce_default", TRUE); purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/pounce_default", ""); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/sound/theme", ""); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/conv_focus", TRUE); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/mute", FALSE); purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/command", ""); @@ -557,6 +560,8 @@ { char *enable_pref; char *file_pref; + const char *theme_name; + PurpleSoundTheme *theme; if ((event == PURPLE_SOUND_BUDDY_ARRIVE) && mute_login_sounds) return; @@ -570,16 +575,35 @@ sounds[event].pref); file_pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s", sounds[event].pref); + + /* check NULL for sounds that don't have an option, ie buddy pounce */ if (purple_prefs_get_bool(enable_pref)) { char *filename = g_strdup(purple_prefs_get_path(file_pref)); - if(!filename || !strlen(filename)) { + theme_name = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"); + + if (theme_name && strlen(theme_name) && (!filename || !strlen(filename))){ /* Use theme */ g_free(filename); + + theme = PURPLE_SOUND_THEME(purple_theme_manager_find_theme(theme_name, "sound")); + filename = purple_sound_theme_get_file_full(theme, sounds[event].pref); + + if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR)){ /* Use Default sound in this case */ + purple_debug_error("sound", "The file: (%s) %s\n from theme: %s, was not found or wasn't readable\n", + sounds[event].pref, filename, theme_name); + g_free(filename); + } + } + + if (!filename || !strlen(filename)) { /* Use Default sounds */ + g_free(filename); + /* XXX Consider creating a constant for "sounds/purple" to be shared with Finch */ filename = g_build_filename(DATADIR, "sounds", "purple", sounds[event].def, NULL); } purple_sound_play_file(filename, NULL); + g_free(filename); } @@ -587,6 +611,29 @@ g_free(file_pref); } +gboolean +pidgin_sound_is_customized(void) +{ + gint i; + gchar *path, *file; + + for (i=0; i < PURPLE_NUM_SOUNDS; i++){ + path = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s", sounds[i].pref); + file = g_strdup(purple_prefs_get_path(path)); + g_free(path); + + if (file && strlen(file)){ + g_free(file); + return TRUE; + } + + g_free(file); + } + + return FALSE; + +} + static PurpleSoundUiOps sound_ui_ops = { pidgin_sound_init,
--- a/pidgin/gtksound.h Fri Oct 31 04:03:44 2008 +0000 +++ b/pidgin/gtksound.h Fri Oct 31 17:46:39 2008 +0000 @@ -63,6 +63,13 @@ */ void *pidgin_sound_get_handle(void); +/** + * Returns true Pidgin is using customized sounds + * + * @return TRUE if non default sounds are used + */ +gboolean pidgin_sound_is_customized(void); + /*@}*/ #endif /* _PIDGINSOUND_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkstatus-icon-theme.c Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,73 @@ +/* + * Status Icon Themes for Pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#include "gtkstatus-icon-theme.h" + +/****************************************************************************** + * Globals + *****************************************************************************/ +static GObjectClass *parent_class = NULL; + +/****************************************************************************** + * GObject Stuff + *****************************************************************************/ + +static void +pidgin_status_icon_theme_finalize(GObject *obj) +{ + parent_class->finalize(obj); +} + +static void +pidgin_status_icon_theme_class_init(PidginStatusIconThemeClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); + + obj_class->finalize = pidgin_status_icon_theme_finalize; +} + +GType +pidgin_status_icon_theme_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PidginStatusIconThemeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_status_icon_theme_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PidginStatusIconTheme), + 0, /* n_preallocs */ + NULL, + NULL, /* value table */ + }; + type = g_type_register_static(PIDGIN_TYPE_ICON_THEME, + "PidginStatusIconTheme", + &info, 0); + } + return type; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkstatus-icon-theme.h Fri Oct 31 17:46:39 2008 +0000 @@ -0,0 +1,71 @@ +/** + * @file status_icon-theme.h Pidgin Icon Theme Class API + */ + +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PIDGIN_STATUS_ICON_THEME_H_ +#define _PIDGIN_STATUS_ICON_THEME_H_ + +#include <glib-object.h> +#include "gtkicon-theme.h" + +/** + * extends PidginIconTheme (gtkicon-theme.h) + * A pidgin status icon theme. + * This object represents a Pidgin status icon theme. + * + * PidginStatusIconTheme is a PidginIconTheme Object. + */ +typedef struct _PidginStatusIconTheme PidginStatusIconTheme; +typedef struct _PidginStatusIconThemeClass PidginStatusIconThemeClass; + +#define PIDGIN_TYPE_STATUS_ICON_THEME (pidgin_status_icon_theme_get_type ()) +#define PIDGIN_STATUS_ICON_THEME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_STATUS_ICON_THEME, PidginStatusIconTheme)) +#define PIDGIN_STATUS_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_STATUS_ICON_THEME, PidginStatusIconThemeClass)) +#define PIDGIN_IS_STATUS_ICON_THEME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_STATUS_ICON_THEME)) +#define PIDGIN_IS_STATUS_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_STATUS_ICON_THEME)) +#define PIDGIN_STATUS_ICON_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_STATUS_ICON_THEME, PidginStatusIconThemeClass)) + +struct _PidginStatusIconTheme +{ + PidginIconTheme parent; +}; + +struct _PidginStatusIconThemeClass +{ + PidginIconThemeClass parent_class; +}; + +/**************************************************************************/ +/** @name Pidgin Status Icon Theme API */ +/**************************************************************************/ +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_status_icon_theme_get_type(void); + +G_END_DECLS +#endif /* _PIDGIN_STATUS_ICON_THEME_H_ */
--- a/pidgin/pidginstock.c Fri Oct 31 04:03:44 2008 +0000 +++ b/pidgin/pidginstock.c Fri Oct 31 17:46:39 2008 +0000 @@ -26,17 +26,32 @@ */ #include "internal.h" #include "pidgin.h" +#include "prefs.h" + +#include "gtkicon-theme-loader.h" +#include "theme-manager.h" #include "pidginstock.h" +/************************************************************************** + * Globals + **************************************************************************/ + +static gboolean stock_initted = FALSE; +static GtkIconSize microscopic, extra_small, small, medium, large, huge; + +/************************************************************************** + * Structures + **************************************************************************/ + static struct StockIcon { const char *name; const char *dir; const char *filename; -} const stock_icons[] = -{ +} const stock_icons[] = { + { PIDGIN_STOCK_ACTION, NULL, GTK_STOCK_EXECUTE }, #if GTK_CHECK_VERSION(2,6,0) { PIDGIN_STOCK_ALIAS, NULL, GTK_STOCK_EDIT }, @@ -96,7 +111,7 @@ { PIDGIN_STOCK_EDIT, N_("_Edit"), 0, 0, NULL } }; -static struct SizedStockIcon { +typedef struct { const char *name; const char *dir; const char *filename; @@ -108,18 +123,9 @@ gboolean huge; gboolean rtl; const char *translucent_name; -} const sized_stock_icons [] = { - { PIDGIN_STOCK_STATUS_AVAILABLE, "status", "available.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AVAILABLE_I }, - { PIDGIN_STOCK_STATUS_AWAY, "status", "away.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AWAY_I }, - { PIDGIN_STOCK_STATUS_BUSY, "status", "busy.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_BUSY_I }, - { PIDGIN_STOCK_STATUS_CHAT, "status", "chat.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_STATUS_INVISIBLE,"status", "invisible.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_STATUS_XA, "status", "extended-away.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, PIDGIN_STOCK_STATUS_XA_I }, - { PIDGIN_STOCK_STATUS_LOGIN, "status", "log-in.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL }, - { PIDGIN_STOCK_STATUS_LOGOUT, "status", "log-out.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL }, - { PIDGIN_STOCK_STATUS_OFFLINE, "status", "offline.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_OFFLINE_I }, - { PIDGIN_STOCK_STATUS_PERSON, "status", "person.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_STATUS_MESSAGE, "toolbar", "message-new.png", TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, +} SizedStockIcon; + +const SizedStockIcon sized_stock_icons [] = { { PIDGIN_STOCK_STATUS_IGNORED, "emblems", "blocked.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, { PIDGIN_STOCK_STATUS_FOUNDER, "emblems", "founder.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, @@ -173,37 +179,56 @@ { PIDGIN_STOCK_ANIMATION_TYPING4, "animations", "typing4.png",FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, { PIDGIN_STOCK_ANIMATION_TYPING5, "animations", "typing5.png",FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_ACCOUNTS, "toolbar", "accounts.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_BGCOLOR, "toolbar", "change-bgcolor.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_BLOCK, "emblems", "blocked.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_FGCOLOR, "toolbar", "change-fgcolor.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_SMILEY, "toolbar", "emote-select.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_FONT_FACE, "toolbar", "font-face.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER, "toolbar", "font-size-down.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_TEXT_LARGER, "toolbar", "font-size-up.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_INSERT, "toolbar", "insert.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, "toolbar", "insert-image.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_INSERT_LINK, "toolbar", "insert-link.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, "toolbar", "message-new.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_PENDING, "tray", "tray-new-im.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_PLUGINS, "toolbar", "plugins.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_TYPING, "toolbar", "typing.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_UNBLOCK, "toolbar", "unblock.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR, "toolbar", "select-avatar.png", FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_SEND_FILE, "toolbar", "send-file.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_TRANSFER, "toolbar", "transfer.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_ACCOUNTS, "toolbar", "accounts.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_BGCOLOR, "toolbar", "change-bgcolor.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_BLOCK, "emblems", "blocked.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_FGCOLOR, "toolbar", "change-fgcolor.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_SMILEY, "toolbar", "emote-select.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_FONT_FACE, "toolbar", "font-face.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER, "toolbar", "font-size-down.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_TEXT_LARGER, "toolbar", "font-size-up.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_INSERT, "toolbar", "insert.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, "toolbar", "insert-image.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_INSERT_LINK, "toolbar", "insert-link.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, "toolbar", "message-new.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_PENDING, "toolbar", "message-new.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_PLUGINS, "toolbar", "plugins.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_TYPING, "toolbar", "typing.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_UNBLOCK, "toolbar", "unblock.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR, "toolbar", "select-avatar.png", FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_SEND_FILE, "toolbar", "send-file.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_TRANSFER, "toolbar", "transfer.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL } +}; + +const SizedStockIcon sized_status_icons [] = { - { PIDGIN_STOCK_TRAY_AVAILABLE, "tray", "tray-online.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TRAY_INVISIBLE, "tray", "tray-invisible.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TRAY_AWAY, "tray", "tray-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TRAY_BUSY, "tray", "tray-busy.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TRAY_XA, "tray", "tray-extended-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TRAY_OFFLINE, "tray", "tray-offline.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TRAY_CONNECT, "tray", "tray-connecting.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TRAY_PENDING, "tray", "tray-new-im.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TRAY_EMAIL, "tray", "tray-message.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL } + { PIDGIN_STOCK_STATUS_AVAILABLE, "status", "available.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AVAILABLE_I }, + { PIDGIN_STOCK_STATUS_AWAY, "status", "away.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AWAY_I }, + { PIDGIN_STOCK_STATUS_BUSY, "status", "busy.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_BUSY_I }, + { PIDGIN_STOCK_STATUS_CHAT, "status", "chat.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_STATUS_INVISIBLE, "status", "invisible.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_STATUS_XA, "status", "extended-away.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, PIDGIN_STOCK_STATUS_XA_I }, + { PIDGIN_STOCK_STATUS_LOGIN, "status", "log-in.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL }, + { PIDGIN_STOCK_STATUS_LOGOUT, "status", "log-out.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL }, + { PIDGIN_STOCK_STATUS_OFFLINE, "status", "offline.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_OFFLINE_I }, + { PIDGIN_STOCK_STATUS_PERSON, "status", "person.png", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_STATUS_MESSAGE, "toolbar", "message-new.png", TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + + { PIDGIN_STOCK_TRAY_AVAILABLE, "tray", "tray-online.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TRAY_INVISIBLE, "tray", "tray-invisible.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TRAY_AWAY, "tray", "tray-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TRAY_BUSY, "tray", "tray-busy.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TRAY_XA, "tray", "tray-extended-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TRAY_OFFLINE, "tray", "tray-offline.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TRAY_CONNECT, "tray", "tray-connecting.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TRAY_PENDING, "tray", "tray-new-im.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TRAY_EMAIL, "tray", "tray-message.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL } }; +/***************************************************************************** + * Private functions + *****************************************************************************/ + static gchar * find_file(const char *dir, const char *base) { @@ -223,55 +248,10 @@ return filename; } -static void -add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir, - gboolean rtl, const char *size, const char *file) -{ - char *filename; - GtkIconSource *source; - - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, file, NULL); - source = gtk_icon_source_new(); - gtk_icon_source_set_filename(source, filename); - gtk_icon_source_set_direction(source, GTK_TEXT_DIR_LTR); - gtk_icon_source_set_direction_wildcarded(source, !rtl); - gtk_icon_source_set_size(source, sizeid); - gtk_icon_source_set_size_wildcarded(source, FALSE); - gtk_icon_source_set_state_wildcarded(source, TRUE); - gtk_icon_set_add_source(iconset, source); - gtk_icon_source_free(source); - - if (sizeid == gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)) { - source = gtk_icon_source_new(); - gtk_icon_source_set_filename(source, filename); - gtk_icon_source_set_direction_wildcarded(source, TRUE); - gtk_icon_source_set_size(source, GTK_ICON_SIZE_MENU); - gtk_icon_source_set_size_wildcarded(source, FALSE); - gtk_icon_source_set_state_wildcarded(source, TRUE); - gtk_icon_set_add_source(iconset, source); - gtk_icon_source_free(source); - } - g_free(filename); - - if (rtl) { - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, "rtl", file, NULL); - source = gtk_icon_source_new(); - gtk_icon_source_set_filename(source, filename); - gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL); - gtk_icon_source_set_size(source, sizeid); - gtk_icon_source_set_size_wildcarded(source, FALSE); - gtk_icon_source_set_state_wildcarded(source, TRUE); - gtk_icon_set_add_source(iconset, source); - g_free(filename); - gtk_icon_source_free(source); - } - - -} /* Altered from do_colorshift in gnome-panel */ static void -do_alphashift (GdkPixbuf *dest, GdkPixbuf *src) +do_alphashift(GdkPixbuf *dest, GdkPixbuf *src) { gint i, j; gint width, height, has_alpha, srcrowstride, destrowstride; @@ -305,25 +285,48 @@ } } -/* TODO: This is almost certainly not the best way to do this, but it's late, I'm tired, - * we're a few hours from getting this thing out, and copy/paste is EASY. - */ +static gchar * +find_icon_file(PidginStatusIconTheme *theme, const gchar *size, SizedStockIcon sized_icon, gboolean rtl) +{ + const gchar *file, *dir; + gchar *file_full = NULL; + + if (theme != NULL) { + file = pidgin_icon_theme_get_icon(PIDGIN_ICON_THEME(theme), sized_icon.name); + dir = purple_theme_get_dir(PURPLE_THEME(theme)); + + if (rtl) + file_full = g_build_filename(dir, size, "rtl", file, NULL); + else file_full = g_build_filename(dir, size, file, NULL); + + if (g_file_test(file_full, G_FILE_TEST_IS_REGULAR)) + return file_full; + + g_free(file_full); + } + + if (rtl) + return g_build_filename(DATADIR, "pixmaps", "pidgin", sized_icon.dir, size, sized_icon.filename, NULL); + else return g_build_filename(DATADIR, "pixmaps", "pidgin", sized_icon.dir, size, sized_icon.filename, NULL); +} + static void -add_translucent_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir, - gboolean rtl, const char *size, const char *file) +add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, PidginStatusIconTheme *theme, + const char *size, SizedStockIcon sized_icon, gboolean translucent) { char *filename; - GtkIconSource *source; + GtkIconSource *source; GdkPixbuf *pixbuf; - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, file, NULL); + filename = find_icon_file(theme, size, sized_icon, FALSE); pixbuf = gdk_pixbuf_new_from_file(filename, NULL); - do_alphashift(pixbuf, pixbuf); + if (translucent) + do_alphashift(pixbuf, pixbuf); source = gtk_icon_source_new(); - gtk_icon_source_set_pixbuf(source, pixbuf); + gtk_icon_source_set_pixbuf(source, pixbuf); gtk_icon_source_set_direction(source, GTK_TEXT_DIR_LTR); - gtk_icon_source_set_direction_wildcarded(source, !rtl); + gtk_icon_source_set_direction_wildcarded(source, !sized_icon.rtl); gtk_icon_source_set_size(source, sizeid); gtk_icon_source_set_size_wildcarded(source, FALSE); gtk_icon_source_set_state_wildcarded(source, TRUE); @@ -343,12 +346,15 @@ g_free(filename); g_object_unref(pixbuf); - if (rtl) { - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, "rtl", file, NULL); - pixbuf = gdk_pixbuf_new_from_file(filename, NULL); - do_alphashift(pixbuf, pixbuf); + if (sized_icon.rtl) { + filename = find_icon_file(theme, size, sized_icon, TRUE); + pixbuf = gdk_pixbuf_new_from_file(filename, NULL); + if (translucent) + do_alphashift(pixbuf, pixbuf); + source = gtk_icon_source_new(); - gtk_icon_source_set_pixbuf(source, pixbuf); + gtk_icon_source_set_pixbuf(source, pixbuf); + gtk_icon_source_set_filename(source, filename); gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL); gtk_icon_source_set_size(source, sizeid); gtk_icon_source_set_size_wildcarded(source, FALSE); @@ -358,25 +364,92 @@ g_object_unref(pixbuf); gtk_icon_source_free(source); } +} + +/***************************************************************************** + * Public API functions + *****************************************************************************/ + +void +pidgin_stock_load_status_icon_theme(PidginStatusIconTheme *theme) +{ + GtkIconFactory *icon_factory; + gint i; + GtkIconSet *normal; + GtkIconSet *translucent = NULL; + GtkWidget *win; + + if (theme != NULL) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/status/icon-theme", + purple_theme_get_name(PURPLE_THEME(theme))); + purple_prefs_set_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir", + purple_theme_get_dir(PURPLE_THEME(theme))); + } + else { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/status/icon-theme", ""); + purple_prefs_set_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir", ""); + } + + icon_factory = gtk_icon_factory_new(); + + gtk_icon_factory_add_default(icon_factory); + + win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_realize(win); + + for (i = 0; i < G_N_ELEMENTS(sized_status_icons); i++) + { + normal = gtk_icon_set_new(); + if (sized_status_icons[i].translucent_name) + translucent = gtk_icon_set_new(); + +#define ADD_SIZED_ICON(name, size) if (sized_status_icons[i].name) { \ + add_sized_icon(normal, name, theme, size, sized_status_icons[i], FALSE); \ + if (sized_status_icons[i].translucent_name) \ + add_sized_icon(translucent, name, theme, size, sized_status_icons[i], TRUE); \ + } + ADD_SIZED_ICON(microscopic, "11"); + ADD_SIZED_ICON(extra_small, "16"); + ADD_SIZED_ICON(small, "22"); + ADD_SIZED_ICON(medium, "32"); + ADD_SIZED_ICON(large, "48"); + ADD_SIZED_ICON(huge, "64"); +#undef ADD_SIZED_ICON + + gtk_icon_factory_add(icon_factory, sized_status_icons[i].name, normal); + gtk_icon_set_unref(normal); + + if (sized_status_icons[i].translucent_name) { + gtk_icon_factory_add(icon_factory, sized_status_icons[i].translucent_name, translucent); + gtk_icon_set_unref(translucent); + } + } + gtk_widget_destroy(win); + g_object_unref(G_OBJECT(icon_factory)); } - void pidgin_stock_init(void) { - static gboolean stock_initted = FALSE; GtkIconFactory *icon_factory; size_t i; GtkWidget *win; - GtkIconSize microscopic, extra_small, small, medium, large, huge; + PidginIconThemeLoader *loader; + const gchar *path = NULL; if (stock_initted) return; stock_initted = TRUE; + /* Setup the status icon theme */ + loader = g_object_new(PIDGIN_TYPE_ICON_THEME_LOADER, "type", "status-icon", NULL); + purple_theme_manager_register_type(PURPLE_THEME_LOADER(loader)); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/status/icon-theme", ""); + purple_prefs_add_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir", ""); + /* Setup the icon factory. */ icon_factory = gtk_icon_factory_new(); @@ -386,6 +459,7 @@ win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_realize(win); + /* All non-sized icons */ for (i = 0; i < G_N_ELEMENTS(stock_icons); i++) { GtkIconSource *source; @@ -425,7 +499,6 @@ } /* register custom icon sizes */ - microscopic = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC, 11, 11); extra_small = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL, 16, 16); small = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_SMALL, 22, 22); @@ -433,18 +506,13 @@ large = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_LARGE, 48, 48); huge = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_HUGE, 64, 64); + /* All non-status sized icons */ for (i = 0; i < G_N_ELEMENTS(sized_stock_icons); i++) { - GtkIconSet *iconset; - - iconset = gtk_icon_set_new(); + GtkIconSet *iconset = gtk_icon_set_new(); -#define ADD_SIZED_ICON(name, size) do { \ - if (sized_stock_icons[i].name) \ - add_sized_icon(iconset, name, \ - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, \ - size, sized_stock_icons[i].filename); \ - } while (0) +#define ADD_SIZED_ICON(name, size) if (sized_stock_icons[i].name) \ + add_sized_icon(iconset, name, NULL, size, sized_stock_icons[i], FALSE); ADD_SIZED_ICON(microscopic, "11"); ADD_SIZED_ICON(extra_small, "16"); ADD_SIZED_ICON(small, "22"); @@ -455,32 +523,21 @@ gtk_icon_factory_add(icon_factory, sized_stock_icons[i].name, iconset); gtk_icon_set_unref(iconset); - - if (sized_stock_icons[i].translucent_name) { - iconset = gtk_icon_set_new(); - -#define ADD_TRANS_ICON(name, size) do { \ - if (sized_stock_icons[i].name) \ - add_translucent_sized_icon(iconset, name, \ - sized_stock_icons[i].dir, sized_stock_icons[i].rtl, \ - size, sized_stock_icons[i].filename); \ - } while (0) - ADD_TRANS_ICON(microscopic, "11"); - ADD_TRANS_ICON(extra_small, "16"); - ADD_TRANS_ICON(small, "22"); - ADD_TRANS_ICON(medium, "32"); - ADD_TRANS_ICON(large, "48"); - ADD_TRANS_ICON(huge, "64"); -#undef ADD_TRANS_ICON - - gtk_icon_factory_add(icon_factory, sized_stock_icons[i].translucent_name, iconset); - gtk_icon_set_unref(iconset); - } } gtk_widget_destroy(win); g_object_unref(G_OBJECT(icon_factory)); + /* Pre-load Status icon theme - this avoids a bug with displaying the correct icon in the tray, theme is destroyed after*/ + if (purple_prefs_get_string(PIDGIN_PREFS_ROOT "/icon/status/theme") && + (path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir"))) { + + PidginStatusIconTheme *theme = PIDGIN_STATUS_ICON_THEME(purple_theme_loader_build(PURPLE_THEME_LOADER(loader), path)); + pidgin_stock_load_status_icon_theme(theme); + g_object_unref(G_OBJECT(theme)); + + } else pidgin_stock_load_status_icon_theme(NULL); + /* Register the stock items. */ gtk_stock_add_static(stock_items, G_N_ELEMENTS(stock_items)); }
--- a/pidgin/pidginstock.h Fri Oct 31 04:03:44 2008 +0000 +++ b/pidgin/pidginstock.h Fri Oct 31 17:46:39 2008 +0000 @@ -24,6 +24,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include <gtk/gtkstock.h> +#include "gtkstatus-icon-theme.h" #ifndef _PIDGIN_STOCK_H_ #define _PIDGIN_STOCK_H_ @@ -176,6 +177,14 @@ #define PIDGIN_ICON_SIZE_TANGO_MEDIUM "pidgin-icon-size-tango-medium" #define PIDGIN_ICON_SIZE_TANGO_LARGE "pidgin-icon-size-tango-large" #define PIDGIN_ICON_SIZE_TANGO_HUGE "pidgin-icon-size-tango-huge" + +/** + * Loades all of the icons from the status icon theme into Pidgin stock + * + * @param theme the theme to load, or null to load all the default icons + */ +void pidgin_stock_load_status_icon_theme(PidginStatusIconTheme *theme); + /** * Sets up the purple stock repository. */