qulogic/pidgin

52718b372f38
merge of '5de5b8ab4627efcfd13902254b53cba107899e55'
and '6337e101f6abb348f6dde60a6b38d16f9a305dff'
--- a/ChangeLog Sat Sep 15 20:33:45 2007 +0000
+++ b/ChangeLog Sat Sep 15 20:35:41 2007 +0000
@@ -6,7 +6,10 @@
Pidgin:
* When aliasing someone to an alias that already exists in the
same group, offer to merge the buddies into the same contact
- * 'Move to' submenu in Buddy List context menu
+ * It's possible to keep a conversation (chat/IM) open even after closing
+ the conversation window/tab.
+ * A music emblem is displayed in the buddy list for a buddy if we know she
+ is listening to some soothing music.
Version 2.2.0 (09/13/2007):
http://developer.pidgin.im/query?status=closed&milestone=2.2.0
--- a/finch/gntconn.c Sat Sep 15 20:33:45 2007 +0000
+++ b/finch/gntconn.c Sat Sep 15 20:35:41 2007 +0000
@@ -104,10 +104,11 @@
{
FinchAutoRecon *info;
PurpleAccount *account = purple_connection_get_account(gc);
-
- info = g_hash_table_lookup(hash, account);
+ GList *list;
if (!gc->wants_to_die) {
+ info = g_hash_table_lookup(hash, account);
+
if (info == NULL) {
info = g_new0(FinchAutoRecon, 1);
g_hash_table_insert(hash, account, info);
@@ -140,6 +141,17 @@
g_free(secondary);
purple_account_set_enabled(account, FINCH_UI, FALSE);
}
+
+ /* If we have any open chats, we probably want to rejoin when we get back online. */
+ list = purple_get_chats();
+ while (list) {
+ PurpleConversation *conv = list->data;
+ list = list->next;
+ if (conv->account != account ||
+ purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
+ continue;
+ purple_conversation_set_data(conv, "want-to-rejoin", GINT_TO_POINTER(TRUE));
+ }
}
static void
--- a/finch/gntconv.c Sat Sep 15 20:33:45 2007 +0000
+++ b/finch/gntconv.c Sat Sep 15 20:35:41 2007 +0000
@@ -310,13 +310,41 @@
static void
account_signed_on_off(PurpleConnection *gc, gpointer null)
{
- GList *ims = purple_get_ims();
- while (ims) {
- PurpleConversation *conv = ims->data;
+ GList *list = purple_get_ims();
+ while (list) {
+ PurpleConversation *conv = list->data;
PurpleConversation *cc = find_conv_with_contact(conv->account, conv->name);
if (cc)
generate_send_to_menu(cc->ui_data);
- ims = ims->next;
+ list = list->next;
+ }
+
+ if (PURPLE_CONNECTION_IS_CONNECTED(gc)) {
+ /* We just signed on. Let's see if there's any chat that we have open,
+ * and hadn't left before the disconnect. */
+ list = purple_get_chats();
+ while (list) {
+ PurpleConversation *conv = list->data;
+ gboolean del = FALSE;
+ PurpleChat *chat;
+
+ list = list->next;
+ if (conv->account != gc->account ||
+ !purple_conversation_get_data(conv, "want-to-rejoin"))
+ continue;
+
+ chat = purple_blist_find_chat(conv->account, conv->name);
+ if (chat == NULL) {
+ GHashTable *hash = NULL;
+ if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL)
+ hash = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, conv->name);
+ chat = purple_chat_new(gc->account, conv->name, hash);
+ del = TRUE;
+ }
+ serv_join_chat(gc, chat->components);
+ if (del)
+ purple_blist_remove_chat(chat);
+ }
}
}
--- a/libpurple/protocols/jabber/adhoccommands.c Sat Sep 15 20:33:45 2007 +0000
+++ b/libpurple/protocols/jabber/adhoccommands.c Sat Sep 15 20:35:41 2007 +0000
@@ -151,8 +151,11 @@
/* display result */
xmlnode *note = xmlnode_get_child(command,"note");
- if(note)
- purple_notify_info(NULL, xmlnode_get_attrib(packet, "from"), xmlnode_get_data(note), NULL);
+ if(note) {
+ char *data = xmlnode_get_data(note);
+ purple_notify_info(NULL, xmlnode_get_attrib(packet, "from"), data, NULL);
+ g_free(data);
+ }
if(xdata)
jabber_x_data_request(js, xdata, (jabber_x_data_cb)do_adhoc_ignoreme, NULL);
--- a/libpurple/protocols/jabber/buddy.c Sat Sep 15 20:33:45 2007 +0000
+++ b/libpurple/protocols/jabber/buddy.c Sat Sep 15 20:35:41 2007 +0000
@@ -1455,10 +1455,13 @@
return;
img = purple_base64_decode(b64data, &size);
- if(!img)
+ if(!img) {
+ g_free(b64data);
return;
+ }
purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, img, size, checksum);
+ g_free(b64data);
}
void jabber_buddy_avatar_update_metadata(JabberStream *js, const char *from, xmlnode *items) {
--- a/libpurple/protocols/jabber/jabber.c Sat Sep 15 20:33:45 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c Sat Sep 15 20:35:41 2007 +0000
@@ -813,7 +813,7 @@
if(account->registration_cb)
(account->registration_cb)(account, FALSE, account->registration_cb_user_data);
jabber_connection_schedule_close(cbdata->js);
-}
+ }
g_free(cbdata->who);
g_free(cbdata);
}
@@ -882,12 +882,12 @@
if((href = xmlnode_get_data(url))) {
purple_notify_uri(NULL, href);
g_free(href);
- if(js->registration) {
- js->gc->wants_to_die = TRUE;
- if(account->registration_cb) /* succeeded, but we have no login info */
- (account->registration_cb)(account, TRUE, account->registration_cb_user_data);
- jabber_connection_schedule_close(js);
- }
+ if(js->registration) {
+ js->gc->wants_to_die = TRUE;
+ if(account->registration_cb) /* succeeded, but we have no login info */
+ (account->registration_cb)(account, TRUE, account->registration_cb_user_data);
+ jabber_connection_schedule_close(js);
+ }
return;
}
}
@@ -987,14 +987,14 @@
purple_request_field_group_add_field(group, field);
}
- if((y = xmlnode_get_child(query, "instructions")))
- instructions = xmlnode_get_data(y);
+ if((y = xmlnode_get_child(query, "instructions")))
+ instructions = xmlnode_get_data(y);
else if(registered)
instructions = g_strdup(_("Please fill out the information below "
"to change your account registration."));
- else
- instructions = g_strdup(_("Please fill out the information below "
- "to register your new account."));
+ else
+ instructions = g_strdup(_("Please fill out the information below "
+ "to register your new account."));
cbdata = g_new0(JabberRegisterCBData, 1);
cbdata->js = js;
@@ -1019,8 +1019,8 @@
g_free(title);
}
- g_free(instructions);
- }
+ g_free(instructions);
+}
void jabber_register_start(JabberStream *js)
{
@@ -1531,15 +1531,15 @@
"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
- "tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
- "tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
+ PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
+ PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
NULL);
@@ -1554,15 +1554,15 @@
"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
- "tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
- "tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
+ PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
+ PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
NULL);
@@ -1577,15 +1577,15 @@
"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
- "tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
- "tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
+ PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
+ PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
NULL);
@@ -1600,15 +1600,15 @@
"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
- "tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
- "tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
+ PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
+ PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
NULL);
@@ -1623,15 +1623,15 @@
"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
- "tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
- "tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
- "tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
+ PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
+ PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
+ PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
NULL);
--- a/libpurple/protocols/jabber/message.c Sat Sep 15 20:33:45 2007 +0000
+++ b/libpurple/protocols/jabber/message.c Sat Sep 15 20:35:41 2007 +0000
@@ -324,7 +324,7 @@
if(type) {
if(!strcmp(type, "normal"))
jm->type = JABBER_MESSAGE_NORMAL;
- else if(!strcmp(type, "chat"))
+ else if(!strcmp(type, "chat"))
jm->type = JABBER_MESSAGE_CHAT;
else if(!strcmp(type, "groupchat"))
jm->type = JABBER_MESSAGE_GROUPCHAT;
--- a/libpurple/protocols/jabber/presence.c Sat Sep 15 20:33:45 2007 +0000
+++ b/libpurple/protocols/jabber/presence.c Sat Sep 15 20:35:41 2007 +0000
@@ -192,16 +192,11 @@
jabber_tune_set(js->gc, &tuneinfo);
/* update old values */
- if(js->old_artist)
- g_free(js->old_artist);
- if(js->old_title)
- g_free(js->old_title);
- if(js->old_source)
- g_free(js->old_source);
- if(js->old_uri)
- g_free(js->old_uri);
- if(js->old_track)
- g_free(js->old_track);
+ g_free(js->old_artist);
+ g_free(js->old_title);
+ g_free(js->old_source);
+ g_free(js->old_uri);
+ g_free(js->old_track);
js->old_artist = g_strdup(artist);
js->old_title = g_strdup(title);
js->old_source = g_strdup(source);
--- a/libpurple/protocols/jabber/usernick.c Sat Sep 15 20:33:45 2007 +0000
+++ b/libpurple/protocols/jabber/usernick.c Sat Sep 15 20:35:41 2007 +0000
@@ -33,7 +33,7 @@
xmlnode *item = xmlnode_get_child(items, "item");
JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE);
xmlnode *nick;
- const char *nickname = NULL;
+ char *nickname = NULL;
/* ignore the tune of people not on our buddy list */
if (!buddy || !item)
@@ -43,8 +43,8 @@
if (!nick)
return;
nickname = xmlnode_get_data(nick);
-
serv_got_alias(js->gc, from, nickname);
+ g_free(nickname);
}
static void do_nick_set(JabberStream *js, const char *nick) {
@@ -64,7 +64,7 @@
}
static void do_nick_got_own_nick_cb(JabberStream *js, const char *from, xmlnode *items) {
- const char *oldnickname = NULL;
+ char *oldnickname = NULL;
xmlnode *item = xmlnode_get_child(items,"item");
if(item) {
@@ -77,6 +77,7 @@
_("This information is visible to all contacts on your contact list, so choose something appropriate."),
oldnickname, FALSE, FALSE, NULL, _("Set"), PURPLE_CALLBACK(do_nick_set), _("Cancel"), NULL,
purple_connection_get_account(js->gc), NULL, NULL, js);
+ g_free(oldnickname);
}
static void do_nick_set_nick(PurplePluginAction *action) {
--- a/libpurple/protocols/jabber/usertune.c Sat Sep 15 20:33:45 2007 +0000
+++ b/libpurple/protocols/jabber/usertune.c Sat Sep 15 20:35:41 2007 +0000
@@ -41,48 +41,61 @@
if (!buddy || !item)
return;
- tuneinfodata.artist = "";
- tuneinfodata.title = "";
- tuneinfodata.album = "";
- tuneinfodata.track = "";
+ tuneinfodata.artist = NULL;
+ tuneinfodata.title = NULL;
+ tuneinfodata.album = NULL;
+ tuneinfodata.track = NULL;
tuneinfodata.time = -1;
- tuneinfodata.url = "";
-
+ tuneinfodata.url = NULL;
+
tune = xmlnode_get_child_with_namespace(item, "tune", "http://jabber.org/protocol/tune");
if (!tune)
return;
+ resource = jabber_buddy_find_resource(buddy, NULL);
+ if(!resource)
+ return; /* huh? */
for (tuneinfo = tune->child; tuneinfo; tuneinfo = tuneinfo->next) {
if (tuneinfo->type == XMLNODE_TYPE_TAG) {
if (!strcmp(tuneinfo->name, "artist")) {
- if (tuneinfodata.artist[0] == '\0') /* only pick the first one */
+ if (tuneinfodata.artist == NULL) /* only pick the first one */
tuneinfodata.artist = xmlnode_get_data(tuneinfo);
} else if (!strcmp(tuneinfo->name, "length")) {
if (tuneinfodata.time == -1) {
char *length = xmlnode_get_data(tuneinfo);
if (length)
tuneinfodata.time = strtol(length, NULL, 10);
+ g_free(length);
}
} else if (!strcmp(tuneinfo->name, "source")) {
- if (tuneinfodata.album[0] == '\0') /* only pick the first one */
+ if (tuneinfodata.album == NULL) /* only pick the first one */
tuneinfodata.album = xmlnode_get_data(tuneinfo);
} else if (!strcmp(tuneinfo->name, "title")) {
- if (tuneinfodata.title[0] == '\0') /* only pick the first one */
+ if (tuneinfodata.title == NULL) /* only pick the first one */
tuneinfodata.title = xmlnode_get_data(tuneinfo);
} else if (!strcmp(tuneinfo->name, "track")) {
- if (tuneinfodata.track[0] == '\0') /* only pick the first one */
+ if (tuneinfodata.track == NULL) /* only pick the first one */
tuneinfodata.track = xmlnode_get_data(tuneinfo);
} else if (!strcmp(tuneinfo->name, "uri")) {
- if (tuneinfodata.url[0] == '\0') /* only pick the first one */
+ if (tuneinfodata.url == NULL) /* only pick the first one */
tuneinfodata.url = xmlnode_get_data(tuneinfo);
}
}
}
- resource = jabber_buddy_find_resource(buddy, NULL);
- if(!resource)
- return; /* huh? */
status_id = jabber_buddy_state_get_status_id(resource->state);
- purple_prpl_got_user_status(js->gc->account, from, status_id, PURPLE_TUNE_ARTIST, tuneinfodata.artist, PURPLE_TUNE_TITLE, tuneinfodata.title, PURPLE_TUNE_ALBUM, tuneinfodata.album, PURPLE_TUNE_TRACK, tuneinfodata.track, PURPLE_TUNE_TIME, tuneinfodata.time, PURPLE_TUNE_URL, tuneinfodata.url, NULL);
+ purple_prpl_got_user_status(js->gc->account, from, status_id,
+ PURPLE_TUNE_ARTIST, tuneinfodata.artist,
+ PURPLE_TUNE_TITLE, tuneinfodata.title,
+ PURPLE_TUNE_ALBUM, tuneinfodata.album,
+ PURPLE_TUNE_TRACK, tuneinfodata.track,
+ PURPLE_TUNE_TIME, tuneinfodata.time,
+ PURPLE_TUNE_URL, tuneinfodata.url, NULL);
+
+ g_free(tuneinfodata.artist);
+ g_free(tuneinfodata.title);
+ g_free(tuneinfodata.album);
+ g_free(tuneinfodata.track);
+ g_free(tuneinfodata.url);
}
void jabber_tune_init(void) {
--- a/libpurple/protocols/myspace/myspace.c Sat Sep 15 20:33:45 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.c Sat Sep 15 20:35:41 2007 +0000
@@ -2856,7 +2856,7 @@
}
/** Callbacks called by Purple, to access this plugin. */
-PurplePluginProtocolInfo prpl_info = {
+static PurplePluginProtocolInfo prpl_info = {
/* options */
OPT_PROTO_USE_POINTSIZE /* specify font size in sane point size */
| OPT_PROTO_MAIL_CHECK,
--- a/pidgin/gtkblist.c Sat Sep 15 20:33:45 2007 +0000
+++ b/pidgin/gtkblist.c Sat Sep 15 20:35:41 2007 +0000
@@ -139,12 +139,21 @@
static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded);
static void pidgin_blist_expand_contact_cb(GtkWidget *w, PurpleBlistNode *node);
-struct _pidgin_blist_node {
+typedef enum {
+ PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE = 1 << 0, /* Whether there's pending message in a conversation */
+} PidginBlistNodeFlags;
+
+typedef struct _pidgin_blist_node {
GtkTreeRowReference *row;
gboolean contact_expanded;
gboolean recent_signonoff;
gint recent_signonoff_timer;
-};
+ struct {
+ PurpleConversation *conv;
+ time_t last_message; /* timestamp for last displayed message */
+ PidginBlistNodeFlags flags;
+ } conv;
+} PidginBlistNode;
static char dim_grey_string[8] = "";
static char *dim_grey()
@@ -319,6 +328,23 @@
gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)));
}
+static void gtk_blist_menu_persistent_cb(GtkWidget *w, PurpleChat *chat)
+{
+ purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-persistent",
+ gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)));
+}
+
+static PurpleConversation *
+find_conversation_with_buddy(PurpleBuddy *buddy)
+{
+ PidginBlistNode *ui = buddy->node.ui_data;
+ if (ui)
+ return ui->conv.conv;
+ return purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+ purple_buddy_get_name(buddy),
+ purple_buddy_get_account(buddy));
+}
+
static void gtk_blist_join_chat(PurpleChat *chat)
{
PurpleConversation *conv;
@@ -1392,16 +1418,19 @@
create_chat_menu(PurpleBlistNode *node, PurpleChat *c)
{
GtkWidget *menu;
- gboolean autojoin;
+ gboolean autojoin, persistent;
menu = gtk_menu_new();
autojoin = (purple_blist_node_get_bool(node, "gtk-autojoin") ||
(purple_blist_node_get_string(node, "gtk-autojoin") != NULL));
+ persistent = purple_blist_node_get_bool(node, "gtk-persistent");
pidgin_new_item_from_stock(menu, _("_Join"), PIDGIN_STOCK_CHAT,
G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL);
pidgin_new_check_item(menu, _("Auto-Join"),
G_CALLBACK(gtk_blist_menu_autojoin_cb), node, autojoin);
+ pidgin_new_check_item(menu, _("Persistent"),
+ G_CALLBACK(gtk_blist_menu_persistent_cb), node, persistent);
pidgin_new_item_from_stock(menu, _("View _Log"), NULL,
G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL);
@@ -3280,8 +3309,6 @@
GdkPixbuf *ret;
PurplePresence *p;
-
-
if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
if(!gtknode->contact_expanded) {
buddy = purple_contact_get_priority_buddy((PurpleContact*)node);
@@ -3323,6 +3350,13 @@
return ret;
}
+ if (purple_status_get_attr_string(purple_presence_get_active_status(p), PURPLE_TUNE_TITLE)) {
+ path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL);
+ ret = gdk_pixbuf_new_from_file(path, NULL);
+ g_free(path);
+ return ret;
+ }
+
prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
if (!prpl)
return NULL;
@@ -3396,17 +3430,17 @@
}
if(buddy) {
- PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- purple_buddy_get_name(buddy),
- purple_buddy_get_account(buddy));
+ PurpleConversation *conv = find_conversation_with_buddy(buddy);
PurplePresence *p;
gboolean trans;
if(conv != NULL) {
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
- if((gtkconv == NULL || pidgin_conv_is_hidden(gtkconv)) && size == PIDGIN_STATUS_ICON_SMALL) {
- return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_MESSAGE,
- icon_size, "GtkTreeView");
+ if (gtkconv == NULL && size == PIDGIN_STATUS_ICON_SMALL) {
+ PidginBlistNode *ui = buddy->node.ui_data;
+ if (ui == NULL || (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE))
+ return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview),
+ PIDGIN_STOCK_STATUS_MESSAGE, icon_size, "GtkTreeView");
}
}
@@ -3462,16 +3496,17 @@
struct _pidgin_blist_node *gtkcontactnode = NULL;
char *idletime = NULL, *statustext = NULL;
time_t t;
- PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- purple_buddy_get_name(b),
- purple_buddy_get_account(b));
- PidginConversation *gtkconv;
+ PurpleConversation *conv = find_conversation_with_buddy(b);
gboolean hidden_conv = FALSE;
- if(conv != NULL) {
- gtkconv = PIDGIN_CONVERSATION(conv);
- if(gtkconv == NULL || pidgin_conv_is_hidden(gtkconv)) {
- hidden_conv = TRUE;
+ if (conv != NULL) {
+ PidginBlistNode *ui = b->node.ui_data;
+ if (ui) {
+ if (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE)
+ hidden_conv = TRUE;
+ } else {
+ if (PIDGIN_CONVERSATION(conv) == NULL)
+ hidden_conv = TRUE;
}
}
@@ -3806,7 +3841,7 @@
menu = NULL;
}
- convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 0);
+ convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_ANY, PIDGIN_UNSEEN_TEXT, TRUE, 0);
if (!convs)
/* no conversations added, don't show the menu */
return;
@@ -3862,7 +3897,7 @@
gtkblist->menutrayicon = NULL;
}
- convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 0);
+ convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_ANY, PIDGIN_UNSEEN_TEXT, TRUE, 0);
if (convs) {
GtkWidget *img = NULL;
GString *tooltip_text = NULL;
@@ -3870,14 +3905,10 @@
tooltip_text = g_string_new("");
l = convs;
while (l != NULL) {
- if (PIDGIN_IS_PIDGIN_CONVERSATION(l->data)) {
- PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)l->data);
-
- g_string_append_printf(tooltip_text,
- ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count),
- gtkconv->unseen_count,
- gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)));
- }
+ int count = GPOINTER_TO_INT(purple_conversation_get_data(l->data, "unseen-count"));
+ g_string_append_printf(tooltip_text,
+ ngettext("%d unread message from %s\n", "%d unread messages from %s\n", count),
+ count, purple_conversation_get_name(l->data));
l = l->next;
}
if(tooltip_text->len > 0) {
@@ -3905,6 +3936,88 @@
conversation_updated_cb(conv, PURPLE_CONV_UPDATE_UNSEEN, gtkblist);
}
+static void
+conversation_deleted_update_ui_cb(PurpleConversation *conv, struct _pidgin_blist_node *ui)
+{
+ if (ui->conv.conv != conv)
+ return;
+ ui->conv.conv = NULL;
+ ui->conv.flags = 0;
+ ui->conv.last_message = 0;
+}
+
+static void
+written_msg_update_ui_cb(PurpleAccount *account, const char *who, const char *message,
+ PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node)
+{
+ PidginBlistNode *ui = node->ui_data;
+ if (ui->conv.conv != conv || PIDGIN_CONVERSATION(conv) ||
+ !(flag & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)))
+ return;
+ ui->conv.flags |= PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE;
+ ui->conv.last_message = time(NULL); /* XXX: for lack of better data */
+ pidgin_blist_update(purple_get_blist(), node);
+}
+
+static void
+displayed_msg_update_ui_cb(PurpleAccount *account, const char *who, const char *message,
+ PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node)
+{
+ PidginBlistNode *ui = node->ui_data;
+ if (ui->conv.conv != conv)
+ return;
+ ui->conv.flags &= ~PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE;
+ pidgin_blist_update(purple_get_blist(), node);
+}
+
+static void
+conversation_created_cb(PurpleConversation *conv, PidginBuddyList *gtkblist)
+{
+ switch (conv->type) {
+ case PURPLE_CONV_TYPE_IM:
+ {
+ GSList *buddies = purple_find_buddies(conv->account, conv->name);
+ while (buddies) {
+ PurpleBlistNode *buddy = buddies->data;
+ struct _pidgin_blist_node *ui = buddy->ui_data;
+ buddies = g_slist_delete_link(buddies, buddies);
+ if (!ui)
+ continue;
+ ui->conv.conv = conv;
+ ui->conv.flags = 0;
+ ui->conv.last_message = 0;
+ purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
+ ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui);
+ purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg",
+ ui, PURPLE_CALLBACK(written_msg_update_ui_cb), buddy);
+ purple_signal_connect(pidgin_conversations_get_handle(), "displayed-im-msg",
+ ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), buddy);
+ }
+ }
+ case PURPLE_CONV_TYPE_CHAT:
+ {
+ PurpleChat *chat = purple_blist_find_chat(conv->account, conv->name);
+ struct _pidgin_blist_node *ui;
+ if (!chat)
+ break;
+ ui = chat->node.ui_data;
+ if (!ui)
+ break;
+ ui->conv.conv = conv;
+ ui->conv.flags = 0;
+ ui->conv.last_message = 0;
+ purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
+ ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui);
+ purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg",
+ ui, PURPLE_CALLBACK(written_msg_update_ui_cb), chat);
+ purple_signal_connect(pidgin_conversations_get_handle(), "displayed-chat-msg",
+ ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), chat);
+ }
+ default:
+ break;
+ }
+}
+
/**********************************************************************************
* Public API Functions *
**********************************************************************************/
@@ -4818,6 +4931,9 @@
purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
gtkblist, PURPLE_CALLBACK(conversation_deleting_cb),
gtkblist);
+ purple_signal_connect(purple_conversations_get_handle(), "conversation-created",
+ gtkblist, PURPLE_CALLBACK(conversation_created_cb),
+ gtkblist);
gtk_widget_hide(gtkblist->headline_hbox);
gtk_widget_hide(gtkblist->error_buttons);
@@ -4924,6 +5040,7 @@
if(gtknode->recent_signonoff_timer > 0)
purple_timeout_remove(gtknode->recent_signonoff_timer);
+ purple_signals_disconnect_by_handle(node->ui_data);
g_free(node->ui_data);
node->ui_data = NULL;
}
@@ -5348,14 +5465,17 @@
GdkPixbuf *emblem;
char *mark;
gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
- const char *name = purple_chat_get_name(chat);
- PurpleConversation *conv =
- purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, chat->account);
- gboolean hidden = (conv && !PIDGIN_CONVERSATION(conv));
-
- if(!insert_node(list, node, &iter))
+ PidginBlistNode *ui;
+ PurpleConversation *conv;
+ gboolean hidden;
+
+ if (!insert_node(list, node, &iter))
return;
+ ui = node->ui_data;
+ conv = ui->conv.conv;
+ hidden = (conv && (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE));
+
status = pidgin_blist_get_status_icon(node,
PIDGIN_STATUS_ICON_SMALL);
emblem = pidgin_blist_get_emblem(node);
--- a/pidgin/gtkconv.c Sat Sep 15 20:33:45 2007 +0000
+++ b/pidgin/gtkconv.c Sat Sep 15 20:35:41 2007 +0000
@@ -69,6 +69,8 @@
#include "gtknickcolors.h"
+#define CLOSE_CONV_TIMEOUT_SECS (10 * 60)
+
#define AUTO_RESPONSE "&lt;AUTO-REPLY&gt; : "
typedef enum
@@ -122,7 +124,6 @@
static GtkWidget *invite_dialog = NULL;
static GtkWidget *warn_close_dialog = NULL;
-static PidginWindow *hidden_convwin = NULL;
static GList *window_list = NULL;
/* Lists of status icons at all available sizes for use as window icons */
@@ -160,6 +161,7 @@
static gboolean infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv);
static gboolean pidgin_userlist_motion_cb (GtkWidget *w, GdkEventMotion *event, PidginConversation *gtkconv);
static void pidgin_conv_leave_cb (GtkWidget *w, GdkEventCrossing *e, PidginConversation *gtkconv);
+static void hide_conv(PidginConversation *gtkconv, gboolean closetimer);
static void pidgin_conv_set_position_size(PidginWindow *win, int x, int y,
int width, int height);
@@ -207,12 +209,49 @@
**************************************************************************/
static gboolean
-close_conv_cb(GtkWidget *w, GdkEventButton *event, PidginConversation *gtkconv)
-{
+close_this_sucker(gpointer data)
+{
+ PidginConversation *gtkconv = data;
GList *list = g_list_copy(gtkconv->convs);
-
g_list_foreach(list, (GFunc)purple_conversation_destroy, NULL);
g_list_free(list);
+ return FALSE;
+}
+
+static gboolean
+close_conv_cb(GtkWidget *w, GdkEventButton *event, PidginConversation *gtkconv)
+{
+ /* We are going to destroy the conversations immediately only if the 'close immediately'
+ * preference is selected. Otherwise, close the conversation after a reasonable timeout
+ * (I am going to consider 10 minutes as a 'reasonable timeout' here.
+ * For chats, close immediately if the chat is not in the buddylist, or if the chat is
+ * not marked 'Persistent' */
+ PurpleConversation *conv = gtkconv->active_conv;
+ PurpleAccount *account = purple_conversation_get_account(conv);
+ const char *name = purple_conversation_get_name(conv);
+
+ switch (purple_conversation_get_type(conv)) {
+ case PURPLE_CONV_TYPE_IM:
+ {
+ if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately"))
+ close_this_sucker(gtkconv);
+ else
+ hide_conv(gtkconv, TRUE);
+ break;
+ }
+ case PURPLE_CONV_TYPE_CHAT:
+ {
+ PurpleChat *chat = purple_blist_find_chat(account, name);
+ if (!chat ||
+ !purple_blist_node_get_bool(&chat->node, "gtk-persistent"))
+ close_this_sucker(gtkconv);
+ else
+ hide_conv(gtkconv, FALSE);
+ break;
+ }
+ default:
+ ;
+ }
return TRUE;
}
@@ -1314,18 +1353,33 @@
add_remove_cb(NULL, PIDGIN_CONVERSATION(conv));
}
-#if 0
-static void
-menu_hide_conv_cb(gpointer data, guint action, GtkWidget *widget)
-{
- PidginWindow *win = data;
- PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(win);
- PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win);
+static gboolean
+close_already(gpointer data)
+{
+ purple_conversation_destroy(data);
+ return FALSE;
+}
+
+static void
+hide_conv(PidginConversation *gtkconv, gboolean closetimer)
+{
+ GList *list;
+
purple_signal_emit(pidgin_conversations_get_handle(),
"conversation-hiding", gtkconv);
- purple_conversation_set_ui_ops(conv, NULL);
-}
-#endif
+
+ for (list = g_list_copy(gtkconv->convs); list; list = g_list_delete_link(list, list)) {
+ PurpleConversation *conv = list->data;
+ if (closetimer) {
+ guint timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
+ if (timer)
+ purple_timeout_remove(timer);
+ timer = purple_timeout_add_seconds(CLOSE_CONV_TIMEOUT_SECS, close_already, conv);
+ purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(timer));
+ }
+ purple_conversation_set_ui_ops(conv, NULL);
+ }
+}
static void
menu_close_conv_cb(gpointer data, guint action, GtkWidget *widget)
@@ -2340,63 +2394,69 @@
return get_prpl_icon_list(account);
}
-GdkPixbuf *
-pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon)
-{
- PurpleAccount *account = NULL;
- const char *name = NULL;
- GdkPixbuf *status = NULL;
- PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
- const char *icon_size = small_icon ? PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC : PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL;
- g_return_val_if_fail(conv != NULL, NULL);
-
- account = purple_conversation_get_account(conv);
- name = purple_conversation_get_name(conv);
-
- g_return_val_if_fail(account != NULL, NULL);
- g_return_val_if_fail(name != NULL, NULL);
-
- /* Use the buddy icon, if possible */
- if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- PurpleBuddy *b = purple_find_buddy(account, name);
- if (b != NULL) {
+static GdkPixbuf *
+pidgin_conv_get_icon(PurpleConversation *conv, GtkWidget *parent, const char *icon_size)
+{
+ PurpleAccount *account = NULL;
+ const char *name = NULL;
+ GdkPixbuf *status = NULL;
+ PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+ g_return_val_if_fail(conv != NULL, NULL);
+
+ account = purple_conversation_get_account(conv);
+ name = purple_conversation_get_name(conv);
+
+ g_return_val_if_fail(account != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ /* Use the buddy icon, if possible */
+ if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+ PurpleBuddy *b = purple_find_buddy(account, name);
+ if (b != NULL) {
PurplePresence *p = purple_buddy_get_presence(b);
- /* I hate this hack. It fixes a bug where the pending message icon
- * displays in the conv tab even though it shouldn't.
- * A better solution would be great. */
- if (ops && ops->update)
- ops->update(NULL, (PurpleBlistNode*)b);
+ /* I hate this hack. It fixes a bug where the pending message icon
+ * displays in the conv tab even though it shouldn't.
+ * A better solution would be great. */
+ if (ops && ops->update)
+ ops->update(NULL, (PurpleBlistNode*)b);
/* XXX Seanegan: We really need a util function to return a pixbuf for a Presence to avoid all this switching */
if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AWAY))
- status = pidgin_create_status_icon(PURPLE_STATUS_AWAY, PIDGIN_CONVERSATION(conv)->icon, icon_size);
+ status = pidgin_create_status_icon(PURPLE_STATUS_AWAY, parent, icon_size);
else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_EXTENDED_AWAY))
- status = pidgin_create_status_icon(PURPLE_STATUS_EXTENDED_AWAY, PIDGIN_CONVERSATION(conv)->icon, icon_size);
- else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_OFFLINE))
- status = pidgin_create_status_icon(PURPLE_STATUS_OFFLINE, PIDGIN_CONVERSATION(conv)->icon, icon_size);
- else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AVAILABLE))
- status = pidgin_create_status_icon(PURPLE_STATUS_AVAILABLE, PIDGIN_CONVERSATION(conv)->icon, icon_size);
- else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_INVISIBLE))
- status = pidgin_create_status_icon(PURPLE_STATUS_INVISIBLE, PIDGIN_CONVERSATION(conv)->icon, icon_size);
- else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_UNAVAILABLE))
- status = pidgin_create_status_icon(PURPLE_STATUS_UNAVAILABLE, PIDGIN_CONVERSATION(conv)->icon, icon_size);
- }
- }
-
- /* If they don't have a buddy icon, then use the PRPL icon */
- if (status == NULL) {
+ status = pidgin_create_status_icon(PURPLE_STATUS_EXTENDED_AWAY, parent, icon_size);
+ else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_OFFLINE))
+ status = pidgin_create_status_icon(PURPLE_STATUS_OFFLINE, parent, icon_size);
+ else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AVAILABLE))
+ status = pidgin_create_status_icon(PURPLE_STATUS_AVAILABLE, parent, icon_size);
+ else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_INVISIBLE))
+ status = pidgin_create_status_icon(PURPLE_STATUS_INVISIBLE, parent, icon_size);
+ else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_UNAVAILABLE))
+ status = pidgin_create_status_icon(PURPLE_STATUS_UNAVAILABLE, parent, icon_size);
+ }
+ }
+
+ /* If they don't have a buddy icon, then use the PRPL icon */
+ if (status == NULL) {
GtkIconSize size = gtk_icon_size_from_name(icon_size);
if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
- status = gtk_widget_render_icon (PIDGIN_CONVERSATION(conv)->icon, PIDGIN_STOCK_STATUS_PERSON,
- size, "GtkWidget");
+ status = gtk_widget_render_icon (parent, PIDGIN_STOCK_STATUS_PERSON,
+ size, "GtkWidget");
} else {
- status = gtk_widget_render_icon (PIDGIN_CONVERSATION(conv)->icon, PIDGIN_STOCK_STATUS_CHAT,
- size, "GtkWidget");
+ status = gtk_widget_render_icon (parent, PIDGIN_STOCK_STATUS_CHAT,
+ size, "GtkWidget");
}
}
return status;
}
+GdkPixbuf *
+pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon)
+{
+ const char *icon_size = small_icon ? PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC : PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL;
+ return pidgin_conv_get_icon(conv, PIDGIN_CONVERSATION(conv)->icon, icon_size);
+}
+
static void
update_tab_icon(PurpleConversation *conv)
@@ -2748,9 +2808,9 @@
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
GdkModifierType state;
- if(gtkconv->win==hidden_convwin) {
- pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
- pidgin_conv_placement_place(gtkconv);
+ if (gtkconv == NULL) {
+ pidgin_conv_attach_to_conversation(conv);
+ gtkconv = PIDGIN_CONVERSATION(conv);
}
pidgin_conv_switch_active_conversation(conv);
@@ -2783,15 +2843,19 @@
PurpleConversation *conv = (PurpleConversation*)l->data;
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
- if(gtkconv == NULL || gtkconv->active_conv != conv)
+ if (gtkconv != NULL && gtkconv->active_conv != conv)
continue;
-
- if (gtkconv->unseen_state >= min_state
- && (!hidden_only ||
- (hidden_only && gtkconv->win == hidden_convwin))) {
-
+ if (gtkconv == NULL) {
+ if (!hidden_only ||
+ !purple_conversation_get_data(conv, "unseen-count"))
+ continue;
r = g_list_prepend(r, conv);
c++;
+ } else {
+ if (gtkconv->unseen_state >= min_state && !hidden_only) {
+ r = g_list_prepend(r, conv);
+ c++;
+ }
}
}
@@ -2831,11 +2895,11 @@
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
GtkWidget *icon = gtk_image_new();
- GdkPixbuf *pbuf = pidgin_conv_get_tab_icon(conv, TRUE);
+ GdkPixbuf *pbuf = pidgin_conv_get_icon(conv, icon, PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC);
GtkWidget *item;
gchar *text = g_strdup_printf("%s (%d)",
- gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)),
- gtkconv->unseen_count);
+ gtkconv ? gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)) : purple_conversation_get_name(conv),
+ gtkconv ? gtkconv->unseen_count : GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")));
gtk_image_set_from_pixbuf(GTK_IMAGE(icon), pbuf);
g_object_unref(pbuf);
@@ -3093,7 +3157,7 @@
PurpleConversation *conv;
GtkWidget *item;
- if (win->window == NULL || win == hidden_convwin)
+ if (win->window == NULL)
return;
gtkconv = pidgin_conv_window_get_active_gtkconv(win);
@@ -4942,6 +5006,9 @@
GtkWidget *tab_cont;
PurpleBlistNode *convnode;
+ if (hidden)
+ return;
+
if (conv_type == PURPLE_CONV_TYPE_IM && (gtkconv = pidgin_conv_find_gtkconv(conv))) {
conv->ui_data = gtkconv;
if (!g_list_find(gtkconv->convs, conv))
@@ -5041,10 +5108,7 @@
G_CALLBACK(gtk_widget_grab_focus),
gtkconv->entry);
- if (hidden)
- pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv);
- else
- pidgin_conv_placement_place(gtkconv);
+ pidgin_conv_placement_place(gtkconv);
if (nick_colors == NULL) {
nbr_nick_colors = NUM_NICK_COLORS;
@@ -5052,11 +5116,13 @@
}
}
+#if 0
static void
pidgin_conv_new_hidden(PurpleConversation *conv)
{
private_gtkconv_new(conv, TRUE);
}
+#endif
void
pidgin_conv_new(PurpleConversation *conv)
@@ -5069,26 +5135,22 @@
PurpleConversation *conv, PurpleMessageFlags flags)
{
PurpleConversationUiOps *ui_ops = pidgin_conversations_get_conv_ui_ops();
- if (conv != NULL)
- return;
/* create hidden conv if hide_new pref is always */
- if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0)
- {
- ui_ops->create_conversation = pidgin_conv_new_hidden;
- purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
- ui_ops->create_conversation = pidgin_conv_new;
- return;
- }
-
- /* create hidden conv if hide_new pref is away and account is away */
- if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 &&
- !purple_status_is_available(purple_account_get_active_status(account)))
- {
- ui_ops->create_conversation = pidgin_conv_new_hidden;
- purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
- ui_ops->create_conversation = pidgin_conv_new;
- return;
+ /* or if hide_new pref is away and account is away */
+ if ((strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0) ||
+ (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 &&
+ !purple_status_is_available(purple_account_get_active_status(account)))) {
+ if (!conv) {
+ ui_ops->create_conversation = NULL;
+ conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
+ purple_conversation_set_ui_ops(conv, NULL);
+ ui_ops->create_conversation = pidgin_conv_new;
+ }
+ } else {
+ /* new message for an IM */
+ if (conv && conv->type == PURPLE_CONV_TYPE_IM)
+ pidgin_conv_attach_to_conversation(conv);
}
}
@@ -5097,6 +5159,9 @@
{
PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+ if (!gtkconv)
+ return;
+
gtkconv->convs = g_list_remove(gtkconv->convs, conv);
/* Don't destroy ourselves until all our convos are gone */
if (gtkconv->convs) {
@@ -6569,6 +6634,19 @@
pidgin_conv_update_fields(conv, flags);
}
+static void
+wrote_msg_update_unseen_cb(PurpleAccount *account, const char *who, const char *message,
+ PurpleConversation *conv, PurpleMessageFlags flag, gpointer null)
+{
+ if (conv == NULL || PIDGIN_IS_PIDGIN_CONVERSATION(conv))
+ return;
+ if (flag & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)) {
+ purple_conversation_set_data(conv, "unseen-count",
+ GINT_TO_POINTER(GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")) + 1));
+ purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN);
+ }
+}
+
static PurpleConversationUiOps conversation_ui_ops =
{
pidgin_conv_new,
@@ -7066,6 +7144,7 @@
account_status_changed_cb(PurpleAccount *account, PurpleStatus *oldstatus,
PurpleStatus *newstatus)
{
+#if 0
GList *l;
PurpleConversation *conv = NULL;
PidginConversation *gtkconv;
@@ -7075,27 +7154,7 @@
if(purple_status_is_available(oldstatus) || !purple_status_is_available(newstatus))
return;
-
- while ((l = hidden_convwin->gtkconvs) != NULL)
- {
- gtkconv = l->data;
-
- conv = gtkconv->active_conv;
-
- while(l && !purple_status_is_available(
- purple_account_get_active_status(
- purple_conversation_get_account(conv))))
- l = l->next;
- if (!l)
- break;
-
- pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
- pidgin_conv_placement_place(gtkconv);
-
- /* TODO: do we need to do anything for any other conversations that are in the same gtkconv here?
- * I'm a little concerned that not doing so will cause the "pending" indicator in the gtkblist not to be cleared. -DAA*/
- purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN);
- }
+#endif
}
static void
@@ -7103,32 +7162,25 @@
gconstpointer value, gpointer data)
{
GList *l;
- PurpleConversation *conv = NULL;
- PidginConversation *gtkconv;
gboolean when_away = FALSE;
- if(!hidden_convwin)
- return;
-
if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always")==0)
return;
if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away")==0)
when_away = TRUE;
- while ((l = hidden_convwin->gtkconvs) != NULL)
+ for (l = purple_get_conversations(); l; l = l->next)
{
- gtkconv = l->data;
-
- conv = gtkconv->active_conv;
-
+ PurpleConversation *conv = l->data;
+ PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+ if (gtkconv)
+ continue;
if(when_away && !purple_status_is_available(
purple_account_get_active_status(
purple_conversation_get_account(conv))))
continue;
-
- pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
- pidgin_conv_placement_place(gtkconv);
+ pidgin_conv_attach_to_conversation(conv);
}
}
@@ -7311,9 +7363,15 @@
PidginConversation *gtkconv = data;
int count = 0;
int timer = gtkconv->attach.timer;
+ time_t when = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtkconv->entry), "attach-start-time"));
+
gtkconv->attach.timer = 0;
while (gtkconv->attach.current && count < 100) { /* XXX: 100 is a random value here */
PurpleConvMessage *msg = gtkconv->attach.current->data;
+ if (when && when < msg->when) {
+ gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR><HR>", 0);
+ g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
+ }
pidgin_conv_write_conv(gtkconv->active_conv, msg->who, msg->who, msg->what, msg->flags, msg->when);
gtkconv->attach.current = gtkconv->attach.current->prev;
count++;
@@ -7333,18 +7391,21 @@
{
GList *list;
PidginConversation *gtkconv;
+ int timer;
if (PIDGIN_IS_PIDGIN_CONVERSATION(conv))
return FALSE;
+ purple_conversation_set_data(conv, "unseen-count", NULL);
purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops());
private_gtkconv_new(conv, FALSE);
gtkconv = PIDGIN_CONVERSATION(conv);
list = purple_conversation_get_message_history(conv);
if (list) {
- list = g_list_last(list);
- gtkconv->attach.current = list;
+ g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time",
+ GINT_TO_POINTER(((PurpleConvMessage*)(list->data))->when));
+ gtkconv->attach.current = g_list_last(list);
gtkconv->attach.timer = g_idle_add(add_message_history_to_gtkconv, gtkconv);
} else {
purple_signal_emit(pidgin_conversations_get_handle(),
@@ -7356,6 +7417,10 @@
pidgin_conv_chat_add_users(conv, PURPLE_CONV_CHAT(conv)->in_room, TRUE);
}
+ timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
+ if (timer)
+ purple_timeout_remove(timer);
+
return TRUE;
}
@@ -7421,6 +7486,7 @@
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", TRUE);
purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "never");
+ purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", FALSE);
#ifdef _WIN32
purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", FALSE);
@@ -7584,9 +7650,6 @@
purple_conversations_set_ui_ops(&conversation_ui_ops);
- hidden_convwin = pidgin_conv_window_new();
- window_list = g_list_remove(window_list, hidden_convwin);
-
purple_signal_connect(purple_accounts_get_handle(), "account-status-changed",
handle, PURPLE_CALLBACK(account_status_changed_cb), NULL);
@@ -7622,6 +7685,10 @@
purple_signal_connect_priority(purple_conversations_get_handle(), "conversation-updated", handle,
PURPLE_CALLBACK(pidgin_conv_updated), NULL,
PURPLE_SIGNAL_PRIORITY_LOWEST);
+ purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg", handle,
+ PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL);
+ purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg", handle,
+ PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL);
}
void
@@ -7630,8 +7697,6 @@
purple_prefs_disconnect_by_handle(pidgin_conversations_get_handle());
purple_signals_disconnect_by_handle(pidgin_conversations_get_handle());
purple_signals_unregister_by_instance(pidgin_conversations_get_handle());
- pidgin_conv_window_destroy(hidden_convwin);
- hidden_convwin=NULL;
}
@@ -9048,7 +9113,7 @@
if (win->gtkconvs && win->gtkconvs->next == NULL)
pidgin_conv_tab_pack(win, win->gtkconvs->data);
- if (!win->gtkconvs && win != hidden_convwin)
+ if (!win->gtkconvs)
pidgin_conv_window_destroy(win);
}
@@ -9587,9 +9652,7 @@
gboolean
pidgin_conv_is_hidden(PidginConversation *gtkconv)
{
- g_return_val_if_fail(gtkconv != NULL, FALSE);
-
- return (gtkconv->win == hidden_convwin);
+ return (gtkconv == NULL);
}
@@ -9690,3 +9753,4 @@
return colors;
}
+
--- a/pidgin/gtkprefs.c Sat Sep 15 20:33:45 2007 +0000
+++ b/pidgin/gtkprefs.c Sat Sep 15 20:35:41 2007 +0000
@@ -993,6 +993,8 @@
pidgin_prefs_checkbox(_("Show _formatting on incoming messages"),
PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", vbox);
+ pidgin_prefs_checkbox(_("Close IMs immediately when the tab is closed"),
+ PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", vbox);
iconpref1 = pidgin_prefs_checkbox(_("Show _detailed information"),
PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", vbox);
--- a/pidgin/pixmaps/emblems/16/Makefile.am Sat Sep 15 20:33:45 2007 +0000
+++ b/pidgin/pixmaps/emblems/16/Makefile.am Sat Sep 15 20:35:41 2007 +0000
@@ -12,6 +12,7 @@
hiptop.png \
male.png \
mobile.png \
+ music.png \
not-authorized.png \
operator.png \
qq-member.png \
Binary file pidgin/pixmaps/emblems/16/music.png has changed
--- a/pidgin/pixmaps/emblems/16/scalable/Makefile.am Sat Sep 15 20:33:45 2007 +0000
+++ b/pidgin/pixmaps/emblems/16/scalable/Makefile.am Sat Sep 15 20:35:41 2007 +0000
@@ -7,6 +7,7 @@
game.svg \
male.svg \
mobile.svg \
+ music.svg \
not-authorized.svg \
qq-member.svg \
secure.svg \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pixmaps/emblems/16/scalable/music.svg Sat Sep 15 20:35:41 2007 +0000
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45"
+ sodipodi:modified="true"
+ version="1.0">
+ <defs
+ id="defs4">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3185">
+ <stop
+ style="stop-color:#4e9a06;stop-opacity:1;"
+ offset="0"
+ id="stop3187" />
+ <stop
+ style="stop-color:#4e9a06;stop-opacity:0"
+ offset="1"
+ id="stop3189" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3177">
+ <stop
+ style="stop-color:#4e9a06;stop-opacity:1;"
+ offset="0"
+ id="stop3179" />
+ <stop
+ style="stop-color:#4e9a06;stop-opacity:0;"
+ offset="1"
+ id="stop3181" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3153">
+ <stop
+ style="stop-color:#eeeeec;stop-opacity:1;"
+ offset="0"
+ id="stop3155" />
+ <stop
+ style="stop-color:#eeeeec;stop-opacity:0;"
+ offset="1"
+ id="stop3157" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3153"
+ id="linearGradient3159"
+ x1="2.5409546"
+ y1="10.048674"
+ x2="10.378205"
+ y2="15.928688"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3177"
+ id="radialGradient3183"
+ cx="5.2116022"
+ cy="8.4051199"
+ fx="5.2116022"
+ fy="8.4051199"
+ r="2.9404981"
+ gradientTransform="matrix(2.6050387,0,0,2.2888674,-8.415579,-10.767812)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3185"
+ id="radialGradient3191"
+ cx="5.1978397"
+ cy="8.4135866"
+ fx="5.1978397"
+ fy="8.4135866"
+ r="3.1428281"
+ gradientTransform="matrix(2.8202152,0,0,2.4999643,-9.461187,-12.455954)"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="22.4"
+ inkscape:cx="19.784002"
+ inkscape:cy="11.848"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ width="16px"
+ height="16px"
+ showgrid="true"
+ inkscape:window-width="1440"
+ inkscape:window-height="847"
+ inkscape:window-x="0"
+ inkscape:window-y="22" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:#555753;fill-opacity:1;stroke:#222728;stroke-width:1.14297926;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path2160"
+ sodipodi:cx="6.4712896"
+ sodipodi:cy="14.484771"
+ sodipodi:rx="3.5986683"
+ sodipodi:ry="2.1781414"
+ d="M 10.069958 14.484771 A 3.5986683 2.1781414 0 1 1 2.8726213,14.484771 A 3.5986683 2.1781414 0 1 1 10.069958 14.484771 z"
+ transform="matrix(0.8336417,0,0,0.918214,4.1052631,-0.8001194)" />
+ <rect
+ style="opacity:1;fill:#222728;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3143"
+ width="1"
+ height="12"
+ x="12"
+ y="1"
+ rx="0.18940361"
+ ry="0.20662212" />
+ <path
+ style="fill:#222728;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+ d="M 12.006464,3.0203051 L 11.981638,1 C 13.945163,1 17.837365,1.1548232 14.996311,7.9684328 C 15.596089,2.2547574 13.743811,3.0203051 12.006464,3.0203051 z "
+ id="rect3146"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:#555753;fill-opacity:1;stroke:url(#linearGradient3159);stroke-width:1.97969818;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3151"
+ sodipodi:cx="6.4712896"
+ sodipodi:cy="14.484771"
+ sodipodi:rx="3.5986683"
+ sodipodi:ry="2.1781414"
+ d="M 10.069958 14.484771 A 3.5986683 2.1781414 0 1 1 2.8726213,14.484771 A 3.5986683 2.1781414 0 1 1 10.069958 14.484771 z"
+ transform="matrix(0.5557611,0,0,0.4591071,5.903509,5.8499391)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3191);stroke-width:0.98568761;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3173"
+ sodipodi:cx="8.0970039"
+ sodipodi:cy="11.122857"
+ sodipodi:rx="4.3089318"
+ sodipodi:ry="3.5513175"
+ d="M 3.9729753,12.152015 A 4.3089318,3.5513175 0 0 1 9.0871283,7.6665672"
+ transform="matrix(1.0443424,0,0,0.9855497,-0.4560443,3.7870959e-2)"
+ sodipodi:start="2.8475788"
+ sodipodi:end="4.9442449"
+ sodipodi:open="true" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3183);stroke-width:0.58102763;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3175"
+ sodipodi:cx="8.0970039"
+ sodipodi:cy="11.122857"
+ sodipodi:rx="4.3089318"
+ sodipodi:ry="3.5513175"
+ d="M 3.9729753,12.152015 A 4.3089318,3.5513175 0 0 1 9.0871283,7.6665672"
+ transform="matrix(1.6981233,0,0,1.7443645,-5.9310642,-8.697544)"
+ sodipodi:start="2.8475788"
+ sodipodi:end="4.9442449"
+ sodipodi:open="true" />
+ </g>
+</svg>