static void detach_signals(PurpleConversation *conv) { PidginConversation *gtkconv = NULL; PidginWindow *gtkwin = NULL; GSList *ids = NULL, *l; gtkconv = PIDGIN_CONVERSATION(conv); if (!gtkconv) return; gtkwin = gtkconv->win; ids = purple_conversation_get_data(conv, "notify-imhtml-signals"); for (l = ids; l != NULL; l = l->next) g_signal_handler_disconnect(gtkconv->imhtml, GPOINTER_TO_INT(l->data)); g_slist_free(ids); ids = purple_conversation_get_data(conv, "notify-entry-signals"); for (l = ids; l != NULL; l = l->next) g_signal_handler_disconnect(gtkconv->entry, GPOINTER_TO_INT(l->data)); g_slist_free(ids); purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0)); purple_conversation_set_data(conv, "notify-imhtml-signals", NULL); purple_conversation_set_data(conv, "notify-entry-signals", NULL); }
static int attach_signals(PurpleConversation *conv) { PidginConversation *gtkconv = NULL; PidginWindow *gtkwin = NULL; GSList *imhtml_ids = NULL, *entry_ids = NULL; guint id; gtkconv = PIDGIN_CONVERSATION(conv); if (!gtkconv) { purple_debug_misc("notify", "Failed to find gtkconv\n"); return 0; } gtkwin = gtkconv->win; if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_focus")) { /* TODO should really find a way to make this work no matter * where the focus is inside the conv window, without having * to bind to focus-in-event on the g(d|t)kwindow */ /* try setting the signal on the focus-in-event for * gtkwin->notebook->container? */ id = g_signal_connect(G_OBJECT(gtkconv->entry), "focus-in-event", G_CALLBACK(unnotify_cb), conv); entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id)); id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "focus-in-event", G_CALLBACK(unnotify_cb), conv); imhtml_ids = g_slist_append(imhtml_ids, GUINT_TO_POINTER(id)); } if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_click")) { /* TODO similarly should really find a way to allow for * clicking in other places of the window */ id = g_signal_connect(G_OBJECT(gtkconv->entry), "button-press-event", G_CALLBACK(unnotify_cb), conv); entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id)); id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "button-press-event", G_CALLBACK(unnotify_cb), conv); imhtml_ids = g_slist_append(imhtml_ids, GUINT_TO_POINTER(id)); } if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_type")) { id = g_signal_connect(G_OBJECT(gtkconv->entry), "key-press-event", G_CALLBACK(unnotify_cb), conv); entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id)); } purple_conversation_set_data(conv, "notify-imhtml-signals", imhtml_ids); purple_conversation_set_data(conv, "notify-entry-signals", entry_ids); return 0; }
static void unnotify(PurpleConversation *conv, gboolean reset) { PurpleConversation *active_conv = NULL; PidginWindow *purplewin = NULL; g_return_if_fail(conv != NULL); if (PIDGIN_CONVERSATION(conv) == NULL) return; purplewin = PIDGIN_CONVERSATION(conv)->win; active_conv = pidgin_conv_window_get_active_conversation(purplewin); /* reset the conversation window title */ purple_conversation_autoset_title(active_conv); if (reset) { /* Only need to actually remove the urgent hinting here, since * removing it just to have it readded in re-notify is an * unnecessary couple extra RTs to the server */ handle_urgent(purplewin, FALSE); purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0)); /* Same logic as for the urgent hint, xprops are also a RT. * This needs to go here so that it gets the updated message * count. */ handle_count_xprop(purplewin); } return; }
static gboolean twitter_conv_icon_displaying_chat_cb(PurpleAccount * account, const char *who, char **message, PurpleConversation * conv, PurpleMessageFlags flags, void *account_signal) { GtkIMHtml *imhtml; GtkTextBuffer *text_buffer; gint linenumber = 0; if (account != account_signal) return FALSE; purple_debug_info(PLUGIN_ID, "called %s\n", G_STRFUNC); /* get text buffer */ imhtml = GTK_IMHTML(PIDGIN_CONVERSATION(conv)->imhtml); text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml)); /* store number of lines */ linenumber = gtk_text_buffer_get_line_count(text_buffer); //this is just used to pass the current line number (before the insertion of the new text) //to the _displayed_chat_cb func. //Since pidgin is (mostly) single threaded, this is probably overkill and we could just use //a single global int. //On another note, we don't insert the icon here because the message may not end up being displayed //based on what other plugins do purple_conversation_set_data(conv, PLUGIN_ID "-icon-ln", GINT_TO_POINTER(linenumber)); return FALSE; }
static void hide_conv(PidginConversation *gtkconv, gboolean closetimer) { GList *list; purple_signal_emit(pidgin_conversations_get_handle(), "conversation-hiding", gtkconv); 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)); } #if 0 /* I will miss you */ purple_conversation_set_ui_ops(conv, NULL); #else pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv); pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv); #endif } }
static void timestamp_new_convo(PurpleConversation *conv) { if (!g_list_find(purple_get_conversations(), conv)) return; purple_conversation_set_data(conv, "timestamp-last", GINT_TO_POINTER(0)); }
void PurpleLine::signal_conversation_created(PurpleConversation *conv) { if (purple_conversation_get_account(conv) != acct) return; // Start queuing messages while the history is fetched purple_conversation_set_data(conv, "line-message-queue", new std::vector<line::Message>()); fetch_conversation_history(conv, 10, false); }
static void twitter_conv_icon_conversation_created_cb(PurpleConversation * conv, PurpleAccount * account) { GList **conv_icons; if (purple_conversation_get_account(conv) != account) return; conv_icons = g_new0(GList *, 1); purple_conversation_set_data(conv, PLUGIN_ID "-conv-icons", conv_icons); }
static void conv_created(PurpleConversation *conv) { purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0)); /* always attach the signals, notify() will take care of conversation * type checking */ attach_signals(conv); }
void irc_msg_join(struct irc_conn *irc, const char *name, const char *from, char **args) { PurpleConnection *gc = purple_account_get_connection(irc->account); PurpleConversation *convo; char *nick = irc_mask_nick(from), *userhost; struct irc_buddy *ib; static int id = 1; if (!gc) { g_free(nick); return; } if (!purple_utf8_strcasecmp(nick, purple_connection_get_display_name(gc))) { /* We are joining a channel for the first time */ serv_got_joined_chat(gc, id++, args[0]); g_free(nick); convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0], irc->account); if (convo == NULL) { purple_debug_error("irc", "tried to join %s but couldn't\n", args[0]); return; } purple_conversation_set_data(convo, IRC_NAMES_FLAG, GINT_TO_POINTER(FALSE)); /* Until purple_conversation_present does something that * one would expect in Pidgin, this call produces buggy * behavior both for the /join and auto-join cases. */ /* purple_conversation_present(convo); */ return; } convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0], irc->account); if (convo == NULL) { purple_debug(PURPLE_DEBUG_ERROR, "irc", "JOIN for %s failed\n", args[0]); g_free(nick); return; } userhost = irc_mask_userhost(from); purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo), nick, userhost, PURPLE_CBFLAGS_NONE, TRUE); if ((ib = g_hash_table_lookup(irc->buddies, nick)) != NULL) { ib->flag = TRUE; irc_buddy_status(nick, ib, irc); } g_free(userhost); g_free(nick); }
std::string PurpleLine::conv_attachment_add(PurpleConversation *conv, line::ContentType::type type, std::string id) { auto atts = (std::vector<Attachment> *)purple_conversation_get_data(conv, "line-attachments"); if (!atts) { atts = new std::vector<Attachment>(); purple_conversation_set_data(conv, "line-attachments", atts); } atts->emplace_back(type, id); return std::to_string(atts->size()); }
void PurpleLine::signal_deleting_conversation(PurpleConversation *conv) { if (purple_conversation_get_account(conv) != acct) return; auto queue = (std::vector<line::Message> *) purple_conversation_get_data(conv, "line-message-queue"); if (queue) { purple_conversation_set_data(conv, "line-message-queue", nullptr); delete queue; } int64_t *end_seq_p = (int64_t *)purple_conversation_get_data(conv, "line-end-seq"); if (end_seq_p) { purple_conversation_set_data(conv, "line-end-seq", nullptr); delete end_seq_p; } auto atts = (std::vector<Attachment> *)purple_conversation_get_data(conv, "line-attachments"); if (atts) { purple_conversation_set_data(conv, "line-attachments", nullptr); delete atts; } }
static void skypeweb_join_chat(PurpleConnection *pc, GHashTable *data) { SkypeWebAccount *sa = purple_connection_get_protocol_data(pc); gchar *chatname; gchar *post; GString *url; PurpleChatConversation *chatconv; chatname = (gchar *)g_hash_table_lookup(data, "chatname"); if (chatname == NULL) { return; } chatconv = purple_conversations_find_chat_with_account(chatname, sa->account); if (chatconv != NULL) { purple_conversation_present(PURPLE_CONVERSATION(chatconv)); return; } url = g_string_new("/v1/threads/"); g_string_append_printf(url, "%s", purple_url_encode(chatname)); g_string_append(url, "/members/"); g_string_append_printf(url, "8:%s", purple_url_encode(sa->username)); post = "{\"role\":\"User\"}"; skypeweb_post_or_get(sa, SKYPEWEB_METHOD_PUT | SKYPEWEB_METHOD_SSL, sa->messages_host, url->str, post, NULL, NULL, TRUE); g_string_free(url, TRUE); skypeweb_get_conversation_history(sa, chatname); skypeweb_get_thread_users(sa, chatname); chatconv = purple_serv_got_joined_chat(pc, g_str_hash(chatname), chatname); purple_conversation_set_data(PURPLE_CONVERSATION(chatconv), "chatname", g_strdup(chatname)); purple_conversation_present(PURPLE_CONVERSATION(chatconv)); }
static gboolean timestamp_displaying_conv_msg(PurpleAccount *account, const char *who, char **buffer, PurpleConversation *conv, PurpleMessageFlags flags, void *data) { time_t now = time(NULL) / interval * interval; time_t then; if (!g_list_find(purple_get_conversations(), conv)) return FALSE; then = GPOINTER_TO_INT(purple_conversation_get_data( conv, "timestamp-last")); if (now - then >= interval) { timestamp_display(conv, then, now); purple_conversation_set_data( conv, "timestamp-last", GINT_TO_POINTER(now)); } return FALSE; }
static int notify(PurpleConversation *conv, gboolean increment) { gint count; gboolean has_focus; PidginWindow *purplewin = NULL; if (conv == NULL || PIDGIN_CONVERSATION(conv) == NULL) return 0; /* We want to remove the notifications, but not reset the counter */ unnotify(conv, FALSE); purplewin = PIDGIN_CONVERSATION(conv)->win; /* If we aren't doing notifications for this type of conversation, return */ if (((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) && !purple_prefs_get_bool("/plugins/gtk/X11/notify/type_im")) || ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) && !purple_prefs_get_bool("/plugins/gtk/X11/notify/type_chat"))) return 0; g_object_get(G_OBJECT(purplewin->window), "has-toplevel-focus", &has_focus, NULL); if (purple_prefs_get_bool("/plugins/gtk/X11/notify/type_focused") || !has_focus) { if (increment) { count = GPOINTER_TO_INT(purple_conversation_get_data(conv, "notify-message-count")); count++; purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(count)); } notify_win(purplewin, conv); } return 0; }
static void deleting_conv(PurpleConversation *conv) { PidginWindow *purplewin = NULL; PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); if (gtkconv == NULL) return; detach_signals(conv); purplewin = gtkconv->win; handle_urgent(purplewin, FALSE); purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0)); return; #if 0 /* i think this line crashes */ if (count_messages(purplewin)) notify_win(purplewin); #endif }
void PurpleLine::fetch_conversation_history(PurpleConversation *conv, int count, bool requested) { PurpleConversationType type = conv->type; std::string name(purple_conversation_get_name(conv)); int64_t end_seq = -1; int64_t *end_seq_p = (int64_t *)purple_conversation_get_data(conv, "line-end-seq"); if (end_seq_p) end_seq = *end_seq_p; if (end_seq != -1) c_out->send_getPreviousMessages(name, end_seq - 1, count); else c_out->send_getRecentMessages(name, count); c_out->send([this, requested, type, name, end_seq]() { int64_t new_end_seq = end_seq; std::vector<line::Message> recent_msgs; if (end_seq != -1) c_out->recv_getPreviousMessages(recent_msgs); else c_out->recv_getRecentMessages(recent_msgs); PurpleConversation *conv = purple_find_conversation_with_account(type, name.c_str(), acct); if (!conv) return; // Conversation died while fetching messages auto *queue = (std::vector<line::Message> *) purple_conversation_get_data(conv, "line-message-queue"); purple_conversation_set_data(conv, "line-message-queue", nullptr); // Find least seq value from messages for future history queries for (line::Message &msg: recent_msgs) { if (msg.contentMetadata.count("seq")) { try { int64_t seq = std::stoll(msg.contentMetadata["seq"]); if (new_end_seq == -1 || seq < new_end_seq) new_end_seq = seq; } catch (...) { /* ignore parse error */ } } } if (queue) { // If there's a message queue, remove any already-queued messages in the recent message // list to prevent them showing up twice. recent_msgs.erase( std::remove_if( recent_msgs.begin(), recent_msgs.end(), [&queue](line::Message &rm) { auto r = find_if( queue->begin(), queue->end(), [&rm](line::Message &qm) { return qm.id == rm.id; }); return (r != queue->end()); }), recent_msgs.end()); } if (recent_msgs.size()) { purple_conversation_write( conv, "", "<strong>Message history</strong>", (PurpleMessageFlags)PURPLE_MESSAGE_RAW, time(NULL)); for (auto msgi = recent_msgs.rbegin(); msgi != recent_msgs.rend(); msgi++) write_message(*msgi, true); purple_conversation_write( conv, "", "<hr>", (PurpleMessageFlags)PURPLE_MESSAGE_RAW, time(NULL)); } else { if (requested) { // If history was requested by the user and there is none, let the user know purple_conversation_write( conv, "", "<strong>No more history</strong>", (PurpleMessageFlags)PURPLE_MESSAGE_RAW, time(NULL)); } } // If there's a message queue, play it back now if (queue) { for (line::Message &msg: *queue) write_message(msg, false); delete queue; } int64_t *end_seq_p = (int64_t *)purple_conversation_get_data(conv, "line-end-seq"); if (end_seq_p) delete end_seq_p; purple_conversation_set_data(conv, "line-end-seq", new int64_t(new_end_seq)); }); }
void irc_msg_names(struct irc_conn *irc, const char *name, const char *from, char **args) { char *names, *cur, *end, *tmp, *msg; PurpleConversation *convo; if (!strcmp(name, "366")) { convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, args[1], irc->account); if (!convo) { purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got a NAMES list for %s, which doesn't exist\n", args[1]); g_string_free(irc->names, TRUE); irc->names = NULL; return; } names = cur = g_string_free(irc->names, FALSE); irc->names = NULL; if (purple_conversation_get_data(convo, IRC_NAMES_FLAG)) { msg = g_strdup_printf(_("Users on %s: %s"), args[1], names ? names : ""); if (purple_conversation_get_type(convo) == PURPLE_CONV_TYPE_CHAT) purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL)); else purple_conv_im_write(PURPLE_CONV_IM(convo), "", msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL)); g_free(msg); } else { GList *users = NULL; GList *flags = NULL; while (*cur) { PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE; end = strchr(cur, ' '); if (!end) end = cur + strlen(cur); if (*cur == '@') { f = PURPLE_CBFLAGS_OP; cur++; } else if (*cur == '%') { f = PURPLE_CBFLAGS_HALFOP; cur++; } else if(*cur == '+') { f = PURPLE_CBFLAGS_VOICE; cur++; } else if(irc->mode_chars && strchr(irc->mode_chars, *cur)) { if (*cur == '~') f = PURPLE_CBFLAGS_FOUNDER; cur++; } tmp = g_strndup(cur, end - cur); users = g_list_prepend(users, tmp); flags = g_list_prepend(flags, GINT_TO_POINTER(f)); cur = end; if (*cur) cur++; } if (users != NULL) { GList *l; purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE); for (l = users; l != NULL; l = l->next) g_free(l->data); g_list_free(users); g_list_free(flags); } purple_conversation_set_data(convo, IRC_NAMES_FLAG, GINT_TO_POINTER(TRUE)); } g_free(names); } else { if (!irc->names) irc->names = g_string_new(""); if (irc->names->len && irc->names->str[irc->names->len - 1] != ' ') irc->names = g_string_append_c(irc->names, ' '); irc->names = g_string_append(irc->names, args[3]); } }