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>
32 #include <webkit/webkit.h>
34 #include <telepathy-glib/telepathy-glib.h>
35 #include <telepathy-glib/proxy-subclass.h>
37 #include <telepathy-yell/telepathy-yell.h>
39 #include <telepathy-logger/telepathy-logger.h>
41 # include <telepathy-logger/call-event.h>
44 #include <extensions/extensions.h>
46 #include <libempathy/action-chain-internal.h>
47 #include <libempathy/empathy-camera-monitor.h>
48 #include <libempathy/empathy-chatroom-manager.h>
49 #include <libempathy/empathy-chatroom.h>
50 #include <libempathy/empathy-gsettings.h>
51 #include <libempathy/empathy-message.h>
52 #include <libempathy/empathy-request-util.h>
53 #include <libempathy/empathy-utils.h>
54 #include <libempathy/empathy-time.h>
56 #include "empathy-log-window.h"
57 #include "empathy-account-chooser.h"
58 #include "empathy-call-utils.h"
59 #include "empathy-chat-view.h"
60 #include "empathy-contact-dialogs.h"
61 #include "empathy-images.h"
62 #include "empathy-theme-manager.h"
63 #include "empathy-ui-utils.h"
64 #include "empathy-webkit-utils.h"
66 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
67 #include <libempathy/empathy-debug.h>
69 #define EMPATHY_NS "http://live.gnome.org/Empathy"
71 G_DEFINE_TYPE (EmpathyLogWindow, empathy_log_window, GTK_TYPE_WINDOW);
73 struct _EmpathyLogWindowPriv
77 GtkWidget *button_profile;
78 GtkWidget *button_chat;
79 GtkWidget *button_call;
80 GtkWidget *button_video;
82 GtkWidget *search_entry;
87 GtkWidget *treeview_who;
88 GtkWidget *treeview_what;
89 GtkWidget *treeview_when;
92 GtkTreeStore *store_events;
94 GtkWidget *account_chooser;
98 /* List of selected GDates, free with g_list_free_full (l, g_date_free) */
101 TplActionChain *chain;
102 TplLogManager *log_manager;
104 /* Hash of TpChannel<->TpAccount for use by the observer until we can
105 * get a TpAccount from a TpConnection or wherever */
106 GHashTable *channels;
107 TpBaseClient *observer;
109 EmpathyContact *selected_contact;
110 EmpathyContact *events_contact;
112 EmpathyCameraMonitor *camera_monitor;
113 GBinding *button_video_binding;
115 /* Used to cancel logger calls when no longer needed */
118 /* List of owned TplLogSearchHits, free with tpl_log_search_hit_free */
122 /* Only used while waiting for the account chooser to be ready */
123 TpAccount *selected_account;
124 gchar *selected_chat_id;
125 gboolean selected_is_chatroom;
127 GSettings *gsettings_chat;
128 GSettings *gsettings_desktop;
131 static void log_window_search_entry_changed_cb (GtkWidget *entry,
132 EmpathyLogWindow *self);
133 static void log_window_search_entry_activate_cb (GtkWidget *widget,
134 EmpathyLogWindow *self);
135 static void log_window_search_entry_icon_pressed_cb (GtkEntry *entry,
136 GtkEntryIconPosition icon_pos,
139 static void log_window_who_populate (EmpathyLogWindow *self);
140 static void log_window_who_setup (EmpathyLogWindow *self);
141 static void log_window_when_setup (EmpathyLogWindow *self);
142 static void log_window_what_setup (EmpathyLogWindow *self);
143 static void log_window_events_setup (EmpathyLogWindow *self);
144 static void log_window_chats_accounts_changed_cb (GtkWidget *combobox,
145 EmpathyLogWindow *self);
146 static void log_window_chats_set_selected (EmpathyLogWindow *self);
147 static void log_window_chats_get_messages (EmpathyLogWindow *self,
148 gboolean force_get_dates);
149 static void log_window_when_changed_cb (GtkTreeSelection *selection,
150 EmpathyLogWindow *self);
151 static void log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
152 EmpathyLogWindow *self);
153 static void start_spinner (void);
155 static void log_window_create_observer (EmpathyLogWindow *window);
156 static gboolean log_window_events_button_press_event (GtkWidget *webview,
157 GdkEventButton *event, EmpathyLogWindow *self);
158 static void log_window_update_buttons_sensitivity (EmpathyLogWindow *self);
161 empathy_account_chooser_filter_has_logs (TpAccount *account,
162 EmpathyAccountChooserFilterResultCallback callback,
163 gpointer callback_data,
213 COL_EVENTS_PRETTY_DATE,
222 #define CALENDAR_ICON "stock_calendar"
224 /* Seconds between two messages to be considered one conversation */
225 #define MAX_GAP 30*60
227 #define WHAT_TYPE_SEPARATOR -1
231 EVENT_CALL_INCOMING = 1 << 0,
232 EVENT_CALL_OUTGOING = 1 << 1,
233 EVENT_CALL_MISSED = 1 << 2,
234 EVENT_CALL_ALL = 1 << 3,
238 log_window_get_selected (EmpathyLogWindow *window,
243 TplEventTypeMask *event_mask,
244 EventSubtype *subtype);
246 static EmpathyLogWindow *log_window = NULL;
248 static gboolean has_element;
251 #define _date_copy(d) g_date_new_julian (g_date_get_julian (d))
256 EmpathyLogWindow *self;
260 TplEventTypeMask event_mask;
261 EventSubtype subtype;
266 ctx_new (EmpathyLogWindow *self,
270 TplEventTypeMask event_mask,
271 EventSubtype subtype,
274 Ctx *ctx = g_slice_new0 (Ctx);
278 ctx->account = g_object_ref (account);
280 ctx->entity = g_object_ref (entity);
282 ctx->date = _date_copy (date);
283 ctx->event_mask = event_mask;
284 ctx->subtype = subtype;
293 tp_clear_object (&ctx->account);
294 tp_clear_object (&ctx->entity);
295 tp_clear_pointer (&ctx->date, g_date_free);
297 g_slice_free (Ctx, ctx);
301 account_chooser_ready_cb (EmpathyAccountChooser *chooser,
302 EmpathyLogWindow *self)
304 /* We'll display the account once the model has been populate with the chats
305 * of this account. */
306 empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER (
307 self->priv->account_chooser), self->priv->selected_account);
311 select_account_once_ready (EmpathyLogWindow *self,
313 const gchar *chat_id,
314 gboolean is_chatroom)
316 EmpathyAccountChooser *account_chooser;
318 account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
320 tp_clear_object (&self->priv->selected_account);
321 self->priv->selected_account = g_object_ref (account);
323 g_free (self->priv->selected_chat_id);
324 self->priv->selected_chat_id = g_strdup (chat_id);
326 self->priv->selected_is_chatroom = is_chatroom;
328 if (empathy_account_chooser_is_ready (account_chooser))
329 account_chooser_ready_cb (account_chooser, self);
331 /* Chat will be selected once the account chooser is ready */
332 g_signal_connect (account_chooser, "ready",
333 G_CALLBACK (account_chooser_ready_cb), self);
337 toolbutton_profile_clicked (GtkToolButton *toolbutton,
338 EmpathyLogWindow *self)
340 g_return_if_fail (self != NULL);
341 g_return_if_fail (EMPATHY_IS_CONTACT (self->priv->selected_contact));
343 empathy_contact_information_dialog_show (self->priv->selected_contact,
348 toolbutton_chat_clicked (GtkToolButton *toolbutton,
349 EmpathyLogWindow *self)
351 g_return_if_fail (self != NULL);
352 g_return_if_fail (EMPATHY_IS_CONTACT (self->priv->selected_contact));
354 empathy_chat_with_contact (self->priv->selected_contact,
355 gtk_get_current_event_time ());
359 toolbutton_av_clicked (GtkToolButton *toolbutton,
360 EmpathyLogWindow *self)
364 g_return_if_fail (self != NULL);
365 g_return_if_fail (EMPATHY_IS_CONTACT (self->priv->selected_contact));
367 video = (GTK_WIDGET (toolbutton) == self->priv->button_video);
369 empathy_call_new_with_streams (
370 empathy_contact_get_id (self->priv->selected_contact),
371 empathy_contact_get_account (self->priv->selected_contact),
372 TRUE, video, gtk_get_current_event_time ());
376 insert_or_change_row (EmpathyLogWindow *self,
382 char *str = gtk_tree_path_to_string (path);
383 char *script, *text, *date, *stock_icon;
386 gtk_tree_model_get (model, iter,
387 COL_EVENTS_TEXT, &text,
388 COL_EVENTS_PRETTY_DATE, &date,
389 COL_EVENTS_ICON, &stock_icon,
392 if (!tp_str_empty (stock_icon))
394 GtkIconInfo *icon_info = gtk_icon_theme_lookup_icon (
395 gtk_icon_theme_get_default (),
397 GTK_ICON_SIZE_MENU, 0);
399 if (icon_info != NULL)
400 icon = g_strdup (gtk_icon_info_get_filename (icon_info));
402 gtk_icon_info_free (icon_info);
405 script = g_strdup_printf ("javascript:%s([%s], '%s', '%s', '%s');",
407 g_strdelimit (str, ":", ','),
409 icon != NULL ? icon : "",
412 webkit_web_view_execute_script (WEBKIT_WEB_VIEW (self->priv->webview),
424 store_events_row_inserted (GtkTreeModel *model,
427 EmpathyLogWindow *self)
429 insert_or_change_row (self, "insertRow", model, path, iter);
433 store_events_row_changed (GtkTreeModel *model,
436 EmpathyLogWindow *self)
438 insert_or_change_row (self, "changeRow", model, path, iter);
442 store_events_row_deleted (GtkTreeModel *model,
444 EmpathyLogWindow *self)
446 char *str = gtk_tree_path_to_string (path);
449 script = g_strdup_printf ("javascript:deleteRow([%s]);",
450 g_strdelimit (str, ":", ','));
452 webkit_web_view_execute_script (WEBKIT_WEB_VIEW (self->priv->webview),
460 store_events_has_child_rows (GtkTreeModel *model,
463 EmpathyLogWindow *self)
465 char *str = gtk_tree_path_to_string (path);
468 script = g_strdup_printf ("javascript:hasChildRows([%s], %u);",
469 g_strdelimit (str, ":", ','),
470 gtk_tree_model_iter_has_child (model, iter));
472 webkit_web_view_execute_script (WEBKIT_WEB_VIEW (self->priv->webview),
480 store_events_rows_reordered (GtkTreeModel *model,
484 EmpathyLogWindow *self)
486 char *str = gtk_tree_path_to_string (path);
487 int i, children = gtk_tree_model_iter_n_children (model, iter);
488 char **new_order_strv, *new_order_s;
491 new_order_strv = g_new0 (char *, children + 1);
493 for (i = 0; i < children; i++)
494 new_order_strv[i] = g_strdup_printf ("%i", new_order[i]);
496 new_order_s = g_strjoinv (",", new_order_strv);
498 script = g_strdup_printf ("javascript:reorderRows([%s], [%s]);",
499 str == NULL ? "" : g_strdelimit (str, ":", ','),
502 webkit_web_view_execute_script (WEBKIT_WEB_VIEW (self->priv->webview),
507 g_free (new_order_s);
508 g_strfreev (new_order_strv);
512 events_webview_handle_navigation (WebKitWebView *webview,
513 WebKitWebFrame *frame,
514 WebKitNetworkRequest *request,
515 WebKitWebNavigationAction *navigation_action,
516 WebKitWebPolicyDecision *policy_decision,
517 EmpathyLogWindow *window)
519 empathy_url_show (GTK_WIDGET (webview),
520 webkit_network_request_get_uri (request));
522 webkit_web_policy_decision_ignore (policy_decision);
527 empathy_log_window_constructor (GType type,
529 GObjectConstructParam *props)
533 if (log_window != NULL)
535 retval = (GObject *) log_window;
539 retval = G_OBJECT_CLASS (empathy_log_window_parent_class)
540 ->constructor (type, n_props, props);
542 log_window = EMPATHY_LOG_WINDOW (retval);
543 g_object_add_weak_pointer (retval, (gpointer) &log_window);
550 empathy_log_window_dispose (GObject *object)
552 EmpathyLogWindow *self = EMPATHY_LOG_WINDOW (object);
554 if (self->priv->source != 0)
556 g_source_remove (self->priv->source);
557 self->priv->source = 0;
560 if (self->priv->current_dates != NULL)
562 g_list_free_full (self->priv->current_dates,
563 (GDestroyNotify) g_date_free);
564 self->priv->current_dates = NULL;
567 tp_clear_pointer (&self->priv->chain, _tpl_action_chain_free);
568 tp_clear_pointer (&self->priv->channels, g_hash_table_unref);
570 tp_clear_object (&self->priv->observer);
571 tp_clear_object (&self->priv->log_manager);
572 tp_clear_object (&self->priv->selected_account);
573 tp_clear_object (&self->priv->selected_contact);
574 tp_clear_object (&self->priv->events_contact);
575 tp_clear_object (&self->priv->camera_monitor);
577 tp_clear_object (&self->priv->gsettings_chat);
578 tp_clear_object (&self->priv->gsettings_desktop);
580 tp_clear_object (&self->priv->store_events);
582 G_OBJECT_CLASS (empathy_log_window_parent_class)->dispose (object);
586 empathy_log_window_finalize (GObject *object)
588 EmpathyLogWindow *self = EMPATHY_LOG_WINDOW (object);
590 g_free (self->priv->last_find);
591 g_free (self->priv->selected_chat_id);
593 G_OBJECT_CLASS (empathy_log_window_parent_class)->finalize (object);
597 empathy_log_window_class_init (
598 EmpathyLogWindowClass *empathy_log_window_class)
600 GObjectClass *object_class = G_OBJECT_CLASS (empathy_log_window_class);
602 g_type_class_add_private (empathy_log_window_class,
603 sizeof (EmpathyLogWindowPriv));
605 object_class->constructor = empathy_log_window_constructor;
606 object_class->dispose = empathy_log_window_dispose;
607 object_class->finalize = empathy_log_window_finalize;
611 empathy_log_window_init (EmpathyLogWindow *self)
613 EmpathyAccountChooser *account_chooser;
617 GtkWidget *vbox, *accounts, *search, *label, *quit;
618 GtkWidget *scrolledwindow_events;
620 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
621 EMPATHY_TYPE_LOG_WINDOW, EmpathyLogWindowPriv);
623 self->priv->chain = _tpl_action_chain_new_async (NULL, NULL, NULL);
625 self->priv->camera_monitor = empathy_camera_monitor_dup_singleton ();
627 self->priv->log_manager = tpl_log_manager_dup_singleton ();
629 self->priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
630 self->priv->gsettings_desktop = g_settings_new (
631 EMPATHY_PREFS_DESKTOP_INTERFACE_SCHEMA);
633 gtk_window_set_title (GTK_WINDOW (self), _("History"));
634 gtk_widget_set_can_focus (GTK_WIDGET (self), FALSE);
635 gtk_window_set_default_size (GTK_WINDOW (self), 800, 600);
637 filename = empathy_file_lookup ("empathy-log-window.ui", "libempathy-gtk");
638 gui = empathy_builder_get_file (filename,
639 "vbox1", &self->priv->vbox,
640 "toolbutton_profile", &self->priv->button_profile,
641 "toolbutton_chat", &self->priv->button_chat,
642 "toolbutton_call", &self->priv->button_call,
643 "toolbutton_video", &self->priv->button_video,
644 "toolbutton_accounts", &accounts,
645 "toolbutton_search", &search,
646 "imagemenuitem_quit", &quit,
647 "treeview_who", &self->priv->treeview_who,
648 "treeview_what", &self->priv->treeview_what,
649 "treeview_when", &self->priv->treeview_when,
650 "scrolledwindow_events", &scrolledwindow_events,
651 "notebook", &self->priv->notebook,
652 "spinner", &self->priv->spinner,
656 empathy_builder_connect (gui, self,
657 "toolbutton_profile", "clicked", toolbutton_profile_clicked,
658 "toolbutton_chat", "clicked", toolbutton_chat_clicked,
659 "toolbutton_call", "clicked", toolbutton_av_clicked,
660 "toolbutton_video", "clicked", toolbutton_av_clicked,
661 "imagemenuitem_delete", "activate", log_window_delete_menu_clicked_cb,
664 gtk_container_add (GTK_CONTAINER (self), self->priv->vbox);
666 g_object_unref (gui);
668 g_signal_connect_swapped (quit, "activate",
669 G_CALLBACK (gtk_widget_destroy), self);
671 /* Account chooser for chats */
672 vbox = gtk_vbox_new (FALSE, 3);
674 self->priv->account_chooser = empathy_account_chooser_new ();
675 account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
676 empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
677 empathy_account_chooser_set_filter (account_chooser,
678 empathy_account_chooser_filter_has_logs, NULL);
679 empathy_account_chooser_set_all (account_chooser);
681 g_signal_connect (self->priv->account_chooser, "changed",
682 G_CALLBACK (log_window_chats_accounts_changed_cb),
685 label = gtk_label_new (_("Show"));
687 gtk_box_pack_start (GTK_BOX (vbox),
688 self->priv->account_chooser,
691 gtk_box_pack_start (GTK_BOX (vbox),
695 gtk_widget_show_all (vbox);
696 gtk_container_add (GTK_CONTAINER (accounts), vbox);
699 vbox = gtk_vbox_new (FALSE, 3);
701 self->priv->search_entry = gtk_entry_new ();
702 gtk_entry_set_icon_from_stock (GTK_ENTRY (self->priv->search_entry),
703 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
704 gtk_entry_set_icon_from_stock (GTK_ENTRY (self->priv->search_entry),
705 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
707 label = gtk_label_new (_("Search"));
709 gtk_box_pack_start (GTK_BOX (vbox),
710 self->priv->search_entry,
713 gtk_box_pack_start (GTK_BOX (vbox),
717 gtk_widget_show_all (vbox);
718 gtk_container_add (GTK_CONTAINER (search), vbox);
720 g_signal_connect (self->priv->search_entry, "changed",
721 G_CALLBACK (log_window_search_entry_changed_cb),
724 g_signal_connect (self->priv->search_entry, "activate",
725 G_CALLBACK (log_window_search_entry_activate_cb),
728 g_signal_connect (self->priv->search_entry, "icon-press",
729 G_CALLBACK (log_window_search_entry_icon_pressed_cb),
733 log_window_events_setup (self);
734 log_window_who_setup (self);
735 log_window_what_setup (self);
736 log_window_when_setup (self);
738 log_window_create_observer (self);
740 log_window_who_populate (self);
743 self->priv->webview = webkit_web_view_new ();
744 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow_events),
745 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
746 gtk_container_add (GTK_CONTAINER (scrolledwindow_events),
747 self->priv->webview);
748 gtk_widget_show (self->priv->webview);
750 empathy_webkit_bind_font_setting (WEBKIT_WEB_VIEW (self->priv->webview),
751 self->priv->gsettings_desktop,
752 EMPATHY_PREFS_DESKTOP_INTERFACE_FONT_NAME);
754 filename = empathy_file_lookup ("empathy-log-window.html", "data");
755 gfile = g_file_new_for_path (filename);
758 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (self->priv->webview),
759 g_file_get_uri (gfile));
760 g_object_unref (gfile);
762 /* handle all navigation externally */
763 g_signal_connect (self->priv->webview, "navigation-policy-decision-requested",
764 G_CALLBACK (events_webview_handle_navigation), self);
766 /* listen to changes to the treemodel */
767 g_signal_connect (self->priv->store_events, "row-inserted",
768 G_CALLBACK (store_events_row_inserted), self);
769 g_signal_connect (self->priv->store_events, "row-changed",
770 G_CALLBACK (store_events_row_changed), self);
771 g_signal_connect (self->priv->store_events, "row-deleted",
772 G_CALLBACK (store_events_row_deleted), self);
773 g_signal_connect (self->priv->store_events, "rows-reordered",
774 G_CALLBACK (store_events_rows_reordered), self);
775 g_signal_connect (self->priv->store_events, "row-has-child-toggled",
776 G_CALLBACK (store_events_has_child_rows), self);
778 /* track clicked row */
779 g_signal_connect (self->priv->webview, "button-press-event",
780 G_CALLBACK (log_window_events_button_press_event), self);
782 log_window_update_buttons_sensitivity (self);
783 gtk_widget_show (GTK_WIDGET (self));
787 empathy_log_window_show (TpAccount *account,
788 const gchar *chat_id,
789 gboolean is_chatroom,
792 log_window = g_object_new (EMPATHY_TYPE_LOG_WINDOW, NULL);
794 gtk_window_present (GTK_WINDOW (log_window));
796 if (account != NULL && chat_id != NULL)
797 select_account_once_ready (log_window, account, chat_id, is_chatroom);
800 gtk_window_set_transient_for (GTK_WINDOW (log_window),
801 GTK_WINDOW (parent));
803 return GTK_WIDGET (log_window);
807 account_equal (TpAccount *a,
810 return g_str_equal (tp_proxy_get_object_path (a),
811 tp_proxy_get_object_path (b));
815 entity_equal (TplEntity *a,
818 return g_str_equal (tpl_entity_get_identifier (a),
819 tpl_entity_get_identifier (b));
823 is_same_confroom (TplEvent *e1,
826 TplEntity *sender1 = tpl_event_get_sender (e1);
827 TplEntity *receiver1 = tpl_event_get_receiver (e1);
828 TplEntity *sender2 = tpl_event_get_sender (e2);
829 TplEntity *receiver2 = tpl_event_get_receiver (e2);
830 TplEntity *room1, *room2;
832 if (receiver1 == NULL || receiver2 == NULL)
835 if (tpl_entity_get_entity_type (sender1) == TPL_ENTITY_ROOM)
837 else if (tpl_entity_get_entity_type (receiver1) == TPL_ENTITY_ROOM)
842 if (tpl_entity_get_entity_type (sender2) == TPL_ENTITY_ROOM)
844 else if (tpl_entity_get_entity_type (receiver2) == TPL_ENTITY_ROOM)
849 return g_str_equal (tpl_entity_get_identifier (room1),
850 tpl_entity_get_identifier (room2));
854 maybe_refresh_logs (TpChannel *channel,
857 GList *accounts = NULL, *entities = NULL, *dates = NULL;
859 TplEventTypeMask event_mask;
860 GDate *anytime = NULL, *today = NULL;
861 GDateTime *now = NULL;
862 gboolean refresh = FALSE;
866 if (!log_window_get_selected (log_window,
867 &accounts, &entities, &anyone, &dates, &event_mask, NULL))
869 DEBUG ("Could not get selected rows");
873 type = tp_channel_get_channel_type (channel);
875 /* If the channel type is not in the What pane, whatever has happened
876 * won't be displayed in the events pane. */
877 if (!tp_strdiff (type, TP_IFACE_CHANNEL_TYPE_TEXT) &&
878 !(event_mask & TPL_EVENT_MASK_TEXT))
880 if ((!tp_strdiff (type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA) ||
881 !tp_strdiff (type, TPY_IFACE_CHANNEL_TYPE_CALL)) &&
882 !(event_mask & TPL_EVENT_MASK_CALL))
885 anytime = g_date_new_dmy (2, 1, -1);
886 now = g_date_time_new_now_local ();
887 today = g_date_new_dmy (g_date_time_get_day_of_month (now),
888 g_date_time_get_month (now),
889 g_date_time_get_year (now));
891 /* If Today (or anytime) isn't selected, anything that has happened now
892 * won't be displayed. */
893 if (!g_list_find_custom (dates, anytime, (GCompareFunc) g_date_compare) &&
894 !g_list_find_custom (dates, today, (GCompareFunc) g_date_compare))
903 for (acc = accounts, ent = entities;
904 acc != NULL && ent != NULL;
905 acc = g_list_next (acc), ent = g_list_next (ent))
907 if (!account_equal (account, acc->data))
910 if (!tp_strdiff (tp_channel_get_identifier (channel),
911 tpl_entity_get_identifier (ent->data)))
919 tp_clear_pointer (&anytime, g_date_free);
920 tp_clear_pointer (&today, g_date_free);
921 tp_clear_pointer (&now, g_date_time_unref);
922 g_list_free_full (accounts, g_object_unref);
923 g_list_free_full (entities, g_object_unref);
924 g_list_free_full (dates, (GFreeFunc) g_date_free);
928 DEBUG ("Refreshing logs after received event");
930 /* FIXME: We need to populate the entities in case we
931 * didn't have any previous logs with this contact. */
932 log_window_chats_get_messages (log_window, FALSE);
937 on_msg_sent (TpTextChannel *channel,
938 TpSignalledMessage *message,
941 EmpathyLogWindow *self)
943 TpAccount *account = g_hash_table_lookup (self->priv->channels, channel);
945 maybe_refresh_logs (TP_CHANNEL (channel), account);
949 on_msg_received (TpTextChannel *channel,
950 TpSignalledMessage *message,
951 EmpathyLogWindow *self)
953 TpMessage *msg = TP_MESSAGE (message);
954 TpChannelTextMessageType type = tp_message_get_message_type (msg);
955 TpAccount *account = g_hash_table_lookup (self->priv->channels, channel);
957 if (type != TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL &&
958 type != TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)
961 maybe_refresh_logs (TP_CHANNEL (channel), account);
965 on_channel_ended (TpChannel *channel,
969 EmpathyLogWindow *self)
971 if (self->priv->channels != NULL)
972 g_hash_table_remove (self->priv->channels, channel);
976 on_call_ended (TpChannel *channel,
980 EmpathyLogWindow *self)
982 TpAccount *account = g_hash_table_lookup (self->priv->channels, channel);
984 maybe_refresh_logs (channel, account);
986 if (self->priv->channels != NULL)
987 g_hash_table_remove (self->priv->channels, channel);
991 observe_channels (TpSimpleObserver *observer,
993 TpConnection *connection,
995 TpChannelDispatchOperation *dispatch_operation,
997 TpObserveChannelsContext *context,
1000 EmpathyLogWindow *self = user_data;
1004 for (l = channels; l != NULL; l = g_list_next (l))
1006 TpChannel *channel = l->data;
1007 const gchar *type = tp_channel_get_channel_type (channel);
1009 if (!tp_strdiff (type, TP_IFACE_CHANNEL_TYPE_TEXT))
1011 TpTextChannel *text_channel = TP_TEXT_CHANNEL (channel);
1013 g_hash_table_insert (self->priv->channels,
1014 g_object_ref (channel), g_object_ref (account));
1016 tp_g_signal_connect_object (text_channel, "message-sent",
1017 G_CALLBACK (on_msg_sent), self, 0);
1018 tp_g_signal_connect_object (text_channel, "message-received",
1019 G_CALLBACK (on_msg_received), self, 0);
1020 tp_g_signal_connect_object (channel, "invalidated",
1021 G_CALLBACK (on_channel_ended), self, 0);
1023 else if (!tp_strdiff (type, TPY_IFACE_CHANNEL_TYPE_CALL) ||
1024 !tp_strdiff (type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
1026 g_hash_table_insert (self->priv->channels,
1027 g_object_ref (channel), g_object_ref (account));
1029 tp_g_signal_connect_object (channel, "invalidated",
1030 G_CALLBACK (on_call_ended), self, 0);
1034 g_warning ("Unknown channel type: %s", type);
1038 tp_observe_channels_context_accept (context);
1042 log_window_create_observer (EmpathyLogWindow *self)
1045 GError *error = NULL;
1047 dbus = tp_dbus_daemon_dup (&error);
1051 DEBUG ("Could not connect to the bus: %s", error->message);
1052 g_error_free (error);
1056 self->priv->observer = tp_simple_observer_new (dbus, TRUE, "LogWindow",
1057 TRUE, observe_channels,
1058 g_object_ref (self), g_object_unref);
1059 self->priv->channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1060 g_object_unref, g_object_unref);
1062 tp_base_client_take_observer_filter (self->priv->observer,
1064 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
1065 TP_IFACE_CHANNEL_TYPE_TEXT,
1067 tp_base_client_take_observer_filter (self->priv->observer,
1069 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
1070 TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
1072 tp_base_client_take_observer_filter (self->priv->observer,
1074 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
1075 TPY_IFACE_CHANNEL_TYPE_CALL,
1078 tp_base_client_register (self->priv->observer, NULL);
1080 g_object_unref (dbus);
1084 event_get_target (TplEvent *event)
1086 TplEntity *sender = tpl_event_get_sender (event);
1087 TplEntity *receiver = tpl_event_get_receiver (event);
1089 if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
1096 model_is_parent (GtkTreeModel *model,
1100 TplEvent *stored_event;
1103 gboolean found = FALSE;
1106 if (gtk_tree_model_iter_parent (model, &parent, iter))
1109 gtk_tree_model_get (model, iter,
1110 COL_EVENTS_ACCOUNT, &account,
1111 COL_EVENTS_TARGET, &target,
1112 COL_EVENTS_EVENT, &stored_event,
1115 if (G_OBJECT_TYPE (event) == G_OBJECT_TYPE (stored_event) &&
1116 account_equal (account, tpl_event_get_account (event)) &&
1117 (entity_equal (target, event_get_target (event)) ||
1118 is_same_confroom (event, stored_event)))
1123 gtk_tree_model_iter_nth_child (model, &child, iter,
1124 gtk_tree_model_iter_n_children (model, iter) - 1);
1126 gtk_tree_model_get (model, &child,
1127 COL_EVENTS_TS, ×tamp,
1130 if (ABS (tpl_event_get_timestamp (event) - timestamp) < MAX_GAP)
1132 /* The gap is smaller than 30 min */
1137 g_object_unref (stored_event);
1138 g_object_unref (account);
1139 g_object_unref (target);
1145 get_display_string_for_chat_message (EmpathyMessage *message,
1148 EmpathyContact *sender, *receiver, *target;
1149 TplEntity *ent_sender, *ent_receiver;
1150 const gchar *format;
1152 sender = empathy_message_get_sender (message);
1153 receiver = empathy_message_get_receiver (message);
1155 ent_sender = tpl_event_get_sender (event);
1156 ent_receiver = tpl_event_get_receiver (event);
1158 /* If this is a MUC, we want to show "Chat in <room>". */
1159 if (tpl_entity_get_entity_type (ent_sender) == TPL_ENTITY_ROOM ||
1160 (ent_receiver != NULL &&
1161 tpl_entity_get_entity_type (ent_receiver) == TPL_ENTITY_ROOM))
1162 format = _("Chat in %s");
1164 format = _("Chat with %s");
1166 if (tpl_entity_get_entity_type (ent_sender) == TPL_ENTITY_ROOM)
1168 else if (ent_receiver != NULL &&
1169 tpl_entity_get_entity_type (ent_receiver) == TPL_ENTITY_ROOM)
1171 else if (empathy_contact_is_user (sender))
1176 return g_markup_printf_escaped (format, empathy_contact_get_alias (target));
1180 get_parent_iter_for_message (TplEvent *event,
1181 EmpathyMessage *message,
1182 GtkTreeIter *parent)
1184 GtkTreeStore *store;
1185 GtkTreeModel *model;
1187 gboolean parent_found = FALSE;
1190 store = log_window->priv->store_events;
1191 model = GTK_TREE_MODEL (store);
1193 for (next = gtk_tree_model_get_iter_first (model, &iter);
1195 next = gtk_tree_model_iter_next (model, &iter))
1197 if ((parent_found = model_is_parent (model, &iter, event)))
1208 gchar *body, *pretty_date;
1210 date = g_date_time_new_from_unix_utc (
1211 tpl_event_get_timestamp (event));
1213 pretty_date = g_date_time_format (date,
1214 C_("A date with the time", "%A, %e %B %Y %X"));
1216 body = get_display_string_for_chat_message (message, event);
1218 gtk_tree_store_append (store, &iter, NULL);
1219 gtk_tree_store_set (store, &iter,
1220 COL_EVENTS_TS, tpl_event_get_timestamp (event),
1221 COL_EVENTS_PRETTY_DATE, pretty_date,
1222 COL_EVENTS_TEXT, body,
1223 COL_EVENTS_ICON, "stock_text_justify",
1224 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
1225 COL_EVENTS_TARGET, event_get_target (event),
1226 COL_EVENTS_EVENT, event,
1232 g_free (pretty_date);
1233 g_date_time_unref (date);
1237 static const gchar *
1238 get_icon_for_event (TplEvent *event)
1240 const gchar *icon = NULL;
1242 if (TPL_IS_TEXT_EVENT (event))
1244 TplTextEvent *text = TPL_TEXT_EVENT (event);
1246 if (!tp_str_empty (tpl_text_event_get_supersedes_token (text)))
1247 icon = EMPATHY_IMAGE_EDIT_MESSAGE;
1249 #ifdef HAVE_CALL_LOGS
1250 else if (TPL_IS_CALL_EVENT (event))
1252 TplCallEvent *call = TPL_CALL_EVENT (event);
1253 TplCallEndReason reason = tpl_call_event_get_end_reason (call);
1254 TplEntity *sender = tpl_event_get_sender (event);
1255 TplEntity *receiver = tpl_event_get_receiver (event);
1257 if (reason == TPL_CALL_END_REASON_NO_ANSWER)
1258 icon = EMPATHY_IMAGE_CALL_MISSED;
1259 else if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
1260 icon = EMPATHY_IMAGE_CALL_OUTGOING;
1261 else if (tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
1262 icon = EMPATHY_IMAGE_CALL_INCOMING;
1270 log_window_append_chat_message (TplEvent *event,
1271 EmpathyMessage *message)
1273 GtkTreeStore *store = log_window->priv->store_events;
1274 GtkTreeIter iter, parent;
1275 gchar *pretty_date, *alias, *body;
1277 EmpathyStringParser *parsers;
1280 date = g_date_time_new_from_unix_utc (
1281 tpl_event_get_timestamp (event));
1283 pretty_date = g_date_time_format (date, "%X");
1285 get_parent_iter_for_message (event, message, &parent);
1287 alias = g_markup_escape_text (
1288 tpl_entity_get_alias (tpl_event_get_sender (event)), -1);
1290 /* escape the text */
1291 parsers = empathy_webkit_get_string_parser (
1292 g_settings_get_boolean (log_window->priv->gsettings_chat,
1293 EMPATHY_PREFS_CHAT_SHOW_SMILEYS));
1294 msg = g_string_new ("");
1296 empathy_string_parser_substr (empathy_message_get_body (message), -1,
1299 if (tpl_text_event_get_message_type (TPL_TEXT_EVENT (event))
1300 == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)
1302 /* Translators: this is an emote: '* Danielle waves' */
1303 body = g_strdup_printf (_("<i>* %s %s</i>"), alias, msg->str);
1307 /* Translators: this is a message: 'Danielle: hello'
1308 * The string in bold is the sender's name */
1309 body = g_strdup_printf (_("<b>%s:</b> %s"), alias, msg->str);
1312 gtk_tree_store_append (store, &iter, &parent);
1313 gtk_tree_store_set (store, &iter,
1314 COL_EVENTS_TS, tpl_event_get_timestamp (event),
1315 COL_EVENTS_PRETTY_DATE, pretty_date,
1316 COL_EVENTS_TEXT, body,
1317 COL_EVENTS_ICON, get_icon_for_event (event),
1318 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
1319 COL_EVENTS_TARGET, event_get_target (event),
1320 COL_EVENTS_EVENT, event,
1323 g_string_free (msg, TRUE);
1326 g_free (pretty_date);
1327 g_date_time_unref (date);
1330 #ifdef HAVE_CALL_LOGS
1332 log_window_append_call (TplEvent *event,
1333 EmpathyMessage *message)
1335 TplCallEvent *call = TPL_CALL_EVENT (event);
1336 GtkTreeStore *store = log_window->priv->store_events;
1337 GtkTreeIter iter, child;
1338 gchar *pretty_date, *duration, *finished;
1339 GDateTime *started_date, *finished_date;
1342 /* If searching, only add the call if the search string appears anywhere */
1343 if (!EMP_STR_EMPTY (log_window->priv->last_find))
1345 if (strstr (tpl_entity_get_identifier (tpl_event_get_sender (event)),
1346 log_window->priv->last_find) == NULL &&
1347 strstr (tpl_entity_get_identifier (tpl_event_get_receiver (event)),
1348 log_window->priv->last_find) == NULL &&
1349 strstr (tpl_call_event_get_detailed_end_reason (call),
1350 log_window->priv->last_find) == NULL)
1352 DEBUG ("TplCallEvent doesn't match search string, ignoring");
1357 started_date = g_date_time_new_from_unix_utc (
1358 tpl_event_get_timestamp (event));
1360 pretty_date = g_date_time_format (started_date,
1361 C_("A date with the time", "%A, %e %B %Y %X"));
1363 gtk_tree_store_append (store, &iter, NULL);
1364 gtk_tree_store_set (store, &iter,
1365 COL_EVENTS_TS, tpl_event_get_timestamp (event),
1366 COL_EVENTS_PRETTY_DATE, pretty_date,
1367 COL_EVENTS_TEXT, empathy_message_get_body (message),
1368 COL_EVENTS_ICON, get_icon_for_event (event),
1369 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
1370 COL_EVENTS_TARGET, event_get_target (event),
1371 COL_EVENTS_EVENT, event,
1374 if (tpl_call_event_get_end_reason (call) != TPL_CALL_END_REASON_NO_ANSWER)
1378 span = tpl_call_event_get_duration (TPL_CALL_EVENT (event));
1380 duration = g_strdup_printf (_("%" G_GINT64_FORMAT " seconds"), span);
1382 duration = g_strdup_printf (_("%" G_GINT64_FORMAT " minutes"),
1385 finished_date = g_date_time_add (started_date, -span);
1386 finished = g_date_time_format (finished_date, "%X");
1387 g_date_time_unref (finished_date);
1389 body = g_strdup_printf (_("Call took %s, ended at %s"),
1390 duration, finished);
1395 gtk_tree_store_append (store, &child, &iter);
1396 gtk_tree_store_set (store, &child,
1397 COL_EVENTS_TS, tpl_event_get_timestamp (event),
1398 COL_EVENTS_TEXT, body,
1399 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
1400 COL_EVENTS_TARGET, event_get_target (event),
1401 COL_EVENTS_EVENT, event,
1407 g_free (pretty_date);
1408 g_date_time_unref (started_date);
1413 log_window_append_message (TplEvent *event,
1414 EmpathyMessage *message)
1416 if (TPL_IS_TEXT_EVENT (event))
1417 log_window_append_chat_message (event, message);
1418 #ifdef HAVE_CALL_LOGS
1419 else if (TPL_IS_CALL_EVENT (event))
1420 log_window_append_call (event, message);
1423 DEBUG ("Message type not handled");
1427 add_all_accounts_and_entities (GList **accounts,
1431 GtkTreeModel *model;
1434 view = GTK_TREE_VIEW (log_window->priv->treeview_who);
1435 model = gtk_tree_view_get_model (view);
1437 if (!gtk_tree_model_get_iter_first (model, &iter))
1446 gtk_tree_model_get (model, &iter,
1447 COL_WHO_ACCOUNT, &account,
1448 COL_WHO_TARGET, &entity,
1449 COL_WHO_TYPE, &type,
1452 if (type != COL_TYPE_NORMAL)
1455 if (accounts != NULL)
1456 *accounts = g_list_append (*accounts, account);
1458 if (entities != NULL)
1459 *entities = g_list_append (*entities, entity);
1461 while (gtk_tree_model_iter_next (model, &iter));
1465 log_window_get_selected (EmpathyLogWindow *self,
1470 TplEventTypeMask *event_mask,
1471 EventSubtype *subtype)
1474 GtkTreeModel *model;
1475 GtkTreeSelection *selection;
1477 TplEventTypeMask ev = 0;
1478 EventSubtype st = 0;
1482 view = GTK_TREE_VIEW (self->priv->treeview_who);
1483 model = gtk_tree_view_get_model (view);
1484 selection = gtk_tree_view_get_selection (view);
1486 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1490 if (accounts != NULL)
1492 if (entities != NULL)
1497 for (l = paths; l != NULL; l = l->next)
1499 GtkTreePath *path = l->data;
1503 gtk_tree_model_get_iter (model, &iter, path);
1504 gtk_tree_model_get (model, &iter,
1505 COL_WHO_ACCOUNT, &account,
1506 COL_WHO_TARGET, &entity,
1507 COL_WHO_TYPE, &type,
1510 if (type == COL_TYPE_ANY)
1512 if (accounts != NULL || entities != NULL)
1513 add_all_accounts_and_entities (accounts, entities);
1519 if (accounts != NULL)
1520 *accounts = g_list_append (*accounts, g_object_ref (account));
1522 if (entities != NULL)
1523 *entities = g_list_append (*entities, g_object_ref (entity));
1525 g_object_unref (account);
1526 g_object_unref (entity);
1528 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1530 view = GTK_TREE_VIEW (self->priv->treeview_what);
1531 model = gtk_tree_view_get_model (view);
1532 selection = gtk_tree_view_get_selection (view);
1534 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1535 for (l = paths; l != NULL; l = l->next)
1537 GtkTreePath *path = l->data;
1538 TplEventTypeMask mask;
1539 EventSubtype submask;
1541 gtk_tree_model_get_iter (model, &iter, path);
1542 gtk_tree_model_get (model, &iter,
1543 COL_WHAT_TYPE, &mask,
1544 COL_WHAT_SUBTYPE, &submask,
1550 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1552 view = GTK_TREE_VIEW (self->priv->treeview_when);
1553 model = gtk_tree_view_get_model (view);
1554 selection = gtk_tree_view_get_selection (view);
1560 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1561 for (l = paths; l != NULL; l = l->next)
1563 GtkTreePath *path = l->data;
1566 gtk_tree_model_get_iter (model, &iter, path);
1567 gtk_tree_model_get (model, &iter,
1568 COL_WHEN_DATE, &date,
1571 *dates = g_list_append (*dates, date);
1573 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1576 if (event_mask != NULL)
1579 if (subtype != NULL)
1586 model_has_entity (GtkTreeModel *model,
1591 TplLogSearchHit *hit = data;
1594 gboolean ret = FALSE;
1596 gtk_tree_model_get (model, iter,
1598 COL_WHO_ACCOUNT, &a,
1601 if (e != NULL && entity_equal (hit->target, e) &&
1602 a != NULL && account_equal (hit->account, a))
1604 ret = has_element = TRUE;
1607 tp_clear_object (&e);
1608 tp_clear_object (&a);
1614 model_has_date (GtkTreeModel *model,
1622 gtk_tree_model_get (model, iter,
1626 if (!g_date_compare (date, d))
1636 get_events_for_date (TplActionChain *chain, gpointer user_data);
1639 populate_events_from_search_hits (GList *accounts,
1643 TplEventTypeMask event_mask;
1644 EventSubtype subtype;
1647 gboolean is_anytime = FALSE;
1649 if (!log_window_get_selected (log_window,
1650 NULL, NULL, NULL, NULL, &event_mask, &subtype))
1653 anytime = g_date_new_dmy (2, 1, -1);
1654 if (g_list_find_custom (dates, anytime, (GCompareFunc) g_date_compare))
1657 for (l = log_window->priv->hits; l != NULL; l = l->next)
1659 TplLogSearchHit *hit = l->data;
1661 gboolean found = FALSE;
1663 /* Protect against invalid data (corrupt or old log files). */
1664 if (hit->account == NULL || hit->target == NULL)
1667 for (acc = accounts, targ = targets;
1668 acc != NULL && targ != NULL && !found;
1669 acc = acc->next, targ = targ->next)
1671 TpAccount *account = acc->data;
1672 TplEntity *target = targ->data;
1674 if (account_equal (hit->account, account) &&
1675 entity_equal (hit->target, target))
1683 g_list_find_custom (dates, hit->date, (GCompareFunc) g_date_compare)
1688 ctx = ctx_new (log_window, hit->account, hit->target, hit->date,
1689 event_mask, subtype, log_window->priv->count);
1690 _tpl_action_chain_append (log_window->priv->chain,
1691 get_events_for_date, ctx);
1696 _tpl_action_chain_start (log_window->priv->chain);
1698 g_date_free (anytime);
1702 format_date_for_display (GDate *date)
1708 /* g_date_strftime sucks */
1710 now = g_date_new ();
1711 g_date_set_time_t (now, time (NULL));
1713 days_elapsed = g_date_days_between (date, now);
1715 if (days_elapsed < 0)
1719 else if (days_elapsed == 0)
1721 text = g_strdup (_("Today"));
1723 else if (days_elapsed == 1)
1725 text = g_strdup (_("Yesterday"));
1731 dt = g_date_time_new_utc (g_date_get_year (date),
1732 g_date_get_month (date), g_date_get_day (date),
1735 if (days_elapsed <= 7)
1736 text = g_date_time_format (dt, "%A");
1738 text = g_date_time_format (dt,
1739 C_("A date such as '23 May 2010', "
1740 "%e is the day, %B the month and %Y the year",
1743 g_date_time_unref (dt);
1752 populate_dates_from_search_hits (GList *accounts,
1757 GtkTreeModel *model;
1758 GtkListStore *store;
1759 GtkTreeSelection *selection;
1762 if (log_window == NULL)
1765 view = GTK_TREE_VIEW (log_window->priv->treeview_when);
1766 model = gtk_tree_view_get_model (view);
1767 store = GTK_LIST_STORE (model);
1768 selection = gtk_tree_view_get_selection (view);
1770 for (l = log_window->priv->hits; l != NULL; l = l->next)
1772 TplLogSearchHit *hit = l->data;
1774 gboolean found = FALSE;
1776 /* Protect against invalid data (corrupt or old log files). */
1777 if (hit->account == NULL || hit->target == NULL)
1780 for (acc = accounts, targ = targets;
1781 acc != NULL && targ != NULL && !found;
1782 acc = acc->next, targ = targ->next)
1784 TpAccount *account = acc->data;
1785 TplEntity *target = targ->data;
1787 if (account_equal (hit->account, account) &&
1788 entity_equal (hit->target, target))
1795 /* Add the date if it's not already there */
1796 has_element = FALSE;
1797 gtk_tree_model_foreach (model, model_has_date, hit->date);
1800 gchar *text = format_date_for_display (hit->date);
1802 gtk_list_store_append (store, &iter);
1803 gtk_list_store_set (store, &iter,
1804 COL_WHEN_DATE, hit->date,
1805 COL_WHEN_TEXT, text,
1806 COL_WHEN_ICON, CALENDAR_ICON,
1811 if (gtk_tree_model_get_iter_first (model, &iter))
1813 gtk_list_store_prepend (store, &iter);
1814 gtk_list_store_set (store, &iter,
1815 COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
1816 COL_WHEN_TEXT, "separator",
1819 gtk_list_store_prepend (store, &iter);
1820 gtk_list_store_set (store, &iter,
1821 COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
1822 COL_WHEN_TEXT, _("Anytime"),
1825 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 2))
1826 gtk_tree_selection_select_iter (selection, &iter);
1831 populate_entities_from_search_hits (void)
1833 EmpathyAccountChooser *account_chooser;
1836 GtkTreeModel *model;
1837 GtkTreeSelection *selection;
1839 GtkListStore *store;
1842 view = GTK_TREE_VIEW (log_window->priv->treeview_who);
1843 model = gtk_tree_view_get_model (view);
1844 store = GTK_LIST_STORE (model);
1845 selection = gtk_tree_view_get_selection (view);
1847 gtk_list_store_clear (store);
1849 account_chooser = EMPATHY_ACCOUNT_CHOOSER (log_window->priv->account_chooser);
1850 account = empathy_account_chooser_get_account (account_chooser);
1852 for (l = log_window->priv->hits; l; l = l->next)
1854 TplLogSearchHit *hit = l->data;
1856 /* Protect against invalid data (corrupt or old log files). */
1857 if (hit->account == NULL || hit->target == NULL)
1860 /* Filter based on the selected account */
1861 if (account != NULL && !account_equal (account, hit->account))
1864 /* Add the entity if it's not already there */
1865 has_element = FALSE;
1866 gtk_tree_model_foreach (model, model_has_entity, hit);
1869 TplEntityType type = tpl_entity_get_entity_type (hit->target);
1870 EmpathyContact *contact;
1871 gboolean room = type == TPL_ENTITY_ROOM;
1873 contact = empathy_contact_from_tpl_contact (hit->account,
1876 gtk_list_store_append (store, &iter);
1877 gtk_list_store_set (store, &iter,
1878 COL_WHO_TYPE, COL_TYPE_NORMAL,
1879 COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
1880 : EMPATHY_IMAGE_AVATAR_DEFAULT,
1881 COL_WHO_NAME, empathy_contact_get_alias (contact),
1882 COL_WHO_ID, tpl_entity_get_identifier (hit->target),
1883 COL_WHO_ACCOUNT, hit->account,
1884 COL_WHO_TARGET, hit->target,
1887 g_object_unref (contact);
1891 if (gtk_tree_model_get_iter_first (model, &iter))
1893 gtk_list_store_prepend (store, &iter);
1894 gtk_list_store_set (store, &iter,
1895 COL_WHO_TYPE, COL_TYPE_SEPARATOR,
1896 COL_WHO_NAME, "separator",
1899 gtk_list_store_prepend (store, &iter);
1900 gtk_list_store_set (store, &iter,
1901 COL_WHO_TYPE, COL_TYPE_ANY,
1902 COL_WHO_NAME, _("Anyone"),
1906 /* Select 'Anyone' */
1907 if (gtk_tree_model_get_iter_first (model, &iter))
1908 gtk_tree_selection_select_iter (selection, &iter);
1912 log_manager_searched_new_cb (GObject *manager,
1913 GAsyncResult *result,
1918 GtkTreeSelection *selection;
1919 GError *error = NULL;
1921 if (log_window == NULL)
1924 if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager),
1925 result, &hits, &error))
1927 DEBUG ("%s. Aborting", error->message);
1928 g_error_free (error);
1932 tp_clear_pointer (&log_window->priv->hits, tpl_log_manager_search_free);
1933 log_window->priv->hits = hits;
1935 view = GTK_TREE_VIEW (log_window->priv->treeview_when);
1936 selection = gtk_tree_view_get_selection (view);
1938 g_signal_handlers_unblock_by_func (selection,
1939 log_window_when_changed_cb,
1942 populate_entities_from_search_hits ();
1946 log_window_find_populate (EmpathyLogWindow *self,
1947 const gchar *search_criteria)
1950 GtkTreeModel *model;
1951 GtkTreeSelection *selection;
1952 GtkListStore *store;
1954 gtk_tree_store_clear (self->priv->store_events);
1956 view = GTK_TREE_VIEW (self->priv->treeview_who);
1957 model = gtk_tree_view_get_model (view);
1958 store = GTK_LIST_STORE (model);
1960 gtk_list_store_clear (store);
1962 view = GTK_TREE_VIEW (self->priv->treeview_when);
1963 model = gtk_tree_view_get_model (view);
1964 store = GTK_LIST_STORE (model);
1965 selection = gtk_tree_view_get_selection (view);
1967 gtk_list_store_clear (store);
1969 if (EMP_STR_EMPTY (search_criteria))
1971 tp_clear_pointer (&self->priv->hits, tpl_log_manager_search_free);
1972 webkit_web_view_set_highlight_text_matches (
1973 WEBKIT_WEB_VIEW (self->priv->webview), FALSE);
1974 log_window_who_populate (self);
1978 g_signal_handlers_block_by_func (selection,
1979 log_window_when_changed_cb,
1982 /* highlight the search text */
1983 webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (self->priv->webview),
1984 search_criteria, FALSE, 0);
1986 tpl_log_manager_search_async (self->priv->log_manager,
1987 search_criteria, TPL_EVENT_MASK_ANY,
1988 log_manager_searched_new_cb, NULL);
1992 start_find_search (EmpathyLogWindow *self)
1996 str = gtk_entry_get_text (GTK_ENTRY (self->priv->search_entry));
1998 /* Don't find the same crap again */
1999 if (self->priv->last_find && !tp_strdiff (self->priv->last_find, str))
2002 g_free (self->priv->last_find);
2003 self->priv->last_find = g_strdup (str);
2005 log_window_find_populate (self, str);
2011 log_window_search_entry_changed_cb (GtkWidget *entry,
2012 EmpathyLogWindow *self)
2014 if (self->priv->source != 0)
2015 g_source_remove (self->priv->source);
2016 self->priv->source = g_timeout_add (500, (GSourceFunc) start_find_search,
2021 log_window_search_entry_activate_cb (GtkWidget *entry,
2022 EmpathyLogWindow *self)
2024 start_find_search (self);
2028 log_window_search_entry_icon_pressed_cb (GtkEntry *entry,
2029 GtkEntryIconPosition icon_pos,
2033 if (icon_pos != GTK_ENTRY_ICON_SECONDARY)
2036 gtk_entry_buffer_set_text (gtk_entry_get_buffer (entry),
2041 log_window_update_buttons_sensitivity (EmpathyLogWindow *self)
2044 GtkTreeModel *model;
2045 GtkTreeSelection *selection;
2046 EmpathyCapabilities capabilities;
2052 gboolean profile, chat, call, video;
2054 profile = chat = call = video = FALSE;
2056 tp_clear_object (&self->priv->button_video_binding);
2057 tp_clear_object (&self->priv->selected_contact);
2059 view = GTK_TREE_VIEW (self->priv->treeview_who);
2060 model = gtk_tree_view_get_model (view);
2061 selection = gtk_tree_view_get_selection (view);
2063 profile = chat = call = video = FALSE;
2065 if (!gtk_tree_model_get_iter_first (model, &iter))
2068 if (gtk_tree_selection_count_selected_rows (selection) != 1)
2071 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2074 paths = gtk_tree_selection_get_selected_rows (selection, &model);
2075 g_return_if_fail (paths != NULL);
2078 gtk_tree_model_get_iter (model, &iter, path);
2079 gtk_tree_model_get (model, &iter,
2080 COL_WHO_ACCOUNT, &account,
2081 COL_WHO_TARGET, &target,
2084 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
2086 self->priv->selected_contact = empathy_contact_from_tpl_contact (account,
2089 g_object_unref (account);
2090 g_object_unref (target);
2092 capabilities = empathy_contact_get_capabilities (self->priv->selected_contact);
2094 profile = chat = TRUE;
2095 call = capabilities & EMPATHY_CAPABILITIES_AUDIO;
2096 video = capabilities & EMPATHY_CAPABILITIES_VIDEO;
2101 /* If the Who pane doesn't contain a contact (e.g. it has many
2102 * selected, or has 'Anyone', let's try to get the contact from
2103 * the selected event. */
2105 if (self->priv->events_contact != NULL)
2106 self->priv->selected_contact = g_object_ref (self->priv->events_contact);
2110 capabilities = empathy_contact_get_capabilities (self->priv->selected_contact);
2112 profile = chat = TRUE;
2113 call = capabilities & EMPATHY_CAPABILITIES_AUDIO;
2114 video = capabilities & EMPATHY_CAPABILITIES_VIDEO;
2117 self->priv->button_video_binding = g_object_bind_property (
2118 self->priv->camera_monitor, "available",
2119 self->priv->button_video, "sensitive",
2120 G_BINDING_SYNC_CREATE);
2123 gtk_widget_set_sensitive (self->priv->button_profile, profile);
2124 gtk_widget_set_sensitive (self->priv->button_chat, chat);
2125 gtk_widget_set_sensitive (self->priv->button_call, call);
2127 /* Don't override the binding */
2129 gtk_widget_set_sensitive (self->priv->button_video, video);
2133 log_window_update_what_iter_sensitivity (GtkTreeModel *model,
2137 GtkTreeStore *store = GTK_TREE_STORE (model);
2141 gtk_tree_store_set (store, iter,
2142 COL_WHAT_SENSITIVE, sensitive,
2145 for (next = gtk_tree_model_iter_children (model, &child, iter);
2147 next = gtk_tree_model_iter_next (model, &child))
2149 gtk_tree_store_set (store, &child,
2150 COL_WHAT_SENSITIVE, sensitive,
2156 log_window_update_what_sensitivity (EmpathyLogWindow *self)
2159 GtkTreeModel *model;
2161 GList *accounts, *targets, *acc, *targ;
2164 if (!log_window_get_selected (self, &accounts, &targets, NULL, NULL,
2168 view = GTK_TREE_VIEW (self->priv->treeview_what);
2169 model = gtk_tree_view_get_model (view);
2171 /* For each event type... */
2172 for (next = gtk_tree_model_get_iter_first (model, &iter);
2174 next = gtk_tree_model_iter_next (model, &iter))
2176 TplEventTypeMask type;
2178 gtk_tree_model_get (model, &iter,
2179 COL_WHAT_TYPE, &type,
2182 /* ...we set the type and its subtypes (if any) unsensitive... */
2183 log_window_update_what_iter_sensitivity (model, &iter, FALSE);
2185 for (acc = accounts, targ = targets;
2186 acc != NULL && targ != NULL;
2187 acc = acc->next, targ = targ->next)
2189 TpAccount *account = acc->data;
2190 TplEntity *target = targ->data;
2192 if (tpl_log_manager_exists (self->priv->log_manager,
2193 account, target, type))
2195 /* And then we set it (and its subtypes, again, if any)
2196 * as sensitive if there are logs of that type. */
2197 log_window_update_what_iter_sensitivity (model, &iter, TRUE);
2203 g_list_free_full (accounts, g_object_unref);
2204 g_list_free_full (targets, g_object_unref);
2208 log_window_who_changed_cb (GtkTreeSelection *selection,
2209 EmpathyLogWindow *self)
2212 GtkTreeModel *model;
2215 DEBUG ("log_window_who_changed_cb");
2217 view = gtk_tree_selection_get_tree_view (selection);
2218 model = gtk_tree_view_get_model (view);
2220 if (gtk_tree_model_get_iter_first (model, &iter))
2222 /* If 'Anyone' is selected, everything else should be deselected */
2223 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2225 g_signal_handlers_block_by_func (selection,
2226 log_window_who_changed_cb,
2229 gtk_tree_selection_unselect_all (selection);
2230 gtk_tree_selection_select_iter (selection, &iter);
2232 g_signal_handlers_unblock_by_func (selection,
2233 log_window_who_changed_cb,
2238 log_window_update_what_sensitivity (self);
2239 log_window_update_buttons_sensitivity (self);
2241 /* The contact changed, so the dates need to be updated */
2242 log_window_chats_get_messages (self, TRUE);
2246 log_manager_got_entities_cb (GObject *manager,
2247 GAsyncResult *result,
2250 Ctx *ctx = user_data;
2254 GtkTreeModel *model;
2255 GtkTreeSelection *selection;
2256 GtkListStore *store;
2258 GError *error = NULL;
2259 gboolean select_account = FALSE;
2261 if (log_window == NULL)
2264 if (log_window->priv->count != ctx->count)
2267 if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
2268 result, &entities, &error))
2270 DEBUG ("%s. Aborting", error->message);
2271 g_error_free (error);
2275 view = GTK_TREE_VIEW (ctx->self->priv->treeview_who);
2276 model = gtk_tree_view_get_model (view);
2277 selection = gtk_tree_view_get_selection (view);
2278 store = GTK_LIST_STORE (model);
2280 /* Block signals to stop the logs being retrieved prematurely */
2281 g_signal_handlers_block_by_func (selection,
2282 log_window_who_changed_cb, ctx->self);
2284 for (l = entities; l; l = l->next)
2286 TplEntity *entity = TPL_ENTITY (l->data);
2287 TplEntityType type = tpl_entity_get_entity_type (entity);
2288 EmpathyContact *contact;
2289 gboolean room = type == TPL_ENTITY_ROOM;
2291 contact = empathy_contact_from_tpl_contact (ctx->account, entity);
2293 gtk_list_store_append (store, &iter);
2294 gtk_list_store_set (store, &iter,
2295 COL_WHO_TYPE, COL_TYPE_NORMAL,
2296 COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
2297 : EMPATHY_IMAGE_AVATAR_DEFAULT,
2298 COL_WHO_NAME, empathy_contact_get_alias (contact),
2299 COL_WHO_ID, tpl_entity_get_identifier (entity),
2300 COL_WHO_ACCOUNT, ctx->account,
2301 COL_WHO_TARGET, entity,
2304 g_object_unref (contact);
2306 if (ctx->self->priv->selected_account != NULL &&
2307 !tp_strdiff (tp_proxy_get_object_path (ctx->account),
2308 tp_proxy_get_object_path (ctx->self->priv->selected_account)))
2309 select_account = TRUE;
2311 g_list_free_full (entities, g_object_unref);
2313 if (gtk_tree_model_get_iter_first (model, &iter))
2317 gtk_tree_model_get (model, &iter,
2318 COL_WHO_TYPE, &type,
2321 if (type != COL_TYPE_ANY)
2323 gtk_list_store_prepend (store, &iter);
2324 gtk_list_store_set (store, &iter,
2325 COL_WHO_TYPE, COL_TYPE_SEPARATOR,
2326 COL_WHO_NAME, "separator",
2329 gtk_list_store_prepend (store, &iter);
2330 gtk_list_store_set (store, &iter,
2331 COL_WHO_TYPE, COL_TYPE_ANY,
2332 COL_WHO_NAME, _("Anyone"),
2337 /* Unblock signals */
2338 g_signal_handlers_unblock_by_func (selection,
2339 log_window_who_changed_cb,
2342 /* We display the selected account if we populate the model with chats from
2345 log_window_chats_set_selected (ctx->self);
2348 _tpl_action_chain_continue (log_window->priv->chain);
2353 get_entities_for_account (TplActionChain *chain, gpointer user_data)
2355 Ctx *ctx = user_data;
2357 tpl_log_manager_get_entities_async (ctx->self->priv->log_manager, ctx->account,
2358 log_manager_got_entities_cb, ctx);
2362 select_first_entity (TplActionChain *chain, gpointer user_data)
2364 EmpathyLogWindow *self = user_data;
2366 GtkTreeModel *model;
2367 GtkTreeSelection *selection;
2370 view = GTK_TREE_VIEW (self->priv->treeview_who);
2371 model = gtk_tree_view_get_model (view);
2372 selection = gtk_tree_view_get_selection (view);
2374 if (gtk_tree_model_get_iter_first (model, &iter))
2375 gtk_tree_selection_select_iter (selection, &iter);
2377 _tpl_action_chain_continue (self->priv->chain);
2381 log_window_who_populate (EmpathyLogWindow *self)
2383 EmpathyAccountChooser *account_chooser;
2385 gboolean all_accounts;
2387 GtkTreeModel *model;
2388 GtkTreeSelection *selection;
2389 GtkListStore *store;
2392 if (self->priv->hits != NULL)
2394 populate_entities_from_search_hits ();
2398 account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
2399 account = empathy_account_chooser_dup_account (account_chooser);
2400 all_accounts = empathy_account_chooser_has_all_selected (account_chooser);
2402 view = GTK_TREE_VIEW (self->priv->treeview_who);
2403 model = gtk_tree_view_get_model (view);
2404 selection = gtk_tree_view_get_selection (view);
2405 store = GTK_LIST_STORE (model);
2407 /* Block signals to stop the logs being retrieved prematurely */
2408 g_signal_handlers_block_by_func (selection,
2409 log_window_who_changed_cb,
2412 gtk_list_store_clear (store);
2414 /* Unblock signals */
2415 g_signal_handlers_unblock_by_func (selection,
2416 log_window_who_changed_cb,
2419 _tpl_action_chain_clear (self->priv->chain);
2420 self->priv->count++;
2422 if (!all_accounts && account == NULL)
2426 else if (!all_accounts)
2428 ctx = ctx_new (self, account, NULL, NULL, 0, 0, self->priv->count);
2429 _tpl_action_chain_append (self->priv->chain, get_entities_for_account, ctx);
2433 TpAccountManager *manager;
2434 GList *accounts, *l;
2436 manager = empathy_account_chooser_get_account_manager (account_chooser);
2437 accounts = tp_account_manager_get_valid_accounts (manager);
2439 for (l = accounts; l != NULL; l = l->next)
2443 ctx = ctx_new (self, account, NULL, NULL, 0, 0, self->priv->count);
2444 _tpl_action_chain_append (self->priv->chain,
2445 get_entities_for_account, ctx);
2448 g_list_free (accounts);
2450 _tpl_action_chain_append (self->priv->chain, select_first_entity, self);
2451 _tpl_action_chain_start (self->priv->chain);
2455 sort_by_name (GtkTreeModel *model,
2460 gchar *name1, *name2;
2464 gtk_tree_model_get (model, a,
2465 COL_WHO_TYPE, &type1,
2466 COL_WHO_NAME, &name1,
2469 gtk_tree_model_get (model, b,
2470 COL_WHO_TYPE, &type2,
2471 COL_WHO_NAME, &name2,
2474 if (type1 == COL_TYPE_ANY)
2476 else if (type2 == COL_TYPE_ANY)
2478 else if (type1 == COL_TYPE_SEPARATOR)
2480 else if (type2 == COL_TYPE_SEPARATOR)
2483 ret = g_strcmp0 (name1, name2);
2492 who_row_is_separator (GtkTreeModel *model,
2498 gtk_tree_model_get (model, iter,
2499 COL_WHO_TYPE, &type,
2502 return (type == COL_TYPE_SEPARATOR);
2506 log_window_find_row (EmpathyLogWindow *self,
2507 GdkEventButton *event)
2509 WebKitHitTestResult *hit = webkit_web_view_get_hit_test_result (
2510 WEBKIT_WEB_VIEW (self->priv->webview), event);
2511 WebKitDOMNode *inner_node;
2513 tp_clear_object (&self->priv->events_contact);
2516 "inner-node", &inner_node,
2519 if (inner_node != NULL)
2521 GtkTreeModel *model = GTK_TREE_MODEL (self->priv->store_events);
2522 WebKitDOMNode *node;
2523 const char *path = NULL;
2526 /* walk back up the DOM tree looking for a node with empathy:path set */
2527 for (node = inner_node; node != NULL;
2528 node = webkit_dom_node_get_parent_node (node))
2530 if (!WEBKIT_DOM_IS_ELEMENT (node))
2533 path = webkit_dom_element_get_attribute_ns (
2534 WEBKIT_DOM_ELEMENT (node), EMPATHY_NS, "path");
2536 if (!tp_str_empty (path))
2540 /* look up the contact for this path */
2541 if (!tp_str_empty (path) &&
2542 gtk_tree_model_get_iter_from_string (model, &iter, path))
2547 gtk_tree_model_get (model, &iter,
2548 COL_EVENTS_ACCOUNT, &account,
2549 COL_EVENTS_TARGET, &target,
2552 self->priv->events_contact = empathy_contact_from_tpl_contact (
2555 g_object_unref (account);
2556 g_object_unref (target);
2559 g_object_unref (inner_node);
2562 g_object_unref (hit);
2564 log_window_update_buttons_sensitivity (self);
2568 log_window_events_button_press_event (GtkWidget *webview,
2569 GdkEventButton *event,
2570 EmpathyLogWindow *self)
2572 switch (event->button)
2575 log_window_find_row (self, event);
2579 empathy_webkit_context_menu_for_event (
2580 WEBKIT_WEB_VIEW (webview), event, 0);
2591 log_window_events_setup (EmpathyLogWindow *self)
2593 GtkTreeSortable *sortable;
2594 GtkTreeStore *store;
2597 self->priv->store_events = store = gtk_tree_store_new (COL_EVENTS_COUNT,
2598 G_TYPE_INT, /* type */
2599 G_TYPE_INT64, /* timestamp */
2600 G_TYPE_STRING, /* stringified date */
2601 G_TYPE_STRING, /* icon */
2602 G_TYPE_STRING, /* name */
2603 TP_TYPE_ACCOUNT, /* account */
2604 TPL_TYPE_ENTITY, /* target */
2605 TPL_TYPE_EVENT); /* event */
2607 sortable = GTK_TREE_SORTABLE (store);
2609 gtk_tree_sortable_set_sort_column_id (sortable,
2611 GTK_SORT_ASCENDING);
2615 log_window_who_setup (EmpathyLogWindow *self)
2618 GtkTreeModel *model;
2619 GtkTreeSelection *selection;
2620 GtkTreeSortable *sortable;
2621 GtkTreeViewColumn *column;
2622 GtkListStore *store;
2623 GtkCellRenderer *cell;
2625 view = GTK_TREE_VIEW (self->priv->treeview_who);
2626 selection = gtk_tree_view_get_selection (view);
2629 store = gtk_list_store_new (COL_WHO_COUNT,
2630 G_TYPE_INT, /* type */
2631 G_TYPE_STRING, /* icon */
2632 G_TYPE_STRING, /* name */
2633 G_TYPE_STRING, /* id */
2634 TP_TYPE_ACCOUNT, /* account */
2635 TPL_TYPE_ENTITY); /* target */
2637 model = GTK_TREE_MODEL (store);
2638 sortable = GTK_TREE_SORTABLE (store);
2640 gtk_tree_view_set_model (view, model);
2643 column = gtk_tree_view_column_new ();
2644 gtk_tree_view_column_set_title (column, _("Who"));
2646 cell = gtk_cell_renderer_pixbuf_new ();
2647 gtk_tree_view_column_pack_start (column, cell, FALSE);
2648 gtk_tree_view_column_add_attribute (column, cell,
2652 cell = gtk_cell_renderer_text_new ();
2653 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2654 gtk_tree_view_column_pack_start (column, cell, TRUE);
2655 gtk_tree_view_column_add_attribute (column, cell,
2659 gtk_tree_view_append_column (view, column);
2661 /* set up treeview properties */
2662 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2663 gtk_tree_view_set_row_separator_func (view, who_row_is_separator,
2666 gtk_tree_sortable_set_sort_column_id (sortable,
2668 GTK_SORT_ASCENDING);
2669 gtk_tree_sortable_set_sort_func (sortable,
2670 COL_WHO_NAME, sort_by_name,
2673 gtk_tree_view_set_search_column (view, COL_WHO_NAME);
2674 gtk_tree_view_set_tooltip_column (view, COL_WHO_ID);
2676 /* set up signals */
2677 g_signal_connect (selection, "changed",
2678 G_CALLBACK (log_window_who_changed_cb), self);
2680 g_object_unref (store);
2684 log_window_chats_accounts_changed_cb (GtkWidget *combobox,
2685 EmpathyLogWindow *self)
2687 /* Clear all current messages shown in the textview */
2688 gtk_tree_store_clear (self->priv->store_events);
2690 log_window_who_populate (self);
2694 log_window_chats_set_selected (EmpathyLogWindow *self)
2697 GtkTreeModel *model;
2698 GtkTreeSelection *selection;
2703 view = GTK_TREE_VIEW (self->priv->treeview_who);
2704 model = gtk_tree_view_get_model (view);
2705 selection = gtk_tree_view_get_selection (view);
2707 for (next = gtk_tree_model_get_iter_first (model, &iter);
2709 next = gtk_tree_model_iter_next (model, &iter))
2711 TpAccount *this_account;
2712 TplEntity *this_target;
2713 const gchar *this_chat_id;
2714 gboolean this_is_chatroom;
2717 gtk_tree_model_get (model, &iter,
2718 COL_WHO_TYPE, &this_type,
2719 COL_WHO_ACCOUNT, &this_account,
2720 COL_WHO_TARGET, &this_target,
2723 if (this_type != COL_TYPE_NORMAL)
2726 this_chat_id = tpl_entity_get_identifier (this_target);
2727 this_is_chatroom = tpl_entity_get_entity_type (this_target)
2730 if (this_account == self->priv->selected_account &&
2731 !tp_strdiff (this_chat_id, self->priv->selected_chat_id) &&
2732 this_is_chatroom == self->priv->selected_is_chatroom)
2734 gtk_tree_selection_select_iter (selection, &iter);
2735 path = gtk_tree_model_get_path (model, &iter);
2736 gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 0.5, 0.0);
2737 gtk_tree_path_free (path);
2738 g_object_unref (this_account);
2739 g_object_unref (this_target);
2743 g_object_unref (this_account);
2744 g_object_unref (this_target);
2747 tp_clear_object (&self->priv->selected_account);
2748 tp_clear_pointer (&self->priv->selected_chat_id, g_free);
2752 sort_by_date (GtkTreeModel *model,
2757 GDate *date1, *date2;
2759 gtk_tree_model_get (model, a,
2760 COL_WHEN_DATE, &date1,
2763 gtk_tree_model_get (model, b,
2764 COL_WHEN_DATE, &date2,
2767 return g_date_compare (date1, date2);
2771 when_row_is_separator (GtkTreeModel *model,
2778 gtk_tree_model_get (model, iter,
2779 COL_WHEN_TEXT, &when,
2782 ret = !tp_strdiff (when, "separator");
2788 log_window_when_changed_cb (GtkTreeSelection *selection,
2789 EmpathyLogWindow *self)
2792 GtkTreeModel *model;
2795 DEBUG ("log_window_when_changed_cb");
2797 view = gtk_tree_selection_get_tree_view (selection);
2798 model = gtk_tree_view_get_model (view);
2800 /* If 'Anytime' is selected, everything else should be deselected */
2801 if (gtk_tree_model_get_iter_first (model, &iter))
2803 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2805 g_signal_handlers_block_by_func (selection,
2806 log_window_when_changed_cb,
2809 gtk_tree_selection_unselect_all (selection);
2810 gtk_tree_selection_select_iter (selection, &iter);
2812 g_signal_handlers_unblock_by_func (selection,
2813 log_window_when_changed_cb,
2818 log_window_chats_get_messages (self, FALSE);
2822 log_window_when_setup (EmpathyLogWindow *self)
2825 GtkTreeModel *model;
2826 GtkTreeSelection *selection;
2827 GtkTreeSortable *sortable;
2828 GtkTreeViewColumn *column;
2829 GtkListStore *store;
2830 GtkCellRenderer *cell;
2832 view = GTK_TREE_VIEW (self->priv->treeview_when);
2833 selection = gtk_tree_view_get_selection (view);
2836 store = gtk_list_store_new (COL_WHEN_COUNT,
2837 G_TYPE_DATE, /* date */
2838 G_TYPE_STRING, /* stringified date */
2839 G_TYPE_STRING); /* icon */
2841 model = GTK_TREE_MODEL (store);
2842 sortable = GTK_TREE_SORTABLE (store);
2844 gtk_tree_view_set_model (view, model);
2847 column = gtk_tree_view_column_new ();
2848 gtk_tree_view_column_set_title (column, _("When"));
2850 cell = gtk_cell_renderer_pixbuf_new ();
2851 gtk_tree_view_column_pack_start (column, cell, FALSE);
2852 gtk_tree_view_column_add_attribute (column, cell,
2853 "icon-name", COL_WHEN_ICON);
2855 cell = gtk_cell_renderer_text_new ();
2856 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2857 gtk_tree_view_column_pack_start (column, cell, TRUE);
2858 gtk_tree_view_column_add_attribute (column, cell,
2862 gtk_tree_view_append_column (view, column);
2864 /* set up treeview properties */
2865 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2866 gtk_tree_view_set_row_separator_func (view, when_row_is_separator,
2868 gtk_tree_sortable_set_sort_column_id (sortable,
2870 GTK_SORT_DESCENDING);
2871 gtk_tree_sortable_set_sort_func (sortable,
2872 COL_WHEN_DATE, sort_by_date,
2875 gtk_tree_view_set_search_column (view, COL_WHEN_TEXT);
2877 /* set up signals */
2878 g_signal_connect (selection, "changed",
2879 G_CALLBACK (log_window_when_changed_cb),
2882 g_object_unref (store);
2886 what_row_is_separator (GtkTreeModel *model,
2892 gtk_tree_model_get (model, iter,
2893 COL_WHAT_TYPE, &type,
2896 return (type == WHAT_TYPE_SEPARATOR);
2900 log_window_what_changed_cb (GtkTreeSelection *selection,
2901 EmpathyLogWindow *self)
2904 GtkTreeModel *model;
2907 DEBUG ("log_window_what_changed_cb");
2909 view = gtk_tree_selection_get_tree_view (selection);
2910 model = gtk_tree_view_get_model (view);
2912 /* If 'Anything' is selected, everything else should be deselected */
2913 if (gtk_tree_model_get_iter_first (model, &iter))
2915 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2917 g_signal_handlers_block_by_func (selection,
2918 log_window_what_changed_cb,
2921 gtk_tree_selection_unselect_all (selection);
2922 gtk_tree_selection_select_iter (selection, &iter);
2924 g_signal_handlers_unblock_by_func (selection,
2925 log_window_what_changed_cb,
2930 /* The dates need to be updated if we're not searching */
2931 log_window_chats_get_messages (self, self->priv->hits == NULL);
2935 log_window_what_collapse_row_cb (GtkTreeView *tree_view,
2940 /* Reject collapsing */
2947 EventSubtype subtype;
2953 log_window_what_setup (EmpathyLogWindow *self)
2956 GtkTreeModel *model;
2957 GtkTreeSelection *selection;
2958 GtkTreeViewColumn *column;
2960 GtkTreeStore *store;
2961 GtkCellRenderer *cell;
2963 struct event events [] = {
2964 { TPL_EVENT_MASK_ANY, 0, NULL, _("Anything") },
2965 { WHAT_TYPE_SEPARATOR, 0, NULL, "separator" },
2966 { TPL_EVENT_MASK_TEXT, 0, "stock_text_justify", _("Text chats") },
2967 #ifdef HAVE_CALL_LOGS
2968 { TPL_EVENT_MASK_CALL, EVENT_CALL_ALL, EMPATHY_IMAGE_CALL, _("Calls") },
2971 #ifdef HAVE_CALL_LOGS
2972 struct event call_events [] = {
2973 { TPL_EVENT_MASK_CALL, EVENT_CALL_INCOMING, EMPATHY_IMAGE_CALL_INCOMING, _("Incoming calls") },
2974 { TPL_EVENT_MASK_CALL, EVENT_CALL_OUTGOING, EMPATHY_IMAGE_CALL_OUTGOING, _("Outgoing calls") },
2975 { TPL_EVENT_MASK_CALL, EVENT_CALL_MISSED, EMPATHY_IMAGE_CALL_MISSED, _("Missed calls") }
2980 view = GTK_TREE_VIEW (self->priv->treeview_what);
2981 selection = gtk_tree_view_get_selection (view);
2984 store = gtk_tree_store_new (COL_WHAT_COUNT,
2985 G_TYPE_INT, /* history type */
2986 G_TYPE_INT, /* history subtype */
2987 G_TYPE_BOOLEAN, /* sensitive */
2988 G_TYPE_STRING, /* stringified history type */
2989 G_TYPE_STRING); /* icon */
2991 model = GTK_TREE_MODEL (store);
2993 gtk_tree_view_set_model (view, model);
2996 column = gtk_tree_view_column_new ();
2997 gtk_tree_view_column_set_title (column, _("What"));
2999 cell = gtk_cell_renderer_pixbuf_new ();
3000 gtk_tree_view_column_pack_start (column, cell, FALSE);
3001 gtk_tree_view_column_add_attribute (column, cell,
3002 "icon-name", COL_WHAT_ICON);
3004 cell = gtk_cell_renderer_text_new ();
3005 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
3006 gtk_tree_view_column_pack_start (column, cell, TRUE);
3007 gtk_tree_view_column_add_attribute (column, cell,
3008 "text", COL_WHAT_TEXT);
3009 gtk_tree_view_column_add_attribute (column, cell,
3010 "sensitive", COL_WHAT_SENSITIVE);
3012 gtk_tree_view_append_column (view, column);
3013 gtk_tree_view_set_search_column (view, COL_WHAT_TEXT);
3015 /* set up treeview properties */
3016 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
3017 gtk_tree_view_set_show_expanders (view, FALSE);
3018 gtk_tree_view_set_level_indentation (view, 12);
3019 gtk_tree_view_expand_all (view);
3020 gtk_tree_view_set_row_separator_func (view, what_row_is_separator,
3024 for (i = 0; i < G_N_ELEMENTS (events); i++)
3026 gtk_tree_store_append (store, &iter, NULL);
3027 gtk_tree_store_set (store, &iter,
3028 COL_WHAT_TYPE, events[i].type,
3029 COL_WHAT_SUBTYPE, events[i].subtype,
3030 COL_WHAT_SENSITIVE, TRUE,
3031 COL_WHAT_TEXT, events[i].text,
3032 COL_WHAT_ICON, events[i].icon,
3036 #ifdef HAVE_CALL_LOGS
3037 gtk_tree_model_iter_nth_child (model, &parent, NULL, 3);
3038 for (i = 0; i < G_N_ELEMENTS (call_events); i++)
3040 gtk_tree_store_append (store, &iter, &parent);
3041 gtk_tree_store_set (store, &iter,
3042 COL_WHAT_TYPE, call_events[i].type,
3043 COL_WHAT_SUBTYPE, call_events[i].subtype,
3044 COL_WHAT_SENSITIVE, TRUE,
3045 COL_WHAT_TEXT, call_events[i].text,
3046 COL_WHAT_ICON, call_events[i].icon,
3051 gtk_tree_view_expand_all (view);
3053 /* select 'Anything' */
3054 if (gtk_tree_model_get_iter_first (model, &iter))
3055 gtk_tree_selection_select_iter (selection, &iter);
3057 /* set up signals */
3058 g_signal_connect (view, "test-collapse-row",
3059 G_CALLBACK (log_window_what_collapse_row_cb),
3061 g_signal_connect (selection, "changed",
3062 G_CALLBACK (log_window_what_changed_cb),
3065 g_object_unref (store);
3069 log_window_maybe_expand_events (void)
3071 GtkTreeModel *model = GTK_TREE_MODEL (log_window->priv->store_events);
3073 /* If there's only one result, expand it */
3074 if (gtk_tree_model_iter_n_children (model, NULL) == 1)
3075 webkit_web_view_execute_script (
3076 WEBKIT_WEB_VIEW (log_window->priv->webview),
3077 "javascript:expandAll()");
3081 show_spinner (gpointer data)
3085 if (log_window == NULL)
3088 g_object_get (log_window->priv->spinner, "active", &active, NULL);
3091 gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->priv->notebook),
3098 show_events (TplActionChain *chain,
3101 log_window_maybe_expand_events ();
3102 gtk_spinner_stop (GTK_SPINNER (log_window->priv->spinner));
3103 gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->priv->notebook),
3106 _tpl_action_chain_continue (chain);
3110 start_spinner (void)
3112 gtk_spinner_start (GTK_SPINNER (log_window->priv->spinner));
3113 gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->priv->notebook),
3116 g_timeout_add (1000, show_spinner, NULL);
3117 _tpl_action_chain_append (log_window->priv->chain, show_events, NULL);
3121 log_window_got_messages_for_date_cb (GObject *manager,
3122 GAsyncResult *result,
3125 Ctx *ctx = user_data;
3126 GtkTreeModel *model;
3130 GError *error = NULL;
3133 if (log_window == NULL)
3139 if (log_window->priv->count != ctx->count)
3142 if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager),
3143 result, &events, &error))
3145 DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
3147 g_error_free (error);
3151 for (l = events; l; l = l->next)
3153 TplEvent *event = l->data;
3154 gboolean append = TRUE;
3156 #ifdef HAVE_CALL_LOGS
3157 if (TPL_IS_CALL_EVENT (l->data)
3158 && ctx->event_mask & TPL_EVENT_MASK_CALL
3159 && ctx->event_mask != TPL_EVENT_MASK_ANY)
3161 TplCallEvent *call = l->data;
3165 if (ctx->subtype & EVENT_CALL_ALL)
3171 TplCallEndReason reason = tpl_call_event_get_end_reason (call);
3172 TplEntity *sender = tpl_event_get_sender (event);
3173 TplEntity *receiver = tpl_event_get_receiver (event);
3175 if (reason == TPL_CALL_END_REASON_NO_ANSWER)
3177 if (ctx->subtype & EVENT_CALL_MISSED)
3180 else if (ctx->subtype & EVENT_CALL_OUTGOING
3181 && tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
3185 else if (ctx->subtype & EVENT_CALL_INCOMING
3186 && tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
3196 EmpathyMessage *msg = empathy_message_from_tpl_log_event (event);
3197 log_window_append_message (event, msg);
3198 tp_clear_object (&msg);
3201 g_object_unref (event);
3203 g_list_free (events);
3205 model = GTK_TREE_MODEL (log_window->priv->store_events);
3206 n = gtk_tree_model_iter_n_children (model, NULL) - 1;
3208 if (n >= 0 && gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
3213 path = gtk_tree_model_get_path (model, &iter);
3214 str = gtk_tree_path_to_string (path);
3216 script = g_strdup_printf ("javascript:scrollToRow([%s]);",
3217 g_strdelimit (str, ":", ','));
3219 webkit_web_view_execute_script (
3220 WEBKIT_WEB_VIEW (log_window->priv->webview),
3223 gtk_tree_path_free (path);
3231 _tpl_action_chain_continue (log_window->priv->chain);
3235 get_events_for_date (TplActionChain *chain, gpointer user_data)
3237 Ctx *ctx = user_data;
3239 tpl_log_manager_get_events_for_date_async (ctx->self->priv->log_manager,
3240 ctx->account, ctx->entity, ctx->event_mask,
3242 log_window_got_messages_for_date_cb,
3247 log_window_get_messages_for_dates (EmpathyLogWindow *self,
3250 GList *accounts, *targets, *acc, *targ, *l;
3251 TplEventTypeMask event_mask;
3252 EventSubtype subtype;
3253 GDate *date, *anytime, *separator;
3255 if (!log_window_get_selected (self,
3256 &accounts, &targets, NULL, NULL, &event_mask, &subtype))
3259 anytime = g_date_new_dmy (2, 1, -1);
3260 separator = g_date_new_dmy (1, 1, -1);
3262 _tpl_action_chain_clear (self->priv->chain);
3263 self->priv->count++;
3265 for (acc = accounts, targ = targets;
3266 acc != NULL && targ != NULL;
3267 acc = acc->next, targ = targ->next)
3269 TpAccount *account = acc->data;
3270 TplEntity *target = targ->data;
3272 for (l = dates; l != NULL; l = l->next)
3277 if (g_date_compare (date, anytime) != 0)
3281 ctx = ctx_new (self, account, target, date, event_mask, subtype,
3283 _tpl_action_chain_append (self->priv->chain, get_events_for_date, ctx);
3287 GtkTreeView *view = GTK_TREE_VIEW (self->priv->treeview_when);
3288 GtkTreeModel *model = gtk_tree_view_get_model (view);
3293 for (next = gtk_tree_model_get_iter_first (model, &iter);
3295 next = gtk_tree_model_iter_next (model, &iter))
3299 gtk_tree_model_get (model, &iter,
3303 if (g_date_compare (d, anytime) != 0 &&
3304 g_date_compare (d, separator) != 0)
3306 ctx = ctx_new (self, account, target, d,
3307 event_mask, subtype, self->priv->count);
3308 _tpl_action_chain_append (self->priv->chain, get_events_for_date, ctx);
3316 _tpl_action_chain_start (self->priv->chain);
3318 g_list_free_full (accounts, g_object_unref);
3319 g_list_free_full (targets, g_object_unref);
3320 g_date_free (separator);
3321 g_date_free (anytime);
3325 log_manager_got_dates_cb (GObject *manager,
3326 GAsyncResult *result,
3329 Ctx *ctx = user_data;
3331 GtkTreeModel *model;
3332 GtkListStore *store;
3336 GError *error = NULL;
3338 if (log_window == NULL)
3344 if (log_window->priv->count != ctx->count)
3347 if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
3348 result, &dates, &error))
3350 DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
3355 view = GTK_TREE_VIEW (log_window->priv->treeview_when);
3356 model = gtk_tree_view_get_model (view);
3357 store = GTK_LIST_STORE (model);
3359 for (l = dates; l != NULL; l = l->next)
3361 GDate *date = l->data;
3363 /* Add the date if it's not already there */
3364 has_element = FALSE;
3365 gtk_tree_model_foreach (model, model_has_date, date);
3368 gchar *text = format_date_for_display (date);
3370 gtk_list_store_append (store, &iter);
3371 gtk_list_store_set (store, &iter,
3372 COL_WHEN_DATE, date,
3373 COL_WHEN_TEXT, text,
3374 COL_WHEN_ICON, CALENDAR_ICON,
3381 if (gtk_tree_model_get_iter_first (model, &iter))
3383 gchar *separator = NULL;
3385 if (gtk_tree_model_iter_next (model, &iter))
3387 gtk_tree_model_get (model, &iter,
3388 COL_WHEN_TEXT, &separator,
3392 if (g_strcmp0 (separator, "separator") != 0)
3394 gtk_list_store_prepend (store, &iter);
3395 gtk_list_store_set (store, &iter,
3396 COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
3397 COL_WHEN_TEXT, "separator",
3400 gtk_list_store_prepend (store, &iter);
3401 gtk_list_store_set (store, &iter,
3402 COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
3403 COL_WHEN_TEXT, _("Anytime"),
3408 g_list_free_full (dates, g_free);
3411 _tpl_action_chain_continue (log_window->priv->chain);
3415 select_date (TplActionChain *chain, gpointer user_data)
3418 GtkTreeModel *model;
3419 GtkTreeSelection *selection;
3422 gboolean selected = FALSE;
3424 view = GTK_TREE_VIEW (log_window->priv->treeview_when);
3425 model = gtk_tree_view_get_model (view);
3426 selection = gtk_tree_view_get_selection (view);
3428 if (log_window->priv->current_dates != NULL)
3430 for (next = gtk_tree_model_get_iter_first (model, &iter);
3432 next = gtk_tree_model_iter_next (model, &iter))
3436 gtk_tree_model_get (model, &iter,
3437 COL_WHEN_DATE, &date,
3440 if (g_list_find_custom (log_window->priv->current_dates, date,
3441 (GCompareFunc) g_date_compare) != NULL)
3445 gtk_tree_selection_select_iter (selection, &iter);
3446 path = gtk_tree_model_get_path (model, &iter);
3447 gtk_tree_view_scroll_to_cell (view, path, NULL, FALSE, 0, 0);
3450 gtk_tree_path_free (path);
3459 /* Show messages of the most recent date */
3460 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 2))
3461 gtk_tree_selection_select_iter (selection, &iter);
3464 _tpl_action_chain_continue (log_window->priv->chain);
3468 get_dates_for_entity (TplActionChain *chain, gpointer user_data)
3470 Ctx *ctx = user_data;
3472 tpl_log_manager_get_dates_async (ctx->self->priv->log_manager,
3473 ctx->account, ctx->entity, ctx->event_mask,
3474 log_manager_got_dates_cb, ctx);
3478 log_window_chats_get_messages (EmpathyLogWindow *self,
3479 gboolean force_get_dates)
3481 GList *accounts, *targets, *dates;
3482 TplEventTypeMask event_mask;
3484 GtkTreeModel *model;
3485 GtkListStore *store;
3486 GtkTreeSelection *selection;
3488 if (!log_window_get_selected (self, &accounts, &targets, NULL,
3489 &dates, &event_mask, NULL))
3492 view = GTK_TREE_VIEW (self->priv->treeview_when);
3493 selection = gtk_tree_view_get_selection (view);
3494 model = gtk_tree_view_get_model (view);
3495 store = GTK_LIST_STORE (model);
3497 /* Clear all current messages shown in the textview */
3498 gtk_tree_store_clear (self->priv->store_events);
3500 _tpl_action_chain_clear (self->priv->chain);
3501 self->priv->count++;
3503 /* If there's a search use the returned hits */
3504 if (self->priv->hits != NULL)
3506 if (force_get_dates)
3508 g_signal_handlers_block_by_func (selection,
3509 log_window_when_changed_cb,
3512 gtk_list_store_clear (store);
3514 g_signal_handlers_unblock_by_func (selection,
3515 log_window_when_changed_cb,
3518 populate_dates_from_search_hits (accounts, targets);
3522 populate_events_from_search_hits (accounts, targets, dates);
3525 /* Either use the supplied date or get the last */
3526 else if (force_get_dates || dates == NULL)
3530 if (self->priv->current_dates != NULL)
3532 g_list_free_full (self->priv->current_dates,
3533 (GDestroyNotify) g_date_free);
3534 self->priv->current_dates = NULL;
3537 if (gtk_tree_selection_count_selected_rows (selection) > 0)
3542 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
3544 for (l = paths; l != NULL; l = l->next)
3546 GtkTreePath *path = l->data;
3549 gtk_tree_model_get_iter (model, &iter, path);
3550 gtk_tree_model_get (model, &iter,
3551 COL_WHEN_DATE, &date,
3554 /* The list takes ownership of the date. */
3555 self->priv->current_dates =
3556 g_list_prepend (self->priv->current_dates, date);
3559 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
3562 g_signal_handlers_block_by_func (selection,
3563 log_window_when_changed_cb,
3566 gtk_list_store_clear (store);
3568 g_signal_handlers_unblock_by_func (selection,
3569 log_window_when_changed_cb,
3572 /* Get a list of dates and show them on the treeview */
3573 for (targ = targets, acc = accounts;
3574 targ != NULL && acc != NULL;
3575 targ = targ->next, acc = acc->next)
3577 TpAccount *account = acc->data;
3578 TplEntity *target = targ->data;
3579 Ctx *ctx = ctx_new (self, account, target, NULL, event_mask, 0,
3582 _tpl_action_chain_append (self->priv->chain, get_dates_for_entity, ctx);
3584 _tpl_action_chain_append (self->priv->chain, select_date, NULL);
3585 _tpl_action_chain_start (self->priv->chain);
3589 /* Show messages of the selected date */
3590 log_window_get_messages_for_dates (self, dates);
3593 g_list_free_full (accounts, g_object_unref);
3594 g_list_free_full (targets, g_object_unref);
3595 g_list_free_full (dates, (GFreeFunc) g_date_free);
3599 EmpathyAccountChooserFilterResultCallback callback;
3601 } FilterCallbackData;
3604 got_entities (GObject *manager,
3605 GAsyncResult *result,
3608 FilterCallbackData *data = user_data;
3610 GError *error = NULL;
3612 if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
3613 result, &entities, &error))
3615 DEBUG ("Could not get entities: %s", error->message);
3616 g_error_free (error);
3617 data->callback (FALSE, data->user_data);
3621 data->callback (entities != NULL, data->user_data);
3623 g_list_free_full (entities, g_object_unref);
3626 g_slice_free (FilterCallbackData, data);
3630 empathy_account_chooser_filter_has_logs (TpAccount *account,
3631 EmpathyAccountChooserFilterResultCallback callback,
3632 gpointer callback_data,
3635 TplLogManager *manager = tpl_log_manager_dup_singleton ();
3636 FilterCallbackData *cb_data = g_slice_new0 (FilterCallbackData);
3638 cb_data->callback = callback;
3639 cb_data->user_data = callback_data;
3641 tpl_log_manager_get_entities_async (manager, account, got_entities, cb_data);
3643 g_object_unref (manager);
3647 log_window_logger_clear_account_cb (TpProxy *proxy,
3648 const GError *error,
3650 GObject *weak_object)
3652 EmpathyLogWindow *self = EMPATHY_LOG_WINDOW (user_data);
3655 g_warning ("Error when clearing logs: %s", error->message);
3657 /* Refresh the log viewer so the logs are cleared if the account
3658 * has been deleted */
3659 gtk_tree_store_clear (self->priv->store_events);
3660 log_window_who_populate (self);
3662 /* Re-filter the account chooser so the accounts without logs get greyed out */
3663 empathy_account_chooser_set_filter (
3664 EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser),
3665 empathy_account_chooser_filter_has_logs, NULL);
3669 log_window_clear_logs_chooser_select_account (EmpathyAccountChooser *chooser,
3670 EmpathyLogWindow *self)
3672 EmpathyAccountChooser *account_chooser;
3674 account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
3676 empathy_account_chooser_set_account (chooser,
3677 empathy_account_chooser_get_account (account_chooser));
3681 log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
3682 EmpathyLogWindow *self)
3684 GtkWidget *dialog, *content_area, *hbox, *label;
3685 EmpathyAccountChooser *account_chooser;
3689 GError *error = NULL;
3691 account_chooser = (EmpathyAccountChooser *) empathy_account_chooser_new ();
3692 empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
3693 empathy_account_chooser_set_filter (account_chooser,
3694 empathy_account_chooser_filter_has_logs, NULL);
3696 /* Select the same account as in the history window */
3697 if (empathy_account_chooser_is_ready (account_chooser))
3698 log_window_clear_logs_chooser_select_account (account_chooser, self);
3700 g_signal_connect (account_chooser, "ready",
3701 G_CALLBACK (log_window_clear_logs_chooser_select_account), self);
3703 dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (self),
3704 GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING,
3706 _("Are you sure you want to delete all logs of previous conversations?"));
3708 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
3709 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3710 _("Clear All"), GTK_RESPONSE_APPLY,
3713 content_area = gtk_message_dialog_get_message_area (
3714 GTK_MESSAGE_DIALOG (dialog));
3716 hbox = gtk_hbox_new (FALSE, 6);
3717 label = gtk_label_new (_("Delete from:"));
3718 gtk_box_pack_start (GTK_BOX (hbox), label,
3720 gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (account_chooser),
3722 gtk_box_pack_start (GTK_BOX (content_area), hbox,
3725 gtk_widget_show_all (hbox);
3727 response_id = gtk_dialog_run (GTK_DIALOG (dialog));
3729 if (response_id != GTK_RESPONSE_APPLY)
3732 bus = tp_dbus_daemon_dup (&error);
3735 g_warning ("Could not delete logs: %s", error->message);
3736 g_error_free (error);
3740 logger = g_object_new (TP_TYPE_PROXY,
3741 "bus-name", "org.freedesktop.Telepathy.Logger",
3742 "object-path", "/org/freedesktop/Telepathy/Logger",
3745 g_object_unref (bus);
3747 tp_proxy_add_interface_by_id (logger, EMP_IFACE_QUARK_LOGGER);
3749 if (empathy_account_chooser_has_all_selected (account_chooser))
3751 DEBUG ("Deleting logs for all the accounts");
3753 emp_cli_logger_call_clear (logger, -1,
3754 log_window_logger_clear_account_cb,
3755 self, NULL, G_OBJECT (self));
3761 account = empathy_account_chooser_get_account (account_chooser);
3763 DEBUG ("Deleting logs for %s", tp_proxy_get_object_path (account));
3765 emp_cli_logger_call_clear_account (logger, -1,
3766 tp_proxy_get_object_path (account),
3767 log_window_logger_clear_account_cb,
3768 self, NULL, G_OBJECT (self));
3771 g_object_unref (logger);
3773 gtk_widget_destroy (dialog);