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>
31 #include <gdk/gdkkeysyms.h>
33 #include <glade/glade.h>
34 #include <glib/gi18n.h>
36 #include <telepathy-glib/util.h>
38 #include <libempathy/empathy-tp-chat.h>
39 #include <libempathy/empathy-tp-chatroom.h>
40 #include <libempathy/empathy-contact.h>
41 #include <libempathy/empathy-utils.h>
42 #include <libempathy/empathy-debug.h>
44 #include "empathy-group-chat.h"
45 #include "empathy-chat.h"
46 #include "empathy-chat-view.h"
47 #include "empathy-contact-list-store.h"
48 #include "empathy-contact-list-view.h"
49 //#include "empathy-chat-invite.h"
50 //#include "empathy-sound.h"
51 #include "empathy-images.h"
52 #include "empathy-ui-utils.h"
53 #include "empathy-conf.h"
54 #include "empathy-preferences.h"
56 #define DEBUG_DOMAIN "GroupChat"
58 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_GROUP_CHAT, EmpathyGroupChatPriv))
60 struct _EmpathyGroupChatPriv {
61 EmpathyContactListStore *store;
62 EmpathyContactListView *view;
63 EmpathyTpChatroom *tp_chat;
68 GtkWidget *scrolled_window_chat;
69 GtkWidget *scrolled_window_input;
70 GtkWidget *scrolled_window_contacts;
71 GtkWidget *hbox_topic;
72 GtkWidget *label_topic;
76 GCompletion *completion;
79 gboolean contacts_visible;
82 static void group_chat_finalize (GObject *object);
83 static void group_chat_create_ui (EmpathyGroupChat *chat);
84 static void group_chat_widget_destroy_cb (GtkWidget *widget,
85 EmpathyGroupChat *chat);
86 static void group_chat_members_changed_cb (EmpathyTpChatroom *tp_chat,
87 EmpathyContact *contact,
88 EmpathyContact *actor,
92 EmpathyGroupChat *chat);
93 static void group_chat_topic_entry_activate_cb (GtkWidget *entry,
95 static void group_chat_topic_response_cb (GtkWidget *dialog,
97 EmpathyGroupChat *chat);
98 static const gchar * group_chat_get_name (EmpathyChat *chat);
99 static gchar * group_chat_get_tooltip (EmpathyChat *chat);
100 static const gchar * group_chat_get_status_icon_name (EmpathyChat *chat);
101 static GtkWidget * group_chat_get_widget (EmpathyChat *chat);
102 static gboolean group_chat_is_group_chat (EmpathyChat *chat);
103 static void group_chat_set_tp_chat (EmpathyChat *chat,
104 EmpathyTpChat *tp_chat);
105 static void group_chat_subject_notify_cb (EmpathyTpChat *tp_chat,
107 EmpathyGroupChat *chat);
108 static void group_chat_name_notify_cb (EmpathyTpChat *tp_chat,
110 EmpathyGroupChat *chat);
111 static gboolean group_chat_key_press_event (EmpathyChat *chat,
113 static gint group_chat_contacts_completion_func (const gchar *s1,
117 G_DEFINE_TYPE (EmpathyGroupChat, empathy_group_chat, EMPATHY_TYPE_CHAT)
120 empathy_group_chat_class_init (EmpathyGroupChatClass *klass)
122 GObjectClass *object_class;
123 EmpathyChatClass *chat_class;
125 object_class = G_OBJECT_CLASS (klass);
126 chat_class = EMPATHY_CHAT_CLASS (klass);
128 object_class->finalize = group_chat_finalize;
130 chat_class->get_name = group_chat_get_name;
131 chat_class->get_tooltip = group_chat_get_tooltip;
132 chat_class->get_status_icon_name = group_chat_get_status_icon_name;
133 chat_class->get_widget = group_chat_get_widget;
134 chat_class->is_group_chat = group_chat_is_group_chat;
135 chat_class->set_tp_chat = group_chat_set_tp_chat;
136 chat_class->key_press_event = group_chat_key_press_event;
138 g_type_class_add_private (object_class, sizeof (EmpathyGroupChatPriv));
142 empathy_group_chat_init (EmpathyGroupChat *chat)
144 EmpathyGroupChatPriv *priv;
145 EmpathyChatView *chatview;
147 priv = GET_PRIV (chat);
149 priv->contacts_visible = TRUE;
151 chatview = EMPATHY_CHAT_VIEW (EMPATHY_CHAT (chat)->view);
152 empathy_chat_view_set_is_group_chat (chatview, TRUE);
154 group_chat_create_ui (chat);
158 group_chat_finalize (GObject *object)
160 EmpathyGroupChat *chat;
161 EmpathyGroupChatPriv *priv;
163 empathy_debug (DEBUG_DOMAIN, "Finalized:%p", object);
165 chat = EMPATHY_GROUP_CHAT (object);
166 priv = GET_PRIV (chat);
169 g_free (priv->topic);
170 g_object_unref (priv->store);
171 g_object_unref (priv->tp_chat);
172 g_completion_free (priv->completion);
174 G_OBJECT_CLASS (empathy_group_chat_parent_class)->finalize (object);
178 empathy_group_chat_new (EmpathyTpChatroom *tp_chat)
180 EmpathyGroupChat *chat;
182 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (tp_chat), NULL);
184 chat = g_object_new (EMPATHY_TYPE_GROUP_CHAT,
192 empathy_group_chat_get_show_contacts (EmpathyGroupChat *chat)
194 EmpathyGroupChat *group_chat;
195 EmpathyGroupChatPriv *priv;
197 g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), FALSE);
199 group_chat = EMPATHY_GROUP_CHAT (chat);
200 priv = GET_PRIV (group_chat);
202 return priv->contacts_visible;
206 empathy_group_chat_set_show_contacts (EmpathyGroupChat *chat,
209 EmpathyGroupChat *group_chat;
210 EmpathyGroupChatPriv *priv;
212 g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat));
214 group_chat = EMPATHY_GROUP_CHAT (chat);
215 priv = GET_PRIV (group_chat);
217 priv->contacts_visible = show;
220 gtk_widget_show (priv->scrolled_window_contacts);
221 gtk_paned_set_position (GTK_PANED (priv->hpaned),
222 priv->contacts_width);
224 priv->contacts_width = gtk_paned_get_position (GTK_PANED (priv->hpaned));
225 gtk_widget_hide (priv->scrolled_window_contacts);
230 empathy_group_chat_set_topic (EmpathyGroupChat *chat)
232 EmpathyGroupChatPriv *priv;
233 EmpathyChatWindow *chat_window;
234 GtkWidget *chat_dialog;
240 g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat));
242 priv = GET_PRIV (chat);
244 chat_window = empathy_chat_get_window (EMPATHY_CHAT (chat));
245 chat_dialog = empathy_chat_window_get_dialog (chat_window);
247 dialog = gtk_message_dialog_new (GTK_WINDOW (chat_dialog),
249 GTK_MESSAGE_QUESTION,
250 GTK_BUTTONS_OK_CANCEL,
251 _("Enter the new topic you want to set for this room:"));
253 topic = gtk_label_get_text (GTK_LABEL (priv->label_topic));
255 hbox = gtk_hbox_new (FALSE, 0);
256 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
257 hbox, FALSE, TRUE, 4);
259 entry = gtk_entry_new ();
260 gtk_entry_set_text (GTK_ENTRY (entry), topic);
261 gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
263 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 4);
265 g_object_set (GTK_MESSAGE_DIALOG (dialog)->label, "use-markup", TRUE, NULL);
266 g_object_set_data (G_OBJECT (dialog), "entry", entry);
268 g_signal_connect (entry, "activate",
269 G_CALLBACK (group_chat_topic_entry_activate_cb),
271 g_signal_connect (dialog, "response",
272 G_CALLBACK (group_chat_topic_response_cb),
275 gtk_widget_show_all (dialog);
279 group_chat_create_ui (EmpathyGroupChat *chat)
281 EmpathyGroupChatPriv *priv;
285 priv = GET_PRIV (chat);
287 glade = empathy_glade_get_file ("empathy-group-chat.glade",
290 "group_chat_widget", &priv->widget,
291 "hpaned", &priv->hpaned,
292 "vbox_left", &priv->vbox_left,
293 "scrolled_window_chat", &priv->scrolled_window_chat,
294 "scrolled_window_input", &priv->scrolled_window_input,
295 "hbox_topic", &priv->hbox_topic,
296 "label_topic", &priv->label_topic,
297 "scrolled_window_contacts", &priv->scrolled_window_contacts,
300 empathy_glade_connect (glade,
302 "group_chat_widget", "destroy", group_chat_widget_destroy_cb,
305 g_object_unref (glade);
307 g_object_set_data (G_OBJECT (priv->widget), "chat", g_object_ref (chat));
309 /* Add room GtkTextView. */
310 gtk_container_add (GTK_CONTAINER (priv->scrolled_window_chat),
311 GTK_WIDGET (EMPATHY_CHAT (chat)->view));
312 gtk_widget_show (GTK_WIDGET (EMPATHY_CHAT (chat)->view));
314 /* Add input GtkTextView */
315 gtk_container_add (GTK_CONTAINER (priv->scrolled_window_input),
316 EMPATHY_CHAT (chat)->input_text_view);
317 gtk_widget_show (EMPATHY_CHAT (chat)->input_text_view);
319 /* Add nick name completion */
320 priv->completion = g_completion_new ((GCompletionFunc) empathy_contact_get_name);
321 g_completion_set_compare (priv->completion,
322 group_chat_contacts_completion_func);
324 /* Set widget focus order */
325 list = g_list_append (NULL, priv->scrolled_window_input);
326 gtk_container_set_focus_chain (GTK_CONTAINER (priv->vbox_left), list);
329 list = g_list_append (NULL, priv->vbox_left);
330 list = g_list_append (list, priv->scrolled_window_contacts);
331 gtk_container_set_focus_chain (GTK_CONTAINER (priv->hpaned), list);
334 list = g_list_append (NULL, priv->hpaned);
335 list = g_list_append (list, priv->hbox_topic);
336 gtk_container_set_focus_chain (GTK_CONTAINER (priv->widget), list);
341 group_chat_widget_destroy_cb (GtkWidget *widget,
342 EmpathyGroupChat *chat)
344 empathy_debug (DEBUG_DOMAIN, "Destroyed");
346 g_object_unref (chat);
350 group_chat_members_changed_cb (EmpathyTpChatroom *tp_chat,
351 EmpathyContact *contact,
352 EmpathyContact *actor,
356 EmpathyGroupChat *chat)
358 EmpathyGroupChatPriv *priv;
361 priv = GET_PRIV (chat);
364 str = g_strdup_printf (_("%s has joined the room"),
365 empathy_contact_get_name (contact));
367 str = g_strdup_printf (_("%s has left the room"),
368 empathy_contact_get_name (contact));
370 empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, str);
375 group_chat_topic_entry_activate_cb (GtkWidget *entry,
378 gtk_dialog_response (dialog, GTK_RESPONSE_OK);
382 group_chat_topic_response_cb (GtkWidget *dialog,
384 EmpathyGroupChat *chat)
386 if (response == GTK_RESPONSE_OK) {
390 entry = g_object_get_data (G_OBJECT (dialog), "entry");
391 topic = gtk_entry_get_text (GTK_ENTRY (entry));
393 if (!G_STR_EMPTY (topic)) {
394 EmpathyGroupChatPriv *priv;
396 priv = GET_PRIV (chat);
398 empathy_tp_chatroom_set_topic (priv->tp_chat, topic);
402 gtk_widget_destroy (dialog);
406 group_chat_get_name (EmpathyChat *chat)
408 EmpathyGroupChat *group_chat;
409 EmpathyGroupChatPriv *priv;
411 g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL);
413 group_chat = EMPATHY_GROUP_CHAT (chat);
414 priv = GET_PRIV (group_chat);
420 id = empathy_chat_get_id (chat);
421 server = strstr (id, "@");
424 priv->name = g_strndup (id, server - id);
426 priv->name = g_strdup (id);
434 group_chat_get_tooltip (EmpathyChat *chat)
436 EmpathyGroupChat *group_chat;
437 EmpathyGroupChatPriv *priv;
439 g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL);
441 group_chat = EMPATHY_GROUP_CHAT (chat);
442 priv = GET_PRIV (group_chat);
447 topic = g_strdup_printf (_("Topic: %s"), priv->topic);
448 tmp = g_strdup_printf ("%s\n%s", priv->name, topic);
454 return g_strdup (priv->name);
458 group_chat_get_status_icon_name (EmpathyChat *chat)
460 return EMPATHY_IMAGE_GROUP_MESSAGE;
464 group_chat_get_widget (EmpathyChat *chat)
466 EmpathyGroupChat *group_chat;
467 EmpathyGroupChatPriv *priv;
469 g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL);
471 group_chat = EMPATHY_GROUP_CHAT (chat);
472 priv = GET_PRIV (group_chat);
478 group_chat_is_group_chat (EmpathyChat *chat)
480 g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), FALSE);
486 group_chat_set_tp_chat (EmpathyChat *chat,
487 EmpathyTpChat *tp_chat)
489 EmpathyGroupChat *group_chat;
490 EmpathyGroupChatPriv *priv;
492 g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat));
494 group_chat = EMPATHY_GROUP_CHAT (chat);
495 priv = GET_PRIV (group_chat);
497 /* Free all resources related to tp_chat */
499 g_object_unref (priv->tp_chat);
500 priv->tp_chat = NULL;
503 gtk_widget_destroy (GTK_WIDGET (priv->view));
504 g_object_unref (priv->store);
507 g_free (priv->topic);
512 /* We are no more connected */
513 gtk_widget_set_sensitive (priv->hbox_topic, FALSE);
514 gtk_widget_set_sensitive (priv->scrolled_window_contacts, FALSE);
518 /* We are connected */
519 gtk_widget_set_sensitive (priv->hbox_topic, TRUE);
520 gtk_widget_set_sensitive (priv->scrolled_window_contacts, TRUE);
522 priv->tp_chat = g_object_ref (tp_chat);
524 if (empathy_tp_chatroom_get_invitation (priv->tp_chat, NULL, NULL)) {
525 empathy_tp_chatroom_accept_invitation (priv->tp_chat);
528 /* Create contact list */
529 priv->store = empathy_contact_list_store_new (EMPATHY_CONTACT_LIST (priv->tp_chat));
530 priv->view = empathy_contact_list_view_new (priv->store,
531 EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CHAT |
532 EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CALL |
533 EMPATHY_CONTACT_LIST_FEATURE_CONTACT_LOG |
534 EMPATHY_CONTACT_LIST_FEATURE_CONTACT_FT |
535 EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INVITE |
536 EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INFO);
538 gtk_container_add (GTK_CONTAINER (priv->scrolled_window_contacts),
539 GTK_WIDGET (priv->view));
540 gtk_widget_show (GTK_WIDGET (priv->view));
542 /* Connect signals */
543 g_signal_connect (priv->tp_chat, "members-changed",
544 G_CALLBACK (group_chat_members_changed_cb),
546 g_signal_connect (priv->tp_chat, "notify::subject",
547 G_CALLBACK (group_chat_subject_notify_cb),
549 g_signal_connect (priv->tp_chat, "notify::name",
550 G_CALLBACK (group_chat_name_notify_cb),
555 group_chat_subject_notify_cb (EmpathyTpChat *tp_chat,
557 EmpathyGroupChat *chat)
559 EmpathyGroupChatPriv *priv;
562 priv = GET_PRIV (chat);
564 g_object_get (priv->tp_chat, "subject", &str, NULL);
565 if (!tp_strdiff (priv->topic, str)) {
570 g_free (priv->topic);
572 gtk_label_set_text (GTK_LABEL (priv->label_topic), priv->topic);
574 if (!G_STR_EMPTY (priv->topic)) {
575 str = g_strdup_printf (_("Topic set to: %s"), priv->topic);
577 str = g_strdup (_("No topic defined"));
579 empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, str);
584 group_chat_name_notify_cb (EmpathyTpChat *tp_chat,
586 EmpathyGroupChat *chat)
588 EmpathyGroupChatPriv *priv;
590 priv = GET_PRIV (chat);
593 g_object_get (priv->tp_chat, "name", &priv->name, NULL);
597 group_chat_key_press_event (EmpathyChat *chat,
600 EmpathyGroupChatPriv *priv = GET_PRIV (chat);
602 if (!(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) &&
603 event->keyval == GDK_Tab) {
604 GtkTextBuffer *buffer;
605 GtkTextIter start, current;
606 gchar *nick, *completed;
607 GList *list, *completed_list;
608 gboolean is_start_of_buffer;
610 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (EMPATHY_CHAT (chat)->input_text_view));
611 gtk_text_buffer_get_iter_at_mark (buffer, ¤t, gtk_text_buffer_get_insert (buffer));
613 /* Get the start of the nick to complete. */
614 gtk_text_buffer_get_iter_at_mark (buffer, &start, gtk_text_buffer_get_insert (buffer));
615 gtk_text_iter_backward_word_start (&start);
616 is_start_of_buffer = gtk_text_iter_is_start (&start);
618 list = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (priv->tp_chat));
619 g_completion_add_items (priv->completion, list);
621 nick = gtk_text_buffer_get_text (buffer, &start, ¤t, FALSE);
622 completed_list = g_completion_complete (priv->completion,
631 gchar *complete_char = NULL;
633 gtk_text_buffer_delete (buffer, &start, ¤t);
635 len = g_list_length (completed_list);
638 /* If we only have one hit, use that text
639 * instead of the text in completed since the
640 * completed text will use the typed string
641 * which might be cased all wrong.
644 text = empathy_contact_get_name (completed_list->data);
649 gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text));
651 if (len == 1 && is_start_of_buffer &&
652 empathy_conf_get_string (empathy_conf_get (),
653 EMPATHY_PREFS_CHAT_NICK_COMPLETION_CHAR,
655 complete_char != NULL) {
656 gtk_text_buffer_insert_at_cursor (buffer,
658 strlen (complete_char));
659 gtk_text_buffer_insert_at_cursor (buffer, " ", 1);
660 g_free (complete_char);
666 g_completion_clear_items (priv->completion);
668 g_list_foreach (list, (GFunc) g_object_unref, NULL);
678 group_chat_contacts_completion_func (const gchar *s1,
682 gchar *tmp, *nick1, *nick2;
685 tmp = g_utf8_normalize (s1, -1, G_NORMALIZE_DEFAULT);
686 nick1 = g_utf8_casefold (tmp, -1);
689 tmp = g_utf8_normalize (s2, -1, G_NORMALIZE_DEFAULT);
690 nick2 = g_utf8_casefold (tmp, -1);
693 ret = strncmp (nick1, nick2, n);