# HG changeset patch # User Yoshiki Yazawa # Date 1246256677 0 # Node ID 518b4a900f1949e52c262b2945cbb9ff854e86e6 # Parent 862b8208a5467a5911c8a983956952735b56820e# Parent 164b1bc23377311d5de92fd507002ac12e99d403 propagate from branch 'im.pidgin.pidgin' (head 11d28a9362597cbea7fa0957f4202037131a3d6b) to branch 'im.pidgin.pidgin.yaz' (head c88eab6ab06f08ebe8a58bbc9614d11818aad9e9) diff -r 164b1bc23377 -r 518b4a900f19 COPYRIGHT --- a/COPYRIGHT Fri Jun 26 06:40:00 2009 +0000 +++ b/COPYRIGHT Mon Jun 29 06:24:37 2009 +0000 @@ -217,6 +217,7 @@ Henry Jen Benjamin Kahn Jan Kaluza +Yuriy Kaminskiy Anders Kaseorg Praveen Karadakal Jaromír Karmazín diff -r 164b1bc23377 -r 518b4a900f19 ChangeLog --- a/ChangeLog Fri Jun 26 06:40:00 2009 +0000 +++ b/ChangeLog Mon Jun 29 06:24:37 2009 +0000 @@ -121,6 +121,33 @@ * Preferences have been reorganized into three tabs for Colors, Fonts, and Miscellaneous categories. +version 2.5.8 (06/27/2009): + ICQ: + * Fix misparsing a web message as an SMS message. (Yuriy Kaminskiy) + + MSN: + * Increase NS command history size to prevent crashes on buddy lists that + have a lot of buddies on other networks like Yahoo!. + + MySpace: + * Accounts with empty buddy lists are now properly marked as connected. + * Fix receiving messages from users of MySpace's web IM client. + + Yahoo: + * Fixed phantom online buddies. They should now properly disappear when + signing out. + * Fixed the crashes some users were seeing with cn.scs.msg.yahoo.com in + 2.5.7. + * Fixed compiling on systems with glib 2.4.x or older. + * Fixed an issue with file transfers. This may not resolve all issues, + but it should resolve at least some of the most common ones. + * The pager server will automatically update to scsa.msg.yahoo.com if the + user empties the field or if it is scs.msg.yahoo.com. This should ease + the pain of transition to the new login method. + + XMPP: + * Fix an incompatibility betweeen Prosody and libpurple clients. + version 2.5.7 (06/20/2009): * Yahoo Protocol 16 support, including new HTTPS login method; this should fix a number of login problems that have recently cropped up. (Sulabh diff -r 164b1bc23377 -r 518b4a900f19 ChangeLog.API --- a/ChangeLog.API Fri Jun 26 06:40:00 2009 +0000 +++ b/ChangeLog.API Mon Jun 29 06:24:37 2009 +0000 @@ -128,6 +128,9 @@ * Purple::Request::Field::string_new * Purple::Request::Field::group_new +version 2.5.8 (06/27/2009): + No changes + version 2.5.7 (06/20/2009): No changes diff -r 164b1bc23377 -r 518b4a900f19 ChangeLog.win32 --- a/ChangeLog.win32 Fri Jun 26 06:40:00 2009 +0000 +++ b/ChangeLog.win32 Mon Jun 29 06:24:37 2009 +0000 @@ -1,5 +1,8 @@ version 2.6.0 (??/??/2009): +version 2.5.8 (06/27/2009): + * No changes + version 2.5.7 (06/20/2009): * No changes diff -r 164b1bc23377 -r 518b4a900f19 NEWS --- a/NEWS Fri Jun 26 06:40:00 2009 +0000 +++ b/NEWS Mon Jun 29 06:24:37 2009 +0000 @@ -2,6 +2,12 @@ Our development blog is available at: http://planet.pidgin.im +2.5.8 (06/27/2009): + John: This release is another somewhat rushed bugfix release to fix + a number of bugs that have come up since we released Pidgin 2.5.7. + Hopefully anything I broke there is fixed now, or at least made to be + less broken. Enjoy! + 2.5.7 (06/20/2009): John: This release is really just a rushed fix for the broken Yahoo protocol plugin. I spent way more time on this release than I care diff -r 164b1bc23377 -r 518b4a900f19 libpurple/core.c --- a/libpurple/core.c Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/core.c Mon Jun 29 06:24:37 2009 +0000 @@ -175,7 +175,6 @@ purple_xfers_init(); purple_idle_init(); purple_smileys_init(); - purple_theme_manager_init(); /* * Call this early on to try to auto-detect our IP address and * hopefully save some time later. diff -r 164b1bc23377 -r 518b4a900f19 libpurple/dnssrv.c --- a/libpurple/dnssrv.c Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/dnssrv.c Mon Jun 29 06:24:37 2009 +0000 @@ -66,7 +66,7 @@ #endif struct _PurpleTxtResponse { - char *content; + char *content; }; struct _PurpleSrvQueryData { @@ -335,13 +335,13 @@ query_data->results = NULL; purple_debug_info("dnssrv", "found %d SRV entries\n", size); - + if(query_data->cb.srv) query_data->cb.srv(srvres, size, query_data->extradata); } else if (query_data->type == DNS_TYPE_TXT) { GSList *lst = query_data->results; purple_debug_info("dnssrv", "found %d TXT entries\n", g_slist_length(lst)); - + if (query_data->cb.txt) { query_data->results = NULL; query_data->cb.txt(lst, query_data->extradata); @@ -382,7 +382,7 @@ GSList *lst = NULL; DNS_SRV_DATA *srv_data; PurpleSrvResponse *srvres; - + for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { /* Discard any incorrect entries. I'm not sure if this is necessary */ if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { @@ -396,7 +396,7 @@ srvres->pref = srv_data->wPriority; srvres->port = srv_data->wPort; srvres->weight = srv_data->wWeight; - + lst = g_slist_insert_sorted(lst, srvres, responsecompare); } @@ -431,7 +431,7 @@ MyDnsRecordListFree(dr, DnsFreeRecordList); query_data->results = lst; } else { - + } } @@ -500,10 +500,9 @@ internal_query.type = T_SRV; strncpy(internal_query.query, query, 255); - + if (write(in[1], &internal_query, sizeof(internal_query)) < 0) purple_debug_error("dnssrv", "Could not write to SRV resolver\n"); - query_data = g_new0(PurpleSrvQueryData, 1); query_data->type = T_SRV; @@ -596,10 +595,10 @@ close(out[1]); close(in[0]); - + internal_query.type = T_TXT; strncpy(internal_query.query, query, 255); - + if (write(in[1], &internal_query, sizeof(internal_query)) < 0) purple_debug_error("dnssrv", "Could not write to TXT resolver\n"); diff -r 164b1bc23377 -r 518b4a900f19 libpurple/protocols/jabber/buddy.c --- a/libpurple/protocols/jabber/buddy.c Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/protocols/jabber/buddy.c Mon Jun 29 06:24:37 2009 +0000 @@ -1102,8 +1102,12 @@ char *txt, *vcard_hash = NULL; if (type == JABBER_IQ_ERROR) { - purple_debug_warning("jabber", "Server returned error while retrieving vCard"); - return; + xmlnode *error; + purple_debug_warning("jabber", "Server returned error while retrieving vCard\n"); + + error = xmlnode_get_child(packet, "error"); + if (!error || !xmlnode_get_child(error, "item-not-found")) + return; } if((vcard = xmlnode_get_child(packet, "vCard")) || diff -r 164b1bc23377 -r 518b4a900f19 libpurple/protocols/jabber/ping.c --- a/libpurple/protocols/jabber/ping.c Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/protocols/jabber/ping.c Mon Jun 29 06:24:37 2009 +0000 @@ -33,11 +33,9 @@ xmlnode *packet, gpointer data) { if (js->keepalive_timeout != 0) { - purple_debug_misc("jabber", "Keepalive PONG\n"); purple_timeout_remove(js->keepalive_timeout); js->keepalive_timeout = 0; - } else - purple_debug_warning("jabber", "Keepalive PONG with no outstanding timeout!\n"); + } } void diff -r 164b1bc23377 -r 518b4a900f19 libpurple/protocols/oscar/bstream.c --- a/libpurple/protocols/oscar/bstream.c Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/protocols/oscar/bstream.c Mon Jun 29 06:24:37 2009 +0000 @@ -161,15 +161,19 @@ return aimutil_getle32(bs->data + bs->offset - 4); } +static void byte_stream_getrawbuf_nocheck(ByteStream *bs, guint8 *buf, int len) +{ + memcpy(buf, bs->data + bs->offset, len); + bs->offset += len; +} + int byte_stream_getrawbuf(ByteStream *bs, guint8 *buf, int len) { if (byte_stream_empty(bs) < len) return 0; - memcpy(buf, bs->data + bs->offset, len); - bs->offset += len; - + byte_stream_getrawbuf_nocheck(bs, buf, len); return len; } @@ -177,12 +181,12 @@ { guint8 *ob; + if (byte_stream_empty(bs) < len) + return NULL; + ob = g_malloc(len); - if (byte_stream_getrawbuf(bs, ob, len) < len) { - g_free(ob); - return NULL; - } + byte_stream_getrawbuf_nocheck(bs, ob, len); return ob; } @@ -191,12 +195,12 @@ { char *ob; + if (byte_stream_empty(bs) < len) + return NULL; + ob = g_malloc(len + 1); - if (byte_stream_getrawbuf(bs, (guint8 *)ob, len) < len) { - g_free(ob); - return NULL; - } + byte_stream_getrawbuf_nocheck(bs, (guint8 *)ob, len); ob[len] = '\0'; diff -r 164b1bc23377 -r 518b4a900f19 libpurple/protocols/oscar/clientlogin.c --- a/libpurple/protocols/oscar/clientlogin.c Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/protocols/oscar/clientlogin.c Mon Jun 29 06:24:37 2009 +0000 @@ -138,7 +138,7 @@ static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **host, unsigned short *port, char **cookie) { xmlnode *response_node, *tmp_node, *data_node; - xmlnode *host_node, *port_node, *cookie_node; + xmlnode *host_node = NULL, *port_node = NULL, *cookie_node = NULL; char *tmp; /* Parse the response as XML */ @@ -321,7 +321,7 @@ static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **token, char **secret, time_t *hosttime) { xmlnode *response_node, *tmp_node, *data_node; - xmlnode *secret_node, *hosttime_node, *token_node, *tokena_node; + xmlnode *secret_node = NULL, *hosttime_node = NULL, *token_node = NULL, *tokena_node = NULL; char *tmp; /* Parse the response as XML */ @@ -518,7 +518,7 @@ /* Tack on the body */ g_string_append_printf(request, "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n"); - g_string_append_printf(request, "Content-Length: %lu\r\n\r\n", body->len); + g_string_append_printf(request, "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n", body->len); g_string_append_len(request, body->str, body->len); g_string_free(body, TRUE); diff -r 164b1bc23377 -r 518b4a900f19 libpurple/protocols/oscar/family_icbm.c --- a/libpurple/protocols/oscar/family_icbm.c Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/protocols/oscar/family_icbm.c Mon Jun 29 06:24:37 2009 +0000 @@ -1605,7 +1605,7 @@ static int incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, ByteStream *bs, guint8 *cookie) { - guint16 type, length, magic1, msglen; + guint16 type, length, magic1, msglen = 0; aim_rxcallback_t userfunc; int ret = 0; int rev = 0; diff -r 164b1bc23377 -r 518b4a900f19 libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.c Mon Jun 29 06:24:37 2009 +0000 @@ -2938,9 +2938,15 @@ /* From libicq2000-0.3.2/src/ICQ.cpp */ byte_stream_init(&qbs, (guint8 *)args->msg, args->msglen); byte_stream_advance(&qbs, 21); + /* expected: 01 00 00 20 00 0e 28 f6 00 11 e7 d3 11 bc f3 00 04 ac 96 9d c2 | 00 00 | 06 00 00 00 | 49 43 51 53 43 53 ...*/ + /* unexpected: 00 00 26 00 81 1a 18 bc 0e 6c 18 47 a5 91 6f 18 dc c7 6f 1a | 00 00 | 0d 00 00 00 | 49 43 51 57 65 62 4d 65 73 73 61 67 65 ... */ smstype = byte_stream_getle16(&qbs); + if (smstype != 0) + break; taglen = byte_stream_getle32(&qbs); tagstr = byte_stream_getstr(&qbs, taglen); + if (tagstr == NULL) + break; byte_stream_advance(&qbs, 3); byte_stream_advance(&qbs, 4); smslen = byte_stream_getle32(&qbs); diff -r 164b1bc23377 -r 518b4a900f19 libpurple/protocols/yahoo/yahoo.c --- a/libpurple/protocols/yahoo/yahoo.c Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Mon Jun 29 06:24:37 2009 +0000 @@ -628,6 +628,9 @@ else g_string_append(yd->tmp_serv_ilist, pair->value); break; + case 89: + yd->profiles = g_strsplit(pair->value, ",", -1); + break; case 59: /* cookies, yum */ yahoo_process_cookie(yd, pair->value); break; @@ -745,7 +748,6 @@ gint val_11 = 0; struct yahoo_data *yd = gc->proto_data; gboolean msn = FALSE; - char *msn_from = NULL; account = purple_connection_get_account(gc); @@ -778,17 +780,16 @@ return; } - if(msn) - msn_from = g_strconcat("msn/", from, NULL); - if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING")) && (purple_privacy_check(account, from))) { if(msn) { + char *msn_from = g_strconcat("msn/", from, NULL); if (*stat == '1') serv_got_typing(gc, msn_from, 0, PURPLE_TYPING); else serv_got_typing_stopped(gc, msn_from); + g_free(msn_from); } else { if (*stat == '1') @@ -822,18 +823,18 @@ purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL)); g_free(buf); } - - g_free(msn_from); } struct _yahoo_im { char *from; + char *active_id; int time; int utf8; int buddy_icon; char *id; char *msg; + gboolean msn; }; static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet *pkt) @@ -907,8 +908,6 @@ struct _yahoo_im *im = NULL; const char *imv = NULL; gint val_11 = 0; - gboolean msn = FALSE; - char *msn_from = NULL; account = purple_connection_get_account(gc); @@ -923,6 +922,8 @@ im->time = time(NULL); im->utf8 = TRUE; } + if (im && pair->key == 5) + im->active_id = pair->value; if (pair->key == 97) if (im) im->utf8 = strtol(pair->value, NULL, 10); @@ -936,9 +937,9 @@ if (im) im->msg = pair->value; } - if (pair->key == 241) { + if (im && pair->key == 241) { if(strtol(pair->value, NULL, 10) == 2) - msn = TRUE; + im->msn = TRUE; } /* peer session id */ if (pair->key == 11) { @@ -960,9 +961,6 @@ _("Your Yahoo! message did not get sent."), NULL); } - if(msn) - msn_from = g_strconcat("msn/", im->from, NULL); - /* disconnect the peer if connected through p2p and sends wrong value for session id */ if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) { purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im->from); @@ -1007,6 +1005,8 @@ for (l = list; l; l = l->next) { YahooFriend *f; char *m, *m2; + char *msn_from = NULL; + const char *from; PurpleConversation *c; im = l->data; @@ -1033,7 +1033,7 @@ pkt2 = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_ACK, YAHOO_STATUS_AVAILABLE, pkt->id); yahoo_packet_hash(pkt2, "ssisii", - 1, purple_connection_get_display_name(gc), + 1, im->active_id, /* May not always be the connection's display name */ 5, im->from, 302, 430, 430, im->id, @@ -1052,24 +1052,22 @@ m = m2; purple_util_chrreplace(m, '\r', '\n'); - c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->from, account); - if ((c == NULL) && msn) - c=purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, msn_from, account); + if (im->msn) { + msn_from = g_strconcat("msn/", im->from, NULL); + from = msn_from; + } else { + from = im->from; + } + + c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, account); if (!strcmp(m, "")) { char *username; - if(c == NULL) { - if(msn) - c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, msn_from); - else - c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, im->from); + if (c == NULL) { + c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, from); } - if(msn) - username = g_markup_escape_text(msn_from, -1); - else - username = g_markup_escape_text(im->from, -1); - + username = g_markup_escape_text(from, -1); purple_prpl_got_attention(gc, username, YAHOO_BUZZ); g_free(username); g_free(m); @@ -1081,15 +1079,11 @@ m2 = yahoo_codes_to_html(m); g_free(m); - if(msn) - serv_got_im(gc, msn_from, m2, 0, im->time); - else - serv_got_im(gc, im->from, m2, 0, im->time); - + serv_got_im(gc, from, m2, 0, im->time); g_free(m2); /* laters : implement buddy icon for msn friends */ - if(!msn) { + if (!im->msn) { if ((f = yahoo_friend_find(gc, im->from)) && im->buddy_icon == 2) { if (yahoo_friend_get_buddy_icon_need_request(f)) { yahoo_send_picture_request(gc, im->from); @@ -3546,6 +3540,7 @@ g_free(yd->pending_chat_id); g_free(yd->pending_chat_topic); g_free(yd->pending_chat_goto); + g_strfreev(yd->profiles); g_free(yd->current_list15_grp); @@ -3931,15 +3926,16 @@ } } -static void yahoo_act_id(PurpleConnection *gc, const char *entry) +static void yahoo_act_id(PurpleConnection *gc, PurpleRequestFields *fields) { struct yahoo_data *yd = gc->proto_data; + const char *name = yd->profiles[purple_request_fields_get_choice(fields, "id")]; struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash_str(pkt, 3, entry); + yahoo_packet_hash_str(pkt, 3, name); yahoo_packet_send_and_free(pkt, yd); - purple_connection_set_display_name(gc, entry); + purple_connection_set_display_name(gc, name); } static void @@ -4021,9 +4017,28 @@ static void yahoo_show_act_id(PurplePluginAction *action) { + PurpleRequestFields *fields; + PurpleRequestFieldGroup *group; + PurpleRequestField *field; PurpleConnection *gc = (PurpleConnection *) action->context; - purple_request_input(gc, NULL, _("Activate which ID?"), NULL, - purple_connection_get_display_name(gc), FALSE, FALSE, NULL, + struct yahoo_data *yd = purple_connection_get_protocol_data(gc); + const char *name = purple_connection_get_display_name(gc); + int iter; + + fields = purple_request_fields_new(); + group = purple_request_field_group_new(NULL); + purple_request_fields_add_group(fields, group); + field = purple_request_field_choice_new("id", "Activate which ID?", 0); + purple_request_field_group_add_field(group, field); + + for (iter = 0; yd->profiles[iter]; iter++) { + purple_request_field_choice_add(field, yd->profiles[iter]); + if (purple_strequal(yd->profiles[iter], name)) + purple_request_field_choice_set_default_value(field, iter); + } + + purple_request_fields(gc, NULL, _("Select the ID you want to activate"), NULL, + fields, _("OK"), G_CALLBACK(yahoo_act_id), _("Cancel"), NULL, purple_connection_get_account(gc), NULL, NULL, diff -r 164b1bc23377 -r 518b4a900f19 libpurple/protocols/yahoo/yahoo.h --- a/libpurple/protocols/yahoo/yahoo.h Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo.h Mon Jun 29 06:24:37 2009 +0000 @@ -89,7 +89,7 @@ #define YAHOOJP_CLIENT_VERSION_ID "4194239" #define YAHOOJP_CLIENT_VERSION "9.0.0.2152" -#define YAHOO_CLIENT_USERAGENT "Mozilla/4.0 (compatible; MSIE 5.5)" +#define YAHOO_CLIENT_USERAGENT "Mozilla/5.0" /* Index into attention types list. */ #define YAHOO_BUZZ 0 @@ -155,6 +155,8 @@ guint txhandler; GHashTable *friends; + char **profiles; /* Multiple profiles can be associated with an account */ + /** * This is used to keep track of the IMVironment chosen * by people you talk to. We don't do very much with diff -r 164b1bc23377 -r 518b4a900f19 libpurple/protocols/yahoo/yahoo_picture.c --- a/libpurple/protocols/yahoo/yahoo_picture.c Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_picture.c Mon Jun 29 06:24:37 2009 +0000 @@ -139,7 +139,7 @@ data->checksum = checksum; /* TODO: Does this need to be MSIE 5.0? */ url_data = purple_util_fetch_url(url, use_whole_url, - "Mozilla/4.0 (compatible; MSIE 5.0)", FALSE, + "Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, yahoo_fetch_picture_cb, data); if (url_data != NULL) { yd = gc->proto_data; @@ -315,8 +315,8 @@ } pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash(pkt, "sssssi", 1, purple_connection_get_display_name(gc), - 4, purple_connection_get_display_name(gc), 5, who, + yahoo_packet_hash(pkt, "ssssi", 1, purple_connection_get_display_name(gc), + 5, who, 13, "2", 20, yd->picture_url, 192, yd->picture_checksum); yahoo_packet_send_and_free(pkt, yd); } @@ -327,7 +327,7 @@ struct yahoo_packet *pkt; pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash_str(pkt, 4, purple_connection_get_display_name(gc)); /* me */ + yahoo_packet_hash_str(pkt, 1, purple_connection_get_display_name(gc)); /* me */ yahoo_packet_hash_str(pkt, 5, who); /* the other guy */ yahoo_packet_hash_str(pkt, 13, "1"); /* 1 = request, 2 = reply */ yahoo_packet_send_and_free(pkt, yd); diff -r 164b1bc23377 -r 518b4a900f19 libpurple/sound-theme-loader.c --- a/libpurple/sound-theme-loader.c Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/sound-theme-loader.c Mon Jun 29 06:24:37 2009 +0000 @@ -25,6 +25,7 @@ #include "sound-theme.h" #include "util.h" #include "xmlnode.h" +#include "debug.h" /***************************************************************************** * Sound Theme Builder @@ -34,8 +35,9 @@ purple_sound_loader_build(const gchar *dir) { xmlnode *root_node = NULL, *sub_node; - gchar *filename_full, *data; + gchar *filename_full, *data = NULL; PurpleSoundTheme *theme = NULL; + const gchar *name; /* Find the theme file */ g_return_val_if_fail(dir != NULL, NULL); @@ -45,30 +47,35 @@ root_node = xmlnode_from_file(dir, "theme.xml", "sound themes", "sound-theme-loader"); g_free(filename_full); - g_return_val_if_fail(root_node != NULL, NULL); + if (root_node == NULL) + return NULL; + + name = xmlnode_get_attrib(root_node, "name"); - /* Parse the tree */ - sub_node = xmlnode_get_child(root_node, "description"); - data = xmlnode_get_data(sub_node); + if (name && purple_strequal(xmlnode_get_attrib(root_node, "type"), "sound")) { + /* 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); + if (xmlnode_get_attrib(root_node, "name") != NULL) { + theme = g_object_new(PURPLE_TYPE_SOUND_THEME, + "type", "sound", + "name", 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"); + 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); + 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); + } } - } + } else purple_debug_warning("sound-theme-loader", "Missing attribute or problem with the root element\n"); xmlnode_free(root_node); g_free(data); diff -r 164b1bc23377 -r 518b4a900f19 libpurple/theme-manager.c --- a/libpurple/theme-manager.c Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/theme-manager.c Mon Jun 29 06:24:37 2009 +0000 @@ -294,3 +294,16 @@ g_hash_table_foreach(theme_table, (GHFunc) purple_theme_manager_function_wrapper, func); } + +PurpleTheme * +purple_theme_manager_load_theme(const gchar *theme_dir, const gchar *type) +{ + PurpleThemeLoader *loader; + + g_return_val_if_fail(theme_dir != NULL && type != NULL, NULL); + + loader = g_hash_table_lookup(theme_table, type); + g_return_val_if_fail(PURPLE_IS_THEME_LOADER(loader), NULL); + + return purple_theme_loader_build(loader, theme_dir); +} diff -r 164b1bc23377 -r 518b4a900f19 libpurple/theme-manager.h --- a/libpurple/theme-manager.h Fri Jun 26 06:40:00 2009 +0000 +++ b/libpurple/theme-manager.h Mon Jun 29 06:24:37 2009 +0000 @@ -127,5 +127,13 @@ */ void purple_theme_manager_for_each_theme(PTFunc func); +/** + * Loads a theme of the given type without adding it to the manager + * + * @param theme_dir the directory of the theme to load + * @param type the type of theme to load + */ +PurpleTheme *purple_theme_manager_load_theme(const gchar *theme_dir, const gchar *type); + G_END_DECLS #endif /* PURPLE_THEME_MANAGER_H */ diff -r 164b1bc23377 -r 518b4a900f19 pidgin/gtkblist-theme-loader.c --- a/pidgin/gtkblist-theme-loader.c Fri Jun 26 06:40:00 2009 +0000 +++ b/pidgin/gtkblist-theme-loader.c Mon Jun 29 06:24:37 2009 +0000 @@ -24,6 +24,8 @@ #include #include "xmlnode.h" +#include "debug.h" +#include "util.h" #include "gtkblist-theme-loader.h" #include "gtkblist-theme.h" @@ -58,8 +60,8 @@ pidgin_blist_loader_build(const gchar *dir) { xmlnode *root_node = NULL, *sub_node, *sub_sub_node; - gchar *filename_full, *data; - const gchar *temp; + gchar *filename_full, *data = NULL; + const gchar *temp, *name; gboolean success = TRUE; GdkColor bgcolor, expanded_bgcolor, collapsed_bgcolor, contact_color; PidginThemeFont *expanded, *collapsed, *contact, *online, *away, *offline, *idle, *message, *message_nick_said, *status; @@ -100,58 +102,81 @@ 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); + if (root_node == NULL) + return NULL; sub_node = xmlnode_get_child(root_node, "description"); data = xmlnode_get_data(sub_node); + name = xmlnode_get_attrib(root_node, "name"); + /* */ - if ((success = (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 - memset(&bgcolor, 0, sizeof(GdkColor)); + success = name && purple_strequal(xmlnode_get_attrib(root_node, "type"), "pidgin buddy list"); + + if (!success) + purple_debug_warning("gtkblist-theme-loader", "Missing attribute or problem with the root element\n"); + + if (success) { + if ((success = (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 + memset(&bgcolor, 0, sizeof(GdkColor)); + + } else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: .\n"); } /* */ - if ((success = (success && (sub_node = xmlnode_get_child(root_node, "groups")) != NULL - && (sub_sub_node = xmlnode_get_child(sub_node, "expanded")) != NULL))) - { - expanded = pidgin_theme_font_parse(sub_sub_node); + if (success) { + if ((success = (sub_node = xmlnode_get_child(root_node, "groups")) != NULL + && (sub_sub_node = xmlnode_get_child(sub_node, "expanded")) != NULL)) { + expanded = pidgin_theme_font_parse(sub_sub_node); - 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 - memset(&expanded_bgcolor, 0, sizeof(GdkColor)); + 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 + memset(&expanded_bgcolor, 0, sizeof(GdkColor)); + + } else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: .\n"); } - if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "collapsed")) != NULL))) - { - collapsed = pidgin_theme_font_parse(sub_sub_node); + if (success) { + if ((success = sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "collapsed")) != NULL)) { + + collapsed = pidgin_theme_font_parse(sub_sub_node); - 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 - memset(&collapsed_bgcolor, 0, sizeof(GdkColor)); + 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 + memset(&collapsed_bgcolor, 0, sizeof(GdkColor)); + + } else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: .\n"); } /* */ - if ((success = (success && (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 (success) { + if ((success = (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; + + } else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: .\n"); } - if ((success = (success && 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 - memset(&contact_color, 0, sizeof(GdkColor)); + if (success) { + if ((success = (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 + memset(&contact_color, 0, sizeof(GdkColor)); + + } else purple_debug_warning("gtkblist-theme-loader", "Missing or problem with tags: .\n"); } for (i = 0; success && lookups[i].tag; i++) { @@ -169,7 +194,7 @@ /* the new theme */ theme = g_object_new(PIDGIN_TYPE_BLIST_THEME, "type", "blist", - "name", xmlnode_get_attrib(root_node, "name"), + "name", name, "author", xmlnode_get_attrib(root_node, "author"), "image", xmlnode_get_attrib(root_node, "image"), "directory", dir, diff -r 164b1bc23377 -r 518b4a900f19 pidgin/gtkblist-theme.c --- a/pidgin/gtkblist-theme.c Fri Jun 26 06:40:00 2009 +0000 +++ b/pidgin/gtkblist-theme.c Mon Jun 29 06:24:37 2009 +0000 @@ -124,7 +124,12 @@ static PidginThemeFont * copy_font_and_color(const PidginThemeFont *pair) { - PidginThemeFont *copy = g_new0(PidginThemeFont, 1); + PidginThemeFont *copy; + + if (pair == NULL) + return NULL; + + copy = g_new0(PidginThemeFont, 1); copy->font = g_strdup(pair->font); strncpy(copy->color, pair->color, sizeof(copy->color) - 1); if (pair->gdkcolor) diff -r 164b1bc23377 -r 518b4a900f19 pidgin/gtkicon-theme-loader.c --- a/pidgin/gtkicon-theme-loader.c Fri Jun 26 06:40:00 2009 +0000 +++ b/pidgin/gtkicon-theme-loader.c Mon Jun 29 06:24:37 2009 +0000 @@ -24,6 +24,7 @@ #include "gtkstatus-icon-theme.h" #include "xmlnode.h" +#include "debug.h" /***************************************************************************** * Icon Theme Builder @@ -33,8 +34,9 @@ pidgin_icon_loader_build(const gchar *dir) { xmlnode *root_node = NULL, *sub_node; - gchar *filename_full, *data; + gchar *filename_full, *data = NULL; PidginIconTheme *theme = NULL; + const gchar *name; /* Find the theme file */ g_return_val_if_fail(dir != NULL, NULL); @@ -44,28 +46,33 @@ root_node = xmlnode_from_file(dir, "theme.xml", "icon themes", "icon-theme-loader"); g_free(filename_full); - g_return_val_if_fail(root_node != NULL, NULL); + if (root_node == NULL) + return NULL; + + name = xmlnode_get_attrib(root_node, "name"); - /* Parse the tree */ - sub_node = xmlnode_get_child(root_node, "description"); - data = xmlnode_get_data(sub_node); + if (name) { + /* 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); + if (xmlnode_get_attrib(root_node, "name") != NULL) { + theme = g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME, + "type", "status-icon", + "name", 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"); + 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); + 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); + } } } diff -r 164b1bc23377 -r 518b4a900f19 pidgin/gtknotify.c --- a/pidgin/gtknotify.c Fri Jun 26 06:40:00 2009 +0000 +++ b/pidgin/gtknotify.c Mon Jun 29 06:24:37 2009 +0000 @@ -557,7 +557,10 @@ gtk_tree_store_remove(treemodel, &iter); advanced = (iter.stamp == 0) ? FALSE : TRUE; #endif - purple_notify_close(PURPLE_NOTIFY_EMAILS, data); + if (data->purple_has_handle) + purple_notify_close(PURPLE_NOTIFY_EMAILS, data); + else + pidgin_close_notify(PURPLE_NOTIFY_EMAILS, data); /* We're completely done if we've processed all entries */ if (!advanced) return NULL; @@ -612,7 +615,7 @@ char *notification; PurpleAccount *account; PidginNotifyMailData *data = NULL, *data2; - gboolean new_data; + gboolean new_data = FALSE; /* Don't bother updating if there aren't new emails and we don't have any displayed currently */ if (count == 0 && mail_dialog == NULL) @@ -660,7 +663,7 @@ /* If we don't keep track of this, will leak "data" for each of the notifications except the last */ data2 = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, 0, FALSE, &new_data); - if (new_data) { + if (data2 && new_data) { if (data) data->purple_has_handle = FALSE; data = data2; @@ -677,7 +680,7 @@ (int)count), *tos, (int)count); data2 = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, count, FALSE, &new_data); - if (new_data) { + if (data2 && new_data) { if (data) data->purple_has_handle = FALSE; data = data2; diff -r 164b1bc23377 -r 518b4a900f19 pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Fri Jun 26 06:40:00 2009 +0000 +++ b/pidgin/gtkprefs.c Mon Jun 29 06:24:37 2009 +0000 @@ -61,6 +61,12 @@ #define PREFS_OPTIMAL_ICON_SIZE 32 +struct theme_info { + gchar *type; + gchar *extension; + gchar *original_name; +}; + static int sound_row_sel = 0; static GtkWidget *prefsnotebook; @@ -74,11 +80,13 @@ 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; +static GtkWidget *prefs_sound_themes_combo_box; +static GtkWidget *prefs_blist_themes_combo_box; +static GtkWidget *prefs_status_themes_combo_box; /* * PROTOTYPES @@ -442,141 +450,6 @@ return row_ref; } -static void theme_install_theme(char *path, char *extn) { -#ifndef _WIN32 - gchar *command; -#endif - gchar *destdir; - gchar *tail; - GtkTreeRowReference *theme_rowref; - - /* Just to be safe */ - g_strchomp(path); - - /* I dont know what you are, get out of here */ - if (extn != NULL) - tail = extn; - else if ((tail = strrchr(path, '.')) == NULL) - return; - - destdir = g_strconcat(purple_user_dir(), G_DIR_SEPARATOR_S "smileys", NULL); - - /* We'll check this just to make sure. This also lets us do something different on - * other platforms, if need be */ - if (!g_ascii_strcasecmp(tail, ".gz") || !g_ascii_strcasecmp(tail, ".tgz")) { -#ifndef _WIN32 - gchar *path_escaped = g_shell_quote(path); - gchar *destdir_escaped = g_shell_quote(destdir); - command = g_strdup_printf("tar > /dev/null xzf %s -C %s", path_escaped, destdir_escaped); - g_free(path_escaped); - g_free(destdir_escaped); -#else - if(!winpidgin_gz_untar(path, destdir)) { - g_free(destdir); - return; - } -#endif - } - else { - g_free(destdir); - return; - } - -#ifndef _WIN32 - /* Fire! */ - if (system(command)) - { - purple_notify_error(NULL, NULL, _("Smiley theme failed to unpack."), NULL); - } - - g_free(command); -#endif - g_free(destdir); - - theme_rowref = theme_refresh_theme_list(); - if (theme_rowref != NULL) { - GtkTreePath *tp = gtk_tree_row_reference_get_path(theme_rowref); - - if (tp) - gtk_tree_selection_select_path(smiley_theme_sel, tp); - gtk_tree_row_reference_free(theme_rowref); - } -} - -static void -theme_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *themedata, size_t len, const gchar *error_message) -{ - FILE *f; - gchar *path; - size_t wc; - - if ((error_message != NULL) || (len == 0)) - return; - - f = purple_mkstemp(&path, TRUE); - wc = fwrite(themedata, len, 1, f); - if (wc != 1) { - purple_debug_warning("theme_got_url", "Unable to write theme data.\n"); - fclose(f); - g_unlink(path); - g_free(path); - return; - } - fclose(f); - - theme_install_theme(path, user_data); - - g_unlink(path); - g_free(path); -} - -static void -theme_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, - GtkSelectionData *sd, guint info, guint t, gpointer data) -{ - gchar *name = (gchar *)sd->data; - - if ((sd->length >= 0) && (sd->format == 8)) { - /* Well, it looks like the drag event was cool. - * Let's do something with it */ - - if (!g_ascii_strncasecmp(name, "file://", 7)) { - GError *converr = NULL; - gchar *tmp; - /* It looks like we're dealing with a local file. Let's - * just untar it in the right place */ - if(!(tmp = g_filename_from_uri(name, NULL, &converr))) { - purple_debug(PURPLE_DEBUG_ERROR, "theme dnd", "%s\n", - (converr ? converr->message : - "g_filename_from_uri error")); - return; - } - theme_install_theme(tmp, NULL); - g_free(tmp); - } else if (!g_ascii_strncasecmp(name, "http://", 7)) { - /* Oo, a web drag and drop. This is where things - * will start to get interesting */ - purple_util_fetch_url(name, TRUE, NULL, FALSE, theme_got_url, ".tgz"); - } else if (!g_ascii_strncasecmp(name, "https://", 8)) { - /* purple_util_fetch_url() doesn't support HTTPS, but we want users - * to be able to drag and drop links from the SF trackers, so - * we'll try it as an HTTP URL. */ - char *tmp = g_strdup(name + 1); - tmp[0] = 'h'; - tmp[1] = 't'; - tmp[2] = 't'; - tmp[3] = 'p'; - purple_util_fetch_url(tmp, TRUE, NULL, FALSE, theme_got_url, ".tgz"); - g_free(tmp); - } - - gtk_drag_finish(dc, TRUE, FALSE, t); - } - - gtk_drag_finish(dc, FALSE, FALSE, t); -} - /* Rebuild the markup for the sound theme selection for "(Custom)" themes */ static void pref_sound_generate_markup() @@ -596,7 +469,7 @@ print_custom = customized && g_str_equal(current_theme, name); - if (g_str_equal(name, "")) + if (!name || *name == '\0') markup = g_strdup_printf("(Default)%s%s - None\nThe default Pidgin sound theme", print_custom ? " " : "", print_custom ? "(Custom)" : ""); else { @@ -617,7 +490,7 @@ } } -/* adds the themes to the theme list from the manager so they can be sisplayed in prefs */ +/* adds the themes to the theme list from the manager so they can be displayed in prefs */ static void prefs_themes_sort(PurpleTheme *theme) { @@ -667,53 +540,388 @@ if (pixbuf != NULL) g_object_unref(G_OBJECT(pixbuf)); } - } -/* init all the theme variables so that the themes can be sorted later and used by pref pages */ static void -prefs_themes_init() +prefs_set_active_theme_combo(GtkWidget *combo_box, GtkListStore *store, const gchar *current_theme) +{ + GtkTreeIter iter; + gchar *theme = NULL; + gboolean unset = TRUE; + + if (current_theme && *current_theme && 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); +} + +static void +prefs_themes_refresh() { GdkPixbuf *pixbuf = NULL; gchar *filename; GtkTreeIter iter; + /* refresh the list of themes in the manager */ + purple_theme_manager_refresh(); + 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_clear(prefs_sound_themes); 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_clear(prefs_blist_themes); gtk_list_store_append(prefs_blist_themes, &iter); - gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, "(Default) - None\n" - "The default Pidgin buddy list theme", 2, "", -1); + gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, + "(Default) - None\n" + "The default Pidgin buddy list theme", 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_clear(prefs_status_icon_themes); gtk_list_store_append(prefs_status_icon_themes, &iter); - gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, "(Default) - None\n" - "The default Pidgin status icon theme", 2, "", -1); - + gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, + "(Default) - None\n" + "The default Pidgin status icon theme", 2, "", -1); g_object_unref(G_OBJECT(pixbuf)); + + purple_theme_manager_for_each_theme(prefs_themes_sort); + pref_sound_generate_markup(); + + /* set active */ + prefs_set_active_theme_combo(prefs_sound_themes_combo_box, prefs_sound_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme")); + prefs_set_active_theme_combo(prefs_blist_themes_combo_box, prefs_blist_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme")); + prefs_set_active_theme_combo(prefs_status_themes_combo_box, prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme")); +} + +/* init all the theme variables so that the themes can be sorted later and used by pref pages */ +static void +prefs_themes_init() +{ + prefs_sound_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + prefs_blist_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + prefs_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); +} + +static PurpleTheme * +prefs_theme_find_theme(const gchar *path, const gchar *type) +{ + PurpleTheme *theme = purple_theme_manager_load_theme(path, type); + GDir *dir = g_dir_open(path, 0, NULL); + const gchar *next; + + while (!PURPLE_IS_THEME(theme) && (next = g_dir_read_name(dir))) { + gchar *next_path = g_build_filename(path, next, NULL); + + if (g_file_test(next_path, G_FILE_TEST_IS_DIR)) + theme = prefs_theme_find_theme(next_path, type); + + g_free(next_path); + } + + g_dir_close(dir); + + return theme; +} + +/* Eww. Seriously ewww. But thanks, grim! This is taken from guifications2 */ +static gboolean +purple_theme_file_copy(const gchar *source, const gchar *destination) +{ + FILE *src, *dest; + gint chr = EOF; + + if(!(src = g_fopen(source, "rb"))) + return FALSE; + if(!(dest = g_fopen(destination, "wb"))) { + fclose(src); + return FALSE; + } + + while((chr = fgetc(src)) != EOF) { + fputc(chr, dest); + } + + fclose(dest); + fclose(src); + + return TRUE; +} + +/* installs a theme, info is freed by function */ +static void +theme_install_theme(char *path, struct theme_info *info) { +#ifndef _WIN32 + gchar *command; +#endif + gchar *destdir, *tail, *type, *original_name; + GtkTreeRowReference *theme_rowref; + gboolean is_smiley_theme, is_archive; + PurpleTheme *theme = NULL; + + if (info == NULL) + return; + + original_name = info->original_name; + type = info->type; + + /* check the extension */ + tail = info->extension ? info->extension : g_strdup(strrchr(path, '.')); + + if (!tail) { + g_free(type); + g_free(original_name); + g_free(info); + return; + } else g_free(info); + + is_archive = !g_ascii_strcasecmp(tail, ".gz") || !g_ascii_strcasecmp(tail, ".tgz"); + + g_free(tail); + + /* Just to be safe */ + g_strchomp(path); + + if ((is_smiley_theme = g_str_equal(type, "smiley"))) + destdir = g_build_filename(purple_user_dir(), "smileys", NULL); + else destdir = g_build_filename(purple_user_dir(), "themes", "temp", NULL); + + /* We'll check this just to make sure. This also lets us do something different on + * other platforms, if need be */ + if (is_archive) { +#ifndef _WIN32 + gchar *path_escaped = g_shell_quote(path); + gchar *destdir_escaped = g_shell_quote(destdir); + + if (!g_file_test(destdir, G_FILE_TEST_IS_DIR)) + purple_build_dir(destdir, S_IRUSR | S_IWUSR | S_IXUSR); + + command = g_strdup_printf("tar > /dev/null xzf %s -C %s", path_escaped, destdir_escaped); + g_free(path_escaped); + g_free(destdir_escaped); + + /* Fire! */ + if (system(command)) { + purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL); + g_free(command); + g_free(destdir); + g_free(type); + g_free(original_name); + return; + } +#else + if(!winpidgin_gz_untar(path, destdir)) { + g_free(destdir); + g_free(type); + g_free(original_name); + return; + } +#endif + } + + if (is_smiley_theme) { + /* just extract the folder to the smiley directory */ + theme_rowref = theme_refresh_theme_list(); + + if (theme_rowref != NULL) { + GtkTreePath *tp = gtk_tree_row_reference_get_path(theme_rowref); + + if (tp) + gtk_tree_selection_select_path(smiley_theme_sel, tp); + + gtk_tree_row_reference_free(theme_rowref); + } + + } else if (is_archive) { + theme = prefs_theme_find_theme(destdir, type); + + if (PURPLE_IS_THEME(theme)) { + /* create the location for the theme */ + gchar *theme_dest = g_build_filename(purple_user_dir(), "themes", + purple_theme_get_name(theme), + "purple", type, NULL); + + if (!g_file_test(theme_dest, G_FILE_TEST_IS_DIR)) + purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR); + + g_free(theme_dest); + theme_dest = g_build_filename(purple_user_dir(), "themes", + purple_theme_get_name(theme), + "purple", type, NULL); + + /* move the entire directory to new location */ + g_rename(purple_theme_get_dir(theme), theme_dest); + + g_free(theme_dest); + g_remove(destdir); + g_object_unref(theme); + + prefs_themes_refresh(); + + } else { + /* something was wrong with the theme archive */ + g_unlink(destdir); + purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL); + } + + } else { /* just a single file so copy it to a new temp directory and attempt to load it*/ + gchar *temp_path, *temp_file; + + temp_path = g_build_filename(purple_user_dir(), "themes", "temp", "sub_folder", NULL); + + if (original_name != NULL) { + /* name was changed from the original (probably a dnd) change it back before loading */ + temp_file = g_build_filename(temp_path, original_name, NULL); + + } else { + gchar *source_name = g_path_get_basename(path); + temp_file = g_build_filename(temp_path, source_name, NULL); + g_free(source_name); + } + + if (!g_file_test(temp_path, G_FILE_TEST_IS_DIR)) + purple_build_dir(temp_path, S_IRUSR | S_IWUSR | S_IXUSR); + + if (purple_theme_file_copy(path, temp_file)) { + /* find the theme, could be in subfolder */ + theme = prefs_theme_find_theme(temp_path, type); + + if (PURPLE_IS_THEME(theme)) { + gchar *theme_dest = g_build_filename(purple_user_dir(), "themes", + purple_theme_get_name(theme), + "purple", type, NULL); + + if(!g_file_test(theme_dest, G_FILE_TEST_IS_DIR)) + purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR); + + g_rename(purple_theme_get_dir(theme), theme_dest); + + g_free(theme_dest); + g_object_unref(theme); + + prefs_themes_refresh(); + } else { + g_remove(temp_path); + purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL); + } + } else { + purple_notify_error(NULL, NULL, _("Theme failed to copy."), NULL); + } + + g_free(temp_file); + g_free(temp_path); + } + + g_free(type); + g_free(original_name); + g_free(destdir); +} + +static void +theme_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data, + const gchar *themedata, size_t len, const gchar *error_message) +{ + FILE *f; + gchar *path; + size_t wc; + + if ((error_message != NULL) || (len == 0)) + return; + + f = purple_mkstemp(&path, TRUE); + wc = fwrite(themedata, len, 1, f); + if (wc != 1) { + purple_debug_warning("theme_got_url", "Unable to write theme data.\n"); + fclose(f); + g_unlink(path); + g_free(path); + return; + } + fclose(f); + + theme_install_theme(path, user_data); + + g_unlink(path); + g_free(path); +} + +static void +theme_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, + GtkSelectionData *sd, guint info, guint t, gpointer user_data) +{ + gchar *name = g_strchomp((gchar *)sd->data); + + if ((sd->length >= 0) && (sd->format == 8)) { + /* Well, it looks like the drag event was cool. + * Let's do something with it */ + gchar *temp; + struct theme_info *info = g_new0(struct theme_info, 1); + info->type = g_strdup((gchar *)user_data); + info->extension = g_strdup(g_strrstr(name,".")); + temp = g_strrstr(name, "/"); + info->original_name = temp ? g_strdup(++temp) : NULL; + + if (!g_ascii_strncasecmp(name, "file://", 7)) { + GError *converr = NULL; + gchar *tmp; + /* It looks like we're dealing with a local file. Let's + * just untar it in the right place */ + if(!(tmp = g_filename_from_uri(name, NULL, &converr))) { + purple_debug(PURPLE_DEBUG_ERROR, "theme dnd", "%s\n", + (converr ? converr->message : + "g_filename_from_uri error")); + return; + } + theme_install_theme(tmp, info); + g_free(tmp); + } else if (!g_ascii_strncasecmp(name, "http://", 7)) { + /* Oo, a web drag and drop. This is where things + * will start to get interesting */ + purple_util_fetch_url(name, TRUE, NULL, FALSE, theme_got_url, info); + } else if (!g_ascii_strncasecmp(name, "https://", 8)) { + /* purple_util_fetch_url() doesn't support HTTPS, but we want users + * to be able to drag and drop links from the SF trackers, so + * we'll try it as an HTTP URL. */ + char *tmp = g_strdup(name + 1); + tmp[0] = 'h'; + tmp[1] = 't'; + tmp[2] = 't'; + tmp[3] = 'p'; + + purple_util_fetch_url(tmp, TRUE, NULL, FALSE, theme_got_url, info); + g_free(tmp); + } + + gtk_drag_finish(dc, TRUE, FALSE, t); + } + + gtk_drag_finish(dc, FALSE, FALSE, t); } /* 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) +prefs_build_theme_combo_box(GtkListStore *store, const gchar *current_theme, gchar *type) { - GtkWidget *combo_box; GtkCellRenderer *cell_rend; - GtkTreeIter iter; - gchar *theme = NULL; - gboolean unset = TRUE; + GtkWidget *combo_box; + GtkTargetEntry te[3] = {{"text/plain", 0, 0},{"text/uri-list", 0, 1},{"STRING", 0, 2}}; g_return_val_if_fail(store != NULL && current_theme != NULL, NULL); @@ -731,21 +939,10 @@ 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); + gtk_drag_dest_set(combo_box, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, te, + sizeof(te) / sizeof(GtkTargetEntry) , GDK_ACTION_COPY | GDK_ACTION_MOVE); + + g_signal_connect(G_OBJECT(combo_box), "drag_data_received", G_CALLBACK(theme_dnd_recv), type); return combo_box; } @@ -757,32 +954,32 @@ gint i; gchar *pref; gchar *new_theme; - gboolean success; GtkTreeIter new_iter; - success = gtk_combo_box_get_active_iter(combo_box, &new_iter); - g_return_if_fail(success); - - 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); + if(gtk_combo_box_get_active_iter(combo_box, &new_iter)) { + + 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); } - - /* 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, @@ -821,11 +1018,18 @@ static void request_theme_file_name_cb(gpointer data, char *theme_file_name) { - theme_install_theme(theme_file_name, NULL) ; + struct theme_info *info = g_new0(struct theme_info, 1); + info->type = g_strdup("smiley"); + info->extension = NULL; + info->original_name = NULL; + + theme_install_theme(theme_file_name, info) ; + + g_free(info); } static void -add_theme_button_clicked_cb(GtkWidget *widget, gpointer null) +add_theme_button_clicked_cb(GtkWidget *widget, gpointer user_data) { purple_request_file(NULL, _("Install Theme"), NULL, FALSE, (GCallback)request_theme_file_name_cb, NULL, NULL, NULL, NULL, NULL) ; } @@ -903,7 +1107,7 @@ gtk_drag_dest_set(view, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, te, sizeof(te) / sizeof(GtkTargetEntry) , GDK_ACTION_COPY | GDK_ACTION_MOVE); - g_signal_connect(G_OBJECT(view), "drag_data_received", G_CALLBACK(theme_dnd_recv), smiley_theme_store); + g_signal_connect(G_OBJECT(view), "drag_data_received", G_CALLBACK(theme_dnd_recv), "smiley"); rend = gtk_cell_renderer_pixbuf_new(); smiley_theme_sel = sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); @@ -1163,35 +1367,42 @@ static void prefs_set_blist_theme_cb(GtkComboBox *combo_box, gpointer user_data) { - PidginBlistTheme *theme = NULL; + PidginBlistTheme *theme = NULL; 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); - - if (name && *name) - theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(name, "blist")); - g_free(name); - - pidgin_blist_set_theme(theme); + if(gtk_combo_box_get_active_iter(combo_box, &iter)) { + + gtk_tree_model_get(GTK_TREE_MODEL(prefs_blist_themes), &iter, 2, &name, -1); + + if(!name || !g_str_equal(name, "")) + 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; + PidginStatusIconTheme *theme = NULL; 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); + if(gtk_combo_box_get_active_iter(combo_box, &iter)) { + + gtk_tree_model_get(GTK_TREE_MODEL(prefs_status_icon_themes), &iter, 2, &name, -1); + + if(!name || !g_str_equal(name, "")) + 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 * @@ -1201,7 +1412,6 @@ GtkWidget *vbox; GtkWidget *vbox2; GtkWidget *label; - GtkWidget *combo_box; GtkSizeGroup *sg; GList *names = NULL; @@ -1213,14 +1423,20 @@ /* 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); + prefs_blist_themes_combo_box = prefs_build_theme_combo_box(prefs_blist_themes, + purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"), + "blist"); + + gtk_box_pack_start(GTK_BOX (vbox), prefs_blist_themes_combo_box, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(prefs_blist_themes_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); + prefs_status_themes_combo_box = prefs_build_theme_combo_box(prefs_status_icon_themes, + purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme"), + "icon"); + + gtk_box_pack_start(GTK_BOX (vbox), prefs_status_themes_combo_box, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(prefs_status_themes_combo_box), "changed", (GCallback)prefs_set_status_icon_theme_cb, NULL); /* System Tray */ vbox = pidgin_make_frame(ret, _("System Tray Icon")); @@ -1430,25 +1646,25 @@ purple_network_set_public_ip(gtk_entry_get_text(entry)); } -static gboolean network_stun_server_changed_cb(GtkWidget *widget, +static gboolean network_stun_server_changed_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data) { GtkEntry *entry = GTK_ENTRY(widget); purple_prefs_set_string("/purple/network/stun_server", gtk_entry_get_text(entry)); purple_network_set_stun_server(gtk_entry_get_text(entry)); - + return FALSE; } -static gboolean network_turn_server_changed_cb(GtkWidget *widget, +static gboolean network_turn_server_changed_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data) { GtkEntry *entry = GTK_ENTRY(widget); purple_prefs_set_string("/purple/network/turn_server", gtk_entry_get_text(entry)); purple_network_set_turn_server(gtk_entry_get_text(entry)); - + return FALSE; } @@ -1622,7 +1838,7 @@ pidgin_prefs_labeled_spin_button(hbox, _("_Port:"), "/purple/network/turn_port", 0, 65535, NULL); - hbox = pidgin_prefs_labeled_entry(vbox, "_Username:", + hbox = pidgin_prefs_labeled_entry(vbox, "_Username:", "/purple/network/turn_username", sg); pidgin_prefs_labeled_password(hbox, "_Password:", "/purple/network/turn_password", NULL); @@ -2205,7 +2421,7 @@ sound_page(void) { GtkWidget *ret; - GtkWidget *vbox, *sw, *button, *combo_box; + GtkWidget *vbox, *sw, *button; GtkSizeGroup *sg; GtkTreeIter iter; GtkWidget *event_view; @@ -2311,11 +2527,14 @@ 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); + prefs_sound_themes_combo_box = prefs_build_theme_combo_box(prefs_sound_themes, + purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"), + "sound"); + + + gtk_box_pack_start(GTK_BOX (vbox), prefs_sound_themes_combo_box, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(prefs_sound_themes_combo_box), "changed", (GCallback)prefs_set_sound_theme_cb, NULL); /* SOUND SELECTION */ sw = gtk_scrolled_window_new(NULL,NULL); @@ -2548,14 +2767,6 @@ return; } - /* Refresh the list of themes before showing the preferences window */ - purple_theme_manager_refresh(); - - /* add everything in the theme 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?) */ @@ -2580,6 +2791,9 @@ prefs_notebook_init(); + /* Refresh the list of themes before showing the preferences window */ + prefs_themes_refresh(); + /* Show everything. */ gtk_widget_show(prefs); } diff -r 164b1bc23377 -r 518b4a900f19 po/ChangeLog --- a/po/ChangeLog Fri Jun 26 06:40:00 2009 +0000 +++ b/po/ChangeLog Mon Jun 29 06:24:37 2009 +0000 @@ -6,6 +6,12 @@ * Slovenian translation updated (Martin Srebotnjak) * Swahili translation added (Paul Msegeya) +version 2.5.8 + * No changes + +version 2.5.7 + * No changes + version 2.5.6 * German translation updated (Björn Vogt) diff -r 164b1bc23377 -r 518b4a900f19 po/de.po --- a/po/de.po Fri Jun 26 06:40:00 2009 +0000 +++ b/po/de.po Mon Jun 29 06:24:37 2009 +0000 @@ -11,9 +11,9 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-06-11 12:42+0200\n" -"PO-Revision-Date: 2009-06-11 12:40+0200\n" -"Last-Translator: Bjoern Voigt \n" +"POT-Creation-Date: 2009-06-28 11:42+0200\n" +"PO-Revision-Date: 2009-06-28 11:42+0200\n" +"Last-Translator: Jochen Kemnade \n" "Language-Team: Deutsch \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -3221,9 +3221,8 @@ msgid "Gadu-Gadu User" msgstr "Gadu-Gadu-Benutzer" -#, fuzzy msgid "GG server" -msgstr "Hole server" +msgstr "GG-Server" #, c-format msgid "Unknown command: %s" @@ -3473,9 +3472,8 @@ msgid "The nickname \"%s\" is already being used." msgstr "Der Spitzname „%s“ existiert bereits." -#, fuzzy msgid "Nickname in use" -msgstr "Spitzname" +msgstr "Spitzname wird benutzt" msgid "Cannot change nick" msgstr "Kann den Spitznamen nicht ändern" @@ -4022,7 +4020,6 @@ msgid "%s ago" msgstr "vor %s" -#, fuzzy msgid "Logged off" msgstr "Abgemeldet" @@ -4223,8 +4220,8 @@ msgid "Invalid XMPP ID. Domain must be set." msgstr "Falsche XMPP-ID. Die Domain muss gesetzt werden." -msgid "Malformed BOSH Connect Server" -msgstr "" +msgid "Malformed BOSH URL" +msgstr "Fehlerhafte BOSH-URL" #, c-format msgid "Registration of %s@%s successful" @@ -4296,6 +4293,9 @@ msgid "Change Registration" msgstr "Ändere Registrierung" +msgid "Malformed BOSH Connect Server" +msgstr "Fehlerhafter BOSH-Verbindungsserver" + msgid "Error unregistering account" msgstr "Fehler beim Aufheben der Kontenregistrierung" @@ -4740,6 +4740,9 @@ msgid "File transfer proxies" msgstr "Proxys für Dateiübertragungen" +msgid "BOSH URL" +msgstr "BOSH-URL" + #. this should probably be part of global smiley theme settings later on, #. shared with MSN msgid "Show Custom Smileys" @@ -5771,15 +5774,17 @@ #, c-format msgid "" -"%s Your password is %d characters, greater than the expected maximum length " -"of %d for MySpaceIM. Please shorten your password at http://profileedit." -"myspace.com/index.cfm?fuseaction=accountSettings.changePassword and try " -"again." -msgstr "" -"%s Ihr Passwort hat %d Buchstaben, mehr als die erwartete maximale Länge von " -"%d für MySpaceIM. Bitte kürzen Sie ihr Passwort unter http://profileedit." -"myspace.com/index.cfm?fuseaction=accountSettings.changePassword und " -"versuchen Sie es erneut." +"%s Your password is %zu characters, which is longer than the maximum length " +"of %d. Please shorten your password at http://profileedit.myspace.com/index." +"cfm?fuseaction=accountSettings.changePassword and try again." +msgstr "" +"%s Ihr Passwort hat %zu Buchstaben, mehr als die erwartete maximale Länge " +"von %d. Bitte kürzen Sie ihr Passwort unter http://profileedit.myspace.com/" +"index.cfm?fuseaction=accountSettings.changePassword und versuchen Sie es " +"erneut." + +msgid "Incorrect username or password" +msgstr "Ungültiger Benutzername oder Passwort" msgid "MySpaceIM Error" msgstr "MySpaceIM-Fehler" @@ -6100,9 +6105,6 @@ msgid "Master archive is misconfigured" msgstr "Master-Archiv ist falsch konfiguriert" -msgid "Incorrect username or password" -msgstr "Ungültiger Benutzername oder Passwort" - msgid "Could not recognize the host of the username you entered" msgstr "" "Konnte den Rechnernamen des Benutzers, den Sie eingegeben haben, nicht " @@ -6633,6 +6635,29 @@ "sein oder mit einem Buchstaben beginnen und nur Buchstaben, Ziffern und " "Leerzeichen enthalten oder nur aus Ziffern bestehen." +#, c-format +msgid "You may be disconnected shortly. If so, check %s for updates." +msgstr "" +"Sie werden eventuell gleich abgemeldet. In diesem Fall, überprüfen Sie %s " +"auf Updates." + +msgid "Unable to get a valid AIM login hash." +msgstr "Konnte keinen gültigen AIM Login-Hash bekommen." + +#, c-format +msgid "You may be disconnected shortly. Check %s for updates." +msgstr "" +"Sie wurden in kurzer Zeit abgemeldet. Überprüfen Sie %s wegen Updates." + +msgid "Unable to get a valid login hash." +msgstr "Konnte keinen gültigen Login-Hash bekommen." + +msgid "Could Not Connect" +msgstr "Verbinden nicht möglich" + +msgid "Received authorization" +msgstr "Autorisierung empfangen" + #. Unregistered username #. uid is not exist msgid "Invalid username." @@ -6650,7 +6675,6 @@ msgstr "Der AOL-Sofortnachrichtendienst ist zur Zeit nicht erreichbar." #. username connecting too frequently -#. IP address connecting too frequently msgid "" "You have been connecting and disconnecting too frequently. Wait ten minutes " "and try again. If you continue to try, you will need to wait even longer." @@ -6664,11 +6688,14 @@ msgstr "" "Die Client-Version, die Sie nutzen ist zu alt. Bitte updaten Sie unter %s" -msgid "Could Not Connect" -msgstr "Verbinden nicht möglich" - -msgid "Received authorization" -msgstr "Autorisierung empfangen" +#. IP address connecting too frequently +msgid "" +"You have been connecting and disconnecting too frequently. Wait a minute and " +"try again. If you continue to try, you will need to wait even longer." +msgstr "" +"Sie haben sich zu schnell an- und abgemeldet. Warten Sie eine Minute und " +"versuchen Sie es noch einmal. Wenn Sie es weiterversuchen, müssen Sie sogar " +"noch länger warten." msgid "The SecurID key entered is invalid." msgstr "Der eingegebene SecurID-Schlüssel ist falsch." @@ -6685,23 +6712,6 @@ msgid "_OK" msgstr "_OK" -#, c-format -msgid "You may be disconnected shortly. If so, check %s for updates." -msgstr "" -"Sie werden eventuell gleich abgemeldet. In diesem Fall, überprüfen Sie %s " -"auf Updates." - -msgid "Unable to get a valid AIM login hash." -msgstr "Konnte keinen gültigen AIM Login-Hash bekommen." - -#, c-format -msgid "You may be disconnected shortly. Check %s for updates." -msgstr "" -"Sie wurden in kurzer Zeit abgemeldet. Überprüfen Sie %s wegen Updates." - -msgid "Unable to get a valid login hash." -msgstr "Konnte keinen gültigen Login-Hash bekommen." - msgid "Password sent" msgstr "Passwort gesendet" @@ -7233,6 +7243,7 @@ msgid "Set User Info (web)..." msgstr "Benutzer-Info (Web) setzen..." +#. This only happens when connecting with the old-style BUCP login msgid "Change Password (web)" msgstr "Ändere Passwort (Web)" @@ -7262,6 +7273,9 @@ msgid "Search for Buddy by Information" msgstr "Suche Buddy nach Information" +msgid "Use clientLogin" +msgstr "clientLogin benutzen" + msgid "" "Always use AIM/ICQ proxy server for\n" "file transfers and direct IM (slower,\n" @@ -9574,26 +9588,26 @@ msgstr "Falsches Passwort" #. security lock from too many failed login attempts -#, fuzzy msgid "" "Account locked: Too many failed login attempts.\n" "Logging into the Yahoo! website may fix this." msgstr "" -"Unbekannte Fehlernummer %d. Vielleicht kann dies repariert werden, wenn Sie " -"sich auf der Yahoo! Webseite anmelden." +"Konto gesperrt: Zu viele fehlgeschlagene Login-Versuche\n" +"Eventuell können Sie dies beheben, wenn Sie sich auf der Yahoo!-Webseite " +"anmelden." #. the username does not exist msgid "Username does not exist" msgstr "Benutzername existiert nicht" #. indicates a lock of some description -#, fuzzy msgid "" "Account locked: Unknown reason.\n" "Logging into the Yahoo! website may fix this." msgstr "" -"Unbekannte Fehlernummer %d. Vielleicht kann dies repariert werden, wenn Sie " -"sich auf der Yahoo! Webseite anmelden." +"Konto gesperrt: Unbekannter Grund\n" +" Eventuell können Sie dies beheben, wenn Sie sich auf der Yahoo!-Webseite " +"anmelden." #. username or password missing msgid "Username or password missing" @@ -9713,8 +9727,8 @@ msgid "Start Doodling" msgstr "Anfangen zu malen" -msgid "Activate which ID?" -msgstr "Welche ID aktivieren?" +msgid "Select the ID you want to activate" +msgstr "Wählen Sie die ID die Sie aktivieren möchten" msgid "Join whom in chat?" msgstr "Wen wollen Sie zum Chat einladen?" @@ -12516,8 +12530,11 @@ msgid "Unknown.... Please report this!" msgstr "Unbekannt.... Bitte berichten Sie dieses Problem!" -msgid "Smiley theme failed to unpack." -msgstr "Smiley-Thema konnte nicht entpackt werden." +msgid "Theme failed to unpack." +msgstr "Thema konnte nicht entpackt werden." + +msgid "Theme failed to load." +msgstr "Thema konnte nicht geladen werden." msgid "Install Theme" msgstr "Thema installieren" @@ -13826,7 +13843,6 @@ msgid "Highlighted Message Name Color" msgstr "Farbe des Absendernamens für hervorgehobene Nachrichten" -#, fuzzy msgid "Typing Notification Color" msgstr "Farbe der Tipp-Benachrichtigung" @@ -13862,19 +13878,17 @@ msgid "Disable Typing Notification Text" msgstr "Tipp-Benachrichtigung aktivieren" -#, fuzzy msgid "GTK+ Theme Control Settings" -msgstr "Pidgin GTK+ Themenkontrolle" - -#, fuzzy +msgstr "Themenkontroll-Einstellungen für GTK+" + msgid "Colors" -msgstr "Schließen" +msgstr "Farben" msgid "Fonts" msgstr "Schrift" msgid "Miscellaneous" -msgstr "" +msgstr "Sonstiges" msgid "Gtkrc File Tools" msgstr "Gtkrc-Datei-Werkzeuge"