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