]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-log-window.c
Use some markup to make the logs more readable
[empathy.git] / libempathy-gtk / empathy-log-window.c
1 /*
2  * Copyright (C) 2006-2007 Imendio AB
3  * Copyright (C) 2007-2011 Collabora Ltd.
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., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301  USA
19  *
20  * Authors: Martyn Russell <martyn@imendio.com>
21  *          Xavier Claessens <xclaesse@gmail.com>
22  *          Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
23  */
24
25 #include "config.h"
26
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include <glib/gi18n-lib.h>
31 #include <gtk/gtk.h>
32
33 #include <telepathy-glib/telepathy-glib.h>
34 #include <telepathy-glib/proxy-subclass.h>
35
36 #include <telepathy-logger/telepathy-logger.h>
37 #include <telepathy-logger/call-event.h>
38
39 #include <extensions/extensions.h>
40
41 #include <libempathy/action-chain-internal.h>
42 #include <libempathy/empathy-chatroom-manager.h>
43 #include <libempathy/empathy-chatroom.h>
44 #include <libempathy/empathy-message.h>
45 #include <libempathy/empathy-request-util.h>
46 #include <libempathy/empathy-utils.h>
47 #include <libempathy/empathy-time.h>
48
49 #include "empathy-log-window.h"
50 #include "empathy-account-chooser.h"
51 #include "empathy-call-utils.h"
52 #include "empathy-chat-view.h"
53 #include "empathy-contact-dialogs.h"
54 #include "empathy-images.h"
55 #include "empathy-theme-manager.h"
56 #include "empathy-ui-utils.h"
57
58 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
59 #include <libempathy/empathy-debug.h>
60
61 typedef struct
62 {
63   GtkWidget *window;
64
65   GtkWidget *button_profile;
66   GtkWidget *button_chat;
67   GtkWidget *button_call;
68   GtkWidget *button_video;
69
70   GtkWidget *search_entry;
71
72   GtkWidget *notebook;
73   GtkWidget *spinner;
74
75   GtkWidget *treeview_who;
76   GtkWidget *treeview_what;
77   GtkWidget *treeview_when;
78   GtkWidget *treeview_events;
79
80   GtkTreeStore *store_events;
81
82   GtkWidget *account_chooser;
83
84   gchar *last_find;
85
86   TplActionChain *chain;
87   TplLogManager *log_manager;
88
89   /* Used to cancel logger calls when no longer needed */
90   guint count;
91
92   /* List of owned TplLogSearchHits, free with tpl_log_search_hit_free */
93   GList *hits;
94   guint source;
95
96   /* Only used while waiting for the account chooser to be ready */
97   TpAccount *selected_account;
98   gchar *selected_chat_id;
99   gboolean selected_is_chatroom;
100 } EmpathyLogWindow;
101
102 static void log_window_destroy_cb                (GtkWidget        *widget,
103                                                   EmpathyLogWindow *window);
104 static void log_window_search_entry_changed_cb   (GtkWidget        *entry,
105                                                   EmpathyLogWindow *window);
106 static void log_window_search_entry_activate_cb  (GtkWidget        *widget,
107                                                   EmpathyLogWindow *window);
108 static void log_window_search_entry_icon_pressed_cb (GtkEntry      *entry,
109                                                   GtkEntryIconPosition icon_pos,
110                                                   GdkEvent *event,
111                                                   gpointer user_data);
112 static void log_window_who_populate              (EmpathyLogWindow *window);
113 static void log_window_who_setup                 (EmpathyLogWindow *window);
114 static void log_window_when_setup                (EmpathyLogWindow *window);
115 static void log_window_what_setup                (EmpathyLogWindow *window);
116 static void log_window_events_setup              (EmpathyLogWindow *window);
117 static void log_window_chats_accounts_changed_cb (GtkWidget        *combobox,
118                                                   EmpathyLogWindow *window);
119 static void log_window_chats_set_selected        (EmpathyLogWindow *window);
120 static void log_window_chats_get_messages        (EmpathyLogWindow *window,
121                                                   gboolean force_get_dates);
122 static void log_window_when_changed_cb           (GtkTreeSelection *selection,
123                                                   EmpathyLogWindow *window);
124 static void log_window_delete_menu_clicked_cb    (GtkMenuItem      *menuitem,
125                                                   EmpathyLogWindow *window);
126
127 static void
128 empathy_account_chooser_filter_has_logs (TpAccount *account,
129     EmpathyAccountChooserFilterResultCallback callback,
130     gpointer callback_data,
131     gpointer user_data);
132
133 enum
134 {
135   PAGE_EVENTS,
136   PAGE_SPINNER,
137   PAGE_EMPTY
138 };
139
140 enum
141 {
142   COL_TYPE_ANY,
143   COL_TYPE_SEPARATOR,
144   COL_TYPE_NORMAL
145 };
146
147 enum
148 {
149   COL_WHO_TYPE,
150   COL_WHO_ICON,
151   COL_WHO_NAME,
152   COL_WHO_ACCOUNT,
153   COL_WHO_TARGET,
154   COL_WHO_COUNT
155 };
156
157 enum
158 {
159   COL_WHAT_TYPE,
160   COL_WHAT_SUBTYPE,
161   COL_WHAT_TEXT,
162   COL_WHAT_ICON,
163   COL_WHAT_COUNT
164 };
165
166 enum
167 {
168   COL_WHEN_DATE,
169   COL_WHEN_TEXT,
170   COL_WHEN_ICON,
171   COL_WHEN_COUNT
172 };
173
174 enum
175 {
176   COL_EVENTS_TYPE,
177   COL_EVENTS_TS,
178   COL_EVENTS_PRETTY_DATE,
179   COL_EVENTS_ICON,
180   COL_EVENTS_TEXT,
181   COL_EVENTS_ACCOUNT,
182   COL_EVENTS_TARGET,
183   COL_EVENTS_EVENT,
184   COL_EVENTS_COUNT
185 };
186
187 #define CALENDAR_ICON "stock_calendar"
188
189 /* Seconds between two messages to be considered one conversation */
190 #define MAX_GAP 30*60
191
192 #define WHAT_TYPE_SEPARATOR -1
193
194 typedef enum
195 {
196   EVENT_CALL_INCOMING = 1 << 0,
197   EVENT_CALL_OUTGOING = 1 << 1,
198   EVENT_CALL_MISSED   = 1 << 2,
199   EVENT_CALL_ALL      = 1 << 3,
200 } EventSubtype;
201
202 static EmpathyLogWindow *log_window = NULL;
203
204 static gboolean has_element;
205
206 #ifndef _date_copy
207 #define _date_copy(d) g_date_new_julian (g_date_get_julian (d))
208 #endif
209
210 typedef struct
211 {
212   EmpathyLogWindow *window;
213   TpAccount *account;
214   TplEntity *entity;
215   GDate *date;
216   TplEventTypeMask event_mask;
217   EventSubtype subtype;
218   guint count;
219 } Ctx;
220
221 static Ctx *
222 ctx_new (EmpathyLogWindow *window,
223     TpAccount *account,
224     TplEntity *entity,
225     GDate *date,
226     TplEventTypeMask event_mask,
227     EventSubtype subtype,
228     guint count)
229 {
230   Ctx *ctx = g_slice_new0 (Ctx);
231
232   ctx->window = window;
233   if (account != NULL)
234     ctx->account = g_object_ref (account);
235   if (entity != NULL)
236     ctx->entity = g_object_ref (entity);
237   if (date != NULL)
238     ctx->date = _date_copy (date);
239   ctx->event_mask = event_mask;
240   ctx->subtype = subtype;
241   ctx->count = count;
242
243   return ctx;
244 }
245
246 static void
247 ctx_free (Ctx *ctx)
248 {
249   tp_clear_object (&ctx->account);
250   tp_clear_object (&ctx->entity);
251   tp_clear_pointer (&ctx->date, g_date_free);
252
253   g_slice_free (Ctx, ctx);
254 }
255
256 static void
257 account_chooser_ready_cb (EmpathyAccountChooser *chooser,
258     EmpathyLogWindow *window)
259 {
260   /* We'll display the account once the model has been populate with the chats
261    * of this account. */
262   empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER (
263       window->account_chooser), window->selected_account);
264 }
265
266 static void
267 select_account_once_ready (EmpathyLogWindow *self,
268     TpAccount *account,
269     const gchar *chat_id,
270     gboolean is_chatroom)
271 {
272   EmpathyAccountChooser *account_chooser;
273
274   account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->account_chooser);
275
276   tp_clear_object (&self->selected_account);
277   self->selected_account = g_object_ref (account);
278
279   g_free (self->selected_chat_id);
280   self->selected_chat_id = g_strdup (chat_id);
281
282   self->selected_is_chatroom = is_chatroom;
283
284   if (empathy_account_chooser_is_ready (account_chooser))
285     account_chooser_ready_cb (account_chooser, self);
286   else
287     /* Chat will be selected once the account chooser is ready */
288     g_signal_connect (account_chooser, "ready",
289         G_CALLBACK (account_chooser_ready_cb), self);
290 }
291
292 static void
293 toolbutton_profile_clicked (GtkToolButton *toolbutton,
294     EmpathyLogWindow *window)
295 {
296   GtkTreeView *view;
297   GtkTreeSelection *selection;
298   GtkTreeModel *model;
299   GtkTreeIter iter;
300   GtkTreePath *path;
301   GList *paths;
302   TpAccount *account;
303   TplEntity *target;
304   EmpathyContact *contact;
305   gint type;
306
307   g_return_if_fail (window != NULL);
308
309   view = GTK_TREE_VIEW (log_window->treeview_who);
310   selection = gtk_tree_view_get_selection (view);
311
312   paths = gtk_tree_selection_get_selected_rows (selection, &model);
313   g_return_if_fail (paths != NULL);
314
315   path = paths->data;
316   gtk_tree_model_get_iter (model, &iter, path);
317   gtk_tree_model_get (model, &iter,
318       COL_WHO_ACCOUNT, &account,
319       COL_WHO_TARGET, &target,
320       COL_WHO_TYPE, &type,
321       -1);
322
323   g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
324
325   g_return_if_fail (type == COL_TYPE_NORMAL);
326
327   contact = empathy_contact_from_tpl_contact (account, target);
328   empathy_contact_information_dialog_show (contact,
329       GTK_WINDOW (window->window));
330   g_object_unref (contact);
331
332   g_object_unref (account);
333   g_object_unref (target);
334 }
335
336 static void
337 toolbutton_chat_clicked (GtkToolButton *toolbutton,
338     EmpathyLogWindow *window)
339 {
340   GtkTreeView *view;
341   GtkTreeSelection *selection;
342   GtkTreeModel *model;
343   GtkTreeIter iter;
344   GtkTreePath *path;
345   GList *paths;
346   TpAccount *account;
347   TplEntity *target;
348   EmpathyContact *contact;
349   gint type;
350
351   g_return_if_fail (window != NULL);
352
353   view = GTK_TREE_VIEW (log_window->treeview_who);
354   selection = gtk_tree_view_get_selection (view);
355
356   paths = gtk_tree_selection_get_selected_rows (selection, &model);
357   g_return_if_fail (paths != NULL);
358
359   path = paths->data;
360   gtk_tree_model_get_iter (model, &iter, path);
361   gtk_tree_model_get (model, &iter,
362       COL_WHO_ACCOUNT, &account,
363       COL_WHO_TARGET, &target,
364       COL_WHO_TYPE, &type,
365       -1);
366
367   g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
368
369   g_return_if_fail (type == COL_TYPE_NORMAL);
370
371   contact = empathy_contact_from_tpl_contact (account, target);
372   empathy_chat_with_contact (contact,
373       gtk_get_current_event_time ());
374
375   g_object_unref (contact);
376   g_object_unref (account);
377   g_object_unref (target);
378 }
379
380 static void
381 toolbutton_av_clicked (GtkToolButton *toolbutton,
382     EmpathyLogWindow *window)
383 {
384   GtkTreeView *view;
385   GtkTreeSelection *selection;
386   GtkTreeModel *model;
387   GtkTreeIter iter;
388   GtkTreePath *path;
389   GList *paths;
390   TpAccount *account;
391   gchar *contact;
392   gint type;
393   gboolean video;
394
395   g_return_if_fail (window != NULL);
396
397   view = GTK_TREE_VIEW (log_window->treeview_who);
398   selection = gtk_tree_view_get_selection (view);
399
400   paths = gtk_tree_selection_get_selected_rows (selection, &model);
401   g_return_if_fail (paths != NULL);
402
403   path = paths->data;
404   gtk_tree_model_get_iter (model, &iter, path);
405   gtk_tree_model_get (model, &iter,
406       COL_WHO_ACCOUNT, &account,
407       COL_WHO_NAME, &contact,
408       COL_WHO_TYPE, &type,
409       -1);
410
411   g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
412
413   g_return_if_fail (type == COL_TYPE_NORMAL);
414
415   video = (GTK_WIDGET (toolbutton) == window->button_video);
416
417   empathy_call_new_with_streams (contact, account,
418       TRUE, video, gtk_get_current_event_time ());
419
420   g_free (contact);
421   g_object_unref (account);
422 }
423
424 GtkWidget *
425 empathy_log_window_show (TpAccount  *account,
426     const gchar *chat_id,
427     gboolean     is_chatroom,
428     GtkWindow   *parent)
429 {
430   EmpathyAccountChooser   *account_chooser;
431   GtkBuilder             *gui;
432   gchar                  *filename;
433   EmpathyLogWindow       *window;
434   GtkWidget *vbox, *accounts, *search, *label, *quit;
435
436   if (log_window != NULL)
437     {
438       gtk_window_present (GTK_WINDOW (log_window->window));
439
440       if (account != NULL && chat_id != NULL)
441         select_account_once_ready (log_window, account, chat_id, is_chatroom);
442
443       return log_window->window;
444     }
445
446   log_window = g_new0 (EmpathyLogWindow, 1);
447   log_window->chain = _tpl_action_chain_new_async (NULL, NULL, NULL);
448
449   log_window->log_manager = tpl_log_manager_dup_singleton ();
450
451   window = log_window;
452
453   filename = empathy_file_lookup ("empathy-log-window.ui", "libempathy-gtk");
454   gui = empathy_builder_get_file (filename,
455       "log_window", &window->window,
456       "toolbutton_profile", &window->button_profile,
457       "toolbutton_chat", &window->button_chat,
458       "toolbutton_call", &window->button_call,
459       "toolbutton_video", &window->button_video,
460       "toolbutton_accounts", &accounts,
461       "toolbutton_search", &search,
462       "imagemenuitem_quit", &quit,
463       "treeview_who", &window->treeview_who,
464       "treeview_what", &window->treeview_what,
465       "treeview_when", &window->treeview_when,
466       "treeview_events", &window->treeview_events,
467       "notebook", &window->notebook,
468       "spinner", &window->spinner,
469       NULL);
470   g_free (filename);
471
472   empathy_builder_connect (gui, window,
473       "log_window", "destroy", log_window_destroy_cb,
474       "toolbutton_profile", "clicked", toolbutton_profile_clicked,
475       "toolbutton_chat", "clicked", toolbutton_chat_clicked,
476       "toolbutton_call", "clicked", toolbutton_av_clicked,
477       "toolbutton_video", "clicked", toolbutton_av_clicked,
478       "imagemenuitem_delete", "activate", log_window_delete_menu_clicked_cb,
479       NULL);
480
481   g_object_unref (gui);
482
483   g_object_add_weak_pointer (G_OBJECT (window->window),
484       (gpointer) &log_window);
485
486   g_signal_connect_swapped (quit, "activate",
487       G_CALLBACK (gtk_widget_destroy), window->window);
488
489   /* Account chooser for chats */
490   vbox = gtk_vbox_new (FALSE, 3);
491
492   window->account_chooser = empathy_account_chooser_new ();
493   account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser);
494   empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
495   empathy_account_chooser_set_filter (account_chooser,
496       empathy_account_chooser_filter_has_logs, NULL);
497   empathy_account_chooser_set_all (account_chooser);
498
499   g_signal_connect (window->account_chooser, "changed",
500       G_CALLBACK (log_window_chats_accounts_changed_cb),
501       window);
502
503   label = gtk_label_new (_("Show"));
504
505   gtk_box_pack_start (GTK_BOX (vbox),
506       window->account_chooser,
507       FALSE, FALSE, 0);
508
509   gtk_box_pack_start (GTK_BOX (vbox),
510       label,
511       FALSE, FALSE, 0);
512
513   gtk_widget_show_all (vbox);
514   gtk_container_add (GTK_CONTAINER (accounts), vbox);
515
516   /* Search entry */
517   vbox = gtk_vbox_new (FALSE, 3);
518
519   window->search_entry = gtk_entry_new ();
520   gtk_entry_set_icon_from_stock (GTK_ENTRY (window->search_entry),
521       GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
522   gtk_entry_set_icon_from_stock (GTK_ENTRY (window->search_entry),
523       GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
524
525   label = gtk_label_new (_("Search"));
526
527   gtk_box_pack_start (GTK_BOX (vbox),
528       window->search_entry,
529       FALSE, FALSE, 0);
530
531   gtk_box_pack_start (GTK_BOX (vbox),
532       label,
533       FALSE, FALSE, 0);
534
535   gtk_widget_show_all (vbox);
536   gtk_container_add (GTK_CONTAINER (search), vbox);
537
538   g_signal_connect (window->search_entry, "changed",
539       G_CALLBACK (log_window_search_entry_changed_cb),
540       window);
541
542   g_signal_connect (window->search_entry, "activate",
543       G_CALLBACK (log_window_search_entry_activate_cb),
544       window);
545
546   g_signal_connect (window->search_entry, "icon-press",
547       G_CALLBACK (log_window_search_entry_icon_pressed_cb),
548       window);
549
550   /* Contacts */
551   log_window_events_setup (window);
552   log_window_who_setup (window);
553   log_window_what_setup (window);
554   log_window_when_setup (window);
555
556   log_window_who_populate (window);
557
558   if (account != NULL && chat_id != NULL)
559     select_account_once_ready (window, account, chat_id, is_chatroom);
560
561   if (parent != NULL)
562     gtk_window_set_transient_for (GTK_WINDOW (window->window),
563         GTK_WINDOW (parent));
564
565   gtk_widget_show (window->window);
566
567   return window->window;
568 }
569
570 static void
571 log_window_destroy_cb (GtkWidget *widget,
572     EmpathyLogWindow *window)
573 {
574   if (window->source != 0)
575     g_source_remove (window->source);
576
577   g_free (window->last_find);
578   _tpl_action_chain_free (window->chain);
579   g_object_unref (window->log_manager);
580   tp_clear_object (&window->selected_account);
581   g_free (window->selected_chat_id);
582
583   g_free (window);
584 }
585
586 static gboolean
587 account_equal (TpAccount *a,
588     TpAccount *b)
589 {
590   return g_str_equal (tp_proxy_get_object_path (a),
591       tp_proxy_get_object_path (b));
592 }
593
594 static gboolean
595 entity_equal (TplEntity *a,
596     TplEntity *b)
597 {
598   return g_str_equal (tpl_entity_get_identifier (a),
599       tpl_entity_get_identifier (b));
600 }
601
602 static gboolean
603 is_same_confroom (TplEvent *e1,
604     TplEvent *e2)
605 {
606   TplEntity *sender1 = tpl_event_get_sender (e1);
607   TplEntity *receiver1 = tpl_event_get_receiver (e1);
608   TplEntity *sender2 = tpl_event_get_sender (e2);
609   TplEntity *receiver2 = tpl_event_get_receiver (e2);
610   TplEntity *room1, *room2;
611
612   if (tpl_entity_get_entity_type (sender1) == TPL_ENTITY_ROOM)
613     room1 = sender1;
614   else if (tpl_entity_get_entity_type (receiver1) == TPL_ENTITY_ROOM)
615     room1 = receiver1;
616   else
617     return FALSE;
618
619   if (tpl_entity_get_entity_type (sender2) == TPL_ENTITY_ROOM)
620     room2 = sender2;
621   else if (tpl_entity_get_entity_type (receiver2) == TPL_ENTITY_ROOM)
622     room2 = receiver2;
623   else
624     return FALSE;
625
626   return g_str_equal (tpl_entity_get_identifier (room1),
627       tpl_entity_get_identifier (room1));
628 }
629
630 static TplEntity *
631 event_get_target (TplEvent *event)
632 {
633   TplEntity *sender = tpl_event_get_sender (event);
634   TplEntity *receiver = tpl_event_get_receiver (event);
635
636   if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
637     return receiver;
638
639   return sender;
640 }
641
642 static gboolean
643 model_is_parent (GtkTreeModel *model,
644     GtkTreeIter *iter,
645     TplEvent *event)
646 {
647   TplEvent *stored_event;
648   TplEntity *target;
649   TpAccount *account;
650   gboolean found = FALSE;
651   GtkTreeIter parent;
652
653   if (gtk_tree_model_iter_parent (model, &parent, iter))
654     return FALSE;
655
656   gtk_tree_model_get (model, iter,
657       COL_EVENTS_ACCOUNT, &account,
658       COL_EVENTS_TARGET, &target,
659       COL_EVENTS_EVENT, &stored_event,
660       -1);
661
662   if (G_OBJECT_TYPE (event) == G_OBJECT_TYPE (stored_event) &&
663       account_equal (account, tpl_event_get_account (event)) &&
664       (entity_equal (target, event_get_target (event)) ||
665       is_same_confroom (event, stored_event)))
666     {
667       GtkTreeIter child;
668       gint64 timestamp;
669
670       gtk_tree_model_iter_nth_child (model, &child, iter,
671           gtk_tree_model_iter_n_children (model, iter) - 1);
672
673       gtk_tree_model_get (model, &child,
674           COL_EVENTS_TS, &timestamp,
675           -1);
676
677       if (ABS (tpl_event_get_timestamp (event) - timestamp) < MAX_GAP)
678         {
679           /* The gap is smaller than 30 min */
680           found = TRUE;
681         }
682     }
683
684   g_object_unref (stored_event);
685   g_object_unref (account);
686   g_object_unref (target);
687
688   return found;
689 }
690
691 static const gchar *
692 get_contact_alias_for_message (EmpathyMessage *message)
693 {
694   EmpathyContact *sender, *receiver;
695
696   sender = empathy_message_get_sender (message);
697   receiver = empathy_message_get_receiver (message);
698
699   if (empathy_contact_is_user (sender))
700     return empathy_contact_get_alias (receiver);
701
702   return empathy_contact_get_alias (sender);
703 }
704
705 static void
706 get_parent_iter_for_message (TplEvent *event,
707     EmpathyMessage *message,
708     GtkTreeIter *parent)
709 {
710   GtkTreeStore *store;
711   GtkTreeModel *model;
712   GtkTreeIter iter;
713   gboolean parent_found = FALSE;
714   gboolean next;
715
716   store = log_window->store_events;
717   model = GTK_TREE_MODEL (store);
718
719   for (next = gtk_tree_model_get_iter_first (model, &iter);
720        next;
721        next = gtk_tree_model_iter_next (model, &iter))
722     {
723       if ((parent_found = model_is_parent (model, &iter, event)))
724         break;
725     }
726
727   if (parent_found)
728     {
729       *parent = iter;
730     }
731   else
732     {
733       GDateTime *date;
734       gchar *body, *pretty_date;
735
736       date = g_date_time_new_from_unix_utc (
737           tpl_event_get_timestamp (event));
738
739       pretty_date = g_date_time_format (date,
740           C_("A date with the time", "%A, %e %B %Y %X"));
741
742       body = g_markup_printf_escaped (_("Chat with %s"),
743           get_contact_alias_for_message (message));
744
745       gtk_tree_store_append (store, &iter, NULL);
746       gtk_tree_store_set (store, &iter,
747           COL_EVENTS_TS, tpl_event_get_timestamp (event),
748           COL_EVENTS_PRETTY_DATE, pretty_date,
749           COL_EVENTS_TEXT, body,
750           COL_EVENTS_ICON, "stock_text_justify",
751           COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
752           COL_EVENTS_TARGET, event_get_target (event),
753           COL_EVENTS_EVENT, event,
754           -1);
755
756       *parent = iter;
757
758       g_free (body);
759       g_free (pretty_date);
760       g_date_time_unref (date);
761     }
762 }
763
764 static const gchar *
765 get_icon_for_event (TplEvent *event)
766 {
767   const gchar *icon = NULL;
768
769   if (TPL_IS_CALL_EVENT (event))
770     {
771       TplCallEvent *call = TPL_CALL_EVENT (event);
772       TplCallEndReason reason = tpl_call_event_get_end_reason (call);
773       TplEntity *sender = tpl_event_get_sender (event);
774       TplEntity *receiver = tpl_event_get_receiver (event);
775
776       if (reason == TPL_CALL_END_REASON_NO_ANSWER)
777         icon = EMPATHY_IMAGE_CALL_MISSED;
778       else if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
779         icon = EMPATHY_IMAGE_CALL_OUTGOING;
780       else if (tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
781         icon = EMPATHY_IMAGE_CALL_INCOMING;
782     }
783
784   return icon;
785 }
786
787 static void
788 log_window_append_chat_message (TplEvent *event,
789     EmpathyMessage *message)
790 {
791   GtkTreeStore *store = log_window->store_events;
792   GtkTreeIter iter, parent;
793   gchar *pretty_date, *body;
794   GDateTime *date;
795
796   date = g_date_time_new_from_unix_utc (
797       tpl_event_get_timestamp (event));
798
799   pretty_date = g_date_time_format (date, "%X");
800
801   get_parent_iter_for_message (event, message, &parent);
802
803   if (tpl_text_event_get_message_type (TPL_TEXT_EVENT (event))
804       == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)
805     {
806       /* Translators: this is an emote: '* Danielle waves' */
807       body = g_markup_printf_escaped (_("<i>* %s %s</i>"),
808           tpl_entity_get_alias (tpl_event_get_sender (event)),
809           empathy_message_get_body (message));
810     }
811   else
812     {
813       /* Translators: this is a message: 'Danielle: hello'
814        * The string in bold is the sender's name */
815       body = g_markup_printf_escaped (_("<b>%s:</b> %s"),
816           tpl_entity_get_alias (tpl_event_get_sender (event)),
817           empathy_message_get_body (message));
818     }
819
820   gtk_tree_store_append (store, &iter, &parent);
821   gtk_tree_store_set (store, &iter,
822       COL_EVENTS_TS, tpl_event_get_timestamp (event),
823       COL_EVENTS_PRETTY_DATE, pretty_date,
824       COL_EVENTS_TEXT, body,
825       COL_EVENTS_ICON, get_icon_for_event (event),
826       COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
827       COL_EVENTS_TARGET, event_get_target (event),
828       COL_EVENTS_EVENT, event,
829       -1);
830
831   g_free (body);
832   g_free (pretty_date);
833   g_date_time_unref (date);
834 }
835
836 static void
837 log_window_append_call (TplEvent *event,
838     EmpathyMessage *message)
839 {
840   TplCallEvent *call = TPL_CALL_EVENT (event);
841   GtkTreeStore *store = log_window->store_events;
842   GtkTreeIter iter, child;
843   gchar *pretty_date, *duration, *finished;
844   GDateTime *started_date, *finished_date;
845   GTimeSpan span;
846
847   started_date = g_date_time_new_from_unix_utc (
848       tpl_event_get_timestamp (event));
849
850   pretty_date = g_date_time_format (started_date,
851       C_("A date with the time", "%A, %e %B %Y %X"));
852
853   gtk_tree_store_append (store, &iter, NULL);
854   gtk_tree_store_set (store, &iter,
855       COL_EVENTS_TS, tpl_event_get_timestamp (event),
856       COL_EVENTS_PRETTY_DATE, pretty_date,
857       COL_EVENTS_TEXT, empathy_message_get_body (message),
858       COL_EVENTS_ICON, get_icon_for_event (event),
859       COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
860       COL_EVENTS_TARGET, event_get_target (event),
861       COL_EVENTS_EVENT, event,
862       -1);
863
864   if (tpl_call_event_get_end_reason (call) != TPL_CALL_END_REASON_NO_ANSWER)
865     {
866       gchar *body;
867
868       span = tpl_call_event_get_duration (TPL_CALL_EVENT (event));
869       if (span < 60)
870         duration = g_strdup_printf (_("%" G_GINT64_FORMAT " seconds"), span);
871       else
872         duration = g_strdup_printf (_("%" G_GINT64_FORMAT " minutes"),
873             span / 60);
874
875       finished_date = g_date_time_add (started_date, -span);
876       finished = g_date_time_format (finished_date, "%X");
877       g_date_time_unref (finished_date);
878
879       body = g_strdup_printf (_("Call took %s, ended at %s"),
880           duration, finished);
881
882       g_free (duration);
883       g_free (finished);
884
885       gtk_tree_store_append (store, &child, &iter);
886       gtk_tree_store_set (store, &child,
887           COL_EVENTS_TS, tpl_event_get_timestamp (event),
888           COL_EVENTS_TEXT, body,
889           COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
890           COL_EVENTS_TARGET, event_get_target (event),
891           COL_EVENTS_EVENT, event,
892           -1);
893
894       g_free (body);
895     }
896
897   g_free (pretty_date);
898   g_date_time_unref (started_date);
899 }
900
901 static void
902 log_window_append_message (TplEvent *event,
903     EmpathyMessage *message)
904 {
905   if (TPL_IS_TEXT_EVENT (event))
906     log_window_append_chat_message (event, message);
907   else if (TPL_IS_CALL_EVENT (event))
908     log_window_append_call (event, message);
909   else
910     DEBUG ("Message type not handled");
911 }
912
913 static void
914 add_all_accounts_and_entities (GList **accounts,
915     GList **entities)
916 {
917   GtkTreeView      *view;
918   GtkTreeModel     *model;
919   GtkTreeIter       iter;
920
921   view = GTK_TREE_VIEW (log_window->treeview_who);
922   model = gtk_tree_view_get_model (view);
923
924   if (!gtk_tree_model_get_iter_first (model, &iter))
925     return;
926
927   do
928     {
929       TpAccount *account;
930       TplEntity *entity;
931       gint type;
932
933       gtk_tree_model_get (model, &iter,
934           COL_WHO_ACCOUNT, &account,
935           COL_WHO_TARGET, &entity,
936           COL_WHO_TYPE, &type,
937           -1);
938
939       if (type != COL_TYPE_NORMAL)
940         continue;
941
942       if (accounts != NULL)
943         *accounts = g_list_append (*accounts, account);
944
945       if (entities != NULL)
946         *entities = g_list_append (*entities, entity);
947     }
948   while (gtk_tree_model_iter_next (model, &iter));
949 }
950
951 static gboolean
952 log_window_get_selected (EmpathyLogWindow *window,
953     GList **accounts,
954     GList **entities,
955     GList **dates,
956     TplEventTypeMask *event_mask,
957     EventSubtype *subtype)
958 {
959   GtkTreeView      *view;
960   GtkTreeModel     *model;
961   GtkTreeSelection *selection;
962   GtkTreeIter       iter;
963   TplEventTypeMask  ev = 0;
964   EventSubtype      st = 0;
965   GList            *paths, *l;
966   gint              type;
967
968   view = GTK_TREE_VIEW (window->treeview_who);
969   model = gtk_tree_view_get_model (view);
970   selection = gtk_tree_view_get_selection (view);
971
972   paths = gtk_tree_selection_get_selected_rows (selection, NULL);
973   if (paths == NULL)
974     return FALSE;
975
976   if (accounts != NULL)
977     *accounts = NULL;
978   if (entities != NULL)
979     *entities = NULL;
980
981   for (l = paths; l != NULL; l = l->next)
982     {
983       GtkTreePath *path = l->data;
984       TpAccount *account;
985       TplEntity *entity;
986
987       gtk_tree_model_get_iter (model, &iter, path);
988       gtk_tree_model_get (model, &iter,
989           COL_WHO_ACCOUNT, &account,
990           COL_WHO_TARGET, &entity,
991           COL_WHO_TYPE, &type,
992           -1);
993
994       if (type == COL_TYPE_ANY)
995         {
996           if (accounts != NULL || entities != NULL)
997             add_all_accounts_and_entities (accounts, entities);
998           break;
999         }
1000
1001       if (accounts != NULL)
1002         *accounts = g_list_append (*accounts, g_object_ref (account));
1003
1004       if (entities != NULL)
1005         *entities = g_list_append (*entities, g_object_ref (entity));
1006
1007       g_object_unref (account);
1008       g_object_unref (entity);
1009     }
1010   g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1011
1012   view = GTK_TREE_VIEW (window->treeview_what);
1013   model = gtk_tree_view_get_model (view);
1014   selection = gtk_tree_view_get_selection (view);
1015
1016   paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1017   for (l = paths; l != NULL; l = l->next)
1018     {
1019       GtkTreePath *path = l->data;
1020       TplEventTypeMask mask;
1021       EventSubtype submask;
1022
1023       gtk_tree_model_get_iter (model, &iter, path);
1024       gtk_tree_model_get (model, &iter,
1025           COL_WHAT_TYPE, &mask,
1026           COL_WHAT_SUBTYPE, &submask,
1027           -1);
1028
1029       ev |= mask;
1030       st |= submask;
1031     }
1032   g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1033
1034   view = GTK_TREE_VIEW (window->treeview_when);
1035   model = gtk_tree_view_get_model (view);
1036   selection = gtk_tree_view_get_selection (view);
1037
1038   if (dates != NULL)
1039     {
1040       *dates = NULL;
1041
1042       paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1043       for (l = paths; l != NULL; l = l->next)
1044         {
1045           GtkTreePath *path = l->data;
1046           GDate *date;
1047
1048           gtk_tree_model_get_iter (model, &iter, path);
1049           gtk_tree_model_get (model, &iter,
1050               COL_WHEN_DATE, &date,
1051               -1);
1052
1053           *dates = g_list_append (*dates, date);
1054         }
1055       g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1056     }
1057
1058   if (event_mask != NULL)
1059     *event_mask = ev;
1060
1061   if (subtype != NULL)
1062     *subtype = st;
1063
1064   return TRUE;
1065 }
1066
1067 static gboolean
1068 model_has_entity (GtkTreeModel *model,
1069     GtkTreePath *path,
1070     GtkTreeIter *iter,
1071     gpointer data)
1072 {
1073   TplLogSearchHit *hit = data;
1074   TplEntity *e;
1075   TpAccount *a;
1076   gboolean ret = FALSE;
1077
1078   gtk_tree_model_get (model, iter,
1079       COL_WHO_TARGET, &e,
1080       COL_WHO_ACCOUNT, &a,
1081       -1);
1082
1083   if (e != NULL && entity_equal (hit->target, e) &&
1084       a != NULL && account_equal (hit->account, a))
1085     {
1086       ret = has_element = TRUE;
1087     }
1088
1089   tp_clear_object (&e);
1090   tp_clear_object (&a);
1091
1092   return ret;
1093 }
1094
1095 static gboolean
1096 model_has_date (GtkTreeModel *model,
1097     GtkTreePath *path,
1098     GtkTreeIter *iter,
1099     gpointer data)
1100 {
1101   GDate *date = data;
1102   GDate *d;
1103
1104   gtk_tree_model_get (model, iter,
1105       COL_WHEN_DATE, &d,
1106       -1);
1107
1108   if (!g_date_compare (date, d))
1109     {
1110       has_element = TRUE;
1111       return TRUE;
1112     }
1113
1114   return FALSE;
1115 }
1116
1117 static void
1118 get_events_for_date (TplActionChain *chain, gpointer user_data);
1119
1120 static void
1121 populate_events_from_search_hits (GList *accounts,
1122     GList *targets,
1123     GList *dates)
1124 {
1125   TplEventTypeMask event_mask;
1126   EventSubtype subtype;
1127   GDate *anytime;
1128   GList *l;
1129   gboolean is_anytime = FALSE;
1130
1131   if (!log_window_get_selected (log_window,
1132       NULL, NULL, NULL, &event_mask, &subtype))
1133     return;
1134
1135   anytime = g_date_new_dmy (2, 1, -1);
1136   if (g_list_find_custom (dates, anytime, (GCompareFunc) g_date_compare))
1137     is_anytime = TRUE;
1138
1139   for (l = log_window->hits; l != NULL; l = l->next)
1140     {
1141       TplLogSearchHit *hit = l->data;
1142       GList *acc, *targ;
1143       gboolean found = FALSE;
1144
1145       /* Protect against invalid data (corrupt or old log files). */
1146       if (hit->account == NULL || hit->target == NULL)
1147         continue;
1148
1149       for (acc = accounts, targ = targets;
1150            acc != NULL && targ != NULL && !found;
1151            acc = acc->next, targ = targ->next)
1152         {
1153           TpAccount *account = acc->data;
1154           TplEntity *target = targ->data;
1155
1156           if (account_equal (hit->account, account) &&
1157               entity_equal (hit->target, target))
1158             found = TRUE;
1159         }
1160
1161         if (!found)
1162           continue;
1163
1164       if (is_anytime ||
1165           g_list_find_custom (dates, hit->date, (GCompareFunc) g_date_compare)
1166               != NULL)
1167         {
1168           Ctx *ctx;
1169
1170           ctx = ctx_new (log_window, hit->account, hit->target, hit->date,
1171               event_mask, subtype, log_window->count);
1172           _tpl_action_chain_append (log_window->chain,
1173               get_events_for_date, ctx);
1174         }
1175     }
1176
1177   _tpl_action_chain_start (log_window->chain);
1178
1179   g_date_free (anytime);
1180 }
1181
1182 static gchar *
1183 format_date_for_display (GDate *date)
1184 {
1185   gchar *text;
1186   GDate *now = NULL;
1187   gint days_elapsed;
1188
1189   /* g_date_strftime sucks */
1190
1191   now = g_date_new ();
1192   g_date_set_time_t (now, time (NULL));
1193
1194   days_elapsed = g_date_days_between (date, now);
1195
1196   if (days_elapsed < 0)
1197     {
1198       text = NULL;
1199     }
1200   else if (days_elapsed == 0)
1201     {
1202       text = g_strdup (_("Today"));
1203     }
1204   else if (days_elapsed == 1)
1205     {
1206       text = g_strdup (_("Yesterday"));
1207     }
1208   else
1209     {
1210       GDateTime *dt;
1211
1212       dt = g_date_time_new_utc (g_date_get_year (date),
1213           g_date_get_month (date), g_date_get_day (date),
1214           0, 0, 0);
1215
1216       if (days_elapsed <= 7)
1217         text = g_date_time_format (dt, "%A");
1218       else
1219         text = g_date_time_format (dt,
1220             C_("A date such as '23 May 2010', "
1221                "%e is the day, %B the month and %Y the year",
1222                "%e %B %Y"));
1223
1224       g_date_time_unref (dt);
1225     }
1226
1227   g_date_free (now);
1228
1229   return text;
1230 }
1231
1232 static void
1233 populate_dates_from_search_hits (GList *accounts,
1234     GList *targets)
1235 {
1236   GList *l;
1237   GtkTreeView *view;
1238   GtkTreeModel *model;
1239   GtkListStore *store;
1240   GtkTreeSelection *selection;
1241   GtkTreeIter iter;
1242
1243   if (log_window == NULL)
1244     return;
1245
1246   view = GTK_TREE_VIEW (log_window->treeview_when);
1247   model = gtk_tree_view_get_model (view);
1248   store = GTK_LIST_STORE (model);
1249   selection = gtk_tree_view_get_selection (view);
1250
1251   for (l = log_window->hits; l != NULL; l = l->next)
1252     {
1253       TplLogSearchHit *hit = l->data;
1254       GList *acc, *targ;
1255       gboolean found = FALSE;
1256
1257       /* Protect against invalid data (corrupt or old log files). */
1258       if (hit->account == NULL || hit->target == NULL)
1259         continue;
1260
1261       for (acc = accounts, targ = targets;
1262            acc != NULL && targ != NULL && !found;
1263            acc = acc->next, targ = targ->next)
1264         {
1265           TpAccount *account = acc->data;
1266           TplEntity *target = targ->data;
1267
1268           if (account_equal (hit->account, account) &&
1269               entity_equal (hit->target, target))
1270             found = TRUE;
1271         }
1272
1273         if (!found)
1274           continue;
1275
1276       /* Add the date if it's not already there */
1277       has_element = FALSE;
1278       gtk_tree_model_foreach (model, model_has_date, hit->date);
1279       if (!has_element)
1280         {
1281           gchar *text = format_date_for_display (hit->date);
1282
1283           gtk_list_store_append (store, &iter);
1284           gtk_list_store_set (store, &iter,
1285               COL_WHEN_DATE, hit->date,
1286               COL_WHEN_TEXT, text,
1287               COL_WHEN_ICON, CALENDAR_ICON,
1288               -1);
1289         }
1290     }
1291
1292   if (gtk_tree_model_get_iter_first (model, &iter))
1293     {
1294       gtk_list_store_prepend (store, &iter);
1295       gtk_list_store_set (store, &iter,
1296           COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
1297           COL_WHEN_TEXT, "separator",
1298           -1);
1299
1300       gtk_list_store_prepend (store, &iter);
1301       gtk_list_store_set (store, &iter,
1302           COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
1303           COL_WHEN_TEXT, _("Anytime"),
1304           -1);
1305
1306       if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 2))
1307         gtk_tree_selection_select_iter (selection, &iter);
1308     }
1309 }
1310
1311 static void
1312 populate_entities_from_search_hits (void)
1313 {
1314   EmpathyAccountChooser *account_chooser;
1315   TpAccount *account;
1316   GtkTreeView *view;
1317   GtkTreeModel *model;
1318   GtkTreeIter iter;
1319   GtkListStore *store;
1320   GList *l;
1321
1322   view = GTK_TREE_VIEW (log_window->treeview_who);
1323   model = gtk_tree_view_get_model (view);
1324   store = GTK_LIST_STORE (model);
1325
1326   gtk_list_store_clear (store);
1327
1328   account_chooser = EMPATHY_ACCOUNT_CHOOSER (log_window->account_chooser);
1329   account = empathy_account_chooser_get_account (account_chooser);
1330
1331   for (l = log_window->hits; l; l = l->next)
1332     {
1333       TplLogSearchHit *hit = l->data;
1334
1335       /* Protect against invalid data (corrupt or old log files). */
1336       if (hit->account == NULL || hit->target == NULL)
1337         continue;
1338
1339       /* Filter based on the selected account */
1340       if (account != NULL && !account_equal (account, hit->account))
1341         continue;
1342
1343       /* Add the entity if it's not already there */
1344       has_element = FALSE;
1345       gtk_tree_model_foreach (model, model_has_entity, hit);
1346       if (!has_element)
1347         {
1348           TplEntityType type = tpl_entity_get_entity_type (hit->target);
1349           gboolean room = type == TPL_ENTITY_ROOM;
1350
1351           gtk_list_store_append (store, &iter);
1352           gtk_list_store_set (store, &iter,
1353               COL_WHO_TYPE, COL_TYPE_NORMAL,
1354               COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
1355                                  : EMPATHY_IMAGE_AVATAR_DEFAULT,
1356               COL_WHO_NAME, tpl_entity_get_alias (hit->target),
1357               COL_WHO_ACCOUNT, hit->account,
1358               COL_WHO_TARGET, hit->target,
1359               -1);
1360         }
1361     }
1362
1363   if (gtk_tree_model_get_iter_first (model, &iter))
1364     {
1365       gtk_list_store_prepend (store, &iter);
1366       gtk_list_store_set (store, &iter,
1367           COL_WHO_TYPE, COL_TYPE_SEPARATOR,
1368           COL_WHO_NAME, "separator",
1369           -1);
1370
1371       gtk_list_store_prepend (store, &iter);
1372       gtk_list_store_set (store, &iter,
1373           COL_WHO_TYPE, COL_TYPE_ANY,
1374           COL_WHO_NAME, _("Anyone"),
1375           -1);
1376     }
1377
1378   /* FIXME: select old entity if still available */
1379 }
1380
1381 static void
1382 log_manager_searched_new_cb (GObject *manager,
1383     GAsyncResult *result,
1384     gpointer user_data)
1385 {
1386   GList *hits;
1387   GtkTreeView *view;
1388   GtkTreeSelection *selection;
1389   GError *error = NULL;
1390
1391   if (log_window == NULL)
1392     return;
1393
1394   if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager),
1395       result, &hits, &error))
1396     {
1397       DEBUG ("%s. Aborting", error->message);
1398       g_error_free (error);
1399       return;
1400     }
1401
1402   tp_clear_pointer (&log_window->hits, tpl_log_manager_search_free);
1403   log_window->hits = hits;
1404
1405   populate_entities_from_search_hits ();
1406
1407   view = GTK_TREE_VIEW (log_window->treeview_when);
1408   selection = gtk_tree_view_get_selection (view);
1409
1410   g_signal_handlers_unblock_by_func (selection,
1411       log_window_when_changed_cb,
1412       log_window);
1413 }
1414
1415 static void
1416 log_window_find_populate (EmpathyLogWindow *window,
1417     const gchar *search_criteria)
1418 {
1419   GtkTreeView *view;
1420   GtkTreeModel *model;
1421   GtkTreeSelection *selection;
1422   GtkListStore *store;
1423
1424   gtk_tree_store_clear (window->store_events);
1425
1426   view = GTK_TREE_VIEW (window->treeview_who);
1427   model = gtk_tree_view_get_model (view);
1428   store = GTK_LIST_STORE (model);
1429
1430   gtk_list_store_clear (store);
1431
1432   view = GTK_TREE_VIEW (window->treeview_when);
1433   model = gtk_tree_view_get_model (view);
1434   store = GTK_LIST_STORE (model);
1435   selection = gtk_tree_view_get_selection (view);
1436
1437   gtk_list_store_clear (store);
1438
1439   if (EMP_STR_EMPTY (search_criteria))
1440     {
1441       tp_clear_pointer (&window->hits, tpl_log_manager_search_free);
1442       log_window_who_populate (window);
1443       return;
1444     }
1445
1446   g_signal_handlers_block_by_func (selection,
1447       log_window_when_changed_cb,
1448       window);
1449
1450   tpl_log_manager_search_async (window->log_manager,
1451       search_criteria, TPL_EVENT_MASK_ANY,
1452       log_manager_searched_new_cb, NULL);
1453 }
1454
1455 static gboolean
1456 start_find_search (EmpathyLogWindow *window)
1457 {
1458   const gchar *str;
1459
1460   str = gtk_entry_get_text (GTK_ENTRY (window->search_entry));
1461
1462   /* Don't find the same crap again */
1463   if (window->last_find && !tp_strdiff (window->last_find, str))
1464     return FALSE;
1465
1466   g_free (window->last_find);
1467   window->last_find = g_strdup (str);
1468
1469   log_window_find_populate (window, str);
1470
1471   return FALSE;
1472 }
1473
1474 static void
1475 log_window_search_entry_changed_cb (GtkWidget *entry,
1476     EmpathyLogWindow *window)
1477 {
1478   if (window->source != 0)
1479     g_source_remove (window->source);
1480   window->source = g_timeout_add (500, (GSourceFunc) start_find_search,
1481       window);
1482 }
1483
1484 static void
1485 log_window_search_entry_activate_cb (GtkWidget *entry,
1486     EmpathyLogWindow *self)
1487 {
1488   start_find_search (self);
1489 }
1490
1491 static void
1492 log_window_search_entry_icon_pressed_cb (GtkEntry *entry,
1493     GtkEntryIconPosition icon_pos,
1494     GdkEvent *event,
1495     gpointer user_data)
1496 {
1497   if (icon_pos != GTK_ENTRY_ICON_SECONDARY)
1498     return;
1499
1500   gtk_entry_buffer_set_text (gtk_entry_get_buffer (entry),
1501     "", -1);
1502 }
1503
1504 static void
1505 log_window_update_buttons_sensitivity (EmpathyLogWindow *window,
1506     GtkTreeModel *model,
1507     GtkTreeSelection *selection)
1508 {
1509   EmpathyContact *contact;
1510   EmpathyCapabilities capabilities;
1511   TpAccount *account;
1512   TplEntity *target;
1513   GtkTreeIter iter;
1514   GList *paths;
1515   GtkTreePath *path;
1516   gboolean profile, chat, call, video;
1517
1518   profile = chat = call = video = FALSE;
1519
1520   if (!gtk_tree_model_get_iter_first (model, &iter))
1521     goto out;
1522
1523   if (gtk_tree_selection_count_selected_rows (selection) != 1)
1524     goto out;
1525
1526   if (gtk_tree_selection_iter_is_selected (selection, &iter))
1527     goto out;
1528
1529   paths = gtk_tree_selection_get_selected_rows (selection, &model);
1530   g_return_if_fail (paths != NULL);
1531
1532   path = paths->data;
1533   gtk_tree_model_get_iter (model, &iter, path);
1534   gtk_tree_model_get (model, &iter,
1535       COL_WHO_ACCOUNT, &account,
1536       COL_WHO_TARGET, &target,
1537       -1);
1538
1539   g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1540
1541   contact = empathy_contact_from_tpl_contact (account, target);
1542
1543   g_object_unref (account);
1544   g_object_unref (target);
1545
1546   capabilities = empathy_contact_get_capabilities (contact);
1547
1548   profile = chat = TRUE;
1549   call = capabilities & EMPATHY_CAPABILITIES_AUDIO;
1550   video = capabilities & EMPATHY_CAPABILITIES_VIDEO;
1551
1552  out:
1553   gtk_widget_set_sensitive (window->button_profile, profile);
1554   gtk_widget_set_sensitive (window->button_chat, chat);
1555   gtk_widget_set_sensitive (window->button_call, call);
1556   gtk_widget_set_sensitive (window->button_video, video);
1557 }
1558
1559 static void
1560 log_window_who_changed_cb (GtkTreeSelection *selection,
1561     EmpathyLogWindow  *window)
1562 {
1563   GtkTreeView *view;
1564   GtkTreeModel *model;
1565   GtkTreeIter iter;
1566
1567   DEBUG ("log_window_who_changed_cb");
1568
1569   view = gtk_tree_selection_get_tree_view (selection);
1570   model = gtk_tree_view_get_model (view);
1571
1572   if (gtk_tree_model_get_iter_first (model, &iter))
1573     {
1574       /* If 'Anyone' is selected, everything else should be deselected */
1575       if (gtk_tree_selection_iter_is_selected (selection, &iter))
1576         {
1577           g_signal_handlers_block_by_func (selection,
1578               log_window_who_changed_cb,
1579               window);
1580
1581           gtk_tree_selection_unselect_all (selection);
1582           gtk_tree_selection_select_iter (selection, &iter);
1583
1584           g_signal_handlers_unblock_by_func (selection,
1585               log_window_who_changed_cb,
1586               window);
1587         }
1588     }
1589
1590   log_window_update_buttons_sensitivity (window, model, selection);
1591
1592   /* The contact changed, so the dates need to be updated */
1593   log_window_chats_get_messages (window, TRUE);
1594 }
1595
1596 static void
1597 log_manager_got_entities_cb (GObject *manager,
1598     GAsyncResult *result,
1599     gpointer user_data)
1600 {
1601   Ctx                   *ctx = user_data;
1602   GList                 *entities;
1603   GList                 *l;
1604   GtkTreeView           *view;
1605   GtkTreeModel          *model;
1606   GtkTreeSelection      *selection;
1607   GtkListStore          *store;
1608   GtkTreeIter            iter;
1609   GError                *error = NULL;
1610   gboolean               select_account = FALSE;
1611
1612   if (log_window == NULL)
1613     goto out;
1614
1615   if (log_window->count != ctx->count)
1616     goto out;
1617
1618   if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
1619       result, &entities, &error))
1620     {
1621       DEBUG ("%s. Aborting", error->message);
1622       g_error_free (error);
1623       goto out;
1624     }
1625
1626   view = GTK_TREE_VIEW (ctx->window->treeview_who);
1627   model = gtk_tree_view_get_model (view);
1628   selection = gtk_tree_view_get_selection (view);
1629   store = GTK_LIST_STORE (model);
1630
1631   /* Block signals to stop the logs being retrieved prematurely  */
1632   g_signal_handlers_block_by_func (selection,
1633       log_window_who_changed_cb, ctx->window);
1634
1635   for (l = entities; l; l = l->next)
1636     {
1637       TplEntity *entity = TPL_ENTITY (l->data);
1638       TplEntityType type = tpl_entity_get_entity_type (entity);
1639       gboolean room = type == TPL_ENTITY_ROOM;
1640
1641       gtk_list_store_append (store, &iter);
1642       gtk_list_store_set (store, &iter,
1643           COL_WHO_TYPE, COL_TYPE_NORMAL,
1644           COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
1645                              : EMPATHY_IMAGE_AVATAR_DEFAULT,
1646           COL_WHO_NAME, tpl_entity_get_alias (entity),
1647           COL_WHO_ACCOUNT, ctx->account,
1648           COL_WHO_TARGET, entity,
1649           -1);
1650
1651       if (ctx->window->selected_account != NULL &&
1652           !tp_strdiff (tp_proxy_get_object_path (ctx->account),
1653           tp_proxy_get_object_path (ctx->window->selected_account)))
1654         select_account = TRUE;
1655     }
1656   g_list_free_full (entities, g_object_unref);
1657
1658   if (gtk_tree_model_get_iter_first (model, &iter))
1659     {
1660       gint type;
1661
1662       gtk_tree_model_get (model, &iter,
1663           COL_WHO_TYPE, &type,
1664           -1);
1665
1666       if (type != COL_TYPE_ANY)
1667         {
1668           gtk_list_store_prepend (store, &iter);
1669           gtk_list_store_set (store, &iter,
1670               COL_WHO_TYPE, COL_TYPE_SEPARATOR,
1671               COL_WHO_NAME, "separator",
1672               -1);
1673
1674           gtk_list_store_prepend (store, &iter);
1675           gtk_list_store_set (store, &iter,
1676               COL_WHO_TYPE, COL_TYPE_ANY,
1677               COL_WHO_NAME, _("Anyone"),
1678               -1);
1679         }
1680     }
1681
1682   /* Unblock signals */
1683   g_signal_handlers_unblock_by_func (selection,
1684       log_window_who_changed_cb,
1685       ctx->window);
1686
1687   /* We display the selected account if we populate the model with chats from
1688    * this account. */
1689   if (select_account)
1690     log_window_chats_set_selected (ctx->window);
1691
1692 out:
1693   _tpl_action_chain_continue (log_window->chain);
1694   ctx_free (ctx);
1695 }
1696
1697 static void
1698 get_entities_for_account (TplActionChain *chain, gpointer user_data)
1699 {
1700   Ctx *ctx = user_data;
1701
1702   tpl_log_manager_get_entities_async (ctx->window->log_manager, ctx->account,
1703       log_manager_got_entities_cb, ctx);
1704 }
1705
1706 static void
1707 select_first_entity (TplActionChain *chain, gpointer user_data)
1708 {
1709   GtkTreeView *view;
1710   GtkTreeModel *model;
1711   GtkTreeSelection *selection;
1712   GtkTreeIter iter;
1713
1714   view = GTK_TREE_VIEW (log_window->treeview_who);
1715   model = gtk_tree_view_get_model (view);
1716   selection = gtk_tree_view_get_selection (view);
1717
1718   if (gtk_tree_model_get_iter_first (model, &iter))
1719     gtk_tree_selection_select_iter (selection, &iter);
1720
1721   _tpl_action_chain_continue (log_window->chain);
1722 }
1723
1724 static void
1725 log_window_who_populate (EmpathyLogWindow *window)
1726 {
1727   EmpathyAccountChooser *account_chooser;
1728   TpAccount *account;
1729   gboolean all_accounts;
1730   GtkTreeView *view;
1731   GtkTreeModel *model;
1732   GtkTreeSelection *selection;
1733   GtkListStore *store;
1734   Ctx *ctx;
1735
1736   if (window->hits != NULL)
1737     {
1738       populate_entities_from_search_hits ();
1739       return;
1740     }
1741
1742   account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser);
1743   account = empathy_account_chooser_dup_account (account_chooser);
1744   all_accounts = empathy_account_chooser_has_all_selected (account_chooser);
1745
1746   view = GTK_TREE_VIEW (window->treeview_who);
1747   model = gtk_tree_view_get_model (view);
1748   selection = gtk_tree_view_get_selection (view);
1749   store = GTK_LIST_STORE (model);
1750
1751   /* Block signals to stop the logs being retrieved prematurely  */
1752   g_signal_handlers_block_by_func (selection,
1753       log_window_who_changed_cb,
1754       window);
1755
1756   gtk_list_store_clear (store);
1757
1758   /* Unblock signals */
1759   g_signal_handlers_unblock_by_func (selection,
1760       log_window_who_changed_cb,
1761       window);
1762
1763   _tpl_action_chain_clear (window->chain);
1764   window->count++;
1765
1766   if (!all_accounts && account == NULL)
1767     {
1768       return;
1769     }
1770   else if (!all_accounts)
1771     {
1772       ctx = ctx_new (window, account, NULL, NULL, 0, 0, window->count);
1773       _tpl_action_chain_append (window->chain, get_entities_for_account, ctx);
1774     }
1775   else
1776     {
1777       TpAccountManager *manager;
1778       GList *accounts, *l;
1779
1780       manager = empathy_account_chooser_get_account_manager (account_chooser);
1781       accounts = tp_account_manager_get_valid_accounts (manager);
1782
1783       for (l = accounts; l != NULL; l = l->next)
1784         {
1785           account = l->data;
1786
1787           ctx = ctx_new (window, account, NULL, NULL, 0, 0, window->count);
1788           _tpl_action_chain_append (window->chain,
1789               get_entities_for_account, ctx);
1790         }
1791
1792       g_list_free (accounts);
1793     }
1794   _tpl_action_chain_append (window->chain, select_first_entity, NULL);
1795   _tpl_action_chain_start (window->chain);
1796 }
1797
1798 static gint
1799 sort_by_name (GtkTreeModel *model,
1800     GtkTreeIter *a,
1801     GtkTreeIter *b,
1802     gpointer user_data)
1803 {
1804   gchar *name1, *name2;
1805   gint type1, type2;
1806   gint ret;
1807
1808   gtk_tree_model_get (model, a,
1809       COL_WHO_TYPE, &type1,
1810       COL_WHO_NAME, &name1,
1811       -1);
1812
1813   gtk_tree_model_get (model, b,
1814       COL_WHO_TYPE, &type2,
1815       COL_WHO_NAME, &name2,
1816       -1);
1817
1818   if (type1 == COL_TYPE_ANY)
1819     ret = -1;
1820   else if (type2 == COL_TYPE_ANY)
1821     ret = 1;
1822   else if (type1 == COL_TYPE_SEPARATOR)
1823     ret = -1;
1824   else if (type2 == COL_TYPE_SEPARATOR)
1825     ret = 1;
1826   else
1827     ret = g_strcmp0 (name1, name2);
1828
1829   g_free (name1);
1830   g_free (name2);
1831
1832   return ret;
1833 }
1834
1835 static gboolean
1836 who_row_is_separator (GtkTreeModel *model,
1837     GtkTreeIter *iter,
1838     gpointer data)
1839 {
1840   gint type;
1841
1842   gtk_tree_model_get (model, iter,
1843       COL_WHO_TYPE, &type,
1844       -1);
1845
1846   return (type == COL_TYPE_SEPARATOR);
1847 }
1848
1849 static void
1850 log_window_events_setup (EmpathyLogWindow *window)
1851 {
1852   GtkTreeView       *view;
1853   GtkTreeModel      *model;
1854   GtkTreeSelection  *selection;
1855   GtkTreeSortable   *sortable;
1856   GtkTreeViewColumn *column;
1857   GtkTreeStore      *store;
1858   GtkCellRenderer   *cell;
1859
1860   view = GTK_TREE_VIEW (window->treeview_events);
1861   selection = gtk_tree_view_get_selection (view);
1862
1863   /* new store */
1864   window->store_events = store = gtk_tree_store_new (COL_EVENTS_COUNT,
1865       G_TYPE_INT,           /* type */
1866       G_TYPE_INT64,         /* timestamp */
1867       G_TYPE_STRING,        /* stringified date */
1868       G_TYPE_STRING,        /* icon */
1869       G_TYPE_STRING,        /* name */
1870       TP_TYPE_ACCOUNT,      /* account */
1871       TPL_TYPE_ENTITY,      /* target */
1872       TPL_TYPE_EVENT);      /* event */
1873
1874   model = GTK_TREE_MODEL (store);
1875   sortable = GTK_TREE_SORTABLE (store);
1876
1877   gtk_tree_view_set_model (view, model);
1878
1879   /* new column */
1880   column = gtk_tree_view_column_new ();
1881
1882   cell = gtk_cell_renderer_pixbuf_new ();
1883   gtk_tree_view_column_pack_start (column, cell, FALSE);
1884   gtk_tree_view_column_add_attribute (column, cell,
1885       "icon-name", COL_EVENTS_ICON);
1886
1887   cell = gtk_cell_renderer_text_new ();
1888   gtk_tree_view_column_pack_start (column, cell, TRUE);
1889   gtk_tree_view_column_add_attribute (column, cell,
1890       "markup", COL_EVENTS_TEXT);
1891
1892   cell = gtk_cell_renderer_text_new ();
1893   g_object_set (cell, "xalign", 1.0, NULL);
1894   gtk_tree_view_column_pack_end (column, cell, FALSE);
1895   gtk_tree_view_column_add_attribute (column, cell,
1896       "text", COL_EVENTS_PRETTY_DATE);
1897
1898   gtk_tree_view_append_column (view, column);
1899
1900   /* set up treeview properties */
1901   gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
1902   gtk_tree_view_set_headers_visible (view, FALSE);
1903
1904   gtk_tree_sortable_set_sort_column_id (sortable,
1905       COL_EVENTS_TS,
1906       GTK_SORT_ASCENDING);
1907
1908   gtk_tree_view_set_enable_search (view, FALSE);
1909
1910   g_object_unref (store);
1911 }
1912
1913 static void
1914 log_window_who_setup (EmpathyLogWindow *window)
1915 {
1916   GtkTreeView       *view;
1917   GtkTreeModel      *model;
1918   GtkTreeSelection  *selection;
1919   GtkTreeSortable   *sortable;
1920   GtkTreeViewColumn *column;
1921   GtkListStore      *store;
1922   GtkCellRenderer   *cell;
1923
1924   view = GTK_TREE_VIEW (window->treeview_who);
1925   selection = gtk_tree_view_get_selection (view);
1926
1927   /* new store */
1928   store = gtk_list_store_new (COL_WHO_COUNT,
1929       G_TYPE_INT,           /* type */
1930       G_TYPE_STRING,        /* icon */
1931       G_TYPE_STRING,        /* name */
1932       TP_TYPE_ACCOUNT,      /* account */
1933       TPL_TYPE_ENTITY);     /* target */
1934
1935   model = GTK_TREE_MODEL (store);
1936   sortable = GTK_TREE_SORTABLE (store);
1937
1938   gtk_tree_view_set_model (view, model);
1939
1940   /* new column */
1941   column = gtk_tree_view_column_new ();
1942   gtk_tree_view_column_set_title (column, _("Who"));
1943
1944   cell = gtk_cell_renderer_pixbuf_new ();
1945   gtk_tree_view_column_pack_start (column, cell, FALSE);
1946   gtk_tree_view_column_add_attribute (column, cell,
1947       "icon-name",
1948       COL_WHO_ICON);
1949
1950   cell = gtk_cell_renderer_text_new ();
1951   g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1952   gtk_tree_view_column_pack_start (column, cell, TRUE);
1953   gtk_tree_view_column_add_attribute (column, cell,
1954       "text",
1955       COL_WHO_NAME);
1956
1957   gtk_tree_view_append_column (view, column);
1958
1959   /* set up treeview properties */
1960   gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
1961   gtk_tree_view_set_row_separator_func (view, who_row_is_separator,
1962       NULL, NULL);
1963
1964   gtk_tree_sortable_set_sort_column_id (sortable,
1965       COL_WHO_NAME,
1966       GTK_SORT_ASCENDING);
1967   gtk_tree_sortable_set_sort_func (sortable,
1968       COL_WHO_NAME, sort_by_name,
1969       NULL, NULL);
1970
1971   gtk_tree_view_set_search_column (view, COL_WHO_NAME);
1972
1973   /* set up signals */
1974   g_signal_connect (selection, "changed",
1975       G_CALLBACK (log_window_who_changed_cb), window);
1976
1977   g_object_unref (store);
1978 }
1979
1980 static void
1981 log_window_chats_accounts_changed_cb (GtkWidget       *combobox,
1982     EmpathyLogWindow *window)
1983 {
1984   /* Clear all current messages shown in the textview */
1985   gtk_tree_store_clear (window->store_events);
1986
1987   log_window_who_populate (window);
1988 }
1989
1990 static void
1991 log_window_chats_set_selected (EmpathyLogWindow *window)
1992 {
1993   GtkTreeView          *view;
1994   GtkTreeModel         *model;
1995   GtkTreeSelection     *selection;
1996   GtkTreeIter           iter;
1997   GtkTreePath          *path;
1998   gboolean              next;
1999
2000   view = GTK_TREE_VIEW (window->treeview_who);
2001   model = gtk_tree_view_get_model (view);
2002   selection = gtk_tree_view_get_selection (view);
2003
2004   for (next = gtk_tree_model_get_iter_first (model, &iter);
2005        next;
2006        next = gtk_tree_model_iter_next (model, &iter))
2007     {
2008       TpAccount   *this_account;
2009       TplEntity   *this_target;
2010       const gchar *this_chat_id;
2011       gboolean     this_is_chatroom;
2012       gint         this_type;
2013
2014       gtk_tree_model_get (model, &iter,
2015           COL_WHO_TYPE, &this_type,
2016           COL_WHO_ACCOUNT, &this_account,
2017           COL_WHO_TARGET, &this_target,
2018           -1);
2019
2020       if (this_type != COL_TYPE_NORMAL)
2021         continue;
2022
2023       this_chat_id = tpl_entity_get_identifier (this_target);
2024       this_is_chatroom = tpl_entity_get_entity_type (this_target)
2025           == TPL_ENTITY_ROOM;
2026
2027       if (this_account == window->selected_account &&
2028           !tp_strdiff (this_chat_id, window->selected_chat_id) &&
2029           this_is_chatroom == window->selected_is_chatroom)
2030         {
2031           gtk_tree_selection_select_iter (selection, &iter);
2032           path = gtk_tree_model_get_path (model, &iter);
2033           gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 0.5, 0.0);
2034           gtk_tree_path_free (path);
2035           g_object_unref (this_account);
2036           g_object_unref (this_target);
2037           break;
2038         }
2039
2040       g_object_unref (this_account);
2041       g_object_unref (this_target);
2042     }
2043
2044   tp_clear_object (&window->selected_account);
2045   tp_clear_pointer (&window->selected_chat_id, g_free);
2046 }
2047
2048 static gint
2049 sort_by_date (GtkTreeModel *model,
2050     GtkTreeIter *a,
2051     GtkTreeIter *b,
2052     gpointer user_data)
2053 {
2054   GDate *date1, *date2;
2055
2056   gtk_tree_model_get (model, a,
2057       COL_WHEN_DATE, &date1,
2058       -1);
2059
2060   gtk_tree_model_get (model, b,
2061       COL_WHEN_DATE, &date2,
2062       -1);
2063
2064   return g_date_compare (date1, date2);
2065 }
2066
2067 static gboolean
2068 when_row_is_separator (GtkTreeModel *model,
2069     GtkTreeIter *iter,
2070     gpointer data)
2071 {
2072   gchar *when;
2073   gboolean ret;
2074
2075   gtk_tree_model_get (model, iter,
2076       COL_WHEN_TEXT, &when,
2077       -1);
2078
2079   ret = g_str_equal (when, "separator");
2080   g_free (when);
2081   return ret;
2082 }
2083
2084 static void
2085 log_window_when_changed_cb (GtkTreeSelection *selection,
2086     EmpathyLogWindow *window)
2087 {
2088   GtkTreeView *view;
2089   GtkTreeModel *model;
2090   GtkTreeIter iter;
2091
2092   DEBUG ("log_window_when_changed_cb");
2093
2094   view = gtk_tree_selection_get_tree_view (selection);
2095   model = gtk_tree_view_get_model (view);
2096
2097   /* If 'Anytime' is selected, everything else should be deselected */
2098   if (gtk_tree_model_get_iter_first (model, &iter))
2099     {
2100       if (gtk_tree_selection_iter_is_selected (selection, &iter))
2101         {
2102           g_signal_handlers_block_by_func (selection,
2103               log_window_when_changed_cb,
2104               window);
2105
2106           gtk_tree_selection_unselect_all (selection);
2107           gtk_tree_selection_select_iter (selection, &iter);
2108
2109           g_signal_handlers_unblock_by_func (selection,
2110               log_window_when_changed_cb,
2111               window);
2112         }
2113     }
2114
2115   log_window_chats_get_messages (window, FALSE);
2116 }
2117
2118 static void
2119 log_window_when_setup (EmpathyLogWindow *window)
2120 {
2121   GtkTreeView       *view;
2122   GtkTreeModel      *model;
2123   GtkTreeSelection  *selection;
2124   GtkTreeSortable   *sortable;
2125   GtkTreeViewColumn *column;
2126   GtkListStore      *store;
2127   GtkCellRenderer   *cell;
2128
2129   view = GTK_TREE_VIEW (window->treeview_when);
2130   selection = gtk_tree_view_get_selection (view);
2131
2132   /* new store */
2133   store = gtk_list_store_new (COL_WHEN_COUNT,
2134       G_TYPE_DATE,        /* date */
2135       G_TYPE_STRING,      /* stringified date */
2136       G_TYPE_STRING);     /* icon */
2137
2138   model = GTK_TREE_MODEL (store);
2139   sortable = GTK_TREE_SORTABLE (store);
2140
2141   gtk_tree_view_set_model (view, model);
2142
2143   /* new column */
2144   column = gtk_tree_view_column_new ();
2145   gtk_tree_view_column_set_title (column, _("When"));
2146
2147   cell = gtk_cell_renderer_pixbuf_new ();
2148   gtk_tree_view_column_pack_start (column, cell, FALSE);
2149   gtk_tree_view_column_add_attribute (column, cell,
2150       "icon-name", COL_WHEN_ICON);
2151
2152   cell = gtk_cell_renderer_text_new ();
2153   g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2154   gtk_tree_view_column_pack_start (column, cell, TRUE);
2155   gtk_tree_view_column_add_attribute (column, cell,
2156       "text",
2157       COL_WHEN_TEXT);
2158
2159   gtk_tree_view_append_column (view, column);
2160
2161   /* set up treeview properties */
2162   gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2163   gtk_tree_view_set_row_separator_func (view, when_row_is_separator,
2164       NULL, NULL);
2165   gtk_tree_sortable_set_sort_column_id (sortable,
2166       COL_WHEN_DATE,
2167       GTK_SORT_DESCENDING);
2168   gtk_tree_sortable_set_sort_func (sortable,
2169       COL_WHEN_DATE, sort_by_date,
2170       NULL, NULL);
2171
2172   gtk_tree_view_set_search_column (view, COL_WHEN_TEXT);
2173
2174   /* set up signals */
2175   g_signal_connect (selection, "changed",
2176       G_CALLBACK (log_window_when_changed_cb),
2177       window);
2178
2179   g_object_unref (store);
2180 }
2181
2182 static gboolean
2183 what_row_is_separator (GtkTreeModel *model,
2184     GtkTreeIter *iter,
2185     gpointer data)
2186 {
2187   gint type;
2188
2189   gtk_tree_model_get (model, iter,
2190       COL_WHAT_TYPE, &type,
2191       -1);
2192
2193   return (type == WHAT_TYPE_SEPARATOR);
2194 }
2195
2196 static void
2197 log_window_what_changed_cb (GtkTreeSelection *selection,
2198     EmpathyLogWindow *window)
2199 {
2200   GtkTreeView *view;
2201   GtkTreeModel *model;
2202   GtkTreeIter iter;
2203
2204   DEBUG ("log_window_what_changed_cb");
2205
2206   view = gtk_tree_selection_get_tree_view (selection);
2207   model = gtk_tree_view_get_model (view);
2208
2209   /* If 'Anything' is selected, everything else should be deselected */
2210   if (gtk_tree_model_get_iter_first (model, &iter))
2211     {
2212       if (gtk_tree_selection_iter_is_selected (selection, &iter))
2213         {
2214           g_signal_handlers_block_by_func (selection,
2215               log_window_what_changed_cb,
2216               window);
2217
2218           gtk_tree_selection_unselect_all (selection);
2219           gtk_tree_selection_select_iter (selection, &iter);
2220
2221           g_signal_handlers_unblock_by_func (selection,
2222               log_window_what_changed_cb,
2223               window);
2224         }
2225     }
2226
2227   /* The dates need to be updated if we're not searching */
2228   log_window_chats_get_messages (window, window->hits == NULL);
2229 }
2230
2231 static gboolean
2232 log_window_what_collapse_row_cb (GtkTreeView *tree_view,
2233     GtkTreeIter *iter,
2234     GtkTreePath *path,
2235     gpointer user_data)
2236 {
2237   /* Reject collapsing */
2238   return TRUE;
2239 }
2240
2241 struct event
2242 {
2243   gint type;
2244   EventSubtype subtype;
2245   const gchar *icon;
2246   const gchar *text;
2247 };
2248
2249 static void
2250 log_window_what_setup (EmpathyLogWindow *window)
2251 {
2252   GtkTreeView       *view;
2253   GtkTreeModel      *model;
2254   GtkTreeSelection  *selection;
2255   GtkTreeSortable   *sortable;
2256   GtkTreeViewColumn *column;
2257   GtkTreeIter        iter, parent;
2258   GtkTreeStore      *store;
2259   GtkCellRenderer   *cell;
2260   guint i;
2261   struct event events [] = {
2262     { TPL_EVENT_MASK_ANY, 0, NULL, _("Anything") },
2263     { WHAT_TYPE_SEPARATOR, 0, NULL, "separator" },
2264     { TPL_EVENT_MASK_TEXT, 0, "stock_text_justify", _("Text chats") },
2265     { TPL_EVENT_MASK_CALL, EVENT_CALL_ALL, "call-start", _("Calls") }
2266   };
2267   struct event call_events [] = {
2268     { TPL_EVENT_MASK_CALL, EVENT_CALL_INCOMING, "call-start", _("Incoming calls") },
2269     { TPL_EVENT_MASK_CALL, EVENT_CALL_OUTGOING, "call-start", _("Outgoing calls") },
2270     { TPL_EVENT_MASK_CALL, EVENT_CALL_MISSED, "call-stop", _("Missed calls") }
2271   };
2272
2273   view = GTK_TREE_VIEW (window->treeview_what);
2274   selection = gtk_tree_view_get_selection (view);
2275
2276   /* new store */
2277   store = gtk_tree_store_new (COL_WHAT_COUNT,
2278       G_TYPE_INT,         /* history type */
2279       G_TYPE_INT,         /* history subtype */
2280       G_TYPE_STRING,      /* stringified history type */
2281       G_TYPE_STRING);     /* icon */
2282
2283   model = GTK_TREE_MODEL (store);
2284   sortable = GTK_TREE_SORTABLE (store);
2285
2286   gtk_tree_view_set_model (view, model);
2287
2288   /* new column */
2289   column = gtk_tree_view_column_new ();
2290   gtk_tree_view_column_set_title (column, _("What"));
2291
2292   cell = gtk_cell_renderer_pixbuf_new ();
2293   gtk_tree_view_column_pack_start (column, cell, FALSE);
2294   gtk_tree_view_column_add_attribute (column, cell,
2295       "icon-name", COL_WHAT_ICON);
2296
2297   cell = gtk_cell_renderer_text_new ();
2298   g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2299   gtk_tree_view_column_pack_start (column, cell, TRUE);
2300   gtk_tree_view_column_add_attribute (column, cell,
2301       "text", COL_WHAT_TEXT);
2302
2303   gtk_tree_view_append_column (view, column);
2304   gtk_tree_view_set_search_column (view, COL_WHAT_TEXT);
2305
2306   /* set up treeview properties */
2307   gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2308   gtk_tree_view_set_show_expanders (view, FALSE);
2309   gtk_tree_view_set_level_indentation (view, 12);
2310   gtk_tree_view_expand_all (view);
2311   gtk_tree_view_set_row_separator_func (view, what_row_is_separator,
2312       NULL, NULL);
2313
2314   /* populate */
2315   for (i = 0; i < G_N_ELEMENTS (events); i++)
2316     {
2317       gtk_tree_store_append (store, &iter, NULL);
2318       gtk_tree_store_set (store, &iter,
2319           COL_WHAT_TYPE, events[i].type,
2320           COL_WHAT_SUBTYPE, events[i].subtype,
2321           COL_WHAT_TEXT, events[i].text,
2322           COL_WHAT_ICON, events[i].icon,
2323           -1);
2324     }
2325
2326   gtk_tree_model_iter_nth_child (model, &parent, NULL, 3);
2327   for (i = 0; i < G_N_ELEMENTS (call_events); i++)
2328     {
2329       gtk_tree_store_append (store, &iter, &parent);
2330       gtk_tree_store_set (store, &iter,
2331           COL_WHAT_TYPE, call_events[i].type,
2332           COL_WHAT_SUBTYPE, call_events[i].subtype,
2333           COL_WHAT_TEXT, call_events[i].text,
2334           COL_WHAT_ICON, call_events[i].icon,
2335           -1);
2336     }
2337
2338   gtk_tree_view_expand_all (view);
2339
2340   /* select 'Anything' */
2341   if (gtk_tree_model_get_iter_first (model, &iter))
2342     gtk_tree_selection_select_iter (selection, &iter);
2343
2344   /* set up signals */
2345   g_signal_connect (view, "test-collapse-row",
2346       G_CALLBACK (log_window_what_collapse_row_cb),
2347       NULL);
2348   g_signal_connect (selection, "changed",
2349       G_CALLBACK (log_window_what_changed_cb),
2350       window);
2351
2352   g_object_unref (store);
2353 }
2354
2355 static void
2356 start_spinner (void)
2357 {
2358   gtk_spinner_start (GTK_SPINNER (log_window->spinner));
2359   gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook),
2360       PAGE_EMPTY);
2361 }
2362
2363 static gboolean
2364 show_spinner (gpointer data)
2365 {
2366   gboolean active;
2367
2368   if (log_window == NULL)
2369     return FALSE;
2370
2371   g_object_get (log_window->spinner, "active", &active, NULL);
2372
2373   if (active)
2374     gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook),
2375         PAGE_SPINNER);
2376
2377   return FALSE;
2378 }
2379
2380 static void
2381 show_events (TplActionChain *chain,
2382     gpointer user_data)
2383 {
2384   gtk_spinner_stop (GTK_SPINNER (log_window->spinner));
2385   gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook),
2386       PAGE_EVENTS);
2387
2388   _tpl_action_chain_continue (chain);
2389 }
2390
2391 static void
2392 log_window_got_messages_for_date_cb (GObject *manager,
2393     GAsyncResult *result,
2394     gpointer user_data)
2395 {
2396   Ctx *ctx = user_data;
2397   GtkTreeView *view;
2398   GtkTreeModel *model;
2399   GtkTreeIter iter;
2400   GList *events;
2401   GList *l;
2402   GError *error = NULL;
2403   gint n;
2404
2405   if (log_window == NULL)
2406     {
2407       ctx_free (ctx);
2408       return;
2409     }
2410
2411   if (log_window->count != ctx->count)
2412     goto out;
2413
2414   if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager),
2415       result, &events, &error))
2416     {
2417       DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
2418           error->message);
2419       g_error_free (error);
2420       goto out;
2421     }
2422
2423   for (l = events; l; l = l->next)
2424     {
2425       TplEvent *event = l->data;
2426       gboolean append = TRUE;
2427
2428       if (TPL_IS_CALL_EVENT (l->data)
2429           && ctx->event_mask & TPL_EVENT_MASK_CALL
2430           && ctx->event_mask != TPL_EVENT_MASK_ANY)
2431         {
2432           TplCallEvent *call = l->data;
2433
2434           append = FALSE;
2435
2436           if (ctx->subtype & EVENT_CALL_ALL)
2437             {
2438               append = TRUE;
2439             }
2440           else
2441             {
2442               TplCallEndReason reason = tpl_call_event_get_end_reason (call);
2443               TplEntity *sender = tpl_event_get_sender (event);
2444               TplEntity *receiver = tpl_event_get_receiver (event);
2445
2446               if (reason == TPL_CALL_END_REASON_NO_ANSWER)
2447                 {
2448                   if (ctx->subtype & EVENT_CALL_MISSED)
2449                     append = TRUE;
2450                 }
2451               else if (ctx->subtype & EVENT_CALL_OUTGOING
2452                   && tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
2453                 {
2454                   append = TRUE;
2455                 }
2456               else if (ctx->subtype & EVENT_CALL_INCOMING
2457                   && tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
2458                 {
2459                   append = TRUE;
2460                 }
2461             }
2462         }
2463
2464       if (append)
2465         {
2466           EmpathyMessage *msg = empathy_message_from_tpl_log_event (event);
2467           log_window_append_message (event, msg);
2468           g_object_unref (msg);
2469         }
2470
2471       g_object_unref (event);
2472     }
2473   g_list_free (events);
2474
2475   view = GTK_TREE_VIEW (log_window->treeview_events);
2476   model = gtk_tree_view_get_model (view);
2477   n = gtk_tree_model_iter_n_children (model, NULL) - 1;
2478
2479   if (n >= 0 && gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
2480     {
2481       GtkTreePath *path;
2482
2483       path = gtk_tree_model_get_path (model, &iter);
2484       gtk_tree_view_scroll_to_cell (view, path, NULL, FALSE, 0, 0);
2485       gtk_tree_path_free (path);
2486     }
2487
2488  out:
2489   ctx_free (ctx);
2490
2491   _tpl_action_chain_continue (log_window->chain);
2492 }
2493
2494 static void
2495 get_events_for_date (TplActionChain *chain, gpointer user_data)
2496 {
2497   Ctx *ctx = user_data;
2498
2499   tpl_log_manager_get_events_for_date_async (ctx->window->log_manager,
2500       ctx->account, ctx->entity, ctx->event_mask,
2501       ctx->date,
2502       log_window_got_messages_for_date_cb,
2503       ctx);
2504 }
2505
2506 static void
2507 log_window_get_messages_for_dates (EmpathyLogWindow *window,
2508     GList *dates)
2509 {
2510   GList *accounts, *targets, *acc, *targ, *l;
2511   TplEventTypeMask event_mask;
2512   EventSubtype subtype;
2513   GDate *date, *anytime, *separator;
2514
2515   if (!log_window_get_selected (window,
2516       &accounts, &targets, NULL, &event_mask, &subtype))
2517     return;
2518
2519   anytime = g_date_new_dmy (2, 1, -1);
2520   separator = g_date_new_dmy (1, 1, -1);
2521
2522   _tpl_action_chain_clear (window->chain);
2523   window->count++;
2524
2525   for (acc = accounts, targ = targets;
2526        acc != NULL && targ != NULL;
2527        acc = acc->next, targ = targ->next)
2528     {
2529       TpAccount *account = acc->data;
2530       TplEntity *target = targ->data;
2531
2532       for (l = dates; l != NULL; l = l->next)
2533         {
2534           date = l->data;
2535
2536           /* Get events */
2537           if (g_date_compare (date, anytime) != 0)
2538             {
2539               Ctx *ctx;
2540
2541               ctx = ctx_new (window, account, target, date, event_mask, subtype,
2542                   window->count);
2543               _tpl_action_chain_append (window->chain, get_events_for_date, ctx);
2544             }
2545           else
2546             {
2547               GtkTreeView *view = GTK_TREE_VIEW (window->treeview_when);
2548               GtkTreeModel *model = gtk_tree_view_get_model (view);
2549               GtkTreeIter iter;
2550               gboolean next;
2551               GDate *d;
2552
2553               for (next = gtk_tree_model_get_iter_first (model, &iter);
2554                    next;
2555                    next = gtk_tree_model_iter_next (model, &iter))
2556                 {
2557                   Ctx *ctx;
2558
2559                   gtk_tree_model_get (model, &iter,
2560                       COL_WHEN_DATE, &d,
2561                       -1);
2562
2563                   if (g_date_compare (d, anytime) != 0 &&
2564                       g_date_compare (d, separator) != 0)
2565                     {
2566                       ctx = ctx_new (window, account, target, d,
2567                           event_mask, subtype, window->count);
2568                       _tpl_action_chain_append (window->chain, get_events_for_date, ctx);
2569                     }
2570                 }
2571             }
2572         }
2573     }
2574
2575   start_spinner ();
2576   g_timeout_add (1000, show_spinner, NULL);
2577   _tpl_action_chain_append (window->chain, show_events, NULL);
2578   _tpl_action_chain_start (window->chain);
2579
2580   g_list_free_full (accounts, g_object_unref);
2581   g_list_free_full (targets, g_object_unref);
2582   g_date_free (separator);
2583   g_date_free (anytime);
2584 }
2585
2586 static void
2587 log_manager_got_dates_cb (GObject *manager,
2588     GAsyncResult *result,
2589     gpointer user_data)
2590 {
2591   Ctx *ctx = user_data;
2592   GtkTreeView *view;
2593   GtkTreeModel *model;
2594   GtkTreeSelection *selection;
2595   GtkListStore *store;
2596   GtkTreeIter iter;
2597   GList *dates;
2598   GList *l;
2599   GError *error = NULL;
2600
2601   if (log_window == NULL)
2602     goto out;
2603
2604   if (log_window->count != ctx->count)
2605     goto out;
2606
2607   if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
2608        result, &dates, &error))
2609     {
2610       DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
2611           error->message);
2612       goto out;
2613     }
2614
2615   view = GTK_TREE_VIEW (log_window->treeview_when);
2616   model = gtk_tree_view_get_model (view);
2617   store = GTK_LIST_STORE (model);
2618   selection = gtk_tree_view_get_selection (view);
2619
2620   for (l = dates; l != NULL; l = l->next)
2621     {
2622       GDate *date = l->data;
2623
2624       /* Add the date if it's not already there */
2625       has_element = FALSE;
2626       gtk_tree_model_foreach (model, model_has_date, date);
2627       if (!has_element)
2628         {
2629           gchar *text = format_date_for_display (date);
2630
2631           gtk_list_store_append (store, &iter);
2632           gtk_list_store_set (store, &iter,
2633               COL_WHEN_DATE, date,
2634               COL_WHEN_TEXT, text,
2635               COL_WHEN_ICON, CALENDAR_ICON,
2636               -1);
2637
2638           g_free (text);
2639         }
2640     }
2641
2642   if (gtk_tree_model_get_iter_first (model, &iter))
2643     {
2644       gchar *separator = NULL;
2645
2646       if (gtk_tree_model_iter_next (model, &iter))
2647         {
2648           gtk_tree_model_get (model, &iter,
2649               COL_WHEN_TEXT, &separator,
2650               -1);
2651         }
2652
2653       if (g_strcmp0 (separator, "separator") != 0)
2654         {
2655           gtk_list_store_prepend (store, &iter);
2656           gtk_list_store_set (store, &iter,
2657               COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
2658               COL_WHEN_TEXT, "separator",
2659               -1);
2660
2661           gtk_list_store_prepend (store, &iter);
2662           gtk_list_store_set (store, &iter,
2663               COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
2664               COL_WHEN_TEXT, _("Anytime"),
2665               -1);
2666         }
2667     }
2668
2669   g_list_free_full (dates, g_free);
2670  out:
2671   ctx_free (ctx);
2672   _tpl_action_chain_continue (log_window->chain);
2673 }
2674
2675 static void
2676 select_first_date (TplActionChain *chain, gpointer user_data)
2677 {
2678   GtkTreeView *view;
2679   GtkTreeModel *model;
2680   GtkTreeSelection *selection;
2681   GtkTreeIter iter;
2682
2683   view = GTK_TREE_VIEW (log_window->treeview_when);
2684   model = gtk_tree_view_get_model (view);
2685   selection = gtk_tree_view_get_selection (view);
2686
2687   /* Show messages of the most recent date */
2688   if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 2))
2689     gtk_tree_selection_select_iter (selection, &iter);
2690
2691   _tpl_action_chain_continue (log_window->chain);
2692 }
2693
2694 static void
2695 get_dates_for_entity (TplActionChain *chain, gpointer user_data)
2696 {
2697   Ctx *ctx = user_data;
2698
2699   tpl_log_manager_get_dates_async (ctx->window->log_manager,
2700       ctx->account, ctx->entity, ctx->event_mask,
2701       log_manager_got_dates_cb, ctx);
2702 }
2703
2704 static void
2705 log_window_chats_get_messages (EmpathyLogWindow *window,
2706     gboolean force_get_dates)
2707 {
2708   GList *accounts, *targets, *dates;
2709   TplEventTypeMask event_mask;
2710   GtkTreeView *view;
2711   GtkTreeModel *model;
2712   GtkListStore *store;
2713   GtkTreeSelection *selection;
2714
2715   if (!log_window_get_selected (window, &accounts, &targets,
2716       &dates, &event_mask, NULL))
2717     return;
2718
2719   view = GTK_TREE_VIEW (window->treeview_when);
2720   selection = gtk_tree_view_get_selection (view);
2721   model = gtk_tree_view_get_model (view);
2722   store = GTK_LIST_STORE (model);
2723
2724   /* Clear all current messages shown in the textview */
2725   gtk_tree_store_clear (window->store_events);
2726
2727   _tpl_action_chain_clear (window->chain);
2728   window->count++;
2729
2730   /* If there's a search use the returned hits */
2731   if (window->hits != NULL)
2732     {
2733       if (force_get_dates)
2734         {
2735           g_signal_handlers_block_by_func (selection,
2736               log_window_when_changed_cb,
2737               window);
2738
2739           gtk_list_store_clear (store);
2740
2741           g_signal_handlers_unblock_by_func (selection,
2742               log_window_when_changed_cb,
2743               window);
2744
2745           populate_dates_from_search_hits (accounts, targets);
2746         }
2747       else
2748         {
2749           populate_events_from_search_hits (accounts, targets, dates);
2750         }
2751     }
2752   /* Either use the supplied date or get the last */
2753   else if (force_get_dates || dates == NULL)
2754     {
2755       GList *acc, *targ;
2756
2757       g_signal_handlers_block_by_func (selection,
2758           log_window_when_changed_cb,
2759           window);
2760
2761       gtk_list_store_clear (store);
2762
2763       g_signal_handlers_unblock_by_func (selection,
2764           log_window_when_changed_cb,
2765           window);
2766
2767       /* Get a list of dates and show them on the treeview */
2768       for (targ = targets, acc = accounts;
2769            targ != NULL && acc != NULL;
2770            targ = targ->next, acc = acc->next)
2771         {
2772           TpAccount *account = acc->data;
2773           TplEntity *target = targ->data;
2774           Ctx *ctx = ctx_new (window, account, target, NULL, event_mask, 0,
2775               window->count);
2776
2777           _tpl_action_chain_append (window->chain, get_dates_for_entity, ctx);
2778         }
2779       _tpl_action_chain_append (window->chain, select_first_date, NULL);
2780       _tpl_action_chain_start (window->chain);
2781     }
2782   else
2783     {
2784       /* Show messages of the selected date */
2785       log_window_get_messages_for_dates (window, dates);
2786     }
2787
2788   g_list_free_full (accounts, g_object_unref);
2789   g_list_free_full (targets, g_object_unref);
2790   g_list_free_full (dates, (GFreeFunc) g_date_free);
2791 }
2792
2793 typedef struct {
2794   EmpathyAccountChooserFilterResultCallback callback;
2795   gpointer user_data;
2796 } FilterCallbackData;
2797
2798 static void
2799 got_entities (GObject *manager,
2800     GAsyncResult *result,
2801     gpointer user_data)
2802 {
2803   FilterCallbackData *data = user_data;
2804   GList *entities;
2805   GError *error = NULL;
2806
2807   if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
2808       result, &entities, &error))
2809     {
2810       DEBUG ("Could not get entities: %s", error->message);
2811       g_error_free (error);
2812       data->callback (FALSE, data->user_data);
2813     }
2814   else
2815     {
2816       data->callback (entities != NULL, data->user_data);
2817
2818       g_list_free_full (entities, g_object_unref);
2819     }
2820
2821   g_slice_free (FilterCallbackData, data);
2822 }
2823
2824 static void
2825 empathy_account_chooser_filter_has_logs (TpAccount *account,
2826     EmpathyAccountChooserFilterResultCallback callback,
2827     gpointer callback_data,
2828     gpointer user_data)
2829 {
2830   TplLogManager *manager = tpl_log_manager_dup_singleton ();
2831   FilterCallbackData *cb_data = g_slice_new0 (FilterCallbackData);
2832
2833   cb_data->callback = callback;
2834   cb_data->user_data = callback_data;
2835
2836   tpl_log_manager_get_entities_async (manager, account, got_entities, cb_data);
2837
2838   g_object_unref (manager);
2839 }
2840
2841 static void
2842 log_window_logger_clear_account_cb (TpProxy *proxy,
2843     const GError *error,
2844     gpointer user_data,
2845     GObject *weak_object)
2846 {
2847   EmpathyLogWindow *window = user_data;
2848
2849   if (error != NULL)
2850     g_warning ("Error when clearing logs: %s", error->message);
2851
2852   /* Refresh the log viewer so the logs are cleared if the account
2853    * has been deleted */
2854   gtk_tree_store_clear (window->store_events);
2855   log_window_who_populate (window);
2856
2857   /* Re-filter the account chooser so the accounts without logs get greyed out */
2858   empathy_account_chooser_set_filter (
2859       EMPATHY_ACCOUNT_CHOOSER (window->account_chooser),
2860       empathy_account_chooser_filter_has_logs, NULL);
2861 }
2862
2863 static void
2864 log_window_clear_logs_chooser_select_account (EmpathyAccountChooser *chooser,
2865     EmpathyLogWindow *window)
2866 {
2867   EmpathyAccountChooser *account_chooser;
2868
2869   account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser);
2870
2871   empathy_account_chooser_set_account (chooser,
2872       empathy_account_chooser_get_account (account_chooser));
2873 }
2874
2875 static void
2876 log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
2877     EmpathyLogWindow *window)
2878 {
2879   GtkWidget *dialog, *content_area, *hbox, *label;
2880   EmpathyAccountChooser *account_chooser;
2881   gint response_id;
2882   TpDBusDaemon *bus;
2883   TpProxy *logger;
2884   GError *error = NULL;
2885
2886   account_chooser = (EmpathyAccountChooser *) empathy_account_chooser_new ();
2887   empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
2888   empathy_account_chooser_set_filter (account_chooser,
2889       empathy_account_chooser_filter_has_logs, NULL);
2890
2891   /* Select the same account as in the history window */
2892   if (empathy_account_chooser_is_ready (account_chooser))
2893     log_window_clear_logs_chooser_select_account (account_chooser, window);
2894   else
2895     g_signal_connect (account_chooser, "ready",
2896         G_CALLBACK (log_window_clear_logs_chooser_select_account), window);
2897
2898   dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window->window),
2899       GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING,
2900       GTK_BUTTONS_NONE,
2901       _("Are you sure you want to delete all logs of previous conversations?"));
2902
2903   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
2904       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2905       _("Clear All"), GTK_RESPONSE_APPLY,
2906       NULL);
2907
2908   content_area = gtk_message_dialog_get_message_area (
2909       GTK_MESSAGE_DIALOG (dialog));
2910
2911   hbox = gtk_hbox_new (FALSE, 6);
2912   label = gtk_label_new (_("Delete from:"));
2913   gtk_box_pack_start (GTK_BOX (hbox), label,
2914       FALSE, FALSE, 0);
2915   gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (account_chooser),
2916       FALSE, FALSE, 0);
2917   gtk_box_pack_start (GTK_BOX (content_area), hbox,
2918       FALSE, FALSE, 0);
2919
2920   gtk_widget_show_all (hbox);
2921
2922   response_id = gtk_dialog_run (GTK_DIALOG (dialog));
2923
2924   if (response_id != GTK_RESPONSE_APPLY)
2925     goto out;
2926
2927   bus = tp_dbus_daemon_dup (&error);
2928   if (error != NULL)
2929     {
2930       g_warning ("Could not delete logs: %s", error->message);
2931       g_error_free (error);
2932       goto out;
2933     }
2934
2935   logger = g_object_new (TP_TYPE_PROXY,
2936       "bus-name", "org.freedesktop.Telepathy.Logger",
2937       "object-path", "/org/freedesktop/Telepathy/Logger",
2938       "dbus-daemon", bus,
2939       NULL);
2940   g_object_unref (bus);
2941
2942   tp_proxy_add_interface_by_id (logger, EMP_IFACE_QUARK_LOGGER);
2943
2944   if (empathy_account_chooser_has_all_selected (account_chooser))
2945     {
2946       DEBUG ("Deleting logs for all the accounts");
2947
2948       emp_cli_logger_call_clear (logger, -1,
2949           log_window_logger_clear_account_cb,
2950           window, NULL, G_OBJECT (window->window));
2951     }
2952   else
2953     {
2954       TpAccount *account;
2955
2956       account = empathy_account_chooser_get_account (account_chooser);
2957
2958       DEBUG ("Deleting logs for %s", tp_proxy_get_object_path (account));
2959
2960       emp_cli_logger_call_clear_account (logger, -1,
2961           tp_proxy_get_object_path (account),
2962           log_window_logger_clear_account_cb,
2963           window, NULL, G_OBJECT (window->window));
2964     }
2965
2966   g_object_unref (logger);
2967  out:
2968   gtk_widget_destroy (dialog);
2969 }