]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-group-chat.c
4f86ef2bab2c2e88a868464184422389aff8dca8
[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 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 void          group_chat_subject_notify_cb        (EmpathyTpChat     *tp_chat,
106                                                           GParamSpec        *param,
107                                                           EmpathyGroupChat  *chat);
108 static void          group_chat_name_notify_cb           (EmpathyTpChat     *tp_chat,
109                                                           GParamSpec        *param,
110                                                           EmpathyGroupChat  *chat);
111 static gboolean      group_chat_key_press_event          (EmpathyChat       *chat,
112                                                           GdkEventKey       *event);
113 static gint          group_chat_contacts_completion_func (const gchar       *s1,
114                                                           const gchar       *s2,
115                                                           gsize              n);
116
117 G_DEFINE_TYPE (EmpathyGroupChat, empathy_group_chat, EMPATHY_TYPE_CHAT)
118
119 static void
120 empathy_group_chat_class_init (EmpathyGroupChatClass *klass)
121 {
122         GObjectClass    *object_class;
123         EmpathyChatClass *chat_class;
124
125         object_class = G_OBJECT_CLASS (klass);
126         chat_class = EMPATHY_CHAT_CLASS (klass);
127
128         object_class->finalize           = group_chat_finalize;
129
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;
137
138         g_type_class_add_private (object_class, sizeof (EmpathyGroupChatPriv));
139 }
140
141 static void
142 empathy_group_chat_init (EmpathyGroupChat *chat)
143 {
144         EmpathyGroupChatPriv *priv;
145         EmpathyChatView      *chatview;
146
147         priv = GET_PRIV (chat);
148
149         priv->contacts_visible = TRUE;
150
151         chatview = EMPATHY_CHAT_VIEW (EMPATHY_CHAT (chat)->view);
152         empathy_chat_view_set_is_group_chat (chatview, TRUE);
153
154         group_chat_create_ui (chat);
155 }
156
157 static void
158 group_chat_finalize (GObject *object)
159 {
160         EmpathyGroupChat     *chat;
161         EmpathyGroupChatPriv *priv;
162
163         empathy_debug (DEBUG_DOMAIN, "Finalized:%p", object);
164
165         chat = EMPATHY_GROUP_CHAT (object);
166         priv = GET_PRIV (chat);
167         
168         g_free (priv->name);
169         g_free (priv->topic);
170         g_object_unref (priv->store);
171         g_object_unref (priv->tp_chat); 
172         g_completion_free (priv->completion);
173
174         G_OBJECT_CLASS (empathy_group_chat_parent_class)->finalize (object);
175 }
176
177 EmpathyGroupChat *
178 empathy_group_chat_new (EmpathyTpChatroom *tp_chat)
179 {
180         EmpathyGroupChat *chat;
181
182         g_return_val_if_fail (EMPATHY_IS_TP_CHAT (tp_chat), NULL);
183
184         chat = g_object_new (EMPATHY_TYPE_GROUP_CHAT,
185                              "tp-chat", tp_chat,
186                              NULL);
187
188         return chat;
189 }
190
191 gboolean
192 empathy_group_chat_get_show_contacts (EmpathyGroupChat *chat)
193 {
194         EmpathyGroupChat     *group_chat;
195         EmpathyGroupChatPriv *priv;
196
197         g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), FALSE);
198
199         group_chat = EMPATHY_GROUP_CHAT (chat);
200         priv = GET_PRIV (group_chat);
201
202         return priv->contacts_visible;
203 }
204
205 void
206 empathy_group_chat_set_show_contacts (EmpathyGroupChat *chat,
207                                      gboolean         show)
208 {
209         EmpathyGroupChat     *group_chat;
210         EmpathyGroupChatPriv *priv;
211
212         g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat));
213
214         group_chat = EMPATHY_GROUP_CHAT (chat);
215         priv = GET_PRIV (group_chat);
216
217         priv->contacts_visible = show;
218
219         if (show) {
220                 gtk_widget_show (priv->scrolled_window_contacts);
221                 gtk_paned_set_position (GTK_PANED (priv->hpaned),
222                                         priv->contacts_width);
223         } else {
224                 priv->contacts_width = gtk_paned_get_position (GTK_PANED (priv->hpaned));
225                 gtk_widget_hide (priv->scrolled_window_contacts);
226         }
227 }
228
229 void
230 empathy_group_chat_set_topic (EmpathyGroupChat *chat)
231 {
232         EmpathyGroupChatPriv *priv;
233         EmpathyChatWindow    *chat_window;
234         GtkWidget           *chat_dialog;
235         GtkWidget           *dialog;
236         GtkWidget           *entry;
237         GtkWidget           *hbox;
238         const gchar         *topic;
239
240         g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat));
241
242         priv = GET_PRIV (chat);
243
244         chat_window = empathy_chat_get_window (EMPATHY_CHAT (chat));
245         chat_dialog = empathy_chat_window_get_dialog (chat_window);
246
247         dialog = gtk_message_dialog_new (GTK_WINDOW (chat_dialog),
248                                          0,
249                                          GTK_MESSAGE_QUESTION,
250                                          GTK_BUTTONS_OK_CANCEL,
251                                          _("Enter the new topic you want to set for this room:"));
252
253         topic = gtk_label_get_text (GTK_LABEL (priv->label_topic));
254
255         hbox = gtk_hbox_new (FALSE, 0);
256         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
257                             hbox, FALSE, TRUE, 4);
258
259         entry = gtk_entry_new ();
260         gtk_entry_set_text (GTK_ENTRY (entry), topic);
261         gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
262                     
263         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 4);
264
265         g_object_set (GTK_MESSAGE_DIALOG (dialog)->label, "use-markup", TRUE, NULL);
266         g_object_set_data (G_OBJECT (dialog), "entry", entry);
267
268         g_signal_connect (entry, "activate",
269                           G_CALLBACK (group_chat_topic_entry_activate_cb),
270                           dialog);
271         g_signal_connect (dialog, "response",
272                           G_CALLBACK (group_chat_topic_response_cb),
273                           chat);
274
275         gtk_widget_show_all (dialog);
276 }
277
278 static void
279 group_chat_create_ui (EmpathyGroupChat *chat)
280 {
281         EmpathyGroupChatPriv *priv;
282         GladeXML            *glade;
283         GList               *list = NULL; 
284
285         priv = GET_PRIV (chat);
286
287         glade = empathy_glade_get_file ("empathy-group-chat.glade",
288                                        "group_chat_widget",
289                                        NULL,
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,
298                                        NULL);
299
300         empathy_glade_connect (glade,
301                               chat,
302                               "group_chat_widget", "destroy", group_chat_widget_destroy_cb,
303                               NULL);
304
305         g_object_unref (glade);
306
307         g_object_set_data (G_OBJECT (priv->widget), "chat", g_object_ref (chat));
308
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));
313
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);
318
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);
323
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);
327         g_list_free (list);
328
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);
332         g_list_free (list);
333
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);
337         g_list_free (list);
338 }
339
340 static void
341 group_chat_widget_destroy_cb (GtkWidget       *widget,
342                               EmpathyGroupChat *chat)
343 {
344         empathy_debug (DEBUG_DOMAIN, "Destroyed");
345
346         g_object_unref (chat);
347 }
348
349 static void
350 group_chat_members_changed_cb (EmpathyTpChatroom *tp_chat,
351                                EmpathyContact     *contact,
352                                EmpathyContact     *actor,
353                                guint               reason,
354                                gchar              *message,
355                                gboolean            is_member,
356                                EmpathyGroupChat   *chat)
357 {
358         EmpathyGroupChatPriv *priv;
359         gchar                *str;
360
361         priv = GET_PRIV (chat);
362
363         if (is_member) {
364                 str = g_strdup_printf (_("%s has joined the room"),
365                                        empathy_contact_get_name (contact));
366         } else {
367                 str = g_strdup_printf (_("%s has left the room"),
368                                        empathy_contact_get_name (contact));
369         }
370         empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, str);
371         g_free (str);
372 }
373
374 static void
375 group_chat_topic_entry_activate_cb (GtkWidget *entry,
376                                     GtkDialog *dialog)
377 {
378         gtk_dialog_response (dialog, GTK_RESPONSE_OK);
379 }
380
381 static void
382 group_chat_topic_response_cb (GtkWidget       *dialog,
383                               gint             response,                              
384                               EmpathyGroupChat *chat)
385 {
386         if (response == GTK_RESPONSE_OK) {
387                 GtkWidget   *entry;
388                 const gchar *topic;
389
390                 entry = g_object_get_data (G_OBJECT (dialog), "entry");
391                 topic = gtk_entry_get_text (GTK_ENTRY (entry));
392                 
393                 if (!G_STR_EMPTY (topic)) {
394                         EmpathyGroupChatPriv *priv;
395
396                         priv = GET_PRIV (chat);
397
398                         empathy_tp_chatroom_set_topic (priv->tp_chat, topic);
399                 }
400         }
401
402         gtk_widget_destroy (dialog);
403 }
404
405 static const gchar *
406 group_chat_get_name (EmpathyChat *chat)
407 {
408         EmpathyGroupChat     *group_chat;
409         EmpathyGroupChatPriv *priv;
410
411         g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL);
412
413         group_chat = EMPATHY_GROUP_CHAT (chat);
414         priv = GET_PRIV (group_chat);
415
416         if (!priv->name) {
417                 const gchar *id;
418                 const gchar *server;
419
420                 id = empathy_chat_get_id (chat);
421                 server = strstr (id, "@");
422
423                 if (server) {
424                         priv->name = g_strndup (id, server - id);
425                 } else {
426                         priv->name = g_strdup (id);
427                 } 
428         }
429
430         return priv->name;
431 }
432
433 static gchar *
434 group_chat_get_tooltip (EmpathyChat *chat)
435 {
436         EmpathyGroupChat     *group_chat;
437         EmpathyGroupChatPriv *priv;
438
439         g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL);
440
441         group_chat = EMPATHY_GROUP_CHAT (chat);
442         priv = GET_PRIV (group_chat);
443
444         if (priv->topic) {
445                 gchar *topic, *tmp;
446
447                 topic = g_strdup_printf (_("Topic: %s"), priv->topic);
448                 tmp = g_strdup_printf ("%s\n%s", priv->name, topic);
449                 g_free (topic);
450
451                 return tmp;
452         }
453
454         return g_strdup (priv->name);
455 }
456
457 static const gchar *
458 group_chat_get_status_icon_name (EmpathyChat *chat)
459 {
460         return EMPATHY_IMAGE_GROUP_MESSAGE;
461 }
462
463 static GtkWidget *
464 group_chat_get_widget (EmpathyChat *chat)
465 {
466         EmpathyGroupChat     *group_chat;
467         EmpathyGroupChatPriv *priv;
468
469         g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), NULL);
470
471         group_chat = EMPATHY_GROUP_CHAT (chat);
472         priv = GET_PRIV (group_chat);
473
474         return priv->widget;
475 }
476
477 static gboolean
478 group_chat_is_group_chat (EmpathyChat *chat)
479 {
480         g_return_val_if_fail (EMPATHY_IS_GROUP_CHAT (chat), FALSE);
481
482         return TRUE;
483 }
484
485 static void
486 group_chat_set_tp_chat (EmpathyChat    *chat,
487                         EmpathyTpChat *tp_chat)
488 {
489         EmpathyGroupChat     *group_chat;
490         EmpathyGroupChatPriv *priv;
491
492         g_return_if_fail (EMPATHY_IS_GROUP_CHAT (chat));
493
494         group_chat = EMPATHY_GROUP_CHAT (chat);
495         priv = GET_PRIV (group_chat);
496
497         /* Free all resources related to tp_chat */
498         if (priv->tp_chat) {
499                 g_object_unref (priv->tp_chat);
500                 priv->tp_chat = NULL;
501         }
502         if (priv->view) {
503                 gtk_widget_destroy (GTK_WIDGET (priv->view));
504                 g_object_unref (priv->store);
505         }
506         g_free (priv->name);
507         g_free (priv->topic);
508         priv->name = NULL;
509         priv->topic = NULL;
510
511         if (!tp_chat) {
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);
515                 return;
516         }
517
518         /* We are connected */
519         gtk_widget_set_sensitive (priv->hbox_topic, TRUE);
520         gtk_widget_set_sensitive (priv->scrolled_window_contacts, TRUE);
521
522         priv->tp_chat = g_object_ref (tp_chat);
523
524         if (empathy_tp_chatroom_get_invitation (priv->tp_chat, NULL, NULL)) {
525                 empathy_tp_chatroom_accept_invitation (priv->tp_chat);
526         }
527
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);
537
538         gtk_container_add (GTK_CONTAINER (priv->scrolled_window_contacts),
539                            GTK_WIDGET (priv->view));
540         gtk_widget_show (GTK_WIDGET (priv->view));
541
542         /* Connect signals */
543         g_signal_connect (priv->tp_chat, "members-changed",
544                           G_CALLBACK (group_chat_members_changed_cb),
545                           chat);
546         g_signal_connect (priv->tp_chat, "notify::subject",
547                           G_CALLBACK (group_chat_subject_notify_cb),
548                           chat);
549         g_signal_connect (priv->tp_chat, "notify::name",
550                           G_CALLBACK (group_chat_name_notify_cb),
551                           chat);
552 }
553
554 static void
555 group_chat_subject_notify_cb (EmpathyTpChat   *tp_chat,
556                               GParamSpec      *param,
557                               EmpathyGroupChat *chat)
558 {
559         EmpathyGroupChatPriv *priv;
560         gchar                *str = NULL;
561
562         priv = GET_PRIV (chat);
563
564         g_object_get (priv->tp_chat, "subject", &str, NULL);
565         if (!tp_strdiff (priv->topic, str)) {
566                 g_free (str);
567                 return;
568         }
569
570         g_free (priv->topic);
571         priv->topic = str;
572         gtk_label_set_text (GTK_LABEL (priv->label_topic), priv->topic);
573
574         if (!G_STR_EMPTY (priv->topic)) {
575                 str = g_strdup_printf (_("Topic set to: %s"), priv->topic);
576         } else {
577                 str = g_strdup (_("No topic defined"));
578         }
579         empathy_chat_view_append_event (EMPATHY_CHAT (chat)->view, str);
580         g_free (str);
581 }
582
583 static void
584 group_chat_name_notify_cb (EmpathyTpChat   *tp_chat,
585                            GParamSpec      *param,
586                            EmpathyGroupChat *chat)
587 {
588         EmpathyGroupChatPriv *priv;
589
590         priv = GET_PRIV (chat);
591
592         g_free (priv->name);
593         g_object_get (priv->tp_chat, "name", &priv->name, NULL);
594 }
595
596 static gboolean
597 group_chat_key_press_event (EmpathyChat *chat,
598                             GdkEventKey *event)
599 {
600         EmpathyGroupChatPriv *priv = GET_PRIV (chat);
601
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;
609
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, &current, gtk_text_buffer_get_insert (buffer));
612
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);
617
618                 list = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (priv->tp_chat));
619                 g_completion_add_items (priv->completion, list);
620
621                 nick = gtk_text_buffer_get_text (buffer, &start, &current, FALSE);
622                 completed_list = g_completion_complete (priv->completion,
623                                                         nick,
624                                                         &completed);
625
626                 g_free (nick);
627
628                 if (completed) {
629                         guint        len;
630                         const gchar *text;
631                         gchar       *complete_char = NULL;
632
633                         gtk_text_buffer_delete (buffer, &start, &current);
634
635                         len = g_list_length (completed_list);
636
637                         if (len == 1) {
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.
642                                  * Fixes #120876
643                                  * */
644                                 text = empathy_contact_get_name (completed_list->data);
645                         } else {
646                                 text = completed;
647                         }
648
649                         gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text));
650
651                         if (len == 1 && is_start_of_buffer &&
652                             empathy_conf_get_string (empathy_conf_get (),
653                                                      EMPATHY_PREFS_CHAT_NICK_COMPLETION_CHAR,
654                                                      &complete_char) &&
655                             complete_char != NULL) {
656                                 gtk_text_buffer_insert_at_cursor (buffer,
657                                                                   complete_char,
658                                                                   strlen (complete_char));
659                                 gtk_text_buffer_insert_at_cursor (buffer, " ", 1);
660                                 g_free (complete_char);
661                         }
662
663                         g_free (completed);
664                 }
665
666                 g_completion_clear_items (priv->completion);
667
668                 g_list_foreach (list, (GFunc) g_object_unref, NULL);
669                 g_list_free (list);
670
671                 return TRUE;
672         }
673
674         return FALSE;
675 }
676
677 static gint
678 group_chat_contacts_completion_func (const gchar *s1,
679                                      const gchar *s2,
680                                      gsize        n)
681 {
682         gchar *tmp, *nick1, *nick2;
683         gint   ret;
684
685         tmp = g_utf8_normalize (s1, -1, G_NORMALIZE_DEFAULT);
686         nick1 = g_utf8_casefold (tmp, -1);
687         g_free (tmp);
688
689         tmp = g_utf8_normalize (s2, -1, G_NORMALIZE_DEFAULT);
690         nick2 = g_utf8_casefold (tmp, -1);
691         g_free (tmp);
692
693         ret = strncmp (nick1, nick2, n);
694
695         g_free (nick1);
696         g_free (nick2);
697
698         return ret;
699 }
700