]> git.0d.be Git - empathy.git/blob - libempathy/empathy-message.c
add myself to AUTHORS
[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
31 #include "empathy-message.h"
32 #include "empathy-utils.h"
33 #include "empathy-enum-types.h"
34
35 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyMessage)
36 typedef struct {
37         TpChannelTextMessageType  type;
38         EmpathyContact           *sender;
39         EmpathyContact           *receiver;
40         gchar                    *body;
41         time_t                    timestamp;
42         gboolean                  is_backlog;
43         guint                     id;
44         gboolean                  incoming;
45         TpChannelTextMessageFlags flags;
46 } EmpathyMessagePriv;
47
48 static void empathy_message_finalize   (GObject            *object);
49 static void message_get_property      (GObject            *object,
50                                        guint               param_id,
51                                        GValue             *value,
52                                        GParamSpec         *pspec);
53 static void message_set_property      (GObject            *object,
54                                        guint               param_id,
55                                        const GValue       *value,
56                                        GParamSpec         *pspec);
57
58 G_DEFINE_TYPE (EmpathyMessage, empathy_message, G_TYPE_OBJECT);
59
60 enum {
61         PROP_0,
62         PROP_TYPE,
63         PROP_SENDER,
64         PROP_RECEIVER,
65         PROP_BODY,
66         PROP_TIMESTAMP,
67         PROP_IS_BACKLOG,
68         PROP_INCOMING,
69         PROP_FLAGS,
70 };
71
72 static void
73 empathy_message_class_init (EmpathyMessageClass *class)
74 {
75         GObjectClass *object_class;
76
77         object_class = G_OBJECT_CLASS (class);
78         object_class->finalize     = empathy_message_finalize;
79         object_class->get_property = message_get_property;
80         object_class->set_property = message_set_property;
81
82         g_object_class_install_property (object_class,
83                                          PROP_TYPE,
84                                          g_param_spec_uint ("type",
85                                                             "Message Type",
86                                                             "The type of message",
87                                                             TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
88                                                             TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY,
89                                                             TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
90                                                             G_PARAM_READWRITE));
91         g_object_class_install_property (object_class,
92                                          PROP_SENDER,
93                                          g_param_spec_object ("sender",
94                                                               "Message Sender",
95                                                               "The sender of the message",
96                                                               EMPATHY_TYPE_CONTACT,
97                                                               G_PARAM_READWRITE));
98         g_object_class_install_property (object_class,
99                                          PROP_RECEIVER,
100                                          g_param_spec_object ("receiver",
101                                                               "Message Receiver",
102                                                               "The receiver of the message",
103                                                               EMPATHY_TYPE_CONTACT,
104                                                               G_PARAM_READWRITE));
105         g_object_class_install_property (object_class,
106                                          PROP_BODY,
107                                          g_param_spec_string ("body",
108                                                               "Message Body",
109                                                               "The content of the message",
110                                                               NULL,
111                                                               G_PARAM_READWRITE));
112         g_object_class_install_property (object_class,
113                                          PROP_TIMESTAMP,
114                                          g_param_spec_long ("timestamp",
115                                                             "timestamp",
116                                                             "timestamp",
117                                                             -1,
118                                                             G_MAXLONG,
119                                                             -1,
120                                                             G_PARAM_READWRITE));
121         g_object_class_install_property (object_class,
122                                          PROP_IS_BACKLOG,
123                                          g_param_spec_boolean ("is-backlog",
124                                                                "History message",
125                                                                "If the message belongs to history",
126                                                                FALSE,
127                                                                G_PARAM_READWRITE));
128
129
130         g_object_class_install_property (object_class,
131                                          PROP_INCOMING,
132                                          g_param_spec_boolean ("incoming",
133                                                                "Incoming",
134                                                                "If this is an incoming (as opposed to sent) message",
135                                                                FALSE,
136                                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
137
138         g_object_class_install_property (object_class,
139                                          PROP_FLAGS,
140                                          g_param_spec_uint ("flags",
141                                                                "Flags",
142                                                                "The TpChannelTextMessageFlags of this message",
143                                                                0, G_MAXUINT, 0,
144                                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
145
146         g_type_class_add_private (object_class, sizeof (EmpathyMessagePriv));
147
148 }
149
150 static void
151 empathy_message_init (EmpathyMessage *message)
152 {
153         EmpathyMessagePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (message,
154                 EMPATHY_TYPE_MESSAGE, EmpathyMessagePriv);
155
156         message->priv = priv;
157         priv->timestamp = empathy_time_get_current ();
158 }
159
160 static void
161 empathy_message_finalize (GObject *object)
162 {
163         EmpathyMessagePriv *priv;
164
165         priv = GET_PRIV (object);
166
167         if (priv->sender) {
168                 g_object_unref (priv->sender);
169         }
170         if (priv->receiver) {
171                 g_object_unref (priv->receiver);
172         }
173
174         g_free (priv->body);
175
176         G_OBJECT_CLASS (empathy_message_parent_class)->finalize (object);
177 }
178
179 static void
180 message_get_property (GObject    *object,
181                       guint       param_id,
182                       GValue     *value,
183                       GParamSpec *pspec)
184 {
185         EmpathyMessagePriv *priv;
186
187         priv = GET_PRIV (object);
188
189         switch (param_id) {
190         case PROP_TYPE:
191                 g_value_set_uint (value, priv->type);
192                 break;
193         case PROP_SENDER:
194                 g_value_set_object (value, priv->sender);
195                 break;
196         case PROP_RECEIVER:
197                 g_value_set_object (value, priv->receiver);
198                 break;
199         case PROP_BODY:
200                 g_value_set_string (value, priv->body);
201                 break;
202         case PROP_INCOMING:
203                 g_value_set_boolean (value, priv->incoming);
204                 break;
205         default:
206                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
207                 break;
208         };
209 }
210
211 static void
212 message_set_property (GObject      *object,
213                       guint         param_id,
214                       const GValue *value,
215                       GParamSpec   *pspec)
216 {
217         EmpathyMessagePriv *priv;
218
219         priv = GET_PRIV (object);
220
221         switch (param_id) {
222         case PROP_TYPE:
223                 empathy_message_set_tptype (EMPATHY_MESSAGE (object),
224                                             g_value_get_uint (value));
225                 break;
226         case PROP_SENDER:
227                 empathy_message_set_sender (EMPATHY_MESSAGE (object),
228                                            EMPATHY_CONTACT (g_value_get_object (value)));
229                 break;
230         case PROP_RECEIVER:
231                 empathy_message_set_receiver (EMPATHY_MESSAGE (object),
232                                              EMPATHY_CONTACT (g_value_get_object (value)));
233                 break;
234         case PROP_BODY:
235                 empathy_message_set_body (EMPATHY_MESSAGE (object),
236                                          g_value_get_string (value));
237                 break;
238         case PROP_INCOMING:
239                 priv->incoming = g_value_get_boolean (value);
240                 break;
241         default:
242                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
243                 break;
244         };
245 }
246
247 EmpathyMessage *
248 empathy_message_new (const gchar *body)
249 {
250         return g_object_new (EMPATHY_TYPE_MESSAGE,
251                              "body", body,
252                              NULL);
253 }
254
255 TpChannelTextMessageType
256 empathy_message_get_tptype (EmpathyMessage *message)
257 {
258         EmpathyMessagePriv *priv;
259
260         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message),
261                               TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL);
262
263         priv = GET_PRIV (message);
264
265         return priv->type;
266 }
267
268 void
269 empathy_message_set_tptype (EmpathyMessage           *message,
270                             TpChannelTextMessageType  type)
271 {
272         EmpathyMessagePriv *priv;
273
274         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
275
276         priv = GET_PRIV (message);
277
278         priv->type = type;
279
280         g_object_notify (G_OBJECT (message), "type");
281 }
282
283 EmpathyContact *
284 empathy_message_get_sender (EmpathyMessage *message)
285 {
286         EmpathyMessagePriv *priv;
287
288         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
289
290         priv = GET_PRIV (message);
291
292         return priv->sender;
293 }
294
295 void
296 empathy_message_set_sender (EmpathyMessage *message, EmpathyContact *contact)
297 {
298         EmpathyMessagePriv *priv;
299         EmpathyContact     *old_sender;
300
301         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
302         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
303
304         priv = GET_PRIV (message);
305
306         old_sender = priv->sender;
307         priv->sender = g_object_ref (contact);
308
309         if (old_sender) {
310                 g_object_unref (old_sender);
311         }
312
313         g_object_notify (G_OBJECT (message), "sender");
314 }
315
316 EmpathyContact *
317 empathy_message_get_receiver (EmpathyMessage *message)
318 {
319         EmpathyMessagePriv *priv;
320
321         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
322
323         priv = GET_PRIV (message);
324
325         return priv->receiver;
326 }
327
328 void
329 empathy_message_set_receiver (EmpathyMessage *message, EmpathyContact *contact)
330 {
331         EmpathyMessagePriv *priv;
332         EmpathyContact     *old_receiver;
333
334         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
335         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
336
337         priv = GET_PRIV (message);
338
339         old_receiver = priv->receiver;
340         priv->receiver = g_object_ref (contact);
341
342         if (old_receiver) {
343                 g_object_unref (old_receiver);
344         }
345
346         g_object_notify (G_OBJECT (message), "receiver");
347 }
348
349 const gchar *
350 empathy_message_get_body (EmpathyMessage *message)
351 {
352         EmpathyMessagePriv *priv;
353
354         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
355
356         priv = GET_PRIV (message);
357
358         return priv->body;
359 }
360
361 void
362 empathy_message_set_body (EmpathyMessage *message,
363                           const gchar    *body)
364 {
365         EmpathyMessagePriv       *priv = GET_PRIV (message);
366
367         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
368
369         g_free (priv->body);
370
371         if (body) {
372                 priv->body = g_strdup (body);
373         } else {
374                 priv->body = NULL;
375         }
376
377         g_object_notify (G_OBJECT (message), "body");
378 }
379
380 time_t
381 empathy_message_get_timestamp (EmpathyMessage *message)
382 {
383         EmpathyMessagePriv *priv;
384
385         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), -1);
386
387         priv = GET_PRIV (message);
388
389         return priv->timestamp;
390 }
391
392 void
393 empathy_message_set_timestamp (EmpathyMessage *message,
394                                time_t          timestamp)
395 {
396         EmpathyMessagePriv *priv;
397
398         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
399         g_return_if_fail (timestamp >= -1);
400
401         priv = GET_PRIV (message);
402
403         if (timestamp <= 0) {
404                 priv->timestamp = empathy_time_get_current ();
405         } else {
406                 priv->timestamp = timestamp;
407         }
408
409         g_object_notify (G_OBJECT (message), "timestamp");
410 }
411
412 gboolean
413 empathy_message_is_backlog (EmpathyMessage *message)
414 {
415         EmpathyMessagePriv *priv;
416
417         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
418
419         priv = GET_PRIV (message);
420
421         return priv->is_backlog;
422 }
423
424 void
425 empathy_message_set_is_backlog (EmpathyMessage *message,
426                                 gboolean is_backlog)
427 {
428         EmpathyMessagePriv *priv;
429
430         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
431
432         priv = GET_PRIV (message);
433
434         priv->is_backlog = is_backlog;
435
436         g_object_notify (G_OBJECT (message), "is-backlog");
437 }
438
439 #define IS_SEPARATOR(ch) (ch == ' ' || ch == ',' || ch == '.' || ch == ':')
440 gboolean
441 empathy_message_should_highlight (EmpathyMessage *message)
442 {
443         EmpathyContact *contact;
444         const gchar   *msg, *to;
445         gchar         *cf_msg, *cf_to;
446         gchar         *ch;
447         gboolean       ret_val;
448         TpChannelTextMessageFlags flags;
449
450         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
451
452         ret_val = FALSE;
453
454         msg = empathy_message_get_body (message);
455         if (!msg) {
456                 return FALSE;
457         }
458
459         contact = empathy_message_get_receiver (message);
460         if (!contact || !empathy_contact_is_user (contact)) {
461                 return FALSE;
462         }
463
464         to = empathy_contact_get_name (contact);
465         if (!to) {
466                 return FALSE;
467         }
468
469         flags = empathy_message_get_flags (message);
470         if (flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK) {
471                 /* FIXME: Ideally we shouldn't highlight scrollback messages only if they
472                  * have already been received by the user before (and so are in the logs) */
473                 return FALSE;
474         }
475
476         cf_msg = g_utf8_casefold (msg, -1);
477         cf_to = g_utf8_casefold (to, -1);
478
479         ch = strstr (cf_msg, cf_to);
480         if (ch == NULL) {
481                 goto finished;
482         }
483         if (ch != cf_msg) {
484                 /* Not first in the message */
485                 if (!IS_SEPARATOR (*(ch - 1))) {
486                         goto finished;
487                 }
488         }
489
490         ch = ch + strlen (cf_to);
491         if (ch >= cf_msg + strlen (cf_msg)) {
492                 ret_val = TRUE;
493                 goto finished;
494         }
495
496         if (IS_SEPARATOR (*ch)) {
497                 ret_val = TRUE;
498                 goto finished;
499         }
500
501 finished:
502         g_free (cf_msg);
503         g_free (cf_to);
504
505         return ret_val;
506 }
507
508 TpChannelTextMessageType
509 empathy_message_type_from_str (const gchar *type_str)
510 {
511         if (strcmp (type_str, "normal") == 0) {
512                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
513         }
514         if (strcmp (type_str, "action") == 0) {
515                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
516         }
517         else if (strcmp (type_str, "notice") == 0) {
518                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE;
519         }
520         else if (strcmp (type_str, "auto-reply") == 0) {
521                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY;
522         }
523
524         return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
525 }
526
527 const gchar *
528 empathy_message_type_to_str (TpChannelTextMessageType type)
529 {
530         switch (type) {
531         case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
532                 return "action";
533         case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
534                 return "notice";
535         case TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY:
536                 return "auto-reply";
537         default:
538                 return "normal";
539         }
540 }
541
542 guint
543 empathy_message_get_id (EmpathyMessage *message)
544 {
545         EmpathyMessagePriv *priv = GET_PRIV (message);
546
547         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), 0);
548
549         return priv->id;
550 }
551
552 void
553 empathy_message_set_id (EmpathyMessage *message, guint id)
554 {
555         EmpathyMessagePriv *priv = GET_PRIV (message);
556
557         priv->id = id;
558 }
559
560 void
561 empathy_message_set_incoming (EmpathyMessage *message, gboolean incoming)
562 {
563         EmpathyMessagePriv *priv;
564
565         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
566
567         priv = GET_PRIV (message);
568
569         priv->incoming = incoming;
570
571         g_object_notify (G_OBJECT (message), "incoming");
572 }
573
574 gboolean
575 empathy_message_is_incoming (EmpathyMessage *message)
576 {
577         EmpathyMessagePriv *priv = GET_PRIV (message);
578
579         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
580
581         return priv->incoming;
582 }
583
584 gboolean
585 empathy_message_equal (EmpathyMessage *message1, EmpathyMessage *message2)
586 {
587         EmpathyMessagePriv *priv1;
588         EmpathyMessagePriv *priv2;
589
590         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message1), FALSE);
591         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message2), FALSE);
592
593         priv1 = GET_PRIV (message1);
594         priv2 = GET_PRIV (message2);
595
596         if (priv1->id == priv2->id && !tp_strdiff (priv1->body, priv2->body)) {
597                 return TRUE;
598         }
599
600         return FALSE;
601 }
602
603 TpChannelTextMessageFlags
604 empathy_message_get_flags (EmpathyMessage *self)
605 {
606         EmpathyMessagePriv *priv = GET_PRIV (self);
607
608         g_return_val_if_fail (EMPATHY_IS_MESSAGE (self), 0);
609
610         return priv->flags;
611 }
612
613 void
614 empathy_message_set_flags        (EmpathyMessage           *self,
615                                 TpChannelTextMessageFlags flags)
616 {
617         EmpathyMessagePriv *priv;
618
619         g_return_if_fail (EMPATHY_IS_MESSAGE (self));
620
621         priv = GET_PRIV (self);
622
623         priv->flags = flags;
624
625         g_object_notify (G_OBJECT (self), "flags");
626 }