]> git.0d.be Git - empathy.git/blob - libempathy-gtk/gossip-group-chat.c
3dc4022bf382794bb4d124c817d0c3a8e89eb4c1
[empathy.git] / libempathy-gtk / gossip-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/gossip-contact.h>
38 #include <libempathy/gossip-utils.h>
39 #include <libempathy/gossip-debug.h>
40
41 #include "gossip-group-chat.h"
42 #include "gossip-chat.h"
43 #include "gossip-chat-view.h"
44 #include "gossip-contact-list-store.h"
45 #include "gossip-contact-list-view.h"
46 //#include "gossip-chat-invite.h"
47 //#include "gossip-sound.h"
48 #include "empathy-images.h"
49 #include "gossip-ui-utils.h"
50
51 #define DEBUG_DOMAIN "GroupChat"
52
53 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_GROUP_CHAT, GossipGroupChatPriv))
54
55 struct _GossipGroupChatPriv {
56         GossipContactListStore *store;
57         GossipContactListView  *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                (GossipGroupChat   *chat);
79 static void          group_chat_widget_destroy_cb        (GtkWidget         *widget,
80                                                           GossipGroupChat   *chat);
81 static void          group_chat_contact_added_cb         (EmpathyTpChatroom *tp_chat,
82                                                           GossipContact     *contact,
83                                                           GossipGroupChat   *chat);
84 static void          group_chat_contact_removed_cb       (EmpathyTpChatroom *tp_chat,
85                                                           GossipContact     *contact,
86                                                           GossipGroupChat   *chat);
87 /*static void          group_chat_topic_changed_cb         (EmpathyTpChatroom *tp_chat,
88                                                           const gchar       *new_topic,
89                                                           GossipGroupChat   *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                                                           GossipGroupChat   *chat);
95 static const gchar * group_chat_get_name                 (GossipChat        *chat);
96 static gchar *       group_chat_get_tooltip              (GossipChat        *chat);
97 static const gchar * group_chat_get_status_icon_name     (GossipChat        *chat);
98 static GtkWidget *   group_chat_get_widget               (GossipChat        *chat);
99 static gboolean      group_chat_is_group_chat            (GossipChat        *chat);
100 /*static gboolean      group_chat_key_press_event          (GtkWidget         *widget,
101                                                           GdkEventKey       *event,
102                                                           GossipGroupChat   *chat);*/
103 static gint          group_chat_contacts_completion_func (const gchar       *s1,
104                                                           const gchar       *s2,
105                                                           gsize              n);
106
107 G_DEFINE_TYPE (GossipGroupChat, gossip_group_chat, GOSSIP_TYPE_CHAT)
108
109 static void
110 gossip_group_chat_class_init (GossipGroupChatClass *klass)
111 {
112         GObjectClass    *object_class;
113         GossipChatClass *chat_class;
114
115         object_class = G_OBJECT_CLASS (klass);
116         chat_class = GOSSIP_CHAT_CLASS (klass);
117
118         object_class->finalize           = group_chat_finalize;
119
120         chat_class->get_name             = group_chat_get_name;
121         chat_class->get_tooltip          = group_chat_get_tooltip;
122         chat_class->get_status_icon_name = group_chat_get_status_icon_name;
123         chat_class->get_widget           = group_chat_get_widget;
124         chat_class->is_group_chat        = group_chat_is_group_chat;
125
126         g_type_class_add_private (object_class, sizeof (GossipGroupChatPriv));
127 }
128
129 static void
130 gossip_group_chat_init (GossipGroupChat *chat)
131 {
132         GossipGroupChatPriv *priv;
133         GossipChatView      *chatview;
134
135         priv = GET_PRIV (chat);
136
137         priv->contacts_visible = TRUE;
138
139         chatview = GOSSIP_CHAT_VIEW (GOSSIP_CHAT (chat)->view);
140         gossip_chat_view_set_is_group_chat (chatview, TRUE);
141
142         group_chat_create_ui (chat);
143 }
144
145 static void
146 group_chat_finalize (GObject *object)
147 {
148         GossipGroupChat     *chat;
149         GossipGroupChatPriv *priv;
150
151         gossip_debug (DEBUG_DOMAIN, "Finalized:%p", object);
152
153         chat = GOSSIP_GROUP_CHAT (object);
154         priv = GET_PRIV (chat);
155         
156         g_free (priv->name);
157         g_free (priv->topic);
158         g_object_unref (priv->store);
159         g_object_unref (priv->tp_chat); 
160         g_completion_free (priv->completion);
161
162         G_OBJECT_CLASS (gossip_group_chat_parent_class)->finalize (object);
163 }
164
165 GossipGroupChat *
166 gossip_group_chat_new (McAccount *account,
167                        TpChan    *tp_chan)
168 {
169         GossipGroupChat     *chat;
170         GossipGroupChatPriv *priv;
171
172         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
173         g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL);
174
175         chat = g_object_new (GOSSIP_TYPE_GROUP_CHAT, NULL);
176
177         priv = GET_PRIV (chat);
178
179         priv->tp_chat = empathy_tp_chatroom_new (account, tp_chan);
180         gossip_chat_set_tp_chat (GOSSIP_CHAT (chat), EMPATHY_TP_CHAT (priv->tp_chat));
181         GOSSIP_CHAT (chat)->account = g_object_ref (account);
182
183         /* FIXME: Ask the user before accepting */
184         empathy_tp_chatroom_accept_invitation (priv->tp_chat);
185
186         /* Create contact list */
187         priv->store = gossip_contact_list_store_new (EMPATHY_CONTACT_LIST (priv->tp_chat));
188         priv->view = gossip_contact_list_view_new (priv->store);
189         gossip_contact_list_store_set_show_offline (priv->store, TRUE);
190         gtk_container_add (GTK_CONTAINER (priv->scrolled_window_contacts),
191                            GTK_WIDGET (priv->view));
192         gtk_widget_show (GTK_WIDGET (priv->view));
193
194         g_signal_connect (priv->tp_chat, "contact-added",
195                           G_CALLBACK (group_chat_contact_added_cb),
196                           chat);
197         g_signal_connect (priv->tp_chat, "contact-removed",
198                           G_CALLBACK (group_chat_contact_removed_cb),
199                           chat);
200 /*      g_signal_connect (priv->tp_chat, "chatroom-topic-changed",
201                           G_CALLBACK (group_chat_topic_changed_cb),
202                           chat);
203         g_signal_connect (priv->tp_chat, "contact-info-changed",
204                           G_CALLBACK (group_chat_contact_info_changed_cb),
205                           chat);*/
206
207         return chat;
208 }
209
210 gboolean
211 gossip_group_chat_get_show_contacts (GossipGroupChat *chat)
212 {
213         GossipGroupChat     *group_chat;
214         GossipGroupChatPriv *priv;
215
216         g_return_val_if_fail (GOSSIP_IS_GROUP_CHAT (chat), FALSE);
217
218         group_chat = GOSSIP_GROUP_CHAT (chat);
219         priv = GET_PRIV (group_chat);
220
221         return priv->contacts_visible;
222 }
223
224 void
225 gossip_group_chat_set_show_contacts (GossipGroupChat *chat,
226                                      gboolean         show)
227 {
228         GossipGroupChat     *group_chat;
229         GossipGroupChatPriv *priv;
230
231         g_return_if_fail (GOSSIP_IS_GROUP_CHAT (chat));
232
233         group_chat = GOSSIP_GROUP_CHAT (chat);
234         priv = GET_PRIV (group_chat);
235
236         priv->contacts_visible = show;
237
238         if (show) {
239                 gtk_widget_show (priv->scrolled_window_contacts);
240                 gtk_paned_set_position (GTK_PANED (priv->hpaned),
241                                         priv->contacts_width);
242         } else {
243                 priv->contacts_width = gtk_paned_get_position (GTK_PANED (priv->hpaned));
244                 gtk_widget_hide (priv->scrolled_window_contacts);
245         }
246 }
247
248 void
249 gossip_group_chat_set_topic (GossipGroupChat *chat)
250 {
251         GossipGroupChatPriv *priv;
252         GossipChatWindow    *chat_window;
253         GtkWidget           *chat_dialog;
254         GtkWidget           *dialog;
255         GtkWidget           *entry;
256         GtkWidget           *hbox;
257         const gchar         *topic;
258
259         g_return_if_fail (GOSSIP_IS_GROUP_CHAT (chat));
260
261         priv = GET_PRIV (chat);
262
263         chat_window = gossip_chat_get_window (GOSSIP_CHAT (chat));
264         chat_dialog = gossip_chat_window_get_dialog (chat_window);
265
266         dialog = gtk_message_dialog_new (GTK_WINDOW (chat_dialog),
267                                          0,
268                                          GTK_MESSAGE_QUESTION,
269                                          GTK_BUTTONS_OK_CANCEL,
270                                          _("Enter the new topic you want to set for this room:"));
271
272         topic = gtk_label_get_text (GTK_LABEL (priv->label_topic));
273
274         hbox = gtk_hbox_new (FALSE, 0);
275         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
276                             hbox, FALSE, TRUE, 4);
277
278         entry = gtk_entry_new ();
279         gtk_entry_set_text (GTK_ENTRY (entry), topic);
280         gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
281                     
282         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 4);
283
284         g_object_set (GTK_MESSAGE_DIALOG (dialog)->label, "use-markup", TRUE, NULL);
285         g_object_set_data (G_OBJECT (dialog), "entry", entry);
286
287         g_signal_connect (entry, "activate",
288                           G_CALLBACK (group_chat_topic_entry_activate_cb),
289                           dialog);
290         g_signal_connect (dialog, "response",
291                           G_CALLBACK (group_chat_topic_response_cb),
292                           chat);
293
294         gtk_widget_show_all (dialog);
295 }
296
297 static void
298 group_chat_create_ui (GossipGroupChat *chat)
299 {
300         GossipGroupChatPriv *priv;
301         GladeXML            *glade;
302         GList               *list = NULL; 
303
304         priv = GET_PRIV (chat);
305
306         glade = gossip_glade_get_file ("gossip-group-chat.glade",
307                                        "group_chat_widget",
308                                        NULL,
309                                        "group_chat_widget", &priv->widget,
310                                        "hpaned", &priv->hpaned,
311                                        "vbox_left", &priv->vbox_left,
312                                        "scrolled_window_chat", &priv->scrolled_window_chat,
313                                        "scrolled_window_input", &priv->scrolled_window_input,
314                                        "hbox_topic", &priv->hbox_topic,
315                                        "label_topic", &priv->label_topic,
316                                        "scrolled_window_contacts", &priv->scrolled_window_contacts,
317                                        NULL);
318
319         gossip_glade_connect (glade,
320                               chat,
321                               "group_chat_widget", "destroy", group_chat_widget_destroy_cb,
322                               NULL);
323
324         g_object_unref (glade);
325
326         g_object_set_data (G_OBJECT (priv->widget), "chat", g_object_ref (chat));
327
328         /* Add room GtkTextView. */
329         gtk_container_add (GTK_CONTAINER (priv->scrolled_window_chat),
330                            GTK_WIDGET (GOSSIP_CHAT (chat)->view));
331         gtk_widget_show (GTK_WIDGET (GOSSIP_CHAT (chat)->view));
332
333         /* Add input GtkTextView */
334         gtk_container_add (GTK_CONTAINER (priv->scrolled_window_input),
335                            GOSSIP_CHAT (chat)->input_text_view);
336         gtk_widget_show (GOSSIP_CHAT (chat)->input_text_view);
337
338         /* Add nick name completion */
339         priv->completion = g_completion_new (NULL);
340         g_completion_set_compare (priv->completion,
341                                   group_chat_contacts_completion_func);
342
343         /* Set widget focus order */
344         list = g_list_append (NULL, priv->scrolled_window_input);
345         gtk_container_set_focus_chain (GTK_CONTAINER (priv->vbox_left), list);
346         g_list_free (list);
347
348         list = g_list_append (NULL, priv->vbox_left);
349         list = g_list_append (list, priv->scrolled_window_contacts);
350         gtk_container_set_focus_chain (GTK_CONTAINER (priv->hpaned), list);
351         g_list_free (list);
352
353         list = g_list_append (NULL, priv->hpaned);
354         list = g_list_append (list, priv->hbox_topic);
355         gtk_container_set_focus_chain (GTK_CONTAINER (priv->widget), list);
356         g_list_free (list);
357 }
358
359 static void
360 group_chat_widget_destroy_cb (GtkWidget       *widget,
361                               GossipGroupChat *chat)
362 {
363         gossip_debug (DEBUG_DOMAIN, "Destroyed");
364
365         g_object_unref (chat);
366 }
367
368 static void
369 group_chat_contact_added_cb (EmpathyTpChatroom *tp_chat,
370                              GossipContact     *contact,
371                              GossipGroupChat   *chat)
372 {
373         GossipGroupChatPriv *priv;
374         gchar               *str;
375
376         priv = GET_PRIV (chat);
377
378         str = g_strdup_printf (_("%s has joined the room"),
379                                gossip_contact_get_name (contact));
380         gossip_chat_view_append_event (GOSSIP_CHAT (chat)->view, str);
381         g_free (str);
382 }
383
384 static void
385 group_chat_contact_removed_cb (EmpathyTpChatroom *tp_chat,
386                                GossipContact     *contact,
387                                GossipGroupChat   *chat)
388 {
389         GossipGroupChatPriv *priv;
390         gchar               *str;
391
392         priv = GET_PRIV (chat);
393
394         str = g_strdup_printf (_("%s has left the room"),
395                                gossip_contact_get_name (contact));
396         gossip_chat_view_append_event (GOSSIP_CHAT (chat)->view, str);
397         g_free (str);
398 }
399 /*
400 static void
401 group_chat_topic_changed_cb (EmpathyTpChatroom *tp_chat,
402                              const gchar       *new_topic,
403                              GossipGroupChat   *chat)
404 {
405         GossipGroupChatPriv *priv;
406         gchar               *str;
407
408         priv = GET_PRIV (chat);
409
410         gossip_debug (DEBUG_DOMAIN, "Topic changed by to:'%s'", new_topic);
411
412         g_free (priv->topic);
413         priv->topic = g_strdup (new_topic);
414         
415         gtk_label_set_text (GTK_LABEL (priv->label_topic), new_topic);
416
417         str = g_strdup_printf (_("Topic set to: %s"), new_topic);
418         gossip_chat_view_append_event (GOSSIP_CHAT (chat)->view, str);
419         g_free (str);
420 }
421 */
422 static void
423 group_chat_topic_entry_activate_cb (GtkWidget *entry,
424                                     GtkDialog *dialog)
425 {
426         gtk_dialog_response (dialog, GTK_RESPONSE_OK);
427 }
428
429 static void
430 group_chat_topic_response_cb (GtkWidget       *dialog,
431                               gint             response,                              
432                               GossipGroupChat *chat)
433 {
434         if (response == GTK_RESPONSE_OK) {
435                 GtkWidget   *entry;
436                 const gchar *topic;
437
438                 entry = g_object_get_data (G_OBJECT (dialog), "entry");
439                 topic = gtk_entry_get_text (GTK_ENTRY (entry));
440                 
441                 if (!G_STR_EMPTY (topic)) {
442                         GossipGroupChatPriv *priv;
443
444                         priv = GET_PRIV (chat);
445
446                         empathy_tp_chatroom_set_topic (priv->tp_chat, topic);
447                 }
448         }
449
450         gtk_widget_destroy (dialog);
451 }
452
453 static const gchar *
454 group_chat_get_name (GossipChat *chat)
455 {
456         GossipGroupChat     *group_chat;
457         GossipGroupChatPriv *priv;
458
459         g_return_val_if_fail (GOSSIP_IS_GROUP_CHAT (chat), NULL);
460
461         group_chat = GOSSIP_GROUP_CHAT (chat);
462         priv = GET_PRIV (group_chat);
463
464         return priv->name;
465 }
466
467 static gchar *
468 group_chat_get_tooltip (GossipChat *chat)
469 {
470         GossipGroupChat     *group_chat;
471         GossipGroupChatPriv *priv;
472
473         g_return_val_if_fail (GOSSIP_IS_GROUP_CHAT (chat), NULL);
474
475         group_chat = GOSSIP_GROUP_CHAT (chat);
476         priv = GET_PRIV (group_chat);
477
478         if (priv->topic) {
479                 gchar *topic, *tmp;
480
481                 topic = g_strdup_printf (_("Topic: %s"), priv->topic);
482                 tmp = g_strdup_printf ("%s\n%s", priv->name, topic);
483                 g_free (topic);
484
485                 return tmp;
486         }
487
488         return g_strdup (priv->name);
489 }
490
491 static const gchar *
492 group_chat_get_status_icon_name (GossipChat *chat)
493 {
494         return EMPATHY_IMAGE_GROUP_MESSAGE;
495 }
496
497 static GtkWidget *
498 group_chat_get_widget (GossipChat *chat)
499 {
500         GossipGroupChat     *group_chat;
501         GossipGroupChatPriv *priv;
502
503         g_return_val_if_fail (GOSSIP_IS_GROUP_CHAT (chat), NULL);
504
505         group_chat = GOSSIP_GROUP_CHAT (chat);
506         priv = GET_PRIV (group_chat);
507
508         return priv->widget;
509 }
510
511 static gboolean
512 group_chat_is_group_chat (GossipChat *chat)
513 {
514         g_return_val_if_fail (GOSSIP_IS_GROUP_CHAT (chat), FALSE);
515
516         return TRUE;
517 }
518 #if 0
519 static gboolean
520 group_chat_key_press_event (GtkWidget       *widget,
521                             GdkEventKey     *event,
522                             GossipGroupChat *chat)
523 {
524         GossipGroupChatPriv *priv;
525         GtkAdjustment       *adj;
526         gdouble              val;
527         GtkTextBuffer       *buffer;
528         GtkTextIter          start, current;
529         gchar               *nick, *completed;
530         gint                 len;
531         GList               *list, *l, *completed_list;
532         gboolean             is_start_of_buffer;
533
534         priv = GET_PRIV (chat);
535
536         if ((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK &&
537             (event->state & GDK_SHIFT_MASK) != GDK_SHIFT_MASK &&
538             event->keyval == GDK_Tab) {
539                 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (GOSSIP_CHAT (chat)->input_text_view));
540                 gtk_text_buffer_get_iter_at_mark (buffer, &current, gtk_text_buffer_get_insert (buffer));
541
542                 /* Get the start of the nick to complete. */
543                 gtk_text_buffer_get_iter_at_mark (buffer, &start, gtk_text_buffer_get_insert (buffer));
544                 gtk_text_iter_backward_word_start (&start);
545                 is_start_of_buffer = gtk_text_iter_is_start (&start);
546
547                 nick = gtk_text_buffer_get_text (buffer, &start, &current, FALSE);
548
549                 g_completion_clear_items (priv->completion);
550
551                 len = strlen (nick);
552
553                 list = group_chat_get_nick_list (chat);
554
555                 g_completion_add_items (priv->completion, list);
556
557                 completed_list = g_completion_complete (priv->completion,
558                                                         nick,
559                                                         &completed);
560
561                 g_free (nick);
562
563                 if (completed) {
564                         int       len;
565                         gchar    *text;
566
567                         gtk_text_buffer_delete (buffer, &start, &current);
568
569                         len = g_list_length (completed_list);
570
571                         if (len == 1) {
572                                 /* If we only have one hit, use that text
573                                  * instead of the text in completed since the
574                                  * completed text will use the typed string
575                                  * which might be cased all wrong.
576                                  * Fixes #120876
577                                  * */
578                                 text = (gchar *) completed_list->data;
579                         } else {
580                                 text = completed;
581                         }
582
583                         gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text));
584
585                         if (len == 1) {
586                                 if (is_start_of_buffer) {
587                                         gtk_text_buffer_insert_at_cursor (buffer, ", ", 2);
588                                 }
589                         }
590
591                         g_free (completed);
592                 }
593
594                 g_completion_clear_items (priv->completion);
595
596                 for (l = list; l; l = l->next) {
597                         g_free (l->data);
598                 }
599
600                 g_list_free (list);
601
602                 return TRUE;
603         }
604
605         return FALSE;
606 }
607 #endif
608
609 static gint
610 group_chat_contacts_completion_func (const gchar *s1,
611                                      const gchar *s2,
612                                      gsize        n)
613 {
614         gchar *tmp, *nick1, *nick2;
615         gint   ret;
616
617         tmp = g_utf8_normalize (s1, -1, G_NORMALIZE_DEFAULT);
618         nick1 = g_utf8_casefold (tmp, -1);
619         g_free (tmp);
620
621         tmp = g_utf8_normalize (s2, -1, G_NORMALIZE_DEFAULT);
622         nick2 = g_utf8_casefold (tmp, -1);
623         g_free (tmp);
624
625         ret = strncmp (nick1, nick2, n);
626
627         g_free (nick1);
628         g_free (nick2);
629
630         return ret;
631 }
632