]> git.0d.be Git - empathy.git/blob - libempathy-gtk/gossip-private-chat.c
[darcs-to-svn @ Adding salut profile]
[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  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Authors: Mikael Hallendal <micke@imendio.com>
21  *          Richard Hult <richard@imendio.com>
22  *          Martyn Russell <martyn@imendio.com>
23  *          Geert-Jan Van den Bogaerde <geertjan@gnome.org>
24  */
25
26 #include "config.h"
27
28 #include <string.h>
29
30 #include <gtk/gtk.h>
31 #include <glade/glade.h>
32 #include <glib/gi18n.h>
33
34 #include <libmissioncontrol/mc-account.h>
35
36 #include <libempathy/gossip-debug.h>
37 #include <libempathy/empathy-tp-chat.h>
38 //#include <libgossip/gossip-log.h>
39
40 #include "gossip-private-chat.h"
41 #include "gossip-chat-view.h"
42 #include "gossip-chat.h"
43 #include "gossip-preferences.h"
44 //#include "gossip-sound.h"
45 #include "gossip-stock.h"
46 #include "gossip-ui-utils.h"
47
48 #define DEBUG_DOMAIN "PrivateChat"
49
50 #define COMPOSING_STOP_TIMEOUT 5
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 GdkPixbuf *    private_chat_get_status_pixbuf            (GossipChat             *chat);
80 static GossipContact *private_chat_get_contact                  (GossipChat             *chat);
81 static GtkWidget *    private_chat_get_widget                   (GossipChat             *chat);
82 /*static GdkPixbuf *    private_chat_pad_to_size                  (GdkPixbuf              *pixbuf,
83                                                                  gint                    width,
84                                                                  gint                    height,
85                                                                  gint                    extra_padding_right);*/
86
87 G_DEFINE_TYPE (GossipPrivateChat, gossip_private_chat, GOSSIP_TYPE_CHAT);
88
89 static void
90 gossip_private_chat_class_init (GossipPrivateChatClass *klass)
91 {
92         GObjectClass    *object_class = G_OBJECT_CLASS (klass);
93         GossipChatClass *chat_class = GOSSIP_CHAT_CLASS (klass);
94
95         object_class->finalize = private_chat_finalize;
96
97         chat_class->get_name          = private_chat_get_name;
98         chat_class->get_tooltip       = private_chat_get_tooltip;
99         chat_class->get_status_pixbuf = private_chat_get_status_pixbuf;
100         chat_class->get_contact       = private_chat_get_contact;
101         chat_class->get_widget        = private_chat_get_widget;
102         chat_class->get_show_contacts = NULL;
103         chat_class->set_show_contacts = NULL;
104         chat_class->is_group_chat     = NULL;
105
106         g_type_class_add_private (object_class, sizeof (GossipPrivateChatPriv));
107 }
108
109 static void
110 gossip_private_chat_init (GossipPrivateChat *chat)
111 {
112         GossipPrivateChatPriv *priv;
113
114         priv = GET_PRIV (chat);
115
116         priv->is_online = FALSE;
117
118         private_chat_create_ui (chat);
119
120 }
121
122 static void
123 private_chat_finalize (GObject *object)
124 {
125         GossipPrivateChat     *chat;
126         GossipPrivateChatPriv *priv;
127         
128         chat = GOSSIP_PRIVATE_CHAT (object);
129         priv = GET_PRIV (chat);
130
131         g_signal_handlers_disconnect_by_func (priv->contact,
132                                               private_chat_contact_updated_cb,
133                                               chat);
134         g_signal_handlers_disconnect_by_func (priv->contact,
135                                               private_chat_contact_presence_updated_cb,
136                                               chat);
137
138         if (priv->contact) {
139                 g_object_unref (priv->contact);
140         }
141
142         if (priv->scroll_idle_id) {
143                 g_source_remove (priv->scroll_idle_id);
144         }
145
146         g_free (priv->name);
147
148         G_OBJECT_CLASS (gossip_private_chat_parent_class)->finalize (object);
149 }
150
151 static void
152 private_chat_create_ui (GossipPrivateChat *chat)
153 {
154         GladeXML              *glade;
155         GossipPrivateChatPriv *priv;
156         GtkWidget             *input_text_view_sw;
157
158         priv = GET_PRIV (chat);
159
160         glade = gossip_glade_get_file ("empathy-chat.glade",
161                                        "chat_widget",
162                                        NULL,
163                                       "chat_widget", &priv->widget,
164                                       "chat_view_sw", &priv->text_view_sw,
165                                       "input_text_view_sw", &input_text_view_sw,
166                                        NULL);
167
168         gossip_glade_connect (glade,
169                               chat,
170                               "chat_widget", "destroy", private_chat_widget_destroy_cb,
171                               NULL);
172
173         g_object_unref (glade);
174
175         g_object_set_data (G_OBJECT (priv->widget), "chat", g_object_ref (chat));
176
177         gtk_container_add (GTK_CONTAINER (priv->text_view_sw),
178                            GTK_WIDGET (GOSSIP_CHAT (chat)->view));
179         gtk_widget_show (GTK_WIDGET (GOSSIP_CHAT (chat)->view));
180
181         gtk_container_add (GTK_CONTAINER (input_text_view_sw),
182                            GOSSIP_CHAT (chat)->input_text_view);
183         gtk_widget_show (GOSSIP_CHAT (chat)->input_text_view);
184 }
185
186 static void
187 private_chat_contact_presence_updated_cb (GossipContact     *contact,
188                                           GParamSpec        *param,
189                                           GossipPrivateChat *chat)
190 {
191         GossipPrivateChatPriv *priv;
192
193         priv = GET_PRIV (chat);
194
195         gossip_debug (DEBUG_DOMAIN, "Presence update for contact: %s",
196                       gossip_contact_get_id (contact));
197
198         if (!gossip_contact_is_online (contact)) {
199                 if (priv->is_online) {
200                         gchar *msg;
201
202                         msg = g_strdup_printf (_("%s went offline"),
203                                                gossip_contact_get_name (priv->contact));
204                         gossip_chat_view_append_event (GOSSIP_CHAT (chat)->view, msg);
205                         g_free (msg);
206                 }
207
208                 priv->is_online = FALSE;
209
210                 g_signal_emit_by_name (chat, "composing", FALSE);
211
212         } else {
213                 if (!priv->is_online) {
214                         gchar *msg;
215
216                         msg = g_strdup_printf (_("%s has come online"),
217                                                gossip_contact_get_name (priv->contact));
218                         gossip_chat_view_append_event (GOSSIP_CHAT (chat)->view, msg);
219                         g_free (msg);
220                 }
221
222                 priv->is_online = TRUE;
223         }
224
225         g_signal_emit_by_name (chat, "status-changed");
226 }
227
228 static void
229 private_chat_contact_updated_cb (GossipContact     *contact,
230                                  GParamSpec        *param,
231                                  GossipPrivateChat *chat)
232 {
233         GossipPrivateChatPriv *priv;
234
235         priv = GET_PRIV (chat);
236
237         if (strcmp (priv->name, gossip_contact_get_name (contact)) != 0) {
238                 g_free (priv->name);
239                 priv->name = g_strdup (gossip_contact_get_name (contact));
240                 g_signal_emit_by_name (chat, "name-changed", priv->name);
241         }
242 }
243
244 static void
245 private_chat_widget_destroy_cb (GtkWidget         *widget,
246                                 GossipPrivateChat *chat)
247 {
248         gossip_debug (DEBUG_DOMAIN, "Destroyed");
249
250         g_object_unref (chat);
251 }
252
253 static const gchar *
254 private_chat_get_name (GossipChat *chat)
255 {
256         GossipPrivateChatPriv *priv;
257
258         g_return_val_if_fail (GOSSIP_IS_PRIVATE_CHAT (chat), NULL);
259
260         priv = GET_PRIV (chat);
261
262         return priv->name;
263 }
264
265 static gchar *
266 private_chat_get_tooltip (GossipChat *chat)
267 {
268         GossipPrivateChatPriv *priv;
269         GossipContact         *contact;
270         const gchar           *status;
271
272         g_return_val_if_fail (GOSSIP_IS_PRIVATE_CHAT (chat), NULL);
273
274         priv = GET_PRIV (chat);
275
276         contact = gossip_chat_get_contact (chat);
277         status = gossip_contact_get_status (contact);
278
279         return g_strdup_printf ("%s\n%s",
280                                 gossip_contact_get_id (contact),
281                                 status);
282 }
283
284 static GdkPixbuf *
285 private_chat_get_status_pixbuf (GossipChat *chat)
286 {
287         GossipPrivateChatPriv *priv;
288         GossipContact         *contact;
289
290         g_return_val_if_fail (GOSSIP_IS_PRIVATE_CHAT (chat), NULL);
291
292         priv = GET_PRIV (chat);
293
294         contact = gossip_chat_get_contact (chat);
295
296         return gossip_pixbuf_for_contact (contact);
297 }
298
299 static GossipContact *
300 private_chat_get_contact (GossipChat *chat)
301 {
302         GossipPrivateChatPriv *priv;
303
304         g_return_val_if_fail (GOSSIP_IS_PRIVATE_CHAT (chat), NULL);
305
306         priv = GET_PRIV (chat);
307
308         return priv->contact;
309 }
310
311 static GtkWidget *
312 private_chat_get_widget (GossipChat *chat)
313 {
314         GossipPrivateChatPriv *priv;
315
316         priv = GET_PRIV (chat);
317
318         return priv->widget;
319 }
320
321 /* Scroll down after the back-log has been received. */
322 static gboolean
323 private_chat_scroll_down_idle_func (GossipChat *chat)
324 {
325         GossipPrivateChatPriv *priv;
326
327         priv = GET_PRIV (chat);
328
329         gossip_chat_scroll_down (chat);
330         g_object_unref (chat);
331
332         priv->scroll_idle_id = 0;
333
334         return FALSE;
335 }
336
337 static void
338 private_chat_setup (GossipPrivateChat *chat,
339                     GossipContact     *contact,
340                     EmpathyTpChat     *tp_chat)
341 {
342         GossipPrivateChatPriv *priv;
343         //GossipLogManager      *log_manager;
344         GossipChatView        *view;
345 /*      GossipContact         *sender;
346         GossipMessage         *message;
347         GList                 *messages, *l;
348         gint                   num_messages, i;*/
349
350         priv = GET_PRIV (chat);
351
352         gossip_chat_set_tp_chat (GOSSIP_CHAT (chat), tp_chat);
353
354         priv->contact = g_object_ref (contact);
355         GOSSIP_CHAT (chat)->account = g_object_ref (gossip_contact_get_account (contact));
356
357         priv->name = g_strdup (gossip_contact_get_name (contact));
358
359         g_signal_connect (priv->contact, 
360                           "notify::name",
361                           G_CALLBACK (private_chat_contact_updated_cb),
362                           chat);
363         g_signal_connect (priv->contact, 
364                           "notify::presences",
365                           G_CALLBACK (private_chat_contact_presence_updated_cb),
366                           chat);
367
368         view = GOSSIP_CHAT (chat)->view;
369
370         /* Turn off scrolling temporarily */
371         gossip_chat_view_scroll (view, FALSE);
372 #if 0
373 FIXME:
374         /* Add messages from last conversation */
375         log_manager = gossip_session_get_log_manager (gossip_app_get_session ());
376         messages = gossip_log_get_last_for_contact (log_manager, priv->contact);
377         num_messages  = g_list_length (messages);
378
379         for (l = messages, i = 0; l; l = l->next, i++) {
380                 message = l->data;
381
382                 if (num_messages - i > 10) {
383                         continue;
384                 }
385
386                 sender = gossip_message_get_sender (message);
387                 if (gossip_contact_equal (priv->own_contact, sender)) {
388                         gossip_chat_view_append_message_from_self (view,
389                                                                    message,
390                                                                    priv->own_contact,
391                                                                    priv->own_avatar);
392                 } else {
393                         gossip_chat_view_append_message_from_other (view,
394                                                                     message,
395                                                                     sender,
396                                                                     priv->other_avatar);
397                 }
398         }
399
400         g_list_foreach (messages, (GFunc) g_object_unref, NULL);
401         g_list_free (messages);
402 #endif
403         /* Turn back on scrolling */
404         gossip_chat_view_scroll (view, TRUE);
405
406         /* Scroll to the most recent messages, we reference the chat
407          * for the duration of the scroll func.
408          */
409         priv->scroll_idle_id = g_idle_add ((GSourceFunc) 
410                                            private_chat_scroll_down_idle_func, 
411                                            g_object_ref (chat));
412
413         priv->is_online = gossip_contact_is_online (priv->contact);
414 }
415
416 GossipPrivateChat *
417 gossip_private_chat_new (GossipContact *contact)
418 {
419         GossipPrivateChat *chat;
420         EmpathyTpChat     *tp_chat;
421
422         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
423
424         chat = g_object_new (GOSSIP_TYPE_PRIVATE_CHAT, NULL);
425         tp_chat = empathy_tp_chat_new_with_contact (contact);
426
427         private_chat_setup (chat, contact, tp_chat);
428         g_object_unref (tp_chat);
429
430         return chat;
431 }
432
433 GossipPrivateChat *
434 gossip_private_chat_new_with_channel (GossipContact *contact,
435                                       TpChan        *tp_chan)
436 {
437         GossipPrivateChat *chat;
438         EmpathyTpChat     *tp_chat;
439         McAccount         *account;
440
441         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
442         g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL);
443
444         account = gossip_contact_get_account (contact);
445         chat = g_object_new (GOSSIP_TYPE_PRIVATE_CHAT, NULL);
446         tp_chat = empathy_tp_chat_new (account, tp_chan);
447
448         private_chat_setup (chat, contact, tp_chat);
449         g_object_unref (tp_chat);
450
451         return chat;
452 }
453
454 /* Pads a pixbuf to the specified size, by centering it in a larger transparent
455  * pixbuf. Returns a new ref.
456  */
457 #if 0
458 FIXME:
459 static GdkPixbuf *
460 private_chat_pad_to_size (GdkPixbuf *pixbuf,
461                           gint       width,
462                           gint       height,
463                           gint       extra_padding_right)
464 {
465         gint       src_width, src_height;
466         GdkPixbuf *padded;
467         gint       x_offset, y_offset;
468
469         src_width = gdk_pixbuf_get_width (pixbuf);
470         src_height = gdk_pixbuf_get_height (pixbuf);
471
472         x_offset = (width - src_width) / 2;
473         y_offset = (height - src_height) / 2;
474
475         padded = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (pixbuf),
476                                  TRUE, /* alpha */
477                                  gdk_pixbuf_get_bits_per_sample (pixbuf),
478                                  width + extra_padding_right,
479                                  height);
480
481         gdk_pixbuf_fill (padded, 0);
482
483         gdk_pixbuf_copy_area (pixbuf,
484                               0, /* source coords */
485                               0,
486                               src_width,
487                               src_height,
488                               padded,
489                               x_offset, /* dest coords */
490                               y_offset);
491
492         return padded;
493 }
494 #endif
495