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