]> git.0d.be Git - empathy.git/blob - libempathy/empathy-message.c
Updated Polish translation
[empathy.git] / libempathy / empathy-message.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2004-2007 Imendio AB
4  * Copyright (C) 2007-2008 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., 51 Franklin St, Fifth Floor,
19  * Boston, MA  02110-1301  USA
20  *
21  * Authors: Mikael Hallendal <micke@imendio.com>
22  *          Xavier Claessens <xclaesse@gmail.com>
23  */
24
25 #include "config.h"
26
27 #include <string.h>
28
29 #include <telepathy-glib/util.h>
30 #ifdef ENABLE_TPL
31 #include <telepathy-glib/account.h>
32 #include <telepathy-glib/account-manager.h>
33
34 #include <telepathy-logger/contact.h>
35 #include <telepathy-logger/log-entry.h>
36 #include <telepathy-logger/log-entry-text.h>
37 #endif /* ENABLE_TPL */
38
39 #include "empathy-message.h"
40 #include "empathy-utils.h"
41 #include "empathy-enum-types.h"
42
43 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyMessage)
44 typedef struct {
45         TpChannelTextMessageType  type;
46         EmpathyContact           *sender;
47         EmpathyContact           *receiver;
48         gchar                    *body;
49         time_t                    timestamp;
50         gboolean                  is_backlog;
51         guint                     id;
52         gboolean                  incoming;
53         TpChannelTextMessageFlags flags;
54 } EmpathyMessagePriv;
55
56 static void empathy_message_finalize   (GObject            *object);
57 static void message_get_property      (GObject            *object,
58                                        guint               param_id,
59                                        GValue             *value,
60                                        GParamSpec         *pspec);
61 static void message_set_property      (GObject            *object,
62                                        guint               param_id,
63                                        const GValue       *value,
64                                        GParamSpec         *pspec);
65
66 G_DEFINE_TYPE (EmpathyMessage, empathy_message, G_TYPE_OBJECT);
67
68 enum {
69         PROP_0,
70         PROP_TYPE,
71         PROP_SENDER,
72         PROP_RECEIVER,
73         PROP_BODY,
74         PROP_TIMESTAMP,
75         PROP_IS_BACKLOG,
76         PROP_INCOMING,
77         PROP_FLAGS,
78 };
79
80 static void
81 empathy_message_class_init (EmpathyMessageClass *class)
82 {
83         GObjectClass *object_class;
84
85         object_class = G_OBJECT_CLASS (class);
86         object_class->finalize     = empathy_message_finalize;
87         object_class->get_property = message_get_property;
88         object_class->set_property = message_set_property;
89
90         g_object_class_install_property (object_class,
91                                          PROP_TYPE,
92                                          g_param_spec_uint ("type",
93                                                             "Message Type",
94                                                             "The type of message",
95                                                             TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
96                                                             TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY,
97                                                             TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
98                                                             G_PARAM_READWRITE));
99         g_object_class_install_property (object_class,
100                                          PROP_SENDER,
101                                          g_param_spec_object ("sender",
102                                                               "Message Sender",
103                                                               "The sender of the message",
104                                                               EMPATHY_TYPE_CONTACT,
105                                                               G_PARAM_READWRITE));
106         g_object_class_install_property (object_class,
107                                          PROP_RECEIVER,
108                                          g_param_spec_object ("receiver",
109                                                               "Message Receiver",
110                                                               "The receiver of the message",
111                                                               EMPATHY_TYPE_CONTACT,
112                                                               G_PARAM_READWRITE));
113         g_object_class_install_property (object_class,
114                                          PROP_BODY,
115                                          g_param_spec_string ("body",
116                                                               "Message Body",
117                                                               "The content of the message",
118                                                               NULL,
119                                                               G_PARAM_READWRITE));
120         g_object_class_install_property (object_class,
121                                          PROP_TIMESTAMP,
122                                          g_param_spec_long ("timestamp",
123                                                             "timestamp",
124                                                             "timestamp",
125                                                             -1,
126                                                             G_MAXLONG,
127                                                             -1,
128                                                             G_PARAM_READWRITE));
129         g_object_class_install_property (object_class,
130                                          PROP_IS_BACKLOG,
131                                          g_param_spec_boolean ("is-backlog",
132                                                                "History message",
133                                                                "If the message belongs to history",
134                                                                FALSE,
135                                                                G_PARAM_READWRITE));
136
137
138         g_object_class_install_property (object_class,
139                                          PROP_INCOMING,
140                                          g_param_spec_boolean ("incoming",
141                                                                "Incoming",
142                                                                "If this is an incoming (as opposed to sent) message",
143                                                                FALSE,
144                                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
145
146         g_object_class_install_property (object_class,
147                                          PROP_FLAGS,
148                                          g_param_spec_uint ("flags",
149                                                                "Flags",
150                                                                "The TpChannelTextMessageFlags of this message",
151                                                                0, G_MAXUINT, 0,
152                                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
153
154         g_type_class_add_private (object_class, sizeof (EmpathyMessagePriv));
155
156 }
157
158 static void
159 empathy_message_init (EmpathyMessage *message)
160 {
161         EmpathyMessagePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (message,
162                 EMPATHY_TYPE_MESSAGE, EmpathyMessagePriv);
163
164         message->priv = priv;
165         priv->timestamp = empathy_time_get_current ();
166 }
167
168 static void
169 empathy_message_finalize (GObject *object)
170 {
171         EmpathyMessagePriv *priv;
172
173         priv = GET_PRIV (object);
174
175         if (priv->sender) {
176                 g_object_unref (priv->sender);
177         }
178         if (priv->receiver) {
179                 g_object_unref (priv->receiver);
180         }
181
182         g_free (priv->body);
183
184         G_OBJECT_CLASS (empathy_message_parent_class)->finalize (object);
185 }
186
187 static void
188 message_get_property (GObject    *object,
189                       guint       param_id,
190                       GValue     *value,
191                       GParamSpec *pspec)
192 {
193         EmpathyMessagePriv *priv;
194
195         priv = GET_PRIV (object);
196
197         switch (param_id) {
198         case PROP_TYPE:
199                 g_value_set_uint (value, priv->type);
200                 break;
201         case PROP_SENDER:
202                 g_value_set_object (value, priv->sender);
203                 break;
204         case PROP_RECEIVER:
205                 g_value_set_object (value, priv->receiver);
206                 break;
207         case PROP_BODY:
208                 g_value_set_string (value, priv->body);
209                 break;
210         case PROP_INCOMING:
211                 g_value_set_boolean (value, priv->incoming);
212                 break;
213         default:
214                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
215                 break;
216         };
217 }
218
219 static void
220 message_set_property (GObject      *object,
221                       guint         param_id,
222                       const GValue *value,
223                       GParamSpec   *pspec)
224 {
225         EmpathyMessagePriv *priv;
226
227         priv = GET_PRIV (object);
228
229         switch (param_id) {
230         case PROP_TYPE:
231                 empathy_message_set_tptype (EMPATHY_MESSAGE (object),
232                                             g_value_get_uint (value));
233                 break;
234         case PROP_SENDER:
235                 empathy_message_set_sender (EMPATHY_MESSAGE (object),
236                                            EMPATHY_CONTACT (g_value_get_object (value)));
237                 break;
238         case PROP_RECEIVER:
239                 empathy_message_set_receiver (EMPATHY_MESSAGE (object),
240                                              EMPATHY_CONTACT (g_value_get_object (value)));
241                 break;
242         case PROP_BODY:
243                 empathy_message_set_body (EMPATHY_MESSAGE (object),
244                                          g_value_get_string (value));
245                 break;
246         case PROP_INCOMING:
247                 priv->incoming = g_value_get_boolean (value);
248                 break;
249         default:
250                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
251                 break;
252         };
253 }
254
255 EmpathyMessage *
256 empathy_message_new (const gchar *body)
257 {
258         return g_object_new (EMPATHY_TYPE_MESSAGE,
259                              "body", body,
260                              NULL);
261 }
262
263 #ifdef ENABLE_TPL
264 EmpathyMessage *
265 empathy_message_from_tpl_log_entry (TplLogEntry *logentry)
266 {
267         EmpathyMessage *retval = NULL;
268         TpAccountManager *acc_man = NULL;
269         TpAccount *account = NULL;
270         TplContact *receiver = NULL;
271         TplContact *sender = NULL;
272         gchar *body= NULL;
273
274         g_return_val_if_fail (TPL_IS_LOG_ENTRY (logentry), NULL);
275
276         acc_man = tp_account_manager_dup ();
277         /* FIXME Currently Empathy shows in the log viewer only valid accounts, so it
278          * won't be selected any non-existing (ie removed) account.
279          * When #610455 will be fixed, calling tp_account_manager_ensure_account ()
280          * might add a not existing account to the AM. tp_account_new () probably
281          * will be the best way to handle it.
282          * Note: When creating an EmpathyContact from a TplContact instance, the
283          * TpAccount is passed *only* to let EmpathyContact be able to retrieve the
284          * avatar (contact_get_avatar_filename () need a TpAccount).
285          * If the way EmpathyContact stores the avatar is changes, it might not be
286          * needed anymore any TpAccount passing and the following call will be
287          * useless */
288         account = tp_account_manager_ensure_account (acc_man,
289                         tpl_log_entry_get_account_path (logentry));
290         g_object_unref (acc_man);
291
292         /* TODO Currently only TplLogEntryText exists as subclass of TplLogEntry, in
293          * future more TplLogEntry will exist and EmpathyMessage should probably
294          * be enhanced to support other types of log entries (ie TplLogEntryCall).
295          *
296          * For now we just check (simply) that we are dealing with the only supported type,
297          * then there will be a if/then/else or switch handling all the supported
298          * cases.
299          */
300         if (!TPL_IS_LOG_ENTRY_TEXT (logentry))
301                 return NULL;
302
303         body = g_strdup (tpl_log_entry_text_get_message (
304                                 TPL_LOG_ENTRY_TEXT (logentry)));
305         receiver = tpl_log_entry_text_get_receiver (TPL_LOG_ENTRY_TEXT (logentry));
306         sender = tpl_log_entry_text_get_sender (TPL_LOG_ENTRY_TEXT (logentry));
307
308         retval = empathy_message_new (body);
309         if (receiver != NULL)
310                 empathy_message_set_receiver (retval,
311                                 empathy_contact_from_tpl_contact (account, receiver));
312         if (sender != NULL)
313                 empathy_message_set_sender (retval,
314                                 empathy_contact_from_tpl_contact (account, sender));
315
316         empathy_message_set_timestamp (retval,
317                         tpl_log_entry_get_timestamp (logentry));
318         empathy_message_set_id (retval,
319                         tpl_log_entry_text_get_log_id (TPL_LOG_ENTRY_TEXT (logentry)));
320         empathy_message_set_is_backlog (retval, FALSE);
321
322         g_free (body);
323
324         return retval;
325 }
326 #endif /* ENABLE_TPL */
327
328 TpChannelTextMessageType
329 empathy_message_get_tptype (EmpathyMessage *message)
330 {
331         EmpathyMessagePriv *priv;
332
333         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message),
334                               TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL);
335
336         priv = GET_PRIV (message);
337
338         return priv->type;
339 }
340
341 void
342 empathy_message_set_tptype (EmpathyMessage           *message,
343                             TpChannelTextMessageType  type)
344 {
345         EmpathyMessagePriv *priv;
346
347         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
348
349         priv = GET_PRIV (message);
350
351         priv->type = type;
352
353         g_object_notify (G_OBJECT (message), "type");
354 }
355
356 EmpathyContact *
357 empathy_message_get_sender (EmpathyMessage *message)
358 {
359         EmpathyMessagePriv *priv;
360
361         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
362
363         priv = GET_PRIV (message);
364
365         return priv->sender;
366 }
367
368 void
369 empathy_message_set_sender (EmpathyMessage *message, EmpathyContact *contact)
370 {
371         EmpathyMessagePriv *priv;
372         EmpathyContact     *old_sender;
373
374         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
375         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
376
377         priv = GET_PRIV (message);
378
379         old_sender = priv->sender;
380         priv->sender = g_object_ref (contact);
381
382         if (old_sender) {
383                 g_object_unref (old_sender);
384         }
385
386         g_object_notify (G_OBJECT (message), "sender");
387 }
388
389 EmpathyContact *
390 empathy_message_get_receiver (EmpathyMessage *message)
391 {
392         EmpathyMessagePriv *priv;
393
394         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
395
396         priv = GET_PRIV (message);
397
398         return priv->receiver;
399 }
400
401 void
402 empathy_message_set_receiver (EmpathyMessage *message, EmpathyContact *contact)
403 {
404         EmpathyMessagePriv *priv;
405         EmpathyContact     *old_receiver;
406
407         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
408         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
409
410         priv = GET_PRIV (message);
411
412         old_receiver = priv->receiver;
413         priv->receiver = g_object_ref (contact);
414
415         if (old_receiver) {
416                 g_object_unref (old_receiver);
417         }
418
419         g_object_notify (G_OBJECT (message), "receiver");
420 }
421
422 const gchar *
423 empathy_message_get_body (EmpathyMessage *message)
424 {
425         EmpathyMessagePriv *priv;
426
427         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
428
429         priv = GET_PRIV (message);
430
431         return priv->body;
432 }
433
434 void
435 empathy_message_set_body (EmpathyMessage *message,
436                           const gchar    *body)
437 {
438         EmpathyMessagePriv       *priv = GET_PRIV (message);
439
440         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
441
442         g_free (priv->body);
443
444         if (body) {
445                 priv->body = g_strdup (body);
446         } else {
447                 priv->body = NULL;
448         }
449
450         g_object_notify (G_OBJECT (message), "body");
451 }
452
453 time_t
454 empathy_message_get_timestamp (EmpathyMessage *message)
455 {
456         EmpathyMessagePriv *priv;
457
458         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), -1);
459
460         priv = GET_PRIV (message);
461
462         return priv->timestamp;
463 }
464
465 void
466 empathy_message_set_timestamp (EmpathyMessage *message,
467                                time_t          timestamp)
468 {
469         EmpathyMessagePriv *priv;
470
471         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
472         g_return_if_fail (timestamp >= -1);
473
474         priv = GET_PRIV (message);
475
476         if (timestamp <= 0) {
477                 priv->timestamp = empathy_time_get_current ();
478         } else {
479                 priv->timestamp = timestamp;
480         }
481
482         g_object_notify (G_OBJECT (message), "timestamp");
483 }
484
485 gboolean
486 empathy_message_is_backlog (EmpathyMessage *message)
487 {
488         EmpathyMessagePriv *priv;
489
490         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
491
492         priv = GET_PRIV (message);
493
494         return priv->is_backlog;
495 }
496
497 void
498 empathy_message_set_is_backlog (EmpathyMessage *message,
499                                 gboolean is_backlog)
500 {
501         EmpathyMessagePriv *priv;
502
503         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
504
505         priv = GET_PRIV (message);
506
507         priv->is_backlog = is_backlog;
508
509         g_object_notify (G_OBJECT (message), "is-backlog");
510 }
511
512 #define IS_SEPARATOR(ch) (ch == ' ' || ch == ',' || ch == '.' || ch == ':')
513 gboolean
514 empathy_message_should_highlight (EmpathyMessage *message)
515 {
516         EmpathyContact *contact;
517         const gchar   *msg, *to;
518         gchar         *cf_msg, *cf_to;
519         gchar         *ch;
520         gboolean       ret_val;
521         TpChannelTextMessageFlags flags;
522
523         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
524
525         ret_val = FALSE;
526
527         msg = empathy_message_get_body (message);
528         if (!msg) {
529                 return FALSE;
530         }
531
532         contact = empathy_message_get_receiver (message);
533         if (!contact || !empathy_contact_is_user (contact)) {
534                 return FALSE;
535         }
536
537         to = empathy_contact_get_name (contact);
538         if (!to) {
539                 return FALSE;
540         }
541
542         flags = empathy_message_get_flags (message);
543         if (flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK) {
544                 /* FIXME: Ideally we shouldn't highlight scrollback messages only if they
545                  * have already been received by the user before (and so are in the logs) */
546                 return FALSE;
547         }
548
549         cf_msg = g_utf8_casefold (msg, -1);
550         cf_to = g_utf8_casefold (to, -1);
551
552         ch = strstr (cf_msg, cf_to);
553         if (ch == NULL) {
554                 goto finished;
555         }
556         if (ch != cf_msg) {
557                 /* Not first in the message */
558                 if (!IS_SEPARATOR (*(ch - 1))) {
559                         goto finished;
560                 }
561         }
562
563         ch = ch + strlen (cf_to);
564         if (ch >= cf_msg + strlen (cf_msg)) {
565                 ret_val = TRUE;
566                 goto finished;
567         }
568
569         if (IS_SEPARATOR (*ch)) {
570                 ret_val = TRUE;
571                 goto finished;
572         }
573
574 finished:
575         g_free (cf_msg);
576         g_free (cf_to);
577
578         return ret_val;
579 }
580
581 TpChannelTextMessageType
582 empathy_message_type_from_str (const gchar *type_str)
583 {
584         if (strcmp (type_str, "normal") == 0) {
585                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
586         }
587         if (strcmp (type_str, "action") == 0) {
588                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
589         }
590         else if (strcmp (type_str, "notice") == 0) {
591                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE;
592         }
593         else if (strcmp (type_str, "auto-reply") == 0) {
594                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY;
595         }
596
597         return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
598 }
599
600 const gchar *
601 empathy_message_type_to_str (TpChannelTextMessageType type)
602 {
603         switch (type) {
604         case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
605                 return "action";
606         case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
607                 return "notice";
608         case TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY:
609                 return "auto-reply";
610         default:
611                 return "normal";
612         }
613 }
614
615 guint
616 empathy_message_get_id (EmpathyMessage *message)
617 {
618         EmpathyMessagePriv *priv = GET_PRIV (message);
619
620         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), 0);
621
622         return priv->id;
623 }
624
625 void
626 empathy_message_set_id (EmpathyMessage *message, guint id)
627 {
628         EmpathyMessagePriv *priv = GET_PRIV (message);
629
630         priv->id = id;
631 }
632
633 void
634 empathy_message_set_incoming (EmpathyMessage *message, gboolean incoming)
635 {
636         EmpathyMessagePriv *priv;
637
638         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
639
640         priv = GET_PRIV (message);
641
642         priv->incoming = incoming;
643
644         g_object_notify (G_OBJECT (message), "incoming");
645 }
646
647 gboolean
648 empathy_message_is_incoming (EmpathyMessage *message)
649 {
650         EmpathyMessagePriv *priv = GET_PRIV (message);
651
652         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
653
654         return priv->incoming;
655 }
656
657 gboolean
658 empathy_message_equal (EmpathyMessage *message1, EmpathyMessage *message2)
659 {
660         EmpathyMessagePriv *priv1;
661         EmpathyMessagePriv *priv2;
662
663         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message1), FALSE);
664         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message2), FALSE);
665
666         priv1 = GET_PRIV (message1);
667         priv2 = GET_PRIV (message2);
668
669 #ifdef ENABLE_TPL
670         if (priv1->timestamp == priv2->timestamp &&
671                         !tp_strdiff (priv1->body, priv2->body)) {
672 #else
673         if (priv1->id == priv2->id && !tp_strdiff (priv1->body, priv2->body)) {
674 #endif /* ENABLE_TPL */
675                 return TRUE;
676         }
677
678         return FALSE;
679 }
680
681 TpChannelTextMessageFlags
682 empathy_message_get_flags (EmpathyMessage *self)
683 {
684         EmpathyMessagePriv *priv = GET_PRIV (self);
685
686         g_return_val_if_fail (EMPATHY_IS_MESSAGE (self), 0);
687
688         return priv->flags;
689 }
690
691 void
692 empathy_message_set_flags        (EmpathyMessage           *self,
693                                 TpChannelTextMessageFlags flags)
694 {
695         EmpathyMessagePriv *priv;
696
697         g_return_if_fail (EMPATHY_IS_MESSAGE (self));
698
699         priv = GET_PRIV (self);
700
701         priv->flags = flags;
702
703         g_object_notify (G_OBJECT (self), "flags");
704 }