]> git.0d.be Git - empathy.git/blob - libempathy-gtk/gossip-private-chat.c
0c27abf26d65ee26061ae4b9c6db994271227d34
[empathy.git] / libempathy-gtk / gossip-private-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  *          Geert-Jan Van den Bogaerde <geertjan@gnome.org>
25  *          Xavier Claessens <xclaesse@gmail.com>
26  */
27
28 #include "config.h"
29
30 #include <string.h>
31
32 #include <gtk/gtk.h>
33 #include <glade/glade.h>
34 #include <glib/gi18n.h>
35
36 #include <libmissioncontrol/mc-account.h>
37
38 #include <libempathy/gossip-debug.h>
39 #include <libempathy/empathy-tp-chat.h>
40 //#include <libgossip/gossip-log.h>
41
42 #include "gossip-private-chat.h"
43 #include "gossip-chat-view.h"
44 #include "gossip-chat.h"
45 #include "gossip-preferences.h"
46 //#include "gossip-sound.h"
47 #include "empathy-images.h"
48 #include "gossip-ui-utils.h"
49
50 #define DEBUG_DOMAIN "PrivateChat"
51
52 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_PRIVATE_CHAT, GossipPrivateChatPriv))
53
54 struct _GossipPrivateChatPriv {   
55         GossipContact *contact;
56         gchar         *name;
57
58         guint          scroll_idle_id;
59         gboolean       is_online;
60
61         GtkWidget     *widget;
62         GtkWidget     *text_view_sw;
63 };
64
65 static void           gossip_private_chat_class_init            (GossipPrivateChatClass *klass);
66 static void           gossip_private_chat_init                  (GossipPrivateChat      *chat);
67 static void           private_chat_finalize                     (GObject                *object);
68 static void           private_chat_create_ui                    (GossipPrivateChat      *chat);
69 static void           private_chat_contact_presence_updated_cb  (GossipContact          *contact,
70                                                                  GParamSpec             *param,
71                                                                  GossipPrivateChat      *chat);
72 static void           private_chat_contact_updated_cb           (GossipContact          *contact,
73                                                                  GParamSpec             *param,
74                                                                  GossipPrivateChat      *chat);
75 static void           private_chat_widget_destroy_cb            (GtkWidget              *widget,
76                                                                  GossipPrivateChat      *chat);
77 static const gchar *  private_chat_get_name                     (GossipChat             *chat);
78 static gchar *        private_chat_get_tooltip                  (GossipChat             *chat);
79 static const gchar *  private_chat_get_status_icon_name         (GossipChat             *chat);
80 static GossipContact *private_chat_get_contact                  (GossipChat             *chat);
81 static GtkWidget *    private_chat_get_widget                   (GossipChat             *chat);
82
83 G_DEFINE_TYPE (GossipPrivateChat, gossip_private_chat, GOSSIP_TYPE_CHAT);
84
85 static void
86 gossip_private_chat_class_init (GossipPrivateChatClass *klass)
87 {
88         GObjectClass    *object_class = G_OBJECT_CLASS (klass);
89         GossipChatClass *chat_class = GOSSIP_CHAT_CLASS (klass);
90
91         object_class->finalize = private_chat_finalize;
92
93         chat_class->get_name             = private_chat_get_name;
94         chat_class->get_tooltip          = private_chat_get_tooltip;
95         chat_class->get_status_icon_name = private_chat_get_status_icon_name;
96         chat_class->get_contact          = private_chat_get_contact;
97         chat_class->get_widget           = private_chat_get_widget;
98         chat_class->get_show_contacts    = NULL;
99         chat_class->set_show_contacts    = NULL;
100         chat_class->is_group_chat        = NULL;
101
102         g_type_class_add_private (object_class, sizeof (GossipPrivateChatPriv));
103 }
104
105 static void
106 gossip_private_chat_init (GossipPrivateChat *chat)
107 {
108         GossipPrivateChatPriv *priv;
109
110         priv = GET_PRIV (chat);
111
112         priv->is_online = FALSE;
113
114         private_chat_create_ui (chat);
115
116 }
117
118 static void
119 private_chat_finalize (GObject *object)
120 {
121         GossipPrivateChat     *chat;
122         GossipPrivateChatPriv *priv;
123         
124         chat = GOSSIP_PRIVATE_CHAT (object);
125         priv = GET_PRIV (chat);
126
127         g_signal_handlers_disconnect_by_func (priv->contact,
128                                               private_chat_contact_updated_cb,
129                                               chat);
130         g_signal_handlers_disconnect_by_func (priv->contact,
131                                               private_chat_contact_presence_updated_cb,
132                                               chat);
133
134         if (priv->contact) {
135                 g_object_unref (priv->contact);
136         }
137
138         if (priv->scroll_idle_id) {
139                 g_source_remove (priv->scroll_idle_id);
140         }
141
142         g_free (priv->name);
143
144         G_OBJECT_CLASS (gossip_private_chat_parent_class)->finalize (object);
145 }
146
147 static void
148 private_chat_create_ui (GossipPrivateChat *chat)
149 {
150         GladeXML              *glade;
151         GossipPrivateChatPriv *priv;
152         GtkWidget             *input_text_view_sw;
153
154         priv = GET_PRIV (chat);
155
156         glade = gossip_glade_get_file ("gossip-chat.glade",
157                                        "chat_widget",
158                                        NULL,
159                                       "chat_widget", &priv->widget,
160                                       "chat_view_sw", &priv->text_view_sw,
161                                       "input_text_view_sw", &input_text_view_sw,
162                                        NULL);
163
164         gossip_glade_connect (glade,
165                               chat,
166                               "chat_widget", "destroy", private_chat_widget_destroy_cb,
167                               NULL);
168
169         g_object_unref (glade);
170
171         g_object_set_data (G_OBJECT (priv->widget), "chat", g_object_ref (chat));
172
173         gtk_container_add (GTK_CONTAINER (priv->text_view_sw),
174                            GTK_WIDGET (GOSSIP_CHAT (chat)->view));
175         gtk_widget_show (GTK_WIDGET (GOSSIP_CHAT (chat)->view));
176
177         gtk_container_add (GTK_CONTAINER (input_text_view_sw),
178                            GOSSIP_CHAT (chat)->input_text_view);
179         gtk_widget_show (GOSSIP_CHAT (chat)->input_text_view);
180 }
181
182 static void
183 private_chat_contact_presence_updated_cb (GossipContact     *contact,
184                                           GParamSpec        *param,
185                                           GossipPrivateChat *chat)
186 {
187         GossipPrivateChatPriv *priv;
188
189         priv = GET_PRIV (chat);
190
191         gossip_debug (DEBUG_DOMAIN, "Presence update for contact: %s",
192                       gossip_contact_get_id (contact));
193
194         if (!gossip_contact_is_online (contact)) {
195                 if (priv->is_online) {
196                         gchar *msg;
197
198                         msg = g_strdup_printf (_("%s went offline"),
199                                                gossip_contact_get_name (priv->contact));
200                         gossip_chat_view_append_event (GOSSIP_CHAT (chat)->view, msg);
201                         g_free (msg);
202                 }
203
204                 priv->is_online = FALSE;
205
206                 g_signal_emit_by_name (chat, "composing", FALSE);
207
208         } else {
209                 if (!priv->is_online) {
210                         gchar *msg;
211
212                         msg = g_strdup_printf (_("%s has come online"),
213                                                gossip_contact_get_name (priv->contact));
214                         gossip_chat_view_append_event (GOSSIP_CHAT (chat)->view, msg);
215                         g_free (msg);
216                 }
217
218                 priv->is_online = TRUE;
219         }
220
221         g_signal_emit_by_name (chat, "status-changed");
222 }
223
224 static void
225 private_chat_contact_updated_cb (GossipContact     *contact,
226                                  GParamSpec        *param,
227                                  GossipPrivateChat *chat)
228 {
229         GossipPrivateChatPriv *priv;
230
231         priv = GET_PRIV (chat);
232
233         if (strcmp (priv->name, gossip_contact_get_name (contact)) != 0) {
234                 g_free (priv->name);
235                 priv->name = g_strdup (gossip_contact_get_name (contact));
236                 g_signal_emit_by_name (chat, "name-changed", priv->name);
237         }
238 }
239
240 static void
241 private_chat_widget_destroy_cb (GtkWidget         *widget,
242                                 GossipPrivateChat *chat)
243 {
244         gossip_debug (DEBUG_DOMAIN, "Destroyed");
245
246         g_object_unref (chat);
247 }
248
249 static const gchar *
250 private_chat_get_name (GossipChat *chat)
251 {
252         GossipPrivateChatPriv *priv;
253
254         g_return_val_if_fail (GOSSIP_IS_PRIVATE_CHAT (chat), NULL);
255
256         priv = GET_PRIV (chat);
257
258         return priv->name;
259 }
260
261 static gchar *
262 private_chat_get_tooltip (GossipChat *chat)
263 {
264         GossipPrivateChatPriv *priv;
265         GossipContact         *contact;
266         const gchar           *status;
267
268         g_return_val_if_fail (GOSSIP_IS_PRIVATE_CHAT (chat), NULL);
269
270         priv = GET_PRIV (chat);
271
272         contact = gossip_chat_get_contact (chat);
273         status = gossip_contact_get_status (contact);
274
275         return g_strdup_printf ("%s\n%s",
276                                 gossip_contact_get_id (contact),
277                                 status);
278 }
279
280 static const gchar *
281 private_chat_get_status_icon_name (GossipChat *chat)
282 {
283         GossipPrivateChatPriv *priv;
284         GossipContact         *contact;
285
286         g_return_val_if_fail (GOSSIP_IS_PRIVATE_CHAT (chat), NULL);
287
288         priv = GET_PRIV (chat);
289
290         contact = gossip_chat_get_contact (chat);
291
292         return gossip_icon_name_for_contact (contact);
293 }
294
295 static GossipContact *
296 private_chat_get_contact (GossipChat *chat)
297 {
298         GossipPrivateChatPriv *priv;
299
300         g_return_val_if_fail (GOSSIP_IS_PRIVATE_CHAT (chat), NULL);
301
302         priv = GET_PRIV (chat);
303
304         return priv->contact;
305 }
306
307 static GtkWidget *
308 private_chat_get_widget (GossipChat *chat)
309 {
310         GossipPrivateChatPriv *priv;
311
312         priv = GET_PRIV (chat);
313
314         return priv->widget;
315 }
316
317 /* Scroll down after the back-log has been received. */
318 static gboolean
319 private_chat_scroll_down_idle_func (GossipChat *chat)
320 {
321         GossipPrivateChatPriv *priv;
322
323         priv = GET_PRIV (chat);
324
325         gossip_chat_scroll_down (chat);
326         g_object_unref (chat);
327
328         priv->scroll_idle_id = 0;
329
330         return FALSE;
331 }
332
333 static void
334 private_chat_setup (GossipPrivateChat *chat,
335                     GossipContact     *contact,
336                     EmpathyTpChat     *tp_chat)
337 {
338         GossipPrivateChatPriv *priv;
339         //GossipLogManager      *log_manager;
340         GossipChatView        *view;
341 /*      GossipContact         *sender;
342         GossipMessage         *message;
343         GList                 *messages, *l;
344         gint                   num_messages, i;*/
345
346         priv = GET_PRIV (chat);
347
348         gossip_chat_set_tp_chat (GOSSIP_CHAT (chat), tp_chat);
349
350         priv->contact = g_object_ref (contact);
351         GOSSIP_CHAT (chat)->account = g_object_ref (gossip_contact_get_account (contact));
352
353         priv->name = g_strdup (gossip_contact_get_name (contact));
354
355         g_signal_connect (priv->contact, 
356                           "notify::name",
357                           G_CALLBACK (private_chat_contact_updated_cb),
358                           chat);
359         g_signal_connect (priv->contact, 
360                           "notify::presence",
361                           G_CALLBACK (private_chat_contact_presence_updated_cb),
362                           chat);
363
364         view = GOSSIP_CHAT (chat)->view;
365
366         /* Turn off scrolling temporarily */
367         gossip_chat_view_scroll (view, FALSE);
368 #if 0
369 FIXME:
370         /* Add messages from last conversation */
371         log_manager = gossip_session_get_log_manager (gossip_app_get_session ());
372         messages = gossip_log_get_last_for_contact (log_manager, priv->contact);
373         num_messages  = g_list_length (messages);
374
375         for (l = messages, i = 0; l; l = l->next, i++) {
376                 message = l->data;
377
378                 if (num_messages - i > 10) {
379                         continue;
380                 }
381
382                 sender = gossip_message_get_sender (message);
383                 if (gossip_contact_equal (priv->own_contact, sender)) {
384                         gossip_chat_view_append_message_from_self (view,
385                                                                    message,
386                                                                    priv->own_contact,
387                                                                    priv->own_avatar);
388                 } else {
389                         gossip_chat_view_append_message_from_other (view,
390                                                                     message,
391                                                                     sender,
392                                                                     priv->other_avatar);
393                 }
394         }
395
396         g_list_foreach (messages, (GFunc) g_object_unref, NULL);
397         g_list_free (messages);
398 #endif
399         /* Turn back on scrolling */
400         gossip_chat_view_scroll (view, TRUE);
401
402         /* Scroll to the most recent messages, we reference the chat
403          * for the duration of the scroll func.
404          */
405         priv->scroll_idle_id = g_idle_add ((GSourceFunc) 
406                                            private_chat_scroll_down_idle_func, 
407                                            g_object_ref (chat));
408
409         priv->is_online = gossip_contact_is_online (priv->contact);
410 }
411
412 GossipPrivateChat *
413 gossip_private_chat_new (GossipContact *contact)
414 {
415         GossipPrivateChat *chat;
416         EmpathyTpChat     *tp_chat;
417
418         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
419
420         chat = g_object_new (GOSSIP_TYPE_PRIVATE_CHAT, NULL);
421         tp_chat = empathy_tp_chat_new_with_contact (contact);
422
423         private_chat_setup (chat, contact, tp_chat);
424         g_object_unref (tp_chat);
425
426         return chat;
427 }
428
429 GossipPrivateChat *
430 gossip_private_chat_new_with_channel (GossipContact *contact,
431                                       TpChan        *tp_chan)
432 {
433         GossipPrivateChat *chat;
434         EmpathyTpChat     *tp_chat;
435         McAccount         *account;
436
437         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
438         g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL);
439
440         account = gossip_contact_get_account (contact);
441         chat = g_object_new (GOSSIP_TYPE_PRIVATE_CHAT, NULL);
442         tp_chat = empathy_tp_chat_new (account, tp_chan);
443
444         private_chat_setup (chat, contact, tp_chat);
445         g_object_unref (tp_chat);
446
447         return chat;
448 }
449