1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2002-2007 Imendio AB
4 * Copyright (C) 2007 Collabora Ltd.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
21 * Authors: Mikael Hallendal <micke@imendio.com>
22 * Richard Hult <richard@imendio.com>
23 * Martyn Russell <martyn@imendio.com>
24 * Xavier Claessens <xclaesse@gmail.com>
32 #include <glade/glade.h>
33 #include <glib/gi18n.h>
35 #include <libempathy/empathy-tp-chat.h>
36 #include <libempathy/empathy-tp-chatroom.h>
37 #include <libempathy/empathy-contact.h>
38 #include <libempathy/empathy-utils.h>
39 #include <libempathy/empathy-debug.h>
41 #include "empathy-group-chat.h"
42 #include "empathy-chat.h"
43 #include "empathy-chat-view.h"
44 #include "empathy-contact-list-store.h"
45 #include "empathy-contact-list-view.h"
46 //#include "empathy-chat-invite.h"
47 //#include "empathy-sound.h"
48 #include "empathy-images.h"
49 #include "empathy-ui-utils.h"
51 #define DEBUG_DOMAIN "GroupChat"
53 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_GROUP_CHAT, EmpathyGroupChatPriv))
55 struct _EmpathyGroupChatPriv {
56 EmpathyContactListStore *store;
57 EmpathyContactListView *view;
58 EmpathyTpChatroom *tp_chat;
63 GtkWidget *scrolled_window_chat;
64 GtkWidget *scrolled_window_input;
65 GtkWidget *scrolled_window_contacts;
66 GtkWidget *hbox_topic;
67 GtkWidget *label_topic;
71 GCompletion *completion;
74 gboolean contacts_visible;
77 static void group_chat_finalize (GObject *object);
78 static void group_chat_create_ui (EmpathyGroupChat *chat);
79 static void group_chat_widget_destroy_cb (GtkWidget *widget,
80 EmpathyGroupChat *chat);
81 static void group_chat_members_changed_cb (EmpathyTpChatroom *tp_chat,
82 EmpathyContact *contact,
83 EmpathyContact *actor,
87 EmpathyGroupChat *chat);
88 static void group_chat_topic_entry_activate_cb (GtkWidget *entry,
90 static void group_chat_topic_response_cb (GtkWidget *dialog,
92 EmpathyGroupChat *chat);
93 static const gchar * group_chat_get_name (EmpathyChat *chat);
94 static gchar * group_chat_get_tooltip (EmpathyChat *chat);
95 static const gchar * group_chat_get_status_icon_name (EmpathyChat *chat);
96 static GtkWidget * group_chat_get_widget (EmpathyChat *chat);
97 static gboolean group_chat_is_group_chat (EmpathyChat *chat);
98 static void group_chat_set_tp_chat (EmpathyChat *chat,
99 EmpathyTpChat *tp_chat);
100 static void group_chat_subject_notify_cb (EmpathyTpChat *tp_chat,
102 EmpathyGroupChat *chat);
103 static void group_chat_name_notify_cb (EmpathyTpChat *tp_chat,
105 EmpathyGroupChat *chat);
106 /*static gboolean group_chat_key_press_event (GtkWidget *widget,
108 EmpathyGroupChat *chat);*/
109 static gint group_chat_contacts_completion_func (const gchar *s1,
113 G_DEFINE_TYPE (EmpathyGroupChat, empathy_group_chat, EMPATHY_TYPE_CHAT)
116 empathy_group_chat_class_init (EmpathyGroupChatClass *klass)
118 GObjectClass *object_class;
119 EmpathyChatClass *chat_class;
121 object_class = G_OBJECT_CLASS (klass);
122 chat_class = EMPATHY_CHAT_CLASS (klass);
124 object_class->finalize = group_chat_finalize;
126 chat_class->get_name = group_chat_get_name;
127 chat_class->get_tooltip = group_chat_get_tooltip;
128 chat_class->get_status_icon_name = group_chat_get_status_icon_name;
129 chat_class->get_widget = group_chat_get_widget;
130 chat_class->is_group_chat = group_chat_is_group_chat;
131 chat_class->set_tp_chat = group_chat_set_tp_chat;
133 g_type_class_add_private (object_class, sizeof (EmpathyGroupChatPriv));
137 empathy_group_chat_init (EmpathyGroupChat *chat)
139 EmpathyGroupChatPriv *priv;
140 EmpathyChatView *chatview;
142 priv = GET_PRIV (chat);
144 priv->contacts_visible = TRUE;
146 chatview = EMPATHY_CHAT_VIEW (EMPATHY_CHAT (chat)->view);
147 empathy_chat_view_set_is_group_chat (chatview, TRUE);
149 group_chat_create_ui (chat);
153 group_chat_finalize (GObject *object)
155 EmpathyGroupChat *chat;
156 EmpathyGroupChatPriv *priv;
158 empathy_debug (DEBUG_DOMAIN, "Finalized:%p", object);
160 chat = EMPATHY_GROUP_CHAT (object);
161 priv = GET_PRIV (chat);
164 g_free (priv->topic);
165 g_object_unref (priv->store);
166 g_object_unref (priv->tp_chat);
167 g_completion_free (priv->completion);
169 G_OBJECT_CLASS (empathy_group_chat_parent_class)->finalize (object);
173 empathy_group_chat_new (McAccount *account,
176 EmpathyGroupChat *chat;
177 EmpathyGroupChatPriv *priv;
179 g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
180 g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL);
182 chat = g_object_new (EMPATHY_TYPE_GROUP_CHAT, NULL);
184 priv = GET_PRIV (chat);
186 EMPATHY_CHAT (chat)->account = g_object_ref (account);
187 priv->tp_chat = empathy_tp_chatroom_new (account, tp_chan);
188 empathy_chat_set_tp_chat (EMPATHY_CHAT (chat), EMPATHY_TP_CHAT (priv->tp_chat));
194 empathy_group_chat_get_show_contacts (EmpathyGroupChat *chat)
196 EmpathyGroupChat *group_chat;
197 EmpathyGroupChatPriv *priv;
199 g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), FALSE);
201 group_chat = EMPATHY_GROUP_CHAT (chat);
202 priv = GET_PRIV (group_chat);
204 return priv->contacts_visible;
208 empathy_group_chat_set_show_contacts (EmpathyGroupChat *chat,
211 EmpathyGroupChat *group_chat;
212 EmpathyGroupChatPriv *priv;
214 g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat));
216 group_chat = EMPATHY_GROUP_CHAT (chat);
217 priv = GET_PRIV (group_chat);
219 priv->contacts_visible = show;
222 gtk_widget_show (priv->scrolled_window_contacts);
223 gtk_paned_set_position (GTK_PANED (priv->hpaned),
224 priv->contacts_width);
226 priv->contacts_width = gtk_paned_get_position (GTK_PANED (priv->hpaned));
227 gtk_widget_hide (priv->scrolled_window_contacts);
232 empathy_group_chat_set_topic (EmpathyGroupChat *chat)
234 EmpathyGroupChatPriv *priv;
235 EmpathyChatWindow *chat_window;
236 GtkWidget *chat_dialog;
242 g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat));
244 priv = GET_PRIV (chat);
246 chat_window = empathy_chat_get_window (EMPATHY_CHAT (chat));
247 chat_dialog = empathy_chat_window_get_dialog (chat_window);
249 dialog = gtk_message_dialog_new (GTK_WINDOW (chat_dialog),
251 GTK_MESSAGE_QUESTION,
252 GTK_BUTTONS_OK_CANCEL,
253 _("Enter the new topic you want to set for this room:"));
255 topic = gtk_label_get_text (GTK_LABEL (priv->label_topic));
257 hbox = gtk_hbox_new (FALSE, 0);
258 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
259 hbox, FALSE, TRUE, 4);
261 entry = gtk_entry_new ();
262 gtk_entry_set_text (GTK_ENTRY (entry), topic);
263 gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
265 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 4);
267 g_object_set (GTK_MESSAGE_DIALOG (dialog)->label, "use-markup", TRUE, NULL);
268 g_object_set_data (G_OBJECT (dialog), "entry", entry);
270 g_signal_connect (entry, "activate",
271 G_CALLBACK (group_chat_topic_entry_activate_cb),
273 g_signal_connect (dialog, "response",
274 G_CALLBACK (group_chat_topic_response_cb),
277 gtk_widget_show_all (dialog);
281 group_chat_create_ui (EmpathyGroupChat *chat)
283 EmpathyGroupChatPriv *priv;
287 priv = GET_PRIV (chat);
289 glade = empathy_glade_get_file ("empathy-group-chat.glade",
292 "group_chat_widget", &priv->widget,
293 "hpaned", &priv->hpaned,
294 "vbox_left", &priv->vbox_left,
295 "scrolled_window_chat", &priv->scrolled_window_chat,
296 "scrolled_window_input", &priv->scrolled_window_input,
297 "hbox_topic", &priv->hbox_topic,
298 "label_topic", &priv->label_topic,
299 "scrolled_window_contacts", &priv->scrolled_window_contacts,
302 empathy_glade_connect (glade,
304 "group_chat_widget", "destroy", group_chat_widget_destroy_cb,
307 g_object_unref (glade);
309 g_object_set_data (G_OBJECT (priv->widget), "chat", g_object_ref (chat));
311 /* Add room GtkTextView. */
312 gtk_container_add (GTK_CONTAINER (priv->scrolled_window_chat),
313 GTK_WIDGET (EMPATHY_CHAT (chat)->view));
314 gtk_widget_show (GTK_WIDGET (EMPATHY_CHAT (chat)->view));
316 /* Add input GtkTextView */
317 gtk_container_add (GTK_CONTAINER (priv->scrolled_window_input),
318 EMPATHY_CHAT (chat)->input_text_view);
319 gtk_widget_show (EMPATHY_CHAT (chat)->input_text_view);
321 /* Add nick name completion */
322 priv->completion = g_completion_new (NULL);
323 g_completion_set_compare (priv->completion,
324 group_chat_contacts_completion_func);
326 /* Set widget focus order */
327 list = g_list_append (NULL, priv->scrolled_window_input);
328 gtk_container_set_focus_chain (GTK_CONTAINER (priv->vbox_left), list);
331 list = g_list_append (NULL, priv->vbox_left);
332 list = g_list_append (list, priv->scrolled_window_contacts);
333 gtk_container_set_focus_chain (GTK_CONTAINER (priv->hpaned), list);
336 list = g_list_append (NULL, priv->hpaned);
337 list = g_list_append (list, priv->hbox_topic);
338 gtk_container_set_focus_chain (GTK_CONTAINER (priv->widget), list);
343 group_chat_widget_destroy_cb (GtkWidget *widget,
344 EmpathyGroupChat *chat)
346 empathy_debug (DEBUG_DOMAIN, "Destroyed");
348 g_object_unref (chat);
352 group_chat_members_changed_cb (EmpathyTpChatroom *tp_chat,
353 EmpathyContact *contact,
354 EmpathyContact *actor,
358 EmpathyGroupChat *chat)
360 EmpathyGroupChatPriv *priv;
363 priv = GET_PRIV (chat);
366 str = g_strdup_printf (_("%s has joined the room"),
367 empathy_contact_get_name (contact));
369 str = g_strdup_printf (_("%s has left the room"),
370 empathy_contact_get_name (contact));
372 empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, str);
377 group_chat_topic_entry_activate_cb (GtkWidget *entry,
380 gtk_dialog_response (dialog, GTK_RESPONSE_OK);
384 group_chat_topic_response_cb (GtkWidget *dialog,
386 EmpathyGroupChat *chat)
388 if (response == GTK_RESPONSE_OK) {
392 entry = g_object_get_data (G_OBJECT (dialog), "entry");
393 topic = gtk_entry_get_text (GTK_ENTRY (entry));
395 if (!G_STR_EMPTY (topic)) {
396 EmpathyGroupChatPriv *priv;
398 priv = GET_PRIV (chat);
400 empathy_tp_chatroom_set_topic (priv->tp_chat, topic);
404 gtk_widget_destroy (dialog);
408 group_chat_get_name (EmpathyChat *chat)
410 EmpathyGroupChat *group_chat;
411 EmpathyGroupChatPriv *priv;
413 g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL);
415 group_chat = EMPATHY_GROUP_CHAT (chat);
416 priv = GET_PRIV (group_chat);
422 id = empathy_chat_get_id (chat);
423 server = strstr (id, "@");
426 priv->name = g_strndup (id, server - id);
428 priv->name = g_strdup (id);
436 group_chat_get_tooltip (EmpathyChat *chat)
438 EmpathyGroupChat *group_chat;
439 EmpathyGroupChatPriv *priv;
441 g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL);
443 group_chat = EMPATHY_GROUP_CHAT (chat);
444 priv = GET_PRIV (group_chat);
449 topic = g_strdup_printf (_("Topic: %s"), priv->topic);
450 tmp = g_strdup_printf ("%s\n%s", priv->name, topic);
456 return g_strdup (priv->name);
460 group_chat_get_status_icon_name (EmpathyChat *chat)
462 return EMPATHY_IMAGE_GROUP_MESSAGE;
466 group_chat_get_widget (EmpathyChat *chat)
468 EmpathyGroupChat *group_chat;
469 EmpathyGroupChatPriv *priv;
471 g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL);
473 group_chat = EMPATHY_GROUP_CHAT (chat);
474 priv = GET_PRIV (group_chat);
480 group_chat_is_group_chat (EmpathyChat *chat)
482 g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), FALSE);
488 group_chat_set_tp_chat (EmpathyChat *chat,
489 EmpathyTpChat *tp_chat)
491 EmpathyGroupChat *group_chat;
492 EmpathyGroupChatPriv *priv;
494 g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat));
496 group_chat = EMPATHY_GROUP_CHAT (chat);
497 priv = GET_PRIV (group_chat);
499 /* Free all resources related to tp_chat */
501 g_object_unref (priv->tp_chat);
502 priv->tp_chat = NULL;
505 gtk_widget_destroy (GTK_WIDGET (priv->view));
506 g_object_unref (priv->store);
509 g_free (priv->topic);
514 /* We are no more connected */
515 gtk_widget_set_sensitive (priv->hbox_topic, FALSE);
516 gtk_widget_set_sensitive (priv->scrolled_window_contacts, FALSE);
520 /* We are connected */
521 gtk_widget_set_sensitive (priv->hbox_topic, TRUE);
522 gtk_widget_set_sensitive (priv->scrolled_window_contacts, TRUE);
524 priv->tp_chat = g_object_ref (tp_chat);
526 /* FIXME: Ask the user before accepting */
527 empathy_tp_chatroom_accept_invitation (priv->tp_chat);
529 /* Create contact list */
530 priv->store = empathy_contact_list_store_new (EMPATHY_CONTACT_LIST (priv->tp_chat));
531 priv->view = empathy_contact_list_view_new (priv->store);
532 gtk_container_add (GTK_CONTAINER (priv->scrolled_window_contacts),
533 GTK_WIDGET (priv->view));
534 gtk_widget_show (GTK_WIDGET (priv->view));
536 /* Connect signals */
537 g_signal_connect (priv->tp_chat, "members-changed",
538 G_CALLBACK (group_chat_members_changed_cb),
540 g_signal_connect (priv->tp_chat, "notify::subject",
541 G_CALLBACK (group_chat_subject_notify_cb),
543 g_signal_connect (priv->tp_chat, "notify::name",
544 G_CALLBACK (group_chat_name_notify_cb),
549 group_chat_subject_notify_cb (EmpathyTpChat *tp_chat,
551 EmpathyGroupChat *chat)
553 EmpathyGroupChatPriv *priv;
556 priv = GET_PRIV (chat);
558 g_object_get (priv->tp_chat, "subject", &str, NULL);
559 if (!empathy_strdiff (priv->topic, str)) {
564 g_free (priv->topic);
566 gtk_label_set_text (GTK_LABEL (priv->label_topic), priv->topic);
568 if (!G_STR_EMPTY (priv->topic)) {
569 str = g_strdup_printf (_("Topic set to: %s"), priv->topic);
571 str = g_strdup (_("No topic defined"));
573 empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, str);
578 group_chat_name_notify_cb (EmpathyTpChat *tp_chat,
580 EmpathyGroupChat *chat)
582 EmpathyGroupChatPriv *priv;
584 priv = GET_PRIV (chat);
587 g_object_get (priv->tp_chat, "name", &priv->name, NULL);
592 group_chat_key_press_event (GtkWidget *widget,
594 EmpathyGroupChat *chat)
596 EmpathyGroupChatPriv *priv;
599 GtkTextBuffer *buffer;
600 GtkTextIter start, current;
601 gchar *nick, *completed;
603 GList *list, *l, *completed_list;
604 gboolean is_start_of_buffer;
606 priv = GET_PRIV (chat);
608 if ((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK &&
609 (event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK &&
610 event->keyval == GDK_Tab) {
611 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (EMPATHY_CHAT (chat)->input_text_view));
612 gtk_text_buffer_get_iter_at_mark (buffer, ¤t, gtk_text_buffer_get_insert (buffer));
614 /* Get the start of the nick to complete. */
615 gtk_text_buffer_get_iter_at_mark (buffer, &start, gtk_text_buffer_get_insert (buffer));
616 gtk_text_iter_backward_word_start (&start);
617 is_start_of_buffer = gtk_text_iter_is_start (&start);
619 nick = gtk_text_buffer_get_text (buffer, &start, ¤t, FALSE);
621 g_completion_clear_items (priv->completion);
625 list = group_chat_get_nick_list (chat);
627 g_completion_add_items (priv->completion, list);
629 completed_list = g_completion_complete (priv->completion,
639 gtk_text_buffer_delete (buffer, &start, ¤t);
641 len = g_list_length (completed_list);
644 /* If we only have one hit, use that text
645 * instead of the text in completed since the
646 * completed text will use the typed string
647 * which might be cased all wrong.
650 text = (gchar *) completed_list->data;
655 gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text));
658 if (is_start_of_buffer) {
659 gtk_text_buffer_insert_at_cursor (buffer, ", ", 2);
666 g_completion_clear_items (priv->completion);
668 for (l = list; l; l = l->next) {
682 group_chat_contacts_completion_func (const gchar *s1,
686 gchar *tmp, *nick1, *nick2;
689 tmp = g_utf8_normalize (s1, -1, G_NORMALIZE_DEFAULT);
690 nick1 = g_utf8_casefold (tmp, -1);
693 tmp = g_utf8_normalize (s2, -1, G_NORMALIZE_DEFAULT);
694 nick2 = g_utf8_casefold (tmp, -1);
697 ret = strncmp (nick1, nick2, n);