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