]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-group-chat.c
5d10da68d1e47401bb231cb5e81d330e69cd00e7
[empathy.git] / libempathy-gtk / empathy-group-chat.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2002-2007 Imendio AB
4  * Copyright (C) 2007-2008 Collabora Ltd.
5  *
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.
10  *
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.
15  *
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.
20  *
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>
25  */
26
27 #include "config.h"
28
29 #include <string.h>
30
31 #include <gdk/gdkkeysyms.h>
32 #include <gtk/gtk.h>
33 #include <glade/glade.h>
34 #include <glib/gi18n.h>
35
36 #include <telepathy-glib/util.h>
37
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>
43
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
55 #define DEBUG_DOMAIN "GroupChat"
56
57 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_GROUP_CHAT, EmpathyGroupChatPriv))
58
59 struct _EmpathyGroupChatPriv {
60         EmpathyContactListStore *store;
61         EmpathyContactListView  *view;
62         EmpathyTpChatroom      *tp_chat;
63
64         GtkWidget              *widget;
65         GtkWidget              *hpaned;
66         GtkWidget              *vbox_left;
67         GtkWidget              *scrolled_window_chat;
68         GtkWidget              *scrolled_window_input;
69         GtkWidget              *scrolled_window_contacts;
70         GtkWidget              *hbox_topic;
71         GtkWidget              *label_topic;
72
73         gchar                  *topic;
74         gchar                  *name;
75         GCompletion            *completion;
76
77         gint                    contacts_width;
78         gboolean                contacts_visible;
79 };
80
81 static void          group_chat_finalize                 (GObject           *object);
82 static void          group_chat_create_ui                (EmpathyGroupChat  *chat);
83 static void          group_chat_widget_destroy_cb        (GtkWidget         *widget,
84                                                           EmpathyGroupChat  *chat);
85 static void          group_chat_members_changed_cb       (EmpathyTpChatroom *tp_chat,
86                                                           EmpathyContact    *contact,
87                                                           EmpathyContact    *actor,
88                                                           guint              reason,
89                                                           gchar             *message,
90                                                           gboolean           is_member,
91                                                           EmpathyGroupChat  *chat);
92 static const gchar * group_chat_get_name                 (EmpathyChat       *chat);
93 static gchar *       group_chat_get_tooltip              (EmpathyChat       *chat);
94 static const gchar * group_chat_get_status_icon_name     (EmpathyChat       *chat);
95 static GtkWidget *   group_chat_get_widget               (EmpathyChat       *chat);
96 static gboolean      group_chat_is_group_chat            (EmpathyChat       *chat);
97 static void          group_chat_set_tp_chat              (EmpathyChat       *chat,
98                                                           EmpathyTpChat     *tp_chat);
99 static gboolean      group_chat_key_press_event          (EmpathyChat       *chat,
100                                                           GdkEventKey       *event);
101 static gint          group_chat_contacts_completion_func (const gchar       *s1,
102                                                           const gchar       *s2,
103                                                           gsize              n);
104
105 G_DEFINE_TYPE (EmpathyGroupChat, empathy_group_chat, EMPATHY_TYPE_CHAT)
106
107 static void
108 empathy_group_chat_class_init (EmpathyGroupChatClass *klass)
109 {
110         GObjectClass    *object_class;
111         EmpathyChatClass *chat_class;
112
113         object_class = G_OBJECT_CLASS (klass);
114         chat_class = EMPATHY_CHAT_CLASS (klass);
115
116         object_class->finalize           = group_chat_finalize;
117
118         chat_class->get_name             = group_chat_get_name;
119         chat_class->get_tooltip          = group_chat_get_tooltip;
120         chat_class->get_status_icon_name = group_chat_get_status_icon_name;
121         chat_class->get_widget           = group_chat_get_widget;
122         chat_class->is_group_chat        = group_chat_is_group_chat;
123         chat_class->set_tp_chat          = group_chat_set_tp_chat;
124         chat_class->key_press_event      = group_chat_key_press_event;
125
126         g_type_class_add_private (object_class, sizeof (EmpathyGroupChatPriv));
127 }
128
129 static void
130 empathy_group_chat_init (EmpathyGroupChat *chat)
131 {
132         EmpathyGroupChatPriv *priv;
133         EmpathyChatView      *chatview;
134
135         priv = GET_PRIV (chat);
136
137         priv->contacts_visible = TRUE;
138
139         chatview = EMPATHY_CHAT_VIEW (EMPATHY_CHAT (chat)->view);
140         empathy_chat_view_set_is_group_chat (chatview, TRUE);
141
142         group_chat_create_ui (chat);
143 }
144
145 static void
146 group_chat_finalize (GObject *object)
147 {
148         EmpathyGroupChat     *chat;
149         EmpathyGroupChatPriv *priv;
150
151         empathy_debug (DEBUG_DOMAIN, "Finalized:%p", object);
152
153         chat = EMPATHY_GROUP_CHAT (object);
154         priv = GET_PRIV (chat);
155         
156         g_free (priv->name);
157         g_free (priv->topic);
158         if (priv->store) {
159                 g_object_unref (priv->store);
160         }
161         if (priv->tp_chat) {
162                 g_object_unref (priv->tp_chat); 
163         }
164         g_completion_free (priv->completion);
165
166         G_OBJECT_CLASS (empathy_group_chat_parent_class)->finalize (object);
167 }
168
169 EmpathyGroupChat *
170 empathy_group_chat_new (EmpathyTpChatroom *tp_chat)
171 {
172         EmpathyGroupChat *chat;
173
174         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (tp_chat), NULL);
175
176         chat = g_object_new (EMPATHY_TYPE_GROUP_CHAT,
177                              "tp-chat", tp_chat,
178                              NULL);
179
180         return chat;
181 }
182
183 gboolean
184 empathy_group_chat_get_show_contacts (EmpathyGroupChat *chat)
185 {
186         EmpathyGroupChat     *group_chat;
187         EmpathyGroupChatPriv *priv;
188
189         g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), FALSE);
190
191         group_chat = EMPATHY_GROUP_CHAT (chat);
192         priv = GET_PRIV (group_chat);
193
194         return priv->contacts_visible;
195 }
196
197 void
198 empathy_group_chat_set_show_contacts (EmpathyGroupChat *chat,
199                                      gboolean         show)
200 {
201         EmpathyGroupChat     *group_chat;
202         EmpathyGroupChatPriv *priv;
203
204         g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat));
205
206         group_chat = EMPATHY_GROUP_CHAT (chat);
207         priv = GET_PRIV (group_chat);
208
209         priv->contacts_visible = show;
210
211         if (show) {
212                 gtk_widget_show (priv->scrolled_window_contacts);
213                 gtk_paned_set_position (GTK_PANED (priv->hpaned),
214                                         priv->contacts_width);
215         } else {
216                 priv->contacts_width = gtk_paned_get_position (GTK_PANED (priv->hpaned));
217                 gtk_widget_hide (priv->scrolled_window_contacts);
218         }
219 }
220
221 static void
222 group_chat_topic_response_cb (GtkWidget        *dialog,
223                               gint              response,
224                               EmpathyGroupChat *chat)
225 {
226         if (response == GTK_RESPONSE_OK) {
227                 GtkWidget   *entry;
228                 const gchar *topic;
229
230                 entry = g_object_get_data (G_OBJECT (dialog), "entry");
231                 topic = gtk_entry_get_text (GTK_ENTRY (entry));
232                 
233                 if (!G_STR_EMPTY (topic)) {
234                         EmpathyGroupChatPriv *priv;
235                         GValue                value = {0, };
236
237                         priv = GET_PRIV (chat);
238
239                         g_value_init (&value, G_TYPE_STRING);
240                         g_value_set_string (&value, topic);
241                         empathy_tp_chat_set_property (EMPATHY_TP_CHAT (priv->tp_chat),
242                                                       "subject", &value);
243                         g_value_unset (&value);
244                 }
245         }
246
247         gtk_widget_destroy (dialog);
248 }
249
250 static void
251 group_chat_topic_entry_activate_cb (GtkWidget *entry,
252                                     GtkDialog *dialog)
253 {
254         gtk_dialog_response (dialog, GTK_RESPONSE_OK);
255 }
256
257 void
258 empathy_group_chat_set_topic (EmpathyGroupChat *chat)
259 {
260         EmpathyGroupChatPriv *priv;
261         GtkWindow            *parent;
262         GtkWidget            *dialog;
263         GtkWidget            *entry;
264         GtkWidget            *hbox;
265         const gchar          *topic;
266
267         priv = GET_PRIV (chat);
268
269         g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat));
270
271         parent = empathy_get_toplevel_window (empathy_chat_get_widget (EMPATHY_CHAT (chat)));
272         dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
273                                          0,
274                                          GTK_MESSAGE_QUESTION,
275                                          GTK_BUTTONS_OK_CANCEL,
276                                          _("Enter the new topic you want to set for this room:"));
277
278         topic = gtk_label_get_text (GTK_LABEL (priv->label_topic));
279
280         hbox = gtk_hbox_new (FALSE, 0);
281         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
282                             hbox, FALSE, TRUE, 4);
283
284         entry = gtk_entry_new ();
285         gtk_entry_set_text (GTK_ENTRY (entry), topic);
286         gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
287                     
288         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 4);
289
290         g_object_set (GTK_MESSAGE_DIALOG (dialog)->label, "use-markup", TRUE, NULL);
291         g_object_set_data (G_OBJECT (dialog), "entry", entry);
292
293         g_signal_connect (entry, "activate",
294                           G_CALLBACK (group_chat_topic_entry_activate_cb),
295                           dialog);
296         g_signal_connect (dialog, "response",
297                           G_CALLBACK (group_chat_topic_response_cb),
298                           chat);
299
300         gtk_widget_show_all (dialog);
301 }
302
303 static void
304 group_chat_create_ui (EmpathyGroupChat *chat)
305 {
306         EmpathyGroupChatPriv *priv;
307         GladeXML            *glade;
308         GList               *list = NULL; 
309         gchar               *filename;
310
311         priv = GET_PRIV (chat);
312
313         filename = empathy_file_lookup ("empathy-group-chat.glade",
314                                         "libempathy-gtk");
315         glade = empathy_glade_get_file (filename,
316                                        "group_chat_widget",
317                                        NULL,
318                                        "group_chat_widget", &priv->widget,
319                                        "hpaned", &priv->hpaned,
320                                        "vbox_left", &priv->vbox_left,
321                                        "scrolled_window_chat", &priv->scrolled_window_chat,
322                                        "scrolled_window_input", &priv->scrolled_window_input,
323                                        "hbox_topic", &priv->hbox_topic,
324                                        "label_topic", &priv->label_topic,
325                                        "scrolled_window_contacts", &priv->scrolled_window_contacts,
326                                        NULL);
327         g_free (filename);
328
329         empathy_glade_connect (glade,
330                               chat,
331                               "group_chat_widget", "destroy", group_chat_widget_destroy_cb,
332                               NULL);
333
334         g_object_unref (glade);
335
336         g_object_set_data (G_OBJECT (priv->widget), "chat", g_object_ref (chat));
337
338         /* Add room GtkTextView. */
339         gtk_container_add (GTK_CONTAINER (priv->scrolled_window_chat),
340                            GTK_WIDGET (EMPATHY_CHAT (chat)->view));
341         gtk_widget_show (GTK_WIDGET (EMPATHY_CHAT (chat)->view));
342
343         /* Add input GtkTextView */
344         gtk_container_add (GTK_CONTAINER (priv->scrolled_window_input),
345                            EMPATHY_CHAT (chat)->input_text_view);
346         gtk_widget_show (EMPATHY_CHAT (chat)->input_text_view);
347
348         /* Add nick name completion */
349         priv->completion = g_completion_new ((GCompletionFunc) empathy_contact_get_name);
350         g_completion_set_compare (priv->completion,
351                                   group_chat_contacts_completion_func);
352
353         /* Set widget focus order */
354         list = g_list_append (NULL, priv->scrolled_window_input);
355         gtk_container_set_focus_chain (GTK_CONTAINER (priv->vbox_left), list);
356         g_list_free (list);
357
358         list = g_list_append (NULL, priv->vbox_left);
359         list = g_list_append (list, priv->scrolled_window_contacts);
360         gtk_container_set_focus_chain (GTK_CONTAINER (priv->hpaned), list);
361         g_list_free (list);
362
363         list = g_list_append (NULL, priv->hpaned);
364         list = g_list_append (list, priv->hbox_topic);
365         gtk_container_set_focus_chain (GTK_CONTAINER (priv->widget), list);
366         g_list_free (list);
367 }
368
369 static void
370 group_chat_widget_destroy_cb (GtkWidget       *widget,
371                               EmpathyGroupChat *chat)
372 {
373         empathy_debug (DEBUG_DOMAIN, "Destroyed");
374
375         g_object_unref (chat);
376 }
377
378 static void
379 group_chat_members_changed_cb (EmpathyTpChatroom *tp_chat,
380                                EmpathyContact     *contact,
381                                EmpathyContact     *actor,
382                                guint               reason,
383                                gchar              *message,
384                                gboolean            is_member,
385                                EmpathyGroupChat   *chat)
386 {
387         if (!EMPATHY_CHAT (chat)->block_events) {
388                 gchar *str;
389                 if (is_member) {
390                         str = g_strdup_printf (_("%s has joined the room"),
391                                                empathy_contact_get_name (contact));
392                 } else {
393                         str = g_strdup_printf (_("%s has left the room"),
394                                                empathy_contact_get_name (contact));
395                 }
396                 empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, str);
397                 g_free (str);
398         }
399 }
400
401 static const gchar *
402 group_chat_get_name (EmpathyChat *chat)
403 {
404         EmpathyGroupChat     *group_chat;
405         EmpathyGroupChatPriv *priv;
406
407         g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL);
408
409         group_chat = EMPATHY_GROUP_CHAT (chat);
410         priv = GET_PRIV (group_chat);
411
412         if (!priv->name) {
413                 const gchar *id;
414                 const gchar *server;
415
416                 id = empathy_chat_get_id (chat);
417                 server = strstr (id, "@");
418
419                 if (server) {
420                         priv->name = g_strndup (id, server - id);
421                 } else {
422                         priv->name = g_strdup (id);
423                 } 
424         }
425
426         return priv->name;
427 }
428
429 static gchar *
430 group_chat_get_tooltip (EmpathyChat *chat)
431 {
432         EmpathyGroupChat     *group_chat;
433         EmpathyGroupChatPriv *priv;
434
435         g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL);
436
437         group_chat = EMPATHY_GROUP_CHAT (chat);
438         priv = GET_PRIV (group_chat);
439
440         if (priv->topic) {
441                 gchar *topic, *tmp;
442
443                 topic = g_strdup_printf (_("Topic: %s"), priv->topic);
444                 tmp = g_strdup_printf ("%s\n%s", priv->name, topic);
445                 g_free (topic);
446
447                 return tmp;
448         }
449
450         return g_strdup (priv->name);
451 }
452
453 static const gchar *
454 group_chat_get_status_icon_name (EmpathyChat *chat)
455 {
456         return EMPATHY_IMAGE_GROUP_MESSAGE;
457 }
458
459 static GtkWidget *
460 group_chat_get_widget (EmpathyChat *chat)
461 {
462         EmpathyGroupChat     *group_chat;
463         EmpathyGroupChatPriv *priv;
464
465         g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL);
466
467         group_chat = EMPATHY_GROUP_CHAT (chat);
468         priv = GET_PRIV (group_chat);
469
470         return priv->widget;
471 }
472
473 static gboolean
474 group_chat_is_group_chat (EmpathyChat *chat)
475 {
476         g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), FALSE);
477
478         return TRUE;
479 }
480
481 static void
482 group_chat_property_changed_cb (EmpathyTpChat    *tp_chat,
483                                 gchar            *name,
484                                 GValue           *value,
485                                 EmpathyGroupChat *chat)
486 {
487         EmpathyGroupChatPriv *priv;
488         const gchar          *str = NULL;
489
490         priv = GET_PRIV (chat);
491
492         /* FIXME: this is ugly, should use properties on EmpathyChat obj */
493
494         if (!tp_strdiff (name, "name")) {
495                 str = g_value_get_string (value);
496                 g_free (priv->name);
497                 priv->name = g_strdup (str);
498                 return;
499         }
500
501         if (tp_strdiff (name, "subject")) {
502                 return;
503         }
504
505         str = g_value_get_string (value);
506         if (!tp_strdiff (priv->topic, str)) {
507                 return;
508         }
509
510         g_free (priv->topic);
511         priv->topic = g_strdup (str);
512         gtk_label_set_text (GTK_LABEL (priv->label_topic), priv->topic);
513
514         if (!EMPATHY_CHAT (chat)->block_events) {
515                 gchar *string;
516
517                 if (!G_STR_EMPTY (priv->topic)) {
518                         string = g_strdup_printf (_("Topic set to: %s"), priv->topic);
519                 } else {
520                         string = g_strdup (_("No topic defined"));
521                 }
522                 empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, string);
523                 g_free (string);
524         }
525 }
526
527 static void
528 group_chat_set_tp_chat (EmpathyChat    *chat,
529                         EmpathyTpChat *tp_chat)
530 {
531         EmpathyGroupChat     *group_chat;
532         EmpathyGroupChatPriv *priv;
533
534         g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat));
535
536         group_chat = EMPATHY_GROUP_CHAT (chat);
537         priv = GET_PRIV (group_chat);
538
539         /* Free all resources related to tp_chat */
540         if (priv->tp_chat) {
541                 g_object_unref (priv->tp_chat);
542                 priv->tp_chat = NULL;
543         }
544         if (priv->view) {
545                 gtk_widget_destroy (GTK_WIDGET (priv->view));
546                 g_object_unref (priv->store);
547         }
548         g_free (priv->name);
549         g_free (priv->topic);
550         priv->name = NULL;
551         priv->topic = NULL;
552
553         if (!tp_chat) {
554                 /* We are no more connected */
555                 gtk_widget_set_sensitive (priv->hbox_topic, FALSE);
556                 gtk_widget_set_sensitive (priv->scrolled_window_contacts, FALSE);
557                 return;
558         }
559
560         /* We are connected */
561         gtk_widget_set_sensitive (priv->hbox_topic, TRUE);
562         gtk_widget_set_sensitive (priv->scrolled_window_contacts, TRUE);
563
564         priv->tp_chat = g_object_ref (tp_chat);
565
566         if (empathy_tp_chatroom_get_invitation (priv->tp_chat, NULL, NULL)) {
567                 empathy_tp_chatroom_accept_invitation (priv->tp_chat);
568         }
569
570         /* Create contact list */
571         priv->store = empathy_contact_list_store_new (EMPATHY_CONTACT_LIST (priv->tp_chat));
572         priv->view = empathy_contact_list_view_new (priv->store,
573                                                     EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CHAT |
574                                                     EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CALL |
575                                                     EMPATHY_CONTACT_LIST_FEATURE_CONTACT_LOG |
576                                                     EMPATHY_CONTACT_LIST_FEATURE_CONTACT_FT |
577                                                     EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INVITE |
578                                                     EMPATHY_CONTACT_LIST_FEATURE_CONTACT_INFO);
579
580         gtk_container_add (GTK_CONTAINER (priv->scrolled_window_contacts),
581                            GTK_WIDGET (priv->view));
582         gtk_widget_show (GTK_WIDGET (priv->view));
583
584         /* Connect signals */
585         g_signal_connect (priv->tp_chat, "members-changed",
586                           G_CALLBACK (group_chat_members_changed_cb),
587                           chat);
588         g_signal_connect (priv->tp_chat, "property-changed",
589                           G_CALLBACK (group_chat_property_changed_cb),
590                           chat);
591 }
592
593 static gboolean
594 group_chat_key_press_event (EmpathyChat *chat,
595                             GdkEventKey *event)
596 {
597         EmpathyGroupChatPriv *priv = GET_PRIV (chat);
598
599         if (!(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) &&
600             event->keyval == GDK_Tab) {
601                 GtkTextBuffer *buffer;
602                 GtkTextIter    start, current;
603                 gchar         *nick, *completed;
604                 GList         *list, *completed_list;
605                 gboolean       is_start_of_buffer;
606
607                 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (EMPATHY_CHAT (chat)->input_text_view));
608                 gtk_text_buffer_get_iter_at_mark (buffer, &current, gtk_text_buffer_get_insert (buffer));
609
610                 /* Get the start of the nick to complete. */
611                 gtk_text_buffer_get_iter_at_mark (buffer, &start, gtk_text_buffer_get_insert (buffer));
612                 gtk_text_iter_backward_word_start (&start);
613                 is_start_of_buffer = gtk_text_iter_is_start (&start);
614
615                 list = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (priv->tp_chat));
616                 g_completion_add_items (priv->completion, list);
617
618                 nick = gtk_text_buffer_get_text (buffer, &start, &current, FALSE);
619                 completed_list = g_completion_complete (priv->completion,
620                                                         nick,
621                                                         &completed);
622
623                 g_free (nick);
624
625                 if (completed) {
626                         guint        len;
627                         const gchar *text;
628                         gchar       *complete_char = NULL;
629
630                         gtk_text_buffer_delete (buffer, &start, &current);
631
632                         len = g_list_length (completed_list);
633
634                         if (len == 1) {
635                                 /* If we only have one hit, use that text
636                                  * instead of the text in completed since the
637                                  * completed text will use the typed string
638                                  * which might be cased all wrong.
639                                  * Fixes #120876
640                                  * */
641                                 text = empathy_contact_get_name (completed_list->data);
642                         } else {
643                                 text = completed;
644                         }
645
646                         gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text));
647
648                         if (len == 1 && is_start_of_buffer &&
649                             empathy_conf_get_string (empathy_conf_get (),
650                                                      EMPATHY_PREFS_CHAT_NICK_COMPLETION_CHAR,
651                                                      &complete_char) &&
652                             complete_char != NULL) {
653                                 gtk_text_buffer_insert_at_cursor (buffer,
654                                                                   complete_char,
655                                                                   strlen (complete_char));
656                                 gtk_text_buffer_insert_at_cursor (buffer, " ", 1);
657                                 g_free (complete_char);
658                         }
659
660                         g_free (completed);
661                 }
662
663                 g_completion_clear_items (priv->completion);
664
665                 g_list_foreach (list, (GFunc) g_object_unref, NULL);
666                 g_list_free (list);
667
668                 return TRUE;
669         }
670
671         return FALSE;
672 }
673
674 static gint
675 group_chat_contacts_completion_func (const gchar *s1,
676                                      const gchar *s2,
677                                      gsize        n)
678 {
679         gchar *tmp, *nick1, *nick2;
680         gint   ret;
681
682         tmp = g_utf8_normalize (s1, -1, G_NORMALIZE_DEFAULT);
683         nick1 = g_utf8_casefold (tmp, -1);
684         g_free (tmp);
685
686         tmp = g_utf8_normalize (s2, -1, G_NORMALIZE_DEFAULT);
687         nick2 = g_utf8_casefold (tmp, -1);
688         g_free (tmp);
689
690         ret = strncmp (nick1, nick2, n);
691
692         g_free (nick1);
693         g_free (nick2);
694
695         return ret;
696 }
697