Drop one more redundant if
[pidgin-git.git] / libpurple / conversation.c
blob8f9225d4dbdc36e469452870dc37c9f28f9ec75d
1 /*
2 * purple
4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
23 #include "glibcompat.h"
25 #include "buddylist.h"
26 #include "cmds.h"
27 #include "conversation.h"
28 #include "debug.h"
29 #include "enums.h"
30 #include "notify.h"
31 #include "prefs.h"
32 #include "protocol.h"
33 #include "request.h"
34 #include "signals.h"
35 #include "smiley-list.h"
36 #include "util.h"
38 typedef struct _PurpleConversationPrivate PurpleConversationPrivate;
40 /* General private data for a conversation */
41 struct _PurpleConversationPrivate
43 PurpleAccount *account; /* The user using this conversation. */
45 char *name; /* The name of the conversation. */
46 char *title; /* The window title. */
48 gboolean logging; /* The status of logging. */
50 GList *logs; /* This conversation's logs */
52 PurpleConversationUiOps *ui_ops; /* UI-specific operations. */
54 PurpleConnectionFlags features; /* The supported features */
55 GList *message_history; /* Message history, as a GList of PurpleMessages */
57 PurpleE2eeState *e2ee_state; /* End-to-end encryption state. */
59 /* The list of remote smileys. This should be per-buddy (PurpleBuddy),
60 * but we don't have any class for people not on our buddy
61 * list (PurpleDude?). So, if we have one, we should switch to it. */
62 PurpleSmileyList *remote_smileys;
65 /* GObject Property enums */
66 enum
68 PROP_0,
69 PROP_ACCOUNT,
70 PROP_NAME,
71 PROP_TITLE,
72 PROP_LOGGING,
73 PROP_FEATURES,
74 PROP_LAST
77 static GParamSpec *properties[PROP_LAST];
79 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(PurpleConversation, purple_conversation,
80 G_TYPE_OBJECT);
82 static void
83 common_send(PurpleConversation *conv, const char *message, PurpleMessageFlags msgflags)
85 PurpleAccount *account;
86 PurpleConnection *gc;
87 PurpleConversationPrivate *priv =
88 purple_conversation_get_instance_private(conv);
89 PurpleMessage *msg;
90 char *displayed = NULL;
91 const char *sent;
92 int err = 0;
94 if (*message == '\0')
95 return;
97 account = purple_conversation_get_account(conv);
98 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
100 gc = purple_account_get_connection(account);
101 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
103 /* Always linkfy the text for display, unless we're
104 * explicitly asked to do otheriwse*/
105 if (!(msgflags & PURPLE_MESSAGE_INVISIBLE)) {
106 if(msgflags & PURPLE_MESSAGE_NO_LINKIFY)
107 displayed = g_strdup(message);
108 else
109 displayed = purple_markup_linkify(message);
112 if (displayed && (priv->features & PURPLE_CONNECTION_FLAG_HTML) &&
113 !(msgflags & PURPLE_MESSAGE_RAW)) {
114 sent = displayed;
115 } else
116 sent = message;
118 msgflags |= PURPLE_MESSAGE_SEND;
120 if (PURPLE_IS_IM_CONVERSATION(conv)) {
121 msg = purple_message_new_outgoing(
122 purple_conversation_get_name(conv), sent, msgflags);
124 purple_signal_emit(purple_conversations_get_handle(), "sending-im-msg",
125 account, msg);
127 if (!purple_message_is_empty(msg)) {
129 err = purple_serv_send_im(gc, msg);
131 if ((err > 0) && (displayed != NULL)) {
132 /* revert the contents in case sending-im-msg changed it */
133 purple_message_set_contents(msg, displayed);
134 purple_conversation_write_message(conv, msg);
137 purple_signal_emit(purple_conversations_get_handle(),
138 "sent-im-msg", account, msg);
141 else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
142 int id = purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv));
144 msg = purple_message_new_outgoing(NULL, sent, msgflags);
146 purple_signal_emit(purple_conversations_get_handle(),
147 "sending-chat-msg", account, msg, id);
149 if (!purple_message_is_empty(msg)) {
150 err = purple_serv_chat_send(gc, id, msg);
152 purple_signal_emit(purple_conversations_get_handle(),
153 "sent-chat-msg", account, msg, id);
157 if (err < 0) {
158 const char *who;
159 const char *msg;
161 who = purple_conversation_get_name(conv);
163 if (err == -E2BIG) {
164 msg = _("Unable to send message: The message is too large.");
166 if (!purple_conversation_present_error(who, account, msg)) {
167 char *msg2 = g_strdup_printf(_("Unable to send message to %s."), who);
168 purple_notify_error(gc, NULL, msg2,
169 _("The message is too large."),
170 purple_request_cpar_from_connection(gc));
171 g_free(msg2);
174 else if (err == -ENOTCONN) {
175 purple_debug(PURPLE_DEBUG_ERROR, "conversation",
176 "Not yet connected.\n");
178 else {
179 msg = _("Unable to send message.");
181 if (!purple_conversation_present_error(who, account, msg)) {
182 char *msg2 = g_strdup_printf(_("Unable to send message to %s."), who);
183 purple_notify_error(gc, NULL, msg2, NULL,
184 purple_request_cpar_from_connection(gc));
185 g_free(msg2);
190 g_free(displayed);
193 /**************************************************************************
194 * Conversation API
195 **************************************************************************/
196 void
197 purple_conversation_present(PurpleConversation *conv) {
198 PurpleConversationUiOps *ops;
200 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
202 ops = purple_conversation_get_ui_ops(conv);
203 if(ops && ops->present)
204 ops->present(conv);
207 void
208 purple_conversation_set_features(PurpleConversation *conv, PurpleConnectionFlags features)
210 PurpleConversationPrivate *priv = NULL;
212 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
214 priv = purple_conversation_get_instance_private(conv);
215 priv->features = features;
217 g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_FEATURES]);
219 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_FEATURES);
222 PurpleConnectionFlags
223 purple_conversation_get_features(PurpleConversation *conv)
225 PurpleConversationPrivate *priv = NULL;
227 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), 0);
229 priv = purple_conversation_get_instance_private(conv);
230 return priv->features;
233 static PurpleConversationUiOps *
234 purple_conversation_ui_ops_copy(PurpleConversationUiOps *ops)
236 PurpleConversationUiOps *ops_new;
238 g_return_val_if_fail(ops != NULL, NULL);
240 ops_new = g_new(PurpleConversationUiOps, 1);
241 *ops_new = *ops;
243 return ops_new;
246 GType
247 purple_conversation_ui_ops_get_type(void)
249 static GType type = 0;
251 if (type == 0) {
252 type = g_boxed_type_register_static("PurpleConversationUiOps",
253 (GBoxedCopyFunc)purple_conversation_ui_ops_copy,
254 (GBoxedFreeFunc)g_free);
257 return type;
260 void
261 purple_conversation_set_ui_ops(PurpleConversation *conv,
262 PurpleConversationUiOps *ops)
264 PurpleConversationPrivate *priv = NULL;
266 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
267 priv = purple_conversation_get_instance_private(conv);
269 if (priv->ui_ops == ops)
270 return;
272 if (priv->ui_ops != NULL && priv->ui_ops->destroy_conversation != NULL)
273 priv->ui_ops->destroy_conversation(conv);
275 priv->ui_ops = ops;
278 PurpleConversationUiOps *
279 purple_conversation_get_ui_ops(PurpleConversation *conv)
281 PurpleConversationPrivate *priv = NULL;
283 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
285 priv = purple_conversation_get_instance_private(conv);
286 return priv->ui_ops;
289 void
290 purple_conversation_set_account(PurpleConversation *conv, PurpleAccount *account)
292 PurpleConversationPrivate *priv = NULL;
294 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
295 priv = purple_conversation_get_instance_private(conv);
297 if (account == purple_conversation_get_account(conv))
298 return;
300 _purple_conversations_update_cache(conv, NULL, account);
301 priv->account = account;
303 g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_ACCOUNT]);
305 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_ACCOUNT);
308 PurpleAccount *
309 purple_conversation_get_account(PurpleConversation *conv)
311 PurpleConversationPrivate *priv = NULL;
313 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
315 priv = purple_conversation_get_instance_private(conv);
316 return priv->account;
319 PurpleConnection *
320 purple_conversation_get_connection(PurpleConversation *conv)
322 PurpleAccount *account;
324 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
326 account = purple_conversation_get_account(conv);
328 if (account == NULL)
329 return NULL;
331 return purple_account_get_connection(account);
334 void
335 purple_conversation_set_title(PurpleConversation *conv, const char *title)
337 PurpleConversationPrivate *priv = NULL;
339 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
340 g_return_if_fail(title != NULL);
342 priv = purple_conversation_get_instance_private(conv);
343 g_free(priv->title);
344 priv->title = g_strdup(title);
346 if (!g_object_get_data(G_OBJECT(conv), "is-finalizing"))
347 g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_TITLE]);
349 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_TITLE);
352 const char *
353 purple_conversation_get_title(PurpleConversation *conv)
355 PurpleConversationPrivate *priv = NULL;
357 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
359 priv = purple_conversation_get_instance_private(conv);
360 return priv->title;
363 void
364 purple_conversation_autoset_title(PurpleConversation *conv)
366 PurpleAccount *account;
367 PurpleBuddy *b;
368 PurpleChat *chat;
369 const char *text = NULL, *name;
371 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
373 account = purple_conversation_get_account(conv);
374 name = purple_conversation_get_name(conv);
376 if (PURPLE_IS_IM_CONVERSATION(conv)) {
377 if (account && ((b = purple_blist_find_buddy(account, name)) != NULL))
378 text = purple_buddy_get_contact_alias(b);
379 } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
380 if (account && ((chat = purple_blist_find_chat(account, name)) != NULL))
381 text = purple_chat_get_name(chat);
384 if(text == NULL)
385 text = name;
387 purple_conversation_set_title(conv, text);
390 void
391 purple_conversation_set_name(PurpleConversation *conv, const char *name)
393 PurpleConversationPrivate *priv = NULL;
395 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
396 priv = purple_conversation_get_instance_private(conv);
398 _purple_conversations_update_cache(conv, name, NULL);
400 g_free(priv->name);
401 priv->name = g_strdup(name);
403 g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_NAME]);
405 purple_conversation_autoset_title(conv);
406 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_NAME);
409 const char *
410 purple_conversation_get_name(PurpleConversation *conv)
412 PurpleConversationPrivate *priv = NULL;
414 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
416 priv = purple_conversation_get_instance_private(conv);
417 return priv->name;
420 void
421 purple_conversation_set_e2ee_state(PurpleConversation *conv,
422 PurpleE2eeState *state)
424 PurpleConversationPrivate *priv = NULL;
426 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
427 priv = purple_conversation_get_instance_private(conv);
429 if (state != NULL && purple_e2ee_state_get_provider(state) !=
430 purple_e2ee_provider_get_main())
432 purple_debug_error("conversation",
433 "This is not the main e2ee provider");
435 return;
438 if (state == priv->e2ee_state)
439 return;
441 if (state)
442 purple_e2ee_state_ref(state);
443 purple_e2ee_state_unref(priv->e2ee_state);
444 priv->e2ee_state = state;
446 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_E2EE);
449 PurpleE2eeState *
450 purple_conversation_get_e2ee_state(PurpleConversation *conv)
452 PurpleConversationPrivate *priv = NULL;
453 PurpleE2eeProvider *provider;
455 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
456 priv = purple_conversation_get_instance_private(conv);
458 if (priv->e2ee_state == NULL)
459 return NULL;
461 provider = purple_e2ee_provider_get_main();
462 if (provider == NULL)
463 return NULL;
465 if (purple_e2ee_state_get_provider(priv->e2ee_state) != provider) {
466 purple_debug_warning("conversation",
467 "e2ee state has invalid provider set");
468 return NULL;
471 return priv->e2ee_state;
474 void
475 purple_conversation_set_logging(PurpleConversation *conv, gboolean log)
477 PurpleConversationPrivate *priv = NULL;
479 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
480 priv = purple_conversation_get_instance_private(conv);
482 if (priv->logging != log)
484 priv->logging = log;
485 if (log && priv->logs == NULL) {
486 GDateTime *dt;
487 PurpleLog *log;
489 dt = g_date_time_new_now_local();
490 log = purple_log_new(PURPLE_IS_CHAT_CONVERSATION(conv)
491 ? PURPLE_LOG_CHAT
492 : PURPLE_LOG_IM,
493 priv->name, priv->account, conv,
494 dt);
495 g_date_time_unref(dt);
497 priv->logs = g_list_append(NULL, log);
500 g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_LOGGING]);
502 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_LOGGING);
506 gboolean
507 purple_conversation_is_logging(PurpleConversation *conv)
509 PurpleConversationPrivate *priv = NULL;
511 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), FALSE);
513 priv = purple_conversation_get_instance_private(conv);
514 return priv->logging;
517 void
518 purple_conversation_close_logs(PurpleConversation *conv)
520 PurpleConversationPrivate *priv = NULL;
522 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
524 priv = purple_conversation_get_instance_private(conv);
525 g_list_free_full(priv->logs, (GDestroyNotify)purple_log_free);
526 priv->logs = NULL;
529 void
530 _purple_conversation_write_common(PurpleConversation *conv, PurpleMessage *pmsg)
532 PurpleConversationPrivate *priv = NULL;
533 PurpleProtocol *protocol = NULL;
534 PurpleConnection *gc = NULL;
535 PurpleAccount *account;
536 PurpleConversationUiOps *ops;
537 PurpleBuddy *b;
538 int plugin_return;
539 /* int logging_font_options = 0; */
541 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
542 g_return_if_fail(pmsg != NULL);
544 priv = purple_conversation_get_instance_private(conv);
545 ops = purple_conversation_get_ui_ops(conv);
547 account = purple_conversation_get_account(conv);
549 if (account != NULL)
550 gc = purple_account_get_connection(account);
552 if (PURPLE_IS_CHAT_CONVERSATION(conv) &&
553 (gc != NULL && !g_slist_find(purple_connection_get_active_chats(gc), conv)))
554 return;
556 if (PURPLE_IS_IM_CONVERSATION(conv) &&
557 !g_list_find(purple_conversations_get_all(), conv))
558 return;
560 plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
561 purple_conversations_get_handle(),
562 (PURPLE_IS_IM_CONVERSATION(conv) ? "writing-im-msg" : "writing-chat-msg"),
563 conv, pmsg));
565 if (purple_message_is_empty(pmsg))
566 return;
568 if (plugin_return)
569 return;
571 if (account != NULL) {
572 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
574 if (PURPLE_IS_IM_CONVERSATION(conv) ||
575 !(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
577 if (purple_message_get_flags(pmsg) & PURPLE_MESSAGE_SEND) {
578 const gchar *alias;
580 b = purple_blist_find_buddy(account,
581 purple_account_get_username(account));
583 if (purple_account_get_private_alias(account) != NULL)
584 alias = purple_account_get_private_alias(account);
585 else if (b != NULL && !purple_strequal(purple_buddy_get_name(b),
586 purple_buddy_get_contact_alias(b)))
588 alias = purple_buddy_get_contact_alias(b);
589 } else if (purple_connection_get_display_name(gc) != NULL)
590 alias = purple_connection_get_display_name(gc);
591 else
592 alias = purple_account_get_username(account);
594 purple_message_set_author_alias(pmsg, alias);
596 else if (purple_message_get_flags(pmsg) & PURPLE_MESSAGE_RECV)
598 /* TODO: PurpleDude - folks not on the buddy list */
599 b = purple_blist_find_buddy(account,
600 purple_message_get_author(pmsg));
602 if (b != NULL) {
603 purple_message_set_author_alias(pmsg,
604 purple_buddy_get_contact_alias(b));
610 if (!(purple_message_get_flags(pmsg) & PURPLE_MESSAGE_NO_LOG) && purple_conversation_is_logging(conv)) {
611 GList *log;
612 GDateTime *dt;
614 dt = g_date_time_new_from_unix_local(purple_message_get_time(pmsg));
615 log = priv->logs;
616 while (log != NULL) {
617 purple_log_write((PurpleLog *)log->data,
618 purple_message_get_flags(pmsg),
619 purple_message_get_author_alias(pmsg),
621 purple_message_get_contents(pmsg));
622 log = log->next;
624 g_date_time_unref(dt);
627 if (ops) {
628 if (PURPLE_IS_CHAT_CONVERSATION(conv) && ops->write_chat)
629 ops->write_chat(PURPLE_CHAT_CONVERSATION(conv), pmsg);
630 else if (PURPLE_IS_IM_CONVERSATION(conv) && ops->write_im)
631 ops->write_im(PURPLE_IM_CONVERSATION(conv), pmsg);
632 else if (ops->write_conv)
633 ops->write_conv(conv, pmsg);
636 g_object_ref(pmsg);
637 priv->message_history = g_list_prepend(priv->message_history, pmsg);
639 purple_signal_emit(purple_conversations_get_handle(),
640 (PURPLE_IS_IM_CONVERSATION(conv) ? "wrote-im-msg" : "wrote-chat-msg"),
641 conv, pmsg);
644 void
645 purple_conversation_write_message(PurpleConversation *conv, PurpleMessage *msg)
647 PurpleConversationClass *klass = NULL;
649 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
651 klass = PURPLE_CONVERSATION_GET_CLASS(conv);
653 if (klass && klass->write_message)
654 klass->write_message(conv, msg);
657 void purple_conversation_write_system_message(PurpleConversation *conv,
658 const gchar *message, PurpleMessageFlags flags)
660 _purple_conversation_write_common(conv,
661 purple_message_new_system(message, flags));
664 void
665 purple_conversation_send(PurpleConversation *conv, const char *message)
667 purple_conversation_send_with_flags(conv, message, 0);
670 void
671 purple_conversation_send_with_flags(PurpleConversation *conv, const char *message,
672 PurpleMessageFlags flags)
674 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
675 g_return_if_fail(message != NULL);
677 common_send(conv, message, flags);
680 gboolean
681 purple_conversation_has_focus(PurpleConversation *conv)
683 gboolean ret = FALSE;
684 PurpleConversationUiOps *ops;
686 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), FALSE);
688 ops = purple_conversation_get_ui_ops(conv);
690 if (ops != NULL && ops->has_focus != NULL)
691 ret = ops->has_focus(conv);
693 return ret;
697 * TODO: Need to make sure calls to this function happen in the core
698 * instead of the UI. That way UIs have less work to do, and the
699 * core/UI split is cleaner. Also need to make sure this is called
700 * when chats are added/removed from the blist.
702 void
703 purple_conversation_update(PurpleConversation *conv, PurpleConversationUpdateType type)
705 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
707 purple_signal_emit(purple_conversations_get_handle(),
708 "conversation-updated", conv, type);
711 gboolean purple_conversation_present_error(const char *who, PurpleAccount *account, const char *what)
713 PurpleConversation *conv;
715 g_return_val_if_fail(who != NULL, FALSE);
716 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
717 g_return_val_if_fail(what != NULL, FALSE);
719 conv = purple_conversations_find_with_account(who, account);
720 if (conv != NULL)
721 purple_conversation_write_system_message(conv, what, PURPLE_MESSAGE_ERROR);
722 else
723 return FALSE;
725 return TRUE;
728 static void
729 purple_conversation_send_confirm_cb(gpointer *data)
731 PurpleConversation *conv = data[0];
732 char *message = data[1];
734 g_free(data);
736 if (!PURPLE_IS_CONVERSATION(conv)) {
737 /* Maybe it was closed before this callback was called. */
738 return;
741 common_send(conv, message, 0);
744 void
745 purple_conversation_send_confirm(PurpleConversation *conv, const char *message)
747 PurpleConversationPrivate *priv = NULL;
748 char *text;
749 gpointer *data;
751 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
752 g_return_if_fail(message != NULL);
754 priv = purple_conversation_get_instance_private(conv);
755 if (priv->ui_ops != NULL && priv->ui_ops->send_confirm != NULL)
757 priv->ui_ops->send_confirm(conv, message);
758 return;
761 text = g_strdup_printf("You are about to send the following message:\n%s", message);
762 data = g_new0(gpointer, 2);
763 data[0] = conv;
764 data[1] = (gpointer)message;
766 purple_request_action(conv, NULL, _("Send Message"), text, 0,
767 purple_request_cpar_from_account(
768 purple_conversation_get_account(conv)),
769 data, 2, _("_Send Message"),
770 G_CALLBACK(purple_conversation_send_confirm_cb), _("Cancel"), NULL);
773 GList *
774 purple_conversation_get_extended_menu(PurpleConversation *conv)
776 GList *menu = NULL;
778 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
780 purple_signal_emit(purple_conversations_get_handle(),
781 "conversation-extended-menu", conv, &menu);
782 return menu;
785 void purple_conversation_clear_message_history(PurpleConversation *conv)
787 PurpleConversationPrivate *priv = NULL;
788 GList *list;
790 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
792 priv = purple_conversation_get_instance_private(conv);
793 list = priv->message_history;
794 g_list_free_full(list, g_object_unref);
795 priv->message_history = NULL;
797 purple_signal_emit(purple_conversations_get_handle(),
798 "cleared-message-history", conv);
801 GList *purple_conversation_get_message_history(PurpleConversation *conv)
803 PurpleConversationPrivate *priv = NULL;
805 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
807 priv = purple_conversation_get_instance_private(conv);
808 return priv->message_history;
811 void purple_conversation_set_ui_data(PurpleConversation *conv, gpointer ui_data)
813 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
815 conv->ui_data = ui_data;
818 gpointer purple_conversation_get_ui_data(const PurpleConversation *conv)
820 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
822 return conv->ui_data;
825 gboolean
826 purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline,
827 const gchar *markup, gchar **error)
829 char *mark = (markup && *markup) ? NULL : g_markup_escape_text(cmdline, -1), *err = NULL;
830 PurpleCmdStatus status = purple_cmd_do_command(conv, cmdline, mark ? mark : markup, error ? error : &err);
831 g_free(mark);
832 g_free(err);
833 return (status == PURPLE_CMD_STATUS_OK);
836 gssize
837 purple_conversation_get_max_message_size(PurpleConversation *conv)
839 PurpleProtocol *protocol;
841 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), 0);
843 protocol = purple_connection_get_protocol(
844 purple_conversation_get_connection(conv));
846 g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), 0);
848 return purple_protocol_client_iface_get_max_message_size(protocol, conv);
851 void
852 purple_conversation_add_smiley(PurpleConversation *conv, PurpleSmiley *smiley) {
853 PurpleConversationPrivate *priv = NULL;
855 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
856 g_return_if_fail(smiley);
858 priv = purple_conversation_get_instance_private(conv);
860 if (priv->remote_smileys == NULL) {
861 priv->remote_smileys = purple_smiley_list_new();
862 g_object_set(priv->remote_smileys,
863 "drop-failed-remotes", TRUE, NULL);
866 if(purple_smiley_list_get_by_shortcut(
867 priv->remote_smileys,
868 purple_smiley_get_shortcut(smiley)))
870 /* smiley was already added */
871 return;
874 if (!purple_smiley_list_add(priv->remote_smileys, smiley)) {
875 purple_debug_error("conversation", "failed adding remote "
876 "smiley to the list");
880 PurpleSmiley *
881 purple_conversation_get_smiley(PurpleConversation *conv, const gchar *shortcut) {
882 PurpleConversationPrivate *priv = NULL;
884 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
885 g_return_val_if_fail(shortcut, NULL);
887 priv = purple_conversation_get_instance_private(conv);
889 if (priv->remote_smileys == NULL)
890 return NULL;
892 return purple_smiley_list_get_by_shortcut(priv->remote_smileys, shortcut);
895 PurpleSmileyList *
896 purple_conversation_get_remote_smileys(PurpleConversation *conv)
898 PurpleConversationPrivate *priv = NULL;
900 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
902 priv = purple_conversation_get_instance_private(conv);
903 return priv->remote_smileys;
907 /**************************************************************************
908 * GObject code
909 **************************************************************************/
911 /* Set method for GObject properties */
912 static void
913 purple_conversation_set_property(GObject *obj, guint param_id, const GValue *value,
914 GParamSpec *pspec)
916 PurpleConversation *conv = PURPLE_CONVERSATION(obj);
917 PurpleConversationPrivate *priv =
918 purple_conversation_get_instance_private(conv);
920 switch (param_id) {
921 case PROP_ACCOUNT:
922 priv->account = g_value_get_object(value);
923 break;
924 case PROP_NAME:
925 g_free(priv->name);
926 priv->name = g_value_dup_string(value);
927 break;
928 case PROP_TITLE:
929 g_free(priv->title);
930 priv->title = g_value_dup_string(value);
931 break;
932 case PROP_LOGGING:
933 purple_conversation_set_logging(conv, g_value_get_boolean(value));
934 break;
935 case PROP_FEATURES:
936 purple_conversation_set_features(conv, g_value_get_flags(value));
937 break;
938 default:
939 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
940 break;
944 /* Get method for GObject properties */
945 static void
946 purple_conversation_get_property(GObject *obj, guint param_id, GValue *value,
947 GParamSpec *pspec)
949 PurpleConversation *conv = PURPLE_CONVERSATION(obj);
951 switch (param_id) {
952 case PROP_ACCOUNT:
953 g_value_set_object(value, purple_conversation_get_account(conv));
954 break;
955 case PROP_NAME:
956 g_value_set_string(value, purple_conversation_get_name(conv));
957 break;
958 case PROP_TITLE:
959 g_value_set_string(value, purple_conversation_get_title(conv));
960 break;
961 case PROP_LOGGING:
962 g_value_set_boolean(value, purple_conversation_is_logging(conv));
963 break;
964 case PROP_FEATURES:
965 g_value_set_flags(value, purple_conversation_get_features(conv));
966 break;
967 default:
968 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
969 break;
973 static void
974 purple_conversation_init(PurpleConversation *conv)
978 /* Called when done constructing */
979 static void
980 purple_conversation_constructed(GObject *object)
982 PurpleConversation *conv = PURPLE_CONVERSATION(object);
983 PurpleAccount *account;
984 PurpleConnection *gc;
985 PurpleConversationUiOps *ops;
987 G_OBJECT_CLASS(purple_conversation_parent_class)->constructed(object);
989 g_object_get(object, "account", &account, NULL);
990 gc = purple_account_get_connection(account);
992 /* copy features from the connection. */
993 purple_conversation_set_features(conv, purple_connection_get_flags(gc));
995 /* add the conversation to the appropriate lists */
996 purple_conversations_add(conv);
998 /* Auto-set the title. */
999 purple_conversation_autoset_title(conv);
1001 /* Don't move this.. it needs to be one of the last things done otherwise
1002 * it causes mysterious crashes on my system.
1003 * -- Gary
1005 ops = purple_conversations_get_ui_ops();
1006 purple_conversation_set_ui_ops(conv, ops);
1007 if (ops != NULL && ops->create_conversation != NULL)
1008 ops->create_conversation(conv);
1010 purple_signal_emit(purple_conversations_get_handle(),
1011 "conversation-created", conv);
1013 g_object_unref(account);
1016 static void
1017 purple_conversation_dispose(GObject *object)
1019 g_object_set_data(object, "is-finalizing", GINT_TO_POINTER(TRUE));
1022 /* GObject finalize function */
1023 static void
1024 purple_conversation_finalize(GObject *object)
1026 PurpleConversation *conv = PURPLE_CONVERSATION(object);
1027 PurpleConversationPrivate *priv =
1028 purple_conversation_get_instance_private(conv);
1029 PurpleConversationUiOps *ops = purple_conversation_get_ui_ops(conv);
1031 purple_request_close_with_handle(conv);
1033 purple_e2ee_state_unref(priv->e2ee_state);
1034 priv->e2ee_state = NULL;
1036 /* remove from conversations and im/chats lists prior to emit */
1037 purple_conversations_remove(conv);
1039 purple_signal_emit(purple_conversations_get_handle(),
1040 "deleting-conversation", conv);
1042 purple_conversation_close_logs(conv);
1043 purple_conversation_clear_message_history(conv);
1045 if (ops != NULL && ops->destroy_conversation != NULL)
1046 ops->destroy_conversation(conv);
1048 g_free(priv->name);
1049 g_free(priv->title);
1051 priv->name = NULL;
1052 priv->title = NULL;
1054 G_OBJECT_CLASS(purple_conversation_parent_class)->finalize(object);
1057 /* Class initializer function */
1058 static void
1059 purple_conversation_class_init(PurpleConversationClass *klass)
1061 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
1063 obj_class->dispose = purple_conversation_dispose;
1064 obj_class->finalize = purple_conversation_finalize;
1065 obj_class->constructed = purple_conversation_constructed;
1067 /* Setup properties */
1068 obj_class->get_property = purple_conversation_get_property;
1069 obj_class->set_property = purple_conversation_set_property;
1071 properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
1072 "The account for the conversation.", PURPLE_TYPE_ACCOUNT,
1073 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1075 properties[PROP_NAME] = g_param_spec_string("name", "Name",
1076 "The name of the conversation.", NULL,
1077 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1079 properties[PROP_TITLE] = g_param_spec_string("title", "Title",
1080 "The title of the conversation.", NULL,
1081 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1083 properties[PROP_LOGGING] = g_param_spec_boolean("logging", "Logging status",
1084 "Whether logging is enabled or not.", FALSE,
1085 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1087 properties[PROP_FEATURES] = g_param_spec_flags("features",
1088 "Connection features",
1089 "The connection features of the conversation.",
1090 PURPLE_TYPE_CONNECTION_FLAGS, 0,
1091 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1093 g_object_class_install_properties(obj_class, PROP_LAST, properties);