2 * Copyright (C) 2006-2007 Imendio AB
3 * Copyright (C) 2007-2011 Collabora Ltd.
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.
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.
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
20 * Authors: Martyn Russell <martyn@imendio.com>
21 * Xavier Claessens <xclaesse@gmail.com>
22 * Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
30 #include <glib/gi18n-lib.h>
33 #include <telepathy-glib/telepathy-glib.h>
34 #include <telepathy-glib/proxy-subclass.h>
36 #include <telepathy-logger/telepathy-logger.h>
37 #include <telepathy-logger/call-event.h>
39 #include <extensions/extensions.h>
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>
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"
58 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
59 #include <libempathy/empathy-debug.h>
65 GtkWidget *button_profile;
66 GtkWidget *button_chat;
67 GtkWidget *button_call;
68 GtkWidget *button_video;
70 GtkWidget *search_entry;
72 GtkWidget *treeview_who;
73 GtkWidget *treeview_what;
74 GtkWidget *treeview_when;
75 GtkWidget *treeview_events;
77 GtkTreeStore *store_events;
79 GtkWidget *account_chooser;
83 TplActionChain *chain;
84 TplLogManager *log_manager;
86 /* Used to cancel logger calls when no longer needed */
89 /* List of owned TplLogSearchHits, free with tpl_log_search_hit_free */
93 /* Only used while waiting for the account chooser to be ready */
94 TpAccount *selected_account;
95 gchar *selected_chat_id;
96 gboolean selected_is_chatroom;
99 static void log_window_destroy_cb (GtkWidget *widget,
100 EmpathyLogWindow *window);
101 static void log_window_search_entry_changed_cb (GtkWidget *entry,
102 EmpathyLogWindow *window);
103 static void log_window_search_entry_activate_cb (GtkWidget *widget,
104 EmpathyLogWindow *window);
105 static void log_window_who_populate (EmpathyLogWindow *window);
106 static void log_window_who_setup (EmpathyLogWindow *window);
107 static void log_window_when_setup (EmpathyLogWindow *window);
108 static void log_window_what_setup (EmpathyLogWindow *window);
109 static void log_window_events_setup (EmpathyLogWindow *window);
110 static void log_window_chats_accounts_changed_cb (GtkWidget *combobox,
111 EmpathyLogWindow *window);
112 static void log_window_chats_set_selected (EmpathyLogWindow *window);
113 static void log_window_chats_get_messages (EmpathyLogWindow *window,
114 gboolean force_get_dates);
115 static void log_window_when_changed_cb (GtkTreeSelection *selection,
116 EmpathyLogWindow *window);
117 static void log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
118 EmpathyLogWindow *window);
121 empathy_account_chooser_filter_has_logs (TpAccount *account,
122 EmpathyAccountChooserFilterResultCallback callback,
123 gpointer callback_data,
165 COL_EVENTS_PRETTY_DATE,
174 #define CALENDAR_ICON "stock_calendar"
178 EVENT_CALL_INCOMING = 1 << 0,
179 EVENT_CALL_OUTGOING = 1 << 1,
180 EVENT_CALL_MISSED = 1 << 2,
181 EVENT_CALL_ALL = 1 << 3,
184 static EmpathyLogWindow *log_window = NULL;
186 static gboolean has_element;
189 #define _date_copy(d) g_date_new_julian (g_date_get_julian (d))
194 EmpathyLogWindow *window;
198 TplEventTypeMask event_mask;
199 EventSubtype subtype;
204 ctx_new (EmpathyLogWindow *window,
208 TplEventTypeMask event_mask,
209 EventSubtype subtype,
212 Ctx *ctx = g_slice_new0 (Ctx);
214 ctx->window = window;
216 ctx->account = g_object_ref (account);
218 ctx->entity = g_object_ref (entity);
220 ctx->date = _date_copy (date);
221 ctx->event_mask = event_mask;
222 ctx->subtype = subtype;
231 tp_clear_object (&ctx->account);
232 tp_clear_object (&ctx->entity);
233 if (ctx->date != NULL)
234 g_date_free (ctx->date);
236 g_slice_free (Ctx, ctx);
240 account_chooser_ready_cb (EmpathyAccountChooser *chooser,
241 EmpathyLogWindow *window)
243 /* We'll display the account once the model has been populate with the chats
244 * of this account. */
245 empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER (
246 window->account_chooser), window->selected_account);
250 select_account_once_ready (EmpathyLogWindow *self,
252 const gchar *chat_id,
253 gboolean is_chatroom)
255 EmpathyAccountChooser *account_chooser;
257 account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->account_chooser);
259 tp_clear_object (&self->selected_account);
260 self->selected_account = g_object_ref (account);
262 g_free (self->selected_chat_id);
263 self->selected_chat_id = g_strdup (chat_id);
265 self->selected_is_chatroom = is_chatroom;
267 if (empathy_account_chooser_is_ready (account_chooser))
268 account_chooser_ready_cb (account_chooser, self);
270 /* Chat will be selected once the account chooser is ready */
271 g_signal_connect (account_chooser, "ready",
272 G_CALLBACK (account_chooser_ready_cb), self);
276 toolbutton_profile_clicked (GtkToolButton *toolbutton,
277 EmpathyLogWindow *window)
280 GtkTreeSelection *selection;
285 EmpathyContact *contact;
288 g_return_if_fail (window != NULL);
290 view = GTK_TREE_VIEW (log_window->treeview_who);
291 selection = gtk_tree_view_get_selection (view);
293 if (gtk_tree_selection_get_selected (selection, &model, &iter))
295 gtk_tree_model_get (model, &iter,
296 COL_WHO_ACCOUNT, &account,
297 COL_WHO_TARGET, &target,
302 g_return_if_fail (type == COL_TYPE_NORMAL);
304 contact = empathy_contact_from_tpl_contact (account, target);
305 empathy_contact_information_dialog_show (contact,
306 GTK_WINDOW (window->window));
308 g_object_unref (contact);
309 g_object_unref (account);
310 g_object_unref (target);
314 toolbutton_chat_clicked (GtkToolButton *toolbutton,
315 EmpathyLogWindow *window)
318 GtkTreeSelection *selection;
323 EmpathyContact *contact;
326 g_return_if_fail (window != NULL);
328 view = GTK_TREE_VIEW (log_window->treeview_who);
329 selection = gtk_tree_view_get_selection (view);
331 if (gtk_tree_selection_get_selected (selection, &model, &iter))
333 gtk_tree_model_get (model, &iter,
334 COL_WHO_ACCOUNT, &account,
335 COL_WHO_TARGET, &target,
340 g_return_if_fail (type == COL_TYPE_NORMAL);
342 contact = empathy_contact_from_tpl_contact (account, target);
343 empathy_chat_with_contact (contact,
344 gtk_get_current_event_time ());
346 g_object_unref (contact);
347 g_object_unref (account);
348 g_object_unref (target);
352 toolbutton_av_clicked (GtkToolButton *toolbutton,
353 EmpathyLogWindow *window)
356 GtkTreeSelection *selection;
364 g_return_if_fail (window != NULL);
366 view = GTK_TREE_VIEW (log_window->treeview_who);
367 selection = gtk_tree_view_get_selection (view);
369 if (gtk_tree_selection_get_selected (selection, &model, &iter))
371 gtk_tree_model_get (model, &iter,
372 COL_WHO_ACCOUNT, &account,
373 COL_WHO_NAME, &contact,
378 g_return_if_fail (type == COL_TYPE_NORMAL);
380 video = (GTK_WIDGET (toolbutton) == window->button_video);
382 empathy_call_new_with_streams (contact, account,
383 TRUE, video, gtk_get_current_event_time ());
386 g_object_unref (account);
390 empathy_log_window_show (TpAccount *account,
391 const gchar *chat_id,
392 gboolean is_chatroom,
395 EmpathyAccountChooser *account_chooser;
398 EmpathyLogWindow *window;
399 GtkWidget *vbox, *accounts, *search, *label, *quit;
401 if (log_window != NULL)
403 gtk_window_present (GTK_WINDOW (log_window->window));
405 if (account != NULL && chat_id != NULL)
406 select_account_once_ready (log_window, account, chat_id, is_chatroom);
408 return log_window->window;
411 log_window = g_new0 (EmpathyLogWindow, 1);
412 log_window->chain = _tpl_action_chain_new_async (NULL, NULL, NULL);
414 log_window->log_manager = tpl_log_manager_dup_singleton ();
418 filename = empathy_file_lookup ("empathy-log-window.ui", "libempathy-gtk");
419 gui = empathy_builder_get_file (filename,
420 "log_window", &window->window,
421 "toolbutton_profile", &window->button_profile,
422 "toolbutton_chat", &window->button_chat,
423 "toolbutton_call", &window->button_call,
424 "toolbutton_video", &window->button_video,
425 "toolbutton_accounts", &accounts,
426 "toolbutton_search", &search,
427 "imagemenuitem_quit", &quit,
428 "treeview_who", &window->treeview_who,
429 "treeview_what", &window->treeview_what,
430 "treeview_when", &window->treeview_when,
431 "treeview_events", &window->treeview_events,
435 empathy_builder_connect (gui, window,
436 "log_window", "destroy", log_window_destroy_cb,
437 "toolbutton_profile", "clicked", toolbutton_profile_clicked,
438 "toolbutton_chat", "clicked", toolbutton_chat_clicked,
439 "toolbutton_call", "clicked", toolbutton_av_clicked,
440 "toolbutton_video", "clicked", toolbutton_av_clicked,
441 "imagemenuitem_delete", "activate", log_window_delete_menu_clicked_cb,
444 g_object_unref (gui);
446 g_object_add_weak_pointer (G_OBJECT (window->window),
447 (gpointer) &log_window);
449 g_signal_connect_swapped (quit, "activate",
450 G_CALLBACK (gtk_widget_destroy), window->window);
452 /* Account chooser for chats */
453 vbox = gtk_vbox_new (FALSE, 3);
455 window->account_chooser = empathy_account_chooser_new ();
456 account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser);
457 empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
458 empathy_account_chooser_set_filter (account_chooser,
459 empathy_account_chooser_filter_has_logs, NULL);
461 g_signal_connect (window->account_chooser, "changed",
462 G_CALLBACK (log_window_chats_accounts_changed_cb),
465 label = gtk_label_new (_("Show"));
467 gtk_box_pack_start (GTK_BOX (vbox),
468 window->account_chooser,
471 gtk_box_pack_start (GTK_BOX (vbox),
475 gtk_widget_show_all (vbox);
476 gtk_container_add (GTK_CONTAINER (accounts), vbox);
479 vbox = gtk_vbox_new (FALSE, 3);
481 window->search_entry = gtk_entry_new ();
482 gtk_entry_set_icon_from_stock (GTK_ENTRY (window->search_entry),
483 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
485 label = gtk_label_new (_("Search"));
487 gtk_box_pack_start (GTK_BOX (vbox),
488 window->search_entry,
491 gtk_box_pack_start (GTK_BOX (vbox),
495 gtk_widget_show_all (vbox);
496 gtk_container_add (GTK_CONTAINER (search), vbox);
498 g_signal_connect (window->search_entry, "changed",
499 G_CALLBACK (log_window_search_entry_changed_cb),
502 g_signal_connect (window->search_entry, "activate",
503 G_CALLBACK (log_window_search_entry_activate_cb),
507 log_window_events_setup (window);
508 log_window_who_setup (window);
509 log_window_what_setup (window);
510 log_window_when_setup (window);
512 log_window_who_populate (window);
514 if (account != NULL && chat_id != NULL)
515 select_account_once_ready (window, account, chat_id, is_chatroom);
518 gtk_window_set_transient_for (GTK_WINDOW (window->window),
519 GTK_WINDOW (parent));
521 gtk_widget_show (window->window);
523 return window->window;
527 log_window_destroy_cb (GtkWidget *widget,
528 EmpathyLogWindow *window)
530 if (window->source != 0)
531 g_source_remove (window->source);
533 g_free (window->last_find);
534 _tpl_action_chain_free (window->chain);
535 g_object_unref (window->log_manager);
536 tp_clear_object (&window->selected_account);
537 g_free (window->selected_chat_id);
543 account_equal (TpAccount *a,
546 return g_str_equal (tp_proxy_get_object_path (a),
547 tp_proxy_get_object_path (b));
551 entity_equal (TplEntity *a,
554 return g_str_equal (tpl_entity_get_identifier (a),
555 tpl_entity_get_identifier (b));
559 event_get_target (TplEvent *event)
561 TplEntity *sender = tpl_event_get_sender (event);
562 TplEntity *receiver = tpl_event_get_receiver (event);
564 if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
570 static gboolean parent_found;
571 static GtkTreeIter model_parent;
574 model_is_parent (GtkTreeModel *model,
579 TplEvent *event = data;
580 TplEvent *stored_event;
584 gboolean found = FALSE;
586 gtk_tree_model_get (model, iter,
587 COL_EVENTS_ACCOUNT, &account,
588 COL_EVENTS_TARGET, &target,
589 COL_EVENTS_TS, ×tamp,
590 COL_EVENTS_EVENT, &stored_event,
593 if (G_OBJECT_TYPE (event) == G_OBJECT_TYPE (stored_event) &&
594 account_equal (account, tpl_event_get_account (event)) &&
595 entity_equal (target, event_get_target (event)))
597 if (ABS (tpl_event_get_timestamp (event) - timestamp) < 1800)
599 /* The gap is smaller than 30 min */
600 model_parent = *iter;
601 parent_found = found = TRUE;
605 g_object_unref (stored_event);
606 g_object_unref (account);
607 g_object_unref (target);
613 get_contact_alias_for_message (EmpathyMessage *message)
615 EmpathyContact *sender, *receiver;
617 sender = empathy_message_get_sender (message);
618 receiver = empathy_message_get_receiver (message);
620 if (empathy_contact_is_user (sender))
621 return empathy_contact_get_alias (receiver);
623 return empathy_contact_get_alias (sender);
627 get_parent_iter_for_message (TplEvent *event,
628 EmpathyMessage *message,
634 store = log_window->store_events;
635 model = GTK_TREE_MODEL (store);
637 parent_found = FALSE;
638 gtk_tree_model_foreach (model, model_is_parent, event);
641 *parent = model_parent;
646 gchar *body, *pretty_date;
648 date = g_date_time_new_from_unix_utc (
649 tpl_event_get_timestamp (event));
651 pretty_date = g_date_time_format (date, "%x");
653 body = g_strdup_printf (_("Chat with %s"),
654 get_contact_alias_for_message (message));
656 gtk_tree_store_append (store, &iter, NULL);
657 gtk_tree_store_set (store, &iter,
658 COL_EVENTS_TS, tpl_event_get_timestamp (event),
659 COL_EVENTS_PRETTY_DATE, pretty_date,
660 COL_EVENTS_TEXT, body,
661 COL_EVENTS_ICON, "stock_text_justify",
662 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
663 COL_EVENTS_TARGET, event_get_target (event),
664 COL_EVENTS_EVENT, event,
670 g_free (pretty_date);
671 g_date_time_unref (date);
676 get_icon_for_event (TplEvent *event)
678 const gchar *icon = NULL;
680 if (TPL_IS_CALL_EVENT (event))
682 TplCallEvent *call = TPL_CALL_EVENT (event);
683 TplCallEndReason reason = tpl_call_event_get_end_reason (call);
684 TplEntity *sender = tpl_event_get_sender (event);
685 TplEntity *receiver = tpl_event_get_receiver (event);
687 if (reason == TPL_CALL_END_REASON_NO_ANSWER)
688 icon = EMPATHY_IMAGE_CALL_MISSED;
689 else if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
690 icon = EMPATHY_IMAGE_CALL_OUTGOING;
691 else if (tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
692 icon = EMPATHY_IMAGE_CALL_INCOMING;
699 log_window_append_chat_message (TplEvent *event,
700 EmpathyMessage *message)
702 GtkTreeStore *store = log_window->store_events;
703 GtkTreeIter iter, parent;
704 gchar *pretty_date, *body;
707 date = g_date_time_new_from_unix_utc (
708 tpl_event_get_timestamp (event));
710 pretty_date = g_date_time_format (date, "%x");
712 get_parent_iter_for_message (event, message, &parent);
714 body = g_strdup_printf (
715 C_("First is a contact, second is what he said", "%s: %s"),
716 tpl_entity_get_alias (tpl_event_get_sender (event)),
717 empathy_message_get_body (message));
719 gtk_tree_store_append (store, &iter, &parent);
720 gtk_tree_store_set (store, &iter,
721 COL_EVENTS_TS, tpl_event_get_timestamp (event),
722 COL_EVENTS_PRETTY_DATE, pretty_date,
723 COL_EVENTS_TEXT, body,
724 COL_EVENTS_ICON, get_icon_for_event (event),
725 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
726 COL_EVENTS_TARGET, event_get_target (event),
727 COL_EVENTS_EVENT, event,
731 g_free (pretty_date);
732 g_date_time_unref (date);
736 log_window_append_call (TplEvent *event,
737 EmpathyMessage *message)
739 TplCallEvent *call = TPL_CALL_EVENT (event);
740 GtkTreeStore *store = log_window->store_events;
741 GtkTreeIter iter, child;
742 gchar *pretty_date, *duration, *finished;
743 GDateTime *started_date, *finished_date;
746 started_date = g_date_time_new_from_unix_utc (
747 tpl_event_get_timestamp (event));
749 pretty_date = g_date_time_format (started_date, "%x");
751 gtk_tree_store_append (store, &iter, NULL);
752 gtk_tree_store_set (store, &iter,
753 COL_EVENTS_TS, tpl_event_get_timestamp (event),
754 COL_EVENTS_PRETTY_DATE, pretty_date,
755 COL_EVENTS_TEXT, empathy_message_get_body (message),
756 COL_EVENTS_ICON, get_icon_for_event (event),
757 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
758 COL_EVENTS_TARGET, event_get_target (event),
759 COL_EVENTS_EVENT, event,
762 if (tpl_call_event_get_end_reason (call) != TPL_CALL_END_REASON_NO_ANSWER)
766 span = tpl_call_event_get_duration (TPL_CALL_EVENT (event));
768 duration = g_strdup_printf (_("%" G_GINT64_FORMAT " seconds"), span);
770 duration = g_strdup_printf (_("%" G_GINT64_FORMAT " minutes"),
773 finished_date = g_date_time_add (started_date, -span);
774 finished = g_date_time_format (finished_date, "%X");
775 g_date_time_unref (finished_date);
777 body = g_strdup_printf (_("Call took %s, ended at %s"),
783 gtk_tree_store_append (store, &child, &iter);
784 gtk_tree_store_set (store, &child,
785 COL_EVENTS_TS, tpl_event_get_timestamp (event),
786 COL_EVENTS_TEXT, body,
787 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
788 COL_EVENTS_TARGET, event_get_target (event),
789 COL_EVENTS_EVENT, event,
795 g_free (pretty_date);
796 g_date_time_unref (started_date);
800 log_window_append_message (TplEvent *event,
801 EmpathyMessage *message)
803 if (TPL_IS_TEXT_EVENT (event))
804 log_window_append_chat_message (event, message);
805 else if (TPL_IS_CALL_EVENT (event))
806 log_window_append_call (event, message);
808 DEBUG ("Message type not handled");
812 add_all_accounts_and_entities (GList **accounts,
819 view = GTK_TREE_VIEW (log_window->treeview_who);
820 model = gtk_tree_view_get_model (view);
822 if (!gtk_tree_model_get_iter_first (model, &iter))
831 gtk_tree_model_get (model, &iter,
832 COL_WHO_ACCOUNT, &account,
833 COL_WHO_TARGET, &entity,
837 if (type != COL_TYPE_NORMAL)
840 if (accounts != NULL)
841 *accounts = g_list_append (*accounts, account);
843 if (entities != NULL)
844 *entities = g_list_append (*entities, entity);
846 while (gtk_tree_model_iter_next (model, &iter));
850 log_window_get_selected (EmpathyLogWindow *window,
854 TplEventTypeMask *event_mask,
855 EventSubtype *subtype)
859 GtkTreeSelection *selection;
861 TplEventTypeMask ev = 0;
866 view = GTK_TREE_VIEW (window->treeview_who);
867 model = gtk_tree_view_get_model (view);
868 selection = gtk_tree_view_get_selection (view);
870 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
879 for (l = paths; l != NULL; l = l->next)
881 GtkTreePath *path = l->data;
885 gtk_tree_model_get_iter (model, &iter, path);
886 gtk_tree_model_get (model, &iter,
887 COL_WHO_ACCOUNT, &account,
888 COL_WHO_TARGET, &entity,
892 if (type == COL_TYPE_ANY)
894 if (accounts != NULL || entities != NULL)
895 add_all_accounts_and_entities (accounts, entities);
899 if (accounts != NULL)
900 *accounts = g_list_append (*accounts, g_object_ref (account));
902 if (entities != NULL)
903 *entities = g_list_append (*entities, g_object_ref (entity));
905 g_object_unref (account);
906 g_object_unref (entity);
908 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
910 view = GTK_TREE_VIEW (window->treeview_what);
911 model = gtk_tree_view_get_model (view);
912 selection = gtk_tree_view_get_selection (view);
914 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
915 for (l = paths; l != NULL; l = l->next)
917 GtkTreePath *path = l->data;
918 TplEventTypeMask mask;
919 EventSubtype submask;
921 gtk_tree_model_get_iter (model, &iter, path);
922 gtk_tree_model_get (model, &iter,
923 COL_WHAT_TYPE, &mask,
924 COL_WHAT_SUBTYPE, &submask,
930 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
932 view = GTK_TREE_VIEW (window->treeview_when);
933 model = gtk_tree_view_get_model (view);
934 selection = gtk_tree_view_get_selection (view);
940 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
941 for (l = paths; l != NULL; l = l->next)
943 GtkTreePath *path = l->data;
946 gtk_tree_model_get_iter (model, &iter, path);
947 gtk_tree_model_get (model, &iter,
948 COL_WHEN_DATE, &date,
951 *dates = g_list_append (*dates, date);
955 if (event_mask != NULL)
965 model_has_entity (GtkTreeModel *model,
970 TplLogSearchHit *hit = data;
974 gtk_tree_model_get (model, iter,
979 if (e != NULL && entity_equal (hit->target, e) &&
980 a != NULL && account_equal (hit->account, a))
990 model_has_date (GtkTreeModel *model,
998 gtk_tree_model_get (model, iter,
1002 if (!g_date_compare (date, d))
1012 get_events_for_date (TplActionChain *chain, gpointer user_data);
1015 populate_events_from_search_hits (GList *accounts,
1019 TplEventTypeMask event_mask;
1020 EventSubtype subtype;
1023 gboolean is_anytime = FALSE;
1025 if (!log_window_get_selected (log_window,
1026 NULL, NULL, NULL, &event_mask, &subtype))
1029 anytime = g_date_new_dmy (2, 1, -1);
1030 if (g_list_find_custom (dates, anytime, (GCompareFunc) g_date_compare))
1033 for (l = log_window->hits; l != NULL; l = l->next)
1035 TplLogSearchHit *hit = l->data;
1037 gboolean found = FALSE;
1039 /* Protect against invalid data (corrupt or old log files). */
1040 if (hit->account == NULL || hit->target == NULL)
1043 for (acc = accounts, targ = targets;
1044 acc != NULL && targ != NULL && !found;
1045 acc = acc->next, targ = targ->next)
1047 TpAccount *account = acc->data;
1048 TplEntity *target = targ->data;
1050 if (account_equal (hit->account, account) &&
1051 entity_equal (hit->target, target))
1059 g_list_find_custom (dates, hit->date, (GCompareFunc) g_date_compare)
1064 ctx = ctx_new (log_window, hit->account, hit->target, hit->date,
1065 event_mask, subtype, log_window->count);
1066 _tpl_action_chain_append (log_window->chain,
1067 get_events_for_date, ctx);
1071 _tpl_action_chain_start (log_window->chain);
1073 g_date_free (anytime);
1077 format_date_for_display (GDate *date)
1083 /* g_date_strftime sucks */
1085 now = g_date_new ();
1086 g_date_set_time_t (now, time (NULL));
1088 days_elapsed = g_date_days_between (date, now);
1090 if (days_elapsed < 0)
1092 else if (days_elapsed == 0)
1093 text = g_strdup (_("Today"));
1094 else if (days_elapsed == 1)
1095 text = g_strdup (_("Yesterday"));
1100 dt = g_date_time_new_utc (g_date_get_year (date),
1101 g_date_get_month (date), g_date_get_day (date),
1104 if (days_elapsed <= 7)
1105 text = g_date_time_format (dt, "%A");
1107 text = g_date_time_format (dt,
1108 C_("A date such as '23 May 2010', "
1109 "%e is the day, %B the month and %Y the year",
1112 g_date_time_unref (dt);
1121 populate_dates_from_search_hits (GList *accounts,
1126 GtkTreeModel *model;
1127 GtkListStore *store;
1128 GtkTreeSelection *selection;
1131 if (log_window == NULL)
1134 view = GTK_TREE_VIEW (log_window->treeview_when);
1135 model = gtk_tree_view_get_model (view);
1136 store = GTK_LIST_STORE (model);
1137 selection = gtk_tree_view_get_selection (view);
1139 for (l = log_window->hits; l != NULL; l = l->next)
1141 TplLogSearchHit *hit = l->data;
1143 gboolean found = FALSE;
1145 /* Protect against invalid data (corrupt or old log files). */
1146 if (hit->account == NULL || hit->target == NULL)
1149 for (acc = accounts, targ = targets;
1150 acc != NULL && targ != NULL && !found;
1151 acc = acc->next, targ = targ->next)
1153 TpAccount *account = acc->data;
1154 TplEntity *target = targ->data;
1156 if (account_equal (hit->account, account) &&
1157 entity_equal (hit->target, target))
1164 /* Add the date if it's not already there */
1165 has_element = FALSE;
1166 gtk_tree_model_foreach (model, model_has_date, hit->date);
1169 gchar *text = format_date_for_display (hit->date);
1171 gtk_list_store_append (store, &iter);
1172 gtk_list_store_set (store, &iter,
1173 COL_WHEN_DATE, hit->date,
1174 COL_WHEN_TEXT, text,
1175 COL_WHEN_ICON, CALENDAR_ICON,
1180 if (gtk_tree_model_get_iter_first (model, &iter))
1182 gtk_list_store_prepend (store, &iter);
1183 gtk_list_store_set (store, &iter,
1184 COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
1185 COL_WHEN_TEXT, "separator",
1188 gtk_list_store_prepend (store, &iter);
1189 gtk_list_store_set (store, &iter,
1190 COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
1191 COL_WHEN_TEXT, _("Anytime"),
1194 gtk_tree_selection_select_iter (selection, &iter);
1199 populate_entities_from_search_hits (void)
1201 EmpathyAccountChooser *account_chooser;
1204 GtkTreeModel *model;
1206 GtkListStore *store;
1209 view = GTK_TREE_VIEW (log_window->treeview_who);
1210 model = gtk_tree_view_get_model (view);
1211 store = GTK_LIST_STORE (model);
1213 gtk_list_store_clear (store);
1215 account_chooser = EMPATHY_ACCOUNT_CHOOSER (log_window->account_chooser);
1216 account = empathy_account_chooser_get_account (account_chooser);
1218 for (l = log_window->hits; l; l = l->next)
1220 TplLogSearchHit *hit = l->data;
1222 /* Protect against invalid data (corrupt or old log files). */
1223 if (hit->account == NULL || hit->target == NULL)
1226 /* Filter based on the selected account */
1227 if (account != NULL && !account_equal (account, hit->account))
1230 /* Add the entity if it's not already there */
1231 has_element = FALSE;
1232 gtk_tree_model_foreach (model, model_has_entity, hit);
1235 TplEntityType type = tpl_entity_get_entity_type (hit->target);
1236 gboolean room = type == TPL_ENTITY_ROOM;
1238 gtk_list_store_append (store, &iter);
1239 gtk_list_store_set (store, &iter,
1240 COL_WHO_TYPE, COL_TYPE_NORMAL,
1241 COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
1242 : EMPATHY_IMAGE_AVATAR_DEFAULT,
1243 COL_WHO_NAME, tpl_entity_get_alias (hit->target),
1244 COL_WHO_ACCOUNT, hit->account,
1245 COL_WHO_TARGET, hit->target,
1250 if (gtk_tree_model_get_iter_first (model, &iter))
1252 gtk_list_store_prepend (store, &iter);
1253 gtk_list_store_set (store, &iter,
1254 COL_WHO_TYPE, COL_TYPE_SEPARATOR,
1255 COL_WHO_NAME, "separator",
1258 gtk_list_store_prepend (store, &iter);
1259 gtk_list_store_set (store, &iter,
1260 COL_WHO_TYPE, COL_TYPE_ANY,
1261 COL_WHO_NAME, _("Anyone"),
1265 /* FIXME: select old entity if still available */
1269 log_manager_searched_new_cb (GObject *manager,
1270 GAsyncResult *result,
1275 GtkTreeSelection *selection;
1276 GError *error = NULL;
1278 if (log_window == NULL)
1281 if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager),
1282 result, &hits, &error))
1284 DEBUG ("%s. Aborting", error->message);
1285 g_error_free (error);
1289 tp_clear_pointer (&log_window->hits, tpl_log_manager_search_free);
1290 log_window->hits = hits;
1292 populate_entities_from_search_hits ();
1294 view = GTK_TREE_VIEW (log_window->treeview_when);
1295 selection = gtk_tree_view_get_selection (view);
1297 g_signal_handlers_unblock_by_func (selection,
1298 log_window_when_changed_cb,
1303 log_window_find_populate (EmpathyLogWindow *window,
1304 const gchar *search_criteria)
1307 GtkTreeModel *model;
1308 GtkTreeSelection *selection;
1309 GtkListStore *store;
1311 gtk_tree_store_clear (window->store_events);
1313 view = GTK_TREE_VIEW (window->treeview_who);
1314 model = gtk_tree_view_get_model (view);
1315 store = GTK_LIST_STORE (model);
1317 gtk_list_store_clear (store);
1319 view = GTK_TREE_VIEW (window->treeview_when);
1320 model = gtk_tree_view_get_model (view);
1321 store = GTK_LIST_STORE (model);
1322 selection = gtk_tree_view_get_selection (view);
1324 gtk_list_store_clear (store);
1326 if (EMP_STR_EMPTY (search_criteria))
1328 tp_clear_pointer (&window->hits, tpl_log_manager_search_free);
1329 log_window_who_populate (window);
1333 g_signal_handlers_block_by_func (selection,
1334 log_window_when_changed_cb,
1337 tpl_log_manager_search_async (window->log_manager,
1338 search_criteria, TPL_EVENT_MASK_ANY,
1339 log_manager_searched_new_cb, NULL);
1343 start_find_search (EmpathyLogWindow *window)
1347 str = gtk_entry_get_text (GTK_ENTRY (window->search_entry));
1349 /* Don't find the same crap again */
1350 if (window->last_find && !tp_strdiff (window->last_find, str))
1353 g_free (window->last_find);
1354 window->last_find = g_strdup (str);
1356 log_window_find_populate (window, str);
1362 log_window_search_entry_changed_cb (GtkWidget *entry,
1363 EmpathyLogWindow *window)
1365 if (window->source != 0)
1366 g_source_remove (window->source);
1367 window->source = g_timeout_add (500, (GSourceFunc) start_find_search,
1372 log_window_search_entry_activate_cb (GtkWidget *entry,
1373 EmpathyLogWindow *self)
1375 start_find_search (self);
1383 log_window_who_changed_cb (GtkTreeSelection *selection,
1384 EmpathyLogWindow *window)
1387 GtkTreeModel *model;
1389 gboolean someone = FALSE;
1391 DEBUG ("log_window_who_changed_cb");
1393 view = gtk_tree_selection_get_tree_view (selection);
1394 model = gtk_tree_view_get_model (view);
1396 if (gtk_tree_model_get_iter_first (model, &iter))
1398 /* If 'Anyone' is selected, everything else should be deselected */
1399 if (gtk_tree_selection_iter_is_selected (selection, &iter))
1401 g_signal_handlers_block_by_func (selection,
1402 log_window_who_changed_cb,
1405 gtk_tree_selection_unselect_all (selection);
1406 gtk_tree_selection_select_iter (selection, &iter);
1408 g_signal_handlers_unblock_by_func (selection,
1409 log_window_who_changed_cb,
1412 else if (gtk_tree_selection_count_selected_rows (selection) == 1)
1416 gtk_widget_set_sensitive (window->button_profile, someone);
1417 gtk_widget_set_sensitive (window->button_chat, someone);
1418 gtk_widget_set_sensitive (window->button_call, someone);
1419 gtk_widget_set_sensitive (window->button_video, someone);
1421 /* The contact changed, so the dates need to be updated */
1422 log_window_chats_get_messages (window, TRUE);
1426 log_manager_got_entities_cb (GObject *manager,
1427 GAsyncResult *result,
1430 Ctx *ctx = user_data;
1434 GtkTreeModel *model;
1435 GtkTreeSelection *selection;
1436 GtkListStore *store;
1438 GError *error = NULL;
1439 gboolean select_account = FALSE;
1441 if (log_window == NULL)
1444 if (log_window->count != ctx->count)
1447 if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
1448 result, &entities, &error))
1450 DEBUG ("%s. Aborting", error->message);
1451 g_error_free (error);
1455 view = GTK_TREE_VIEW (ctx->window->treeview_who);
1456 model = gtk_tree_view_get_model (view);
1457 selection = gtk_tree_view_get_selection (view);
1458 store = GTK_LIST_STORE (model);
1460 /* Block signals to stop the logs being retrieved prematurely */
1461 g_signal_handlers_block_by_func (selection,
1462 log_window_who_changed_cb, ctx->window);
1464 for (l = entities; l; l = l->next)
1466 TplEntity *entity = TPL_ENTITY (l->data);
1467 TplEntityType type = tpl_entity_get_entity_type (entity);
1468 gboolean room = type == TPL_ENTITY_ROOM;
1470 gtk_list_store_append (store, &iter);
1471 gtk_list_store_set (store, &iter,
1472 COL_WHO_TYPE, COL_TYPE_NORMAL,
1473 COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
1474 : EMPATHY_IMAGE_AVATAR_DEFAULT,
1475 COL_WHO_NAME, tpl_entity_get_alias (entity),
1476 COL_WHO_ACCOUNT, ctx->account,
1477 COL_WHO_TARGET, entity,
1480 if (ctx->window->selected_account != NULL &&
1481 !tp_strdiff (tp_proxy_get_object_path (ctx->account),
1482 tp_proxy_get_object_path (ctx->window->selected_account)))
1483 select_account = TRUE;
1485 g_list_free_full (entities, g_object_unref);
1487 if (gtk_tree_model_get_iter_first (model, &iter))
1491 gtk_tree_model_get (model, &iter,
1492 COL_WHO_TYPE, &type,
1495 if (type != COL_TYPE_ANY)
1497 gtk_list_store_prepend (store, &iter);
1498 gtk_list_store_set (store, &iter,
1499 COL_WHO_TYPE, COL_TYPE_SEPARATOR,
1500 COL_WHO_NAME, "separator",
1503 gtk_list_store_prepend (store, &iter);
1504 gtk_list_store_set (store, &iter,
1505 COL_WHO_TYPE, COL_TYPE_ANY,
1506 COL_WHO_NAME, _("Anyone"),
1511 /* Unblock signals */
1512 g_signal_handlers_unblock_by_func (selection,
1513 log_window_who_changed_cb,
1516 /* We display the selected account if we populate the model with chats from
1519 log_window_chats_set_selected (ctx->window);
1522 _tpl_action_chain_continue (log_window->chain);
1527 get_entities_for_account (TplActionChain *chain, gpointer user_data)
1529 Ctx *ctx = user_data;
1531 tpl_log_manager_get_entities_async (ctx->window->log_manager, ctx->account,
1532 log_manager_got_entities_cb, ctx);
1536 select_first_entity (TplActionChain *chain, gpointer user_data)
1539 GtkTreeModel *model;
1540 GtkTreeSelection *selection;
1543 view = GTK_TREE_VIEW (log_window->treeview_who);
1544 model = gtk_tree_view_get_model (view);
1545 selection = gtk_tree_view_get_selection (view);
1547 if (gtk_tree_model_get_iter_first (model, &iter))
1548 gtk_tree_selection_select_iter (selection, &iter);
1550 _tpl_action_chain_continue (log_window->chain);
1554 log_window_who_populate (EmpathyLogWindow *window)
1556 EmpathyAccountChooser *account_chooser;
1558 gboolean all_accounts;
1560 GtkTreeModel *model;
1561 GtkTreeSelection *selection;
1562 GtkListStore *store;
1565 if (window->hits != NULL)
1567 populate_entities_from_search_hits ();
1571 account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser);
1572 account = empathy_account_chooser_dup_account (account_chooser);
1573 all_accounts = empathy_account_chooser_has_all_selected (account_chooser);
1575 view = GTK_TREE_VIEW (window->treeview_who);
1576 model = gtk_tree_view_get_model (view);
1577 selection = gtk_tree_view_get_selection (view);
1578 store = GTK_LIST_STORE (model);
1580 /* Block signals to stop the logs being retrieved prematurely */
1581 g_signal_handlers_block_by_func (selection,
1582 log_window_who_changed_cb,
1585 gtk_list_store_clear (store);
1587 /* Unblock signals */
1588 g_signal_handlers_unblock_by_func (selection,
1589 log_window_who_changed_cb,
1592 _tpl_action_chain_clear (window->chain);
1595 if (!all_accounts && account == NULL)
1599 else if (!all_accounts)
1601 ctx = ctx_new (window, account, NULL, NULL, 0, 0, window->count);
1602 _tpl_action_chain_append (window->chain, get_entities_for_account, ctx);
1606 TpAccountManager *manager;
1607 GList *accounts, *l;
1609 manager = empathy_account_chooser_get_account_manager (account_chooser);
1610 accounts = tp_account_manager_get_valid_accounts (manager);
1612 for (l = accounts; l != NULL; l = l->next)
1616 ctx = ctx_new (window, account, NULL, NULL, 0, 0, window->count);
1617 _tpl_action_chain_append (window->chain,
1618 get_entities_for_account, ctx);
1621 g_list_free (accounts);
1623 _tpl_action_chain_append (window->chain, select_first_entity, NULL);
1624 _tpl_action_chain_start (window->chain);
1628 sort_by_name (GtkTreeModel *model,
1633 gchar *name1, *name2;
1637 gtk_tree_model_get (model, a,
1638 COL_WHO_TYPE, &type1,
1639 COL_WHO_NAME, &name1,
1642 gtk_tree_model_get (model, b,
1643 COL_WHO_TYPE, &type2,
1644 COL_WHO_NAME, &name2,
1647 if (type1 == COL_TYPE_ANY)
1649 else if (type2 == COL_TYPE_ANY)
1651 else if (type1 == COL_TYPE_SEPARATOR)
1653 else if (type2 == COL_TYPE_SEPARATOR)
1656 ret = g_strcmp0 (name1, name2);
1665 who_row_is_separator (GtkTreeModel *model,
1671 gtk_tree_model_get (model, iter,
1672 COL_WHO_TYPE, &type,
1675 return (type == COL_TYPE_SEPARATOR);
1679 log_window_events_setup (EmpathyLogWindow *window)
1682 GtkTreeModel *model;
1683 GtkTreeSelection *selection;
1684 GtkTreeSortable *sortable;
1685 GtkTreeViewColumn *column;
1686 GtkTreeStore *store;
1687 GtkCellRenderer *cell;
1689 view = GTK_TREE_VIEW (window->treeview_events);
1690 selection = gtk_tree_view_get_selection (view);
1693 window->store_events = store = gtk_tree_store_new (COL_EVENTS_COUNT,
1694 G_TYPE_INT, /* type */
1695 G_TYPE_INT64, /* timestamp */
1696 G_TYPE_STRING, /* stringified date */
1697 G_TYPE_STRING, /* icon */
1698 G_TYPE_STRING, /* name */
1699 TP_TYPE_ACCOUNT, /* account */
1700 TPL_TYPE_ENTITY, /* target */
1701 TPL_TYPE_EVENT); /* event */
1703 model = GTK_TREE_MODEL (store);
1704 sortable = GTK_TREE_SORTABLE (store);
1706 gtk_tree_view_set_model (view, model);
1709 column = gtk_tree_view_column_new ();
1711 cell = gtk_cell_renderer_pixbuf_new ();
1712 gtk_tree_view_column_pack_start (column, cell, FALSE);
1713 gtk_tree_view_column_add_attribute (column, cell,
1714 "icon-name", COL_EVENTS_ICON);
1716 cell = gtk_cell_renderer_text_new ();
1717 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1718 gtk_tree_view_column_pack_start (column, cell, TRUE);
1719 gtk_tree_view_column_add_attribute (column, cell,
1720 "text", COL_EVENTS_TEXT);
1722 cell = gtk_cell_renderer_text_new ();
1723 gtk_tree_view_column_pack_end (column, cell, FALSE);
1724 gtk_tree_view_column_add_attribute (column, cell,
1725 "text", COL_EVENTS_PRETTY_DATE);
1727 gtk_tree_view_append_column (view, column);
1729 /* set up treeview properties */
1730 gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
1731 gtk_tree_view_set_headers_visible (view, FALSE);
1733 gtk_tree_sortable_set_sort_column_id (sortable,
1735 GTK_SORT_ASCENDING);
1737 g_object_unref (store);
1741 log_window_who_setup (EmpathyLogWindow *window)
1744 GtkTreeModel *model;
1745 GtkTreeSelection *selection;
1746 GtkTreeSortable *sortable;
1747 GtkTreeViewColumn *column;
1748 GtkListStore *store;
1749 GtkCellRenderer *cell;
1751 view = GTK_TREE_VIEW (window->treeview_who);
1752 selection = gtk_tree_view_get_selection (view);
1755 store = gtk_list_store_new (COL_WHO_COUNT,
1756 G_TYPE_INT, /* type */
1757 G_TYPE_STRING, /* icon */
1758 G_TYPE_STRING, /* name */
1759 TP_TYPE_ACCOUNT, /* account */
1760 TPL_TYPE_ENTITY); /* target */
1762 model = GTK_TREE_MODEL (store);
1763 sortable = GTK_TREE_SORTABLE (store);
1765 gtk_tree_view_set_model (view, model);
1768 column = gtk_tree_view_column_new ();
1769 gtk_tree_view_column_set_title (column, _("Who"));
1771 cell = gtk_cell_renderer_pixbuf_new ();
1772 gtk_tree_view_column_pack_start (column, cell, FALSE);
1773 gtk_tree_view_column_add_attribute (column, cell,
1777 cell = gtk_cell_renderer_text_new ();
1778 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1779 gtk_tree_view_column_pack_start (column, cell, TRUE);
1780 gtk_tree_view_column_add_attribute (column, cell,
1784 gtk_tree_view_append_column (view, column);
1786 /* set up treeview properties */
1787 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
1788 gtk_tree_view_set_row_separator_func (view, who_row_is_separator,
1791 gtk_tree_sortable_set_sort_column_id (sortable,
1793 GTK_SORT_ASCENDING);
1794 gtk_tree_sortable_set_sort_func (sortable,
1795 COL_WHO_NAME, sort_by_name,
1798 /* set up signals */
1799 g_signal_connect (selection, "changed",
1800 G_CALLBACK (log_window_who_changed_cb), window);
1802 g_object_unref (store);
1806 log_window_chats_accounts_changed_cb (GtkWidget *combobox,
1807 EmpathyLogWindow *window)
1809 /* Clear all current messages shown in the textview */
1810 gtk_tree_store_clear (window->store_events);
1812 log_window_who_populate (window);
1816 log_window_chats_set_selected (EmpathyLogWindow *window)
1819 GtkTreeModel *model;
1820 GtkTreeSelection *selection;
1824 view = GTK_TREE_VIEW (window->treeview_who);
1825 model = gtk_tree_view_get_model (view);
1826 selection = gtk_tree_view_get_selection (view);
1828 if (!gtk_tree_model_get_iter_first (model, &iter))
1833 TpAccount *this_account;
1834 TplEntity *this_target;
1835 const gchar *this_chat_id;
1836 gboolean this_is_chatroom;
1838 gtk_tree_model_get (model, &iter,
1839 COL_WHO_ACCOUNT, &this_account,
1840 COL_WHO_TARGET, &this_target,
1843 this_chat_id = tpl_entity_get_identifier (this_target);
1844 this_is_chatroom = tpl_entity_get_entity_type (this_target)
1847 if (this_account == window->selected_account &&
1848 !tp_strdiff (this_chat_id, window->selected_chat_id) &&
1849 this_is_chatroom == window->selected_is_chatroom)
1851 gtk_tree_selection_select_iter (selection, &iter);
1852 path = gtk_tree_model_get_path (model, &iter);
1853 gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 0.5, 0.0);
1854 gtk_tree_path_free (path);
1855 g_object_unref (this_account);
1856 g_object_unref (this_target);
1860 g_object_unref (this_account);
1861 g_object_unref (this_target);
1863 while (gtk_tree_model_iter_next (model, &iter));
1865 tp_clear_object (&window->selected_account);
1866 tp_clear_pointer (&window->selected_chat_id, g_free);
1870 sort_by_date (GtkTreeModel *model,
1875 GDate *date1, *date2;
1877 gtk_tree_model_get (model, a,
1878 COL_WHEN_DATE, &date1,
1881 gtk_tree_model_get (model, b,
1882 COL_WHEN_DATE, &date2,
1885 return g_date_compare (date1, date2);
1889 when_row_is_separator (GtkTreeModel *model,
1896 gtk_tree_model_get (model, iter,
1897 COL_WHEN_TEXT, &when,
1900 ret = g_str_equal (when, "separator");
1906 log_window_when_changed_cb (GtkTreeSelection *selection,
1907 EmpathyLogWindow *window)
1910 GtkTreeModel *model;
1913 DEBUG ("log_window_when_changed_cb");
1915 view = gtk_tree_selection_get_tree_view (selection);
1916 model = gtk_tree_view_get_model (view);
1918 /* If 'Anytime' is selected, everything else should be deselected */
1919 if (gtk_tree_model_get_iter_first (model, &iter))
1921 if (gtk_tree_selection_iter_is_selected (selection, &iter))
1923 g_signal_handlers_block_by_func (selection,
1924 log_window_when_changed_cb,
1927 gtk_tree_selection_unselect_all (selection);
1928 gtk_tree_selection_select_iter (selection, &iter);
1930 g_signal_handlers_unblock_by_func (selection,
1931 log_window_when_changed_cb,
1936 log_window_chats_get_messages (window, FALSE);
1940 log_window_when_setup (EmpathyLogWindow *window)
1943 GtkTreeModel *model;
1944 GtkTreeSelection *selection;
1945 GtkTreeSortable *sortable;
1946 GtkTreeViewColumn *column;
1947 GtkListStore *store;
1948 GtkCellRenderer *cell;
1950 view = GTK_TREE_VIEW (window->treeview_when);
1951 selection = gtk_tree_view_get_selection (view);
1954 store = gtk_list_store_new (COL_WHEN_COUNT,
1955 G_TYPE_DATE, /* date */
1956 G_TYPE_STRING, /* stringified date */
1957 G_TYPE_STRING); /* icon */
1959 model = GTK_TREE_MODEL (store);
1960 sortable = GTK_TREE_SORTABLE (store);
1962 gtk_tree_view_set_model (view, model);
1965 column = gtk_tree_view_column_new ();
1966 gtk_tree_view_column_set_title (column, _("When"));
1968 cell = gtk_cell_renderer_pixbuf_new ();
1969 gtk_tree_view_column_pack_start (column, cell, FALSE);
1970 gtk_tree_view_column_add_attribute (column, cell,
1971 "icon-name", COL_WHEN_ICON);
1973 cell = gtk_cell_renderer_text_new ();
1974 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1975 gtk_tree_view_column_pack_start (column, cell, TRUE);
1976 gtk_tree_view_column_add_attribute (column, cell,
1980 gtk_tree_view_append_column (view, column);
1982 /* set up treeview properties */
1983 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
1984 gtk_tree_view_set_row_separator_func (view, when_row_is_separator,
1986 gtk_tree_sortable_set_sort_column_id (sortable,
1988 GTK_SORT_DESCENDING);
1989 gtk_tree_sortable_set_sort_func (sortable,
1990 COL_WHEN_DATE, sort_by_date,
1993 /* set up signals */
1994 g_signal_connect (selection, "changed",
1995 G_CALLBACK (log_window_when_changed_cb),
1998 g_object_unref (store);
2002 what_row_is_separator (GtkTreeModel *model,
2008 gtk_tree_model_get (model, iter,
2009 COL_WHAT_TYPE, &type,
2012 return (type == -1);
2016 log_window_what_changed_cb (GtkTreeSelection *selection,
2017 EmpathyLogWindow *window)
2020 GtkTreeModel *model;
2023 DEBUG ("log_window_what_changed_cb");
2025 view = gtk_tree_selection_get_tree_view (selection);
2026 model = gtk_tree_view_get_model (view);
2028 /* If 'Anything' is selected, everything else should be deselected */
2029 if (gtk_tree_model_get_iter_first (model, &iter))
2031 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2033 g_signal_handlers_block_by_func (selection,
2034 log_window_what_changed_cb,
2037 gtk_tree_selection_unselect_all (selection);
2038 gtk_tree_selection_select_iter (selection, &iter);
2040 g_signal_handlers_unblock_by_func (selection,
2041 log_window_what_changed_cb,
2046 /* The dates need to be updated if we're not searching */
2047 log_window_chats_get_messages (window, window->hits == NULL);
2051 log_window_what_collapse_row_cb (GtkTreeView *tree_view,
2056 /* Reject collapsing */
2063 EventSubtype subtype;
2069 log_window_what_setup (EmpathyLogWindow *window)
2072 GtkTreeModel *model;
2073 GtkTreeSelection *selection;
2074 GtkTreeSortable *sortable;
2075 GtkTreeViewColumn *column;
2076 GtkTreeIter iter, parent;
2077 GtkTreeStore *store;
2078 GtkCellRenderer *cell;
2080 struct event events [] = {
2081 { TPL_EVENT_MASK_ANY, 0, NULL, _("Anything") },
2082 { -1, 0, NULL, "separator" },
2083 { TPL_EVENT_MASK_TEXT, 0, "stock_text_justify", _("Text chats") },
2084 { TPL_EVENT_MASK_CALL, EVENT_CALL_ALL, "call-start", _("Calls") }
2086 struct event call_events [] = {
2087 { TPL_EVENT_MASK_CALL, EVENT_CALL_INCOMING, "call-start", _("Incoming calls") },
2088 { TPL_EVENT_MASK_CALL, EVENT_CALL_OUTGOING, "call-start", _("Outgoing calls") },
2089 { TPL_EVENT_MASK_CALL, EVENT_CALL_MISSED, "call-stop", _("Missed calls") }
2092 view = GTK_TREE_VIEW (window->treeview_what);
2093 selection = gtk_tree_view_get_selection (view);
2096 store = gtk_tree_store_new (COL_WHAT_COUNT,
2097 G_TYPE_INT, /* history type */
2098 G_TYPE_INT, /* history subtype */
2099 G_TYPE_STRING, /* stringified history type */
2100 G_TYPE_STRING, /* icon */
2101 G_TYPE_BOOLEAN); /* expander (hidden) */
2103 model = GTK_TREE_MODEL (store);
2104 sortable = GTK_TREE_SORTABLE (store);
2106 gtk_tree_view_set_model (view, model);
2109 column = gtk_tree_view_column_new ();
2110 gtk_tree_view_column_set_title (column, _("What"));
2112 cell = gtk_cell_renderer_pixbuf_new ();
2113 gtk_tree_view_column_pack_start (column, cell, FALSE);
2114 gtk_tree_view_column_add_attribute (column, cell,
2115 "icon-name", COL_WHAT_ICON);
2117 cell = gtk_cell_renderer_text_new ();
2118 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2119 gtk_tree_view_column_pack_start (column, cell, TRUE);
2120 gtk_tree_view_column_add_attribute (column, cell,
2121 "text", COL_WHAT_TEXT);
2123 gtk_tree_view_append_column (view, column);
2125 /* set up treeview properties */
2126 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2127 gtk_tree_view_set_show_expanders (view, FALSE);
2128 gtk_tree_view_set_level_indentation (view, 12);
2129 gtk_tree_view_expand_all (view);
2130 gtk_tree_view_set_row_separator_func (view, what_row_is_separator,
2134 for (i = 0; i < G_N_ELEMENTS (events); i++)
2136 gtk_tree_store_append (store, &iter, NULL);
2137 gtk_tree_store_set (store, &iter,
2138 COL_WHAT_TYPE, events[i].type,
2139 COL_WHAT_SUBTYPE, events[i].subtype,
2140 COL_WHAT_TEXT, events[i].text,
2141 COL_WHAT_ICON, events[i].icon,
2145 gtk_tree_model_iter_nth_child (model, &parent, NULL, 3);
2146 for (i = 0; i < G_N_ELEMENTS (call_events); i++)
2148 gtk_tree_store_append (store, &iter, &parent);
2149 gtk_tree_store_set (store, &iter,
2150 COL_WHAT_TYPE, call_events[i].type,
2151 COL_WHAT_SUBTYPE, call_events[i].subtype,
2152 COL_WHAT_TEXT, call_events[i].text,
2153 COL_WHAT_ICON, call_events[i].icon,
2157 gtk_tree_view_expand_all (view);
2159 /* select 'Anything' */
2160 if (gtk_tree_model_get_iter_first (model, &iter))
2161 gtk_tree_selection_select_iter (selection, &iter);
2163 /* set up signals */
2164 g_signal_connect (view, "test-collapse-row",
2165 G_CALLBACK (log_window_what_collapse_row_cb),
2167 g_signal_connect (selection, "changed",
2168 G_CALLBACK (log_window_what_changed_cb),
2171 g_object_unref (store);
2175 log_window_got_messages_for_date_cb (GObject *manager,
2176 GAsyncResult *result,
2179 Ctx *ctx = user_data;
2182 GError *error = NULL;
2184 if (log_window == NULL)
2187 if (log_window->count != ctx->count)
2190 if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager),
2191 result, &events, &error))
2193 DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
2195 g_error_free (error);
2199 for (l = events; l; l = l->next)
2201 TplEvent *event = l->data;
2202 gboolean append = TRUE;
2204 if (TPL_IS_CALL_EVENT (l->data)
2205 && ctx->event_mask & TPL_EVENT_MASK_CALL
2206 && ctx->event_mask != TPL_EVENT_MASK_ANY)
2208 TplCallEvent *call = l->data;
2212 if (ctx->subtype & EVENT_CALL_ALL)
2216 TplCallEndReason reason = tpl_call_event_get_end_reason (call);
2217 TplEntity *sender = tpl_event_get_sender (event);
2218 TplEntity *receiver = tpl_event_get_receiver (event);
2220 if (reason == TPL_CALL_END_REASON_NO_ANSWER)
2222 if (ctx->subtype & EVENT_CALL_MISSED)
2225 else if (ctx->subtype & EVENT_CALL_OUTGOING
2226 && tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
2228 else if (ctx->subtype & EVENT_CALL_INCOMING
2229 && tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
2236 EmpathyMessage *msg = empathy_message_from_tpl_log_event (event);
2237 log_window_append_message (event, msg);
2238 g_object_unref (msg);
2241 g_object_unref (event);
2243 g_list_free (events);
2248 _tpl_action_chain_continue (log_window->chain);
2252 get_events_for_date (TplActionChain *chain, gpointer user_data)
2254 Ctx *ctx = user_data;
2256 tpl_log_manager_get_events_for_date_async (ctx->window->log_manager,
2257 ctx->account, ctx->entity, ctx->event_mask,
2259 log_window_got_messages_for_date_cb,
2264 log_window_get_messages_for_dates (EmpathyLogWindow *window,
2267 GList *accounts, *targets, *acc, *targ, *l;
2268 TplEventTypeMask event_mask;
2269 EventSubtype subtype;
2270 GDate *date, *anytime, *separator;
2272 if (!log_window_get_selected (window,
2273 &accounts, &targets, NULL, &event_mask, &subtype))
2276 anytime = g_date_new_dmy (2, 1, -1);
2277 separator = g_date_new_dmy (1, 1, -1);
2279 _tpl_action_chain_clear (window->chain);
2282 for (acc = accounts, targ = targets;
2283 acc != NULL && targ != NULL;
2284 acc = acc->next, targ = targ->next)
2286 TpAccount *account = acc->data;
2287 TplEntity *target = targ->data;
2289 for (l = dates; l != NULL; l = l->next)
2294 if (g_date_compare (date, anytime) != 0)
2298 ctx = ctx_new (window, account, target, date, event_mask, subtype,
2300 _tpl_action_chain_append (window->chain, get_events_for_date, ctx);
2304 GtkTreeView *view = GTK_TREE_VIEW (window->treeview_when);
2305 GtkTreeModel *model = gtk_tree_view_get_model (view);
2310 for (next = gtk_tree_model_get_iter_first (model, &iter);
2312 next = gtk_tree_model_iter_next (model, &iter))
2316 gtk_tree_model_get (model, &iter,
2320 if (g_date_compare (d, anytime) != 0 &&
2321 g_date_compare (d, separator) != 0)
2323 ctx = ctx_new (window, account, target, d,
2324 event_mask, subtype, window->count);
2325 _tpl_action_chain_append (window->chain, get_events_for_date, ctx);
2332 _tpl_action_chain_start (window->chain);
2334 g_list_free_full (accounts, g_object_unref);
2335 g_list_free_full (targets, g_object_unref);
2336 g_date_free (separator);
2337 g_date_free (anytime);
2341 log_manager_got_dates_cb (GObject *manager,
2342 GAsyncResult *result,
2345 Ctx *ctx = user_data;
2347 GtkTreeModel *model;
2348 GtkTreeSelection *selection;
2349 GtkListStore *store;
2353 GError *error = NULL;
2355 if (log_window == NULL)
2358 if (log_window->count != ctx->count)
2361 if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
2362 result, &dates, &error))
2364 DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
2369 view = GTK_TREE_VIEW (log_window->treeview_when);
2370 model = gtk_tree_view_get_model (view);
2371 store = GTK_LIST_STORE (model);
2372 selection = gtk_tree_view_get_selection (view);
2374 for (l = dates; l != NULL; l = l->next)
2376 GDate *date = l->data;
2378 /* Add the date if it's not already there */
2379 has_element = FALSE;
2380 gtk_tree_model_foreach (model, model_has_date, date);
2383 gchar *text = format_date_for_display (date);
2385 gtk_list_store_append (store, &iter);
2386 gtk_list_store_set (store, &iter,
2387 COL_WHEN_DATE, date,
2388 COL_WHEN_TEXT, text,
2389 COL_WHEN_ICON, CALENDAR_ICON,
2396 if (gtk_tree_model_get_iter_first (model, &iter))
2398 gchar *separator = NULL;
2400 if (gtk_tree_model_iter_next (model, &iter))
2402 gtk_tree_model_get (model, &iter,
2403 COL_WHEN_TEXT, &separator,
2407 if (g_strcmp0 (separator, "separator") != 0)
2409 gtk_list_store_prepend (store, &iter);
2410 gtk_list_store_set (store, &iter,
2411 COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
2412 COL_WHEN_TEXT, "separator",
2415 gtk_list_store_prepend (store, &iter);
2416 gtk_list_store_set (store, &iter,
2417 COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
2418 COL_WHEN_TEXT, _("Anytime"),
2423 g_list_free_full (dates, g_free);
2426 _tpl_action_chain_continue (log_window->chain);
2430 select_first_date (TplActionChain *chain, gpointer user_data)
2433 GtkTreeModel *model;
2434 GtkTreeSelection *selection;
2437 view = GTK_TREE_VIEW (log_window->treeview_when);
2438 model = gtk_tree_view_get_model (view);
2439 selection = gtk_tree_view_get_selection (view);
2441 /* Show messages of the most recent date */
2442 if (gtk_tree_model_get_iter_first (model, &iter))
2443 gtk_tree_selection_select_iter (selection, &iter);
2445 _tpl_action_chain_continue (log_window->chain);
2449 get_dates_for_entity (TplActionChain *chain, gpointer user_data)
2451 Ctx *ctx = user_data;
2453 tpl_log_manager_get_dates_async (ctx->window->log_manager,
2454 ctx->account, ctx->entity, ctx->event_mask,
2455 log_manager_got_dates_cb, ctx);
2459 log_window_chats_get_messages (EmpathyLogWindow *window,
2460 gboolean force_get_dates)
2462 GList *accounts, *targets, *dates;
2463 TplEventTypeMask event_mask;
2465 GtkTreeModel *model;
2466 GtkListStore *store;
2467 GtkTreeSelection *selection;
2469 if (!log_window_get_selected (window, &accounts, &targets,
2470 &dates, &event_mask, NULL))
2473 view = GTK_TREE_VIEW (window->treeview_when);
2474 selection = gtk_tree_view_get_selection (view);
2475 model = gtk_tree_view_get_model (view);
2476 store = GTK_LIST_STORE (model);
2478 /* Clear all current messages shown in the textview */
2479 gtk_tree_store_clear (window->store_events);
2481 _tpl_action_chain_clear (window->chain);
2484 /* If there's a search use the returned hits */
2485 if (window->hits != NULL)
2487 if (force_get_dates)
2489 g_signal_handlers_block_by_func (selection,
2490 log_window_when_changed_cb,
2493 gtk_list_store_clear (store);
2495 g_signal_handlers_unblock_by_func (selection,
2496 log_window_when_changed_cb,
2499 populate_dates_from_search_hits (accounts, targets);
2502 populate_events_from_search_hits (accounts, targets, dates);
2504 /* Either use the supplied date or get the last */
2505 else if (force_get_dates || dates == NULL)
2509 g_signal_handlers_block_by_func (selection,
2510 log_window_when_changed_cb,
2513 gtk_list_store_clear (store);
2515 g_signal_handlers_unblock_by_func (selection,
2516 log_window_when_changed_cb,
2519 /* Get a list of dates and show them on the treeview */
2520 for (targ = targets, acc = accounts;
2521 targ != NULL && acc != NULL;
2522 targ = targ->next, acc = acc->next)
2524 TpAccount *account = acc->data;
2525 TplEntity *target = targ->data;
2526 Ctx *ctx = ctx_new (window, account, target, NULL, event_mask, 0,
2529 _tpl_action_chain_append (window->chain, get_dates_for_entity, ctx);
2531 _tpl_action_chain_append (window->chain, select_first_date, NULL);
2532 _tpl_action_chain_start (window->chain);
2536 /* Show messages of the selected date */
2537 log_window_get_messages_for_dates (window, dates);
2540 g_list_free_full (accounts, g_object_unref);
2541 g_list_free_full (targets, g_object_unref);
2542 g_list_free_full (dates, (GFreeFunc) g_date_free);
2546 EmpathyAccountChooserFilterResultCallback callback;
2548 } FilterCallbackData;
2551 got_entities (GObject *manager,
2552 GAsyncResult *result,
2555 FilterCallbackData *data = user_data;
2557 GError *error = NULL;
2559 if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
2560 result, &entities, &error))
2562 DEBUG ("Could not get entities: %s", error->message);
2563 g_error_free (error);
2564 data->callback (FALSE, data->user_data);
2568 data->callback (entities != NULL, data->user_data);
2570 g_list_free_full (entities, g_object_unref);
2573 g_slice_free (FilterCallbackData, data);
2577 empathy_account_chooser_filter_has_logs (TpAccount *account,
2578 EmpathyAccountChooserFilterResultCallback callback,
2579 gpointer callback_data,
2582 TplLogManager *manager = tpl_log_manager_dup_singleton ();
2583 FilterCallbackData *cb_data = g_slice_new0 (FilterCallbackData);
2585 cb_data->callback = callback;
2586 cb_data->user_data = callback_data;
2588 tpl_log_manager_get_entities_async (manager, account, got_entities, cb_data);
2590 g_object_unref (manager);
2594 log_window_logger_clear_account_cb (TpProxy *proxy,
2595 const GError *error,
2597 GObject *weak_object)
2599 EmpathyLogWindow *window = user_data;
2602 g_warning ("Error when clearing logs: %s", error->message);
2604 /* Refresh the log viewer so the logs are cleared if the account
2605 * has been deleted */
2606 gtk_tree_store_clear (window->store_events);
2607 log_window_who_populate (window);
2609 /* Re-filter the account chooser so the accounts without logs get greyed out */
2610 empathy_account_chooser_set_filter (
2611 EMPATHY_ACCOUNT_CHOOSER (window->account_chooser),
2612 empathy_account_chooser_filter_has_logs, NULL);
2616 log_window_clear_logs_chooser_select_account (EmpathyAccountChooser *chooser,
2617 EmpathyLogWindow *window)
2619 EmpathyAccountChooser *account_chooser;
2621 account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser);
2623 empathy_account_chooser_set_account (chooser,
2624 empathy_account_chooser_get_account (account_chooser));
2628 log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
2629 EmpathyLogWindow *window)
2631 GtkWidget *dialog, *content_area, *hbox, *label;
2632 EmpathyAccountChooser *account_chooser;
2636 GError *error = NULL;
2638 account_chooser = (EmpathyAccountChooser *) empathy_account_chooser_new ();
2639 empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
2640 empathy_account_chooser_set_filter (account_chooser,
2641 empathy_account_chooser_filter_has_logs, NULL);
2643 /* Select the same account as in the history window */
2644 if (empathy_account_chooser_is_ready (account_chooser))
2645 log_window_clear_logs_chooser_select_account (account_chooser, window);
2647 g_signal_connect (account_chooser, "ready",
2648 G_CALLBACK (log_window_clear_logs_chooser_select_account), window);
2650 dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window->window),
2651 GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING,
2653 _("Are you sure you want to delete all logs of previous conversations?"));
2655 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
2656 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2657 _("Clear All"), GTK_RESPONSE_APPLY,
2660 content_area = gtk_message_dialog_get_message_area (
2661 GTK_MESSAGE_DIALOG (dialog));
2663 hbox = gtk_hbox_new (FALSE, 6);
2664 label = gtk_label_new (_("Delete from:"));
2665 gtk_box_pack_start (GTK_BOX (hbox), label,
2667 gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (account_chooser),
2669 gtk_box_pack_start (GTK_BOX (content_area), hbox,
2672 gtk_widget_show_all (hbox);
2674 response_id = gtk_dialog_run (GTK_DIALOG (dialog));
2676 if (response_id != GTK_RESPONSE_APPLY)
2679 bus = tp_dbus_daemon_dup (&error);
2682 g_warning ("Could not delete logs: %s", error->message);
2683 g_error_free (error);
2687 logger = g_object_new (TP_TYPE_PROXY,
2688 "bus-name", "org.freedesktop.Telepathy.Logger",
2689 "object-path", "/org/freedesktop/Telepathy/Logger",
2692 g_object_unref (bus);
2694 tp_proxy_add_interface_by_id (logger, EMP_IFACE_QUARK_LOGGER);
2696 if (empathy_account_chooser_has_all_selected (account_chooser))
2698 DEBUG ("Deleting logs for all the accounts");
2700 emp_cli_logger_call_clear (logger, -1,
2701 log_window_logger_clear_account_cb,
2702 window, NULL, G_OBJECT (window->window));
2708 account = empathy_account_chooser_get_account (account_chooser);
2710 DEBUG ("Deleting logs for %s", tp_proxy_get_object_path (account));
2712 emp_cli_logger_call_clear_account (logger, -1,
2713 tp_proxy_get_object_path (account),
2714 log_window_logger_clear_account_cb,
2715 window, NULL, G_OBJECT (window->window));
2718 g_object_unref (logger);
2720 gtk_widget_destroy (dialog);