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, *closeitem;
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_close", &closeitem,
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 (closeitem, "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)
1044 TpAccountManager *am;
1046 am = tp_account_manager_dup ();
1048 self->priv->observer = tp_simple_observer_new_with_am (am, TRUE, "LogWindow",
1049 TRUE, observe_channels,
1050 g_object_ref (self), g_object_unref);
1052 self->priv->channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1053 g_object_unref, g_object_unref);
1055 tp_base_client_take_observer_filter (self->priv->observer,
1057 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
1058 TP_IFACE_CHANNEL_TYPE_TEXT,
1060 tp_base_client_take_observer_filter (self->priv->observer,
1062 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
1063 TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
1065 tp_base_client_take_observer_filter (self->priv->observer,
1067 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
1068 TPY_IFACE_CHANNEL_TYPE_CALL,
1071 tp_base_client_register (self->priv->observer, NULL);
1073 g_object_unref (am);
1077 event_get_target (TplEvent *event)
1079 TplEntity *sender = tpl_event_get_sender (event);
1080 TplEntity *receiver = tpl_event_get_receiver (event);
1082 if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
1089 model_is_parent (GtkTreeModel *model,
1093 TplEvent *stored_event;
1096 gboolean found = FALSE;
1099 if (gtk_tree_model_iter_parent (model, &parent, iter))
1102 gtk_tree_model_get (model, iter,
1103 COL_EVENTS_ACCOUNT, &account,
1104 COL_EVENTS_TARGET, &target,
1105 COL_EVENTS_EVENT, &stored_event,
1108 if (G_OBJECT_TYPE (event) == G_OBJECT_TYPE (stored_event) &&
1109 account_equal (account, tpl_event_get_account (event)) &&
1110 (entity_equal (target, event_get_target (event)) ||
1111 is_same_confroom (event, stored_event)))
1116 gtk_tree_model_iter_nth_child (model, &child, iter,
1117 gtk_tree_model_iter_n_children (model, iter) - 1);
1119 gtk_tree_model_get (model, &child,
1120 COL_EVENTS_TS, ×tamp,
1123 if (ABS (tpl_event_get_timestamp (event) - timestamp) < MAX_GAP)
1125 /* The gap is smaller than 30 min */
1130 g_object_unref (stored_event);
1131 g_object_unref (account);
1132 g_object_unref (target);
1138 get_display_string_for_chat_message (EmpathyMessage *message,
1141 EmpathyContact *sender, *receiver, *target;
1142 TplEntity *ent_sender, *ent_receiver;
1143 const gchar *format;
1145 sender = empathy_message_get_sender (message);
1146 receiver = empathy_message_get_receiver (message);
1148 ent_sender = tpl_event_get_sender (event);
1149 ent_receiver = tpl_event_get_receiver (event);
1151 /* If this is a MUC, we want to show "Chat in <room>". */
1152 if (tpl_entity_get_entity_type (ent_sender) == TPL_ENTITY_ROOM ||
1153 (ent_receiver != NULL &&
1154 tpl_entity_get_entity_type (ent_receiver) == TPL_ENTITY_ROOM))
1155 format = _("Chat in %s");
1157 format = _("Chat with %s");
1159 if (tpl_entity_get_entity_type (ent_sender) == TPL_ENTITY_ROOM)
1161 else if (ent_receiver != NULL &&
1162 tpl_entity_get_entity_type (ent_receiver) == TPL_ENTITY_ROOM)
1164 else if (empathy_contact_is_user (sender))
1169 return g_markup_printf_escaped (format, empathy_contact_get_alias (target));
1173 get_parent_iter_for_message (TplEvent *event,
1174 EmpathyMessage *message,
1175 GtkTreeIter *parent)
1177 GtkTreeStore *store;
1178 GtkTreeModel *model;
1180 gboolean parent_found = FALSE;
1183 store = log_window->priv->store_events;
1184 model = GTK_TREE_MODEL (store);
1186 for (next = gtk_tree_model_get_iter_first (model, &iter);
1188 next = gtk_tree_model_iter_next (model, &iter))
1190 if ((parent_found = model_is_parent (model, &iter, event)))
1201 gchar *body, *pretty_date;
1203 date = g_date_time_new_from_unix_local (
1204 tpl_event_get_timestamp (event));
1206 pretty_date = g_date_time_format (date,
1207 C_("A date with the time", "%A, %e %B %Y %X"));
1209 body = get_display_string_for_chat_message (message, event);
1211 gtk_tree_store_append (store, &iter, NULL);
1212 gtk_tree_store_set (store, &iter,
1213 COL_EVENTS_TS, tpl_event_get_timestamp (event),
1214 COL_EVENTS_PRETTY_DATE, pretty_date,
1215 COL_EVENTS_TEXT, body,
1216 COL_EVENTS_ICON, "stock_text_justify",
1217 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
1218 COL_EVENTS_TARGET, event_get_target (event),
1219 COL_EVENTS_EVENT, event,
1225 g_free (pretty_date);
1226 g_date_time_unref (date);
1230 static const gchar *
1231 get_icon_for_event (TplEvent *event)
1233 const gchar *icon = NULL;
1235 if (TPL_IS_TEXT_EVENT (event))
1237 TplTextEvent *text = TPL_TEXT_EVENT (event);
1239 if (!tp_str_empty (tpl_text_event_get_supersedes_token (text)))
1240 icon = EMPATHY_IMAGE_EDIT_MESSAGE;
1242 #ifdef HAVE_CALL_LOGS
1243 else if (TPL_IS_CALL_EVENT (event))
1245 TplCallEvent *call = TPL_CALL_EVENT (event);
1246 TplCallEndReason reason = tpl_call_event_get_end_reason (call);
1247 TplEntity *sender = tpl_event_get_sender (event);
1248 TplEntity *receiver = tpl_event_get_receiver (event);
1250 if (reason == TPL_CALL_END_REASON_NO_ANSWER)
1251 icon = EMPATHY_IMAGE_CALL_MISSED;
1252 else if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
1253 icon = EMPATHY_IMAGE_CALL_OUTGOING;
1254 else if (tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
1255 icon = EMPATHY_IMAGE_CALL_INCOMING;
1263 log_window_append_chat_message (TplEvent *event,
1264 EmpathyMessage *message)
1266 GtkTreeStore *store = log_window->priv->store_events;
1267 GtkTreeIter iter, parent;
1268 gchar *pretty_date, *alias, *body;
1270 EmpathyStringParser *parsers;
1273 date = g_date_time_new_from_unix_local (
1274 tpl_event_get_timestamp (event));
1276 pretty_date = g_date_time_format (date, "%X");
1278 get_parent_iter_for_message (event, message, &parent);
1280 alias = g_markup_escape_text (
1281 tpl_entity_get_alias (tpl_event_get_sender (event)), -1);
1283 /* escape the text */
1284 parsers = empathy_webkit_get_string_parser (
1285 g_settings_get_boolean (log_window->priv->gsettings_chat,
1286 EMPATHY_PREFS_CHAT_SHOW_SMILEYS));
1287 msg = g_string_new ("");
1289 empathy_string_parser_substr (empathy_message_get_body (message), -1,
1292 if (tpl_text_event_get_message_type (TPL_TEXT_EVENT (event))
1293 == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)
1295 /* Translators: this is an emote: '* Danielle waves' */
1296 body = g_strdup_printf (_("<i>* %s %s</i>"), alias, msg->str);
1300 /* Translators: this is a message: 'Danielle: hello'
1301 * The string in bold is the sender's name */
1302 body = g_strdup_printf (_("<b>%s:</b> %s"), alias, msg->str);
1305 gtk_tree_store_append (store, &iter, &parent);
1306 gtk_tree_store_set (store, &iter,
1307 COL_EVENTS_TS, tpl_event_get_timestamp (event),
1308 COL_EVENTS_PRETTY_DATE, pretty_date,
1309 COL_EVENTS_TEXT, body,
1310 COL_EVENTS_ICON, get_icon_for_event (event),
1311 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
1312 COL_EVENTS_TARGET, event_get_target (event),
1313 COL_EVENTS_EVENT, event,
1316 g_string_free (msg, TRUE);
1319 g_free (pretty_date);
1320 g_date_time_unref (date);
1323 #ifdef HAVE_CALL_LOGS
1325 log_window_append_call (TplEvent *event,
1326 EmpathyMessage *message)
1328 TplCallEvent *call = TPL_CALL_EVENT (event);
1329 GtkTreeStore *store = log_window->priv->store_events;
1330 GtkTreeIter iter, child;
1331 gchar *pretty_date, *duration, *finished;
1332 GDateTime *started_date, *finished_date;
1335 /* If searching, only add the call if the search string appears anywhere */
1336 if (!EMP_STR_EMPTY (log_window->priv->last_find))
1338 if (strstr (tpl_entity_get_identifier (tpl_event_get_sender (event)),
1339 log_window->priv->last_find) == NULL &&
1340 strstr (tpl_entity_get_identifier (tpl_event_get_receiver (event)),
1341 log_window->priv->last_find) == NULL &&
1342 strstr (tpl_call_event_get_detailed_end_reason (call),
1343 log_window->priv->last_find) == NULL)
1345 DEBUG ("TplCallEvent doesn't match search string, ignoring");
1350 started_date = g_date_time_new_from_unix_local (
1351 tpl_event_get_timestamp (event));
1353 pretty_date = g_date_time_format (started_date,
1354 C_("A date with the time", "%A, %e %B %Y %X"));
1356 gtk_tree_store_append (store, &iter, NULL);
1357 gtk_tree_store_set (store, &iter,
1358 COL_EVENTS_TS, tpl_event_get_timestamp (event),
1359 COL_EVENTS_PRETTY_DATE, pretty_date,
1360 COL_EVENTS_TEXT, empathy_message_get_body (message),
1361 COL_EVENTS_ICON, get_icon_for_event (event),
1362 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
1363 COL_EVENTS_TARGET, event_get_target (event),
1364 COL_EVENTS_EVENT, event,
1367 if (tpl_call_event_get_end_reason (call) != TPL_CALL_END_REASON_NO_ANSWER)
1371 span = tpl_call_event_get_duration (TPL_CALL_EVENT (event));
1373 duration = g_strdup_printf (_("%" G_GINT64_FORMAT " seconds"), span);
1375 duration = g_strdup_printf (_("%" G_GINT64_FORMAT " minutes"),
1378 finished_date = g_date_time_add (started_date, -span);
1379 finished = g_date_time_format (finished_date, "%X");
1380 g_date_time_unref (finished_date);
1382 body = g_strdup_printf (_("Call took %s, ended at %s"),
1383 duration, finished);
1388 gtk_tree_store_append (store, &child, &iter);
1389 gtk_tree_store_set (store, &child,
1390 COL_EVENTS_TS, tpl_event_get_timestamp (event),
1391 COL_EVENTS_TEXT, body,
1392 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
1393 COL_EVENTS_TARGET, event_get_target (event),
1394 COL_EVENTS_EVENT, event,
1400 g_free (pretty_date);
1401 g_date_time_unref (started_date);
1406 log_window_append_message (TplEvent *event,
1407 EmpathyMessage *message)
1409 if (TPL_IS_TEXT_EVENT (event))
1410 log_window_append_chat_message (event, message);
1411 #ifdef HAVE_CALL_LOGS
1412 else if (TPL_IS_CALL_EVENT (event))
1413 log_window_append_call (event, message);
1416 DEBUG ("Message type not handled");
1420 add_all_accounts_and_entities (GList **accounts,
1424 GtkTreeModel *model;
1427 view = GTK_TREE_VIEW (log_window->priv->treeview_who);
1428 model = gtk_tree_view_get_model (view);
1430 if (!gtk_tree_model_get_iter_first (model, &iter))
1439 gtk_tree_model_get (model, &iter,
1440 COL_WHO_ACCOUNT, &account,
1441 COL_WHO_TARGET, &entity,
1442 COL_WHO_TYPE, &type,
1445 if (type != COL_TYPE_NORMAL)
1448 if (accounts != NULL)
1449 *accounts = g_list_append (*accounts, account);
1451 if (entities != NULL)
1452 *entities = g_list_append (*entities, entity);
1454 while (gtk_tree_model_iter_next (model, &iter));
1458 log_window_get_selected (EmpathyLogWindow *self,
1463 TplEventTypeMask *event_mask,
1464 EventSubtype *subtype)
1467 GtkTreeModel *model;
1468 GtkTreeSelection *selection;
1470 TplEventTypeMask ev = 0;
1471 EventSubtype st = 0;
1475 view = GTK_TREE_VIEW (self->priv->treeview_who);
1476 model = gtk_tree_view_get_model (view);
1477 selection = gtk_tree_view_get_selection (view);
1479 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1483 if (accounts != NULL)
1485 if (entities != NULL)
1490 for (l = paths; l != NULL; l = l->next)
1492 GtkTreePath *path = l->data;
1496 gtk_tree_model_get_iter (model, &iter, path);
1497 gtk_tree_model_get (model, &iter,
1498 COL_WHO_ACCOUNT, &account,
1499 COL_WHO_TARGET, &entity,
1500 COL_WHO_TYPE, &type,
1503 if (type == COL_TYPE_ANY)
1505 if (accounts != NULL || entities != NULL)
1506 add_all_accounts_and_entities (accounts, entities);
1512 if (accounts != NULL)
1513 *accounts = g_list_append (*accounts, g_object_ref (account));
1515 if (entities != NULL)
1516 *entities = g_list_append (*entities, g_object_ref (entity));
1518 g_object_unref (account);
1519 g_object_unref (entity);
1521 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1523 view = GTK_TREE_VIEW (self->priv->treeview_what);
1524 model = gtk_tree_view_get_model (view);
1525 selection = gtk_tree_view_get_selection (view);
1527 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1528 for (l = paths; l != NULL; l = l->next)
1530 GtkTreePath *path = l->data;
1531 TplEventTypeMask mask;
1532 EventSubtype submask;
1534 gtk_tree_model_get_iter (model, &iter, path);
1535 gtk_tree_model_get (model, &iter,
1536 COL_WHAT_TYPE, &mask,
1537 COL_WHAT_SUBTYPE, &submask,
1543 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1545 view = GTK_TREE_VIEW (self->priv->treeview_when);
1546 model = gtk_tree_view_get_model (view);
1547 selection = gtk_tree_view_get_selection (view);
1553 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1554 for (l = paths; l != NULL; l = l->next)
1556 GtkTreePath *path = l->data;
1559 gtk_tree_model_get_iter (model, &iter, path);
1560 gtk_tree_model_get (model, &iter,
1561 COL_WHEN_DATE, &date,
1564 *dates = g_list_append (*dates, date);
1566 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1569 if (event_mask != NULL)
1572 if (subtype != NULL)
1579 model_has_entity (GtkTreeModel *model,
1584 TplLogSearchHit *hit = data;
1587 gboolean ret = FALSE;
1589 gtk_tree_model_get (model, iter,
1591 COL_WHO_ACCOUNT, &a,
1594 if (e != NULL && entity_equal (hit->target, e) &&
1595 a != NULL && account_equal (hit->account, a))
1597 ret = has_element = TRUE;
1600 tp_clear_object (&e);
1601 tp_clear_object (&a);
1607 model_has_date (GtkTreeModel *model,
1615 gtk_tree_model_get (model, iter,
1619 if (!g_date_compare (date, d))
1629 get_events_for_date (TplActionChain *chain, gpointer user_data);
1632 populate_events_from_search_hits (GList *accounts,
1636 TplEventTypeMask event_mask;
1637 EventSubtype subtype;
1640 gboolean is_anytime = FALSE;
1642 if (!log_window_get_selected (log_window,
1643 NULL, NULL, NULL, NULL, &event_mask, &subtype))
1646 anytime = g_date_new_dmy (2, 1, -1);
1647 if (g_list_find_custom (dates, anytime, (GCompareFunc) g_date_compare))
1650 for (l = log_window->priv->hits; l != NULL; l = l->next)
1652 TplLogSearchHit *hit = l->data;
1654 gboolean found = FALSE;
1656 /* Protect against invalid data (corrupt or old log files). */
1657 if (hit->account == NULL || hit->target == NULL)
1660 for (acc = accounts, targ = targets;
1661 acc != NULL && targ != NULL && !found;
1662 acc = acc->next, targ = targ->next)
1664 TpAccount *account = acc->data;
1665 TplEntity *target = targ->data;
1667 if (account_equal (hit->account, account) &&
1668 entity_equal (hit->target, target))
1676 g_list_find_custom (dates, hit->date, (GCompareFunc) g_date_compare)
1681 ctx = ctx_new (log_window, hit->account, hit->target, hit->date,
1682 event_mask, subtype, log_window->priv->count);
1683 _tpl_action_chain_append (log_window->priv->chain,
1684 get_events_for_date, ctx);
1689 _tpl_action_chain_start (log_window->priv->chain);
1691 g_date_free (anytime);
1695 format_date_for_display (GDate *date)
1701 /* g_date_strftime sucks */
1703 now = g_date_new ();
1704 g_date_set_time_t (now, time (NULL));
1706 days_elapsed = g_date_days_between (date, now);
1708 if (days_elapsed < 0)
1712 else if (days_elapsed == 0)
1714 text = g_strdup (_("Today"));
1716 else if (days_elapsed == 1)
1718 text = g_strdup (_("Yesterday"));
1724 dt = g_date_time_new_utc (g_date_get_year (date),
1725 g_date_get_month (date), g_date_get_day (date),
1728 if (days_elapsed <= 7)
1729 text = g_date_time_format (dt, "%A");
1731 text = g_date_time_format (dt,
1732 C_("A date such as '23 May 2010', "
1733 "%e is the day, %B the month and %Y the year",
1736 g_date_time_unref (dt);
1745 populate_dates_from_search_hits (GList *accounts,
1750 GtkTreeModel *model;
1751 GtkListStore *store;
1752 GtkTreeSelection *selection;
1755 if (log_window == NULL)
1758 view = GTK_TREE_VIEW (log_window->priv->treeview_when);
1759 model = gtk_tree_view_get_model (view);
1760 store = GTK_LIST_STORE (model);
1761 selection = gtk_tree_view_get_selection (view);
1763 for (l = log_window->priv->hits; l != NULL; l = l->next)
1765 TplLogSearchHit *hit = l->data;
1767 gboolean found = FALSE;
1769 /* Protect against invalid data (corrupt or old log files). */
1770 if (hit->account == NULL || hit->target == NULL)
1773 for (acc = accounts, targ = targets;
1774 acc != NULL && targ != NULL && !found;
1775 acc = acc->next, targ = targ->next)
1777 TpAccount *account = acc->data;
1778 TplEntity *target = targ->data;
1780 if (account_equal (hit->account, account) &&
1781 entity_equal (hit->target, target))
1788 /* Add the date if it's not already there */
1789 has_element = FALSE;
1790 gtk_tree_model_foreach (model, model_has_date, hit->date);
1793 gchar *text = format_date_for_display (hit->date);
1795 gtk_list_store_append (store, &iter);
1796 gtk_list_store_set (store, &iter,
1797 COL_WHEN_DATE, hit->date,
1798 COL_WHEN_TEXT, text,
1799 COL_WHEN_ICON, CALENDAR_ICON,
1804 if (gtk_tree_model_get_iter_first (model, &iter))
1806 gtk_list_store_prepend (store, &iter);
1807 gtk_list_store_set (store, &iter,
1808 COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
1809 COL_WHEN_TEXT, "separator",
1812 gtk_list_store_prepend (store, &iter);
1813 gtk_list_store_set (store, &iter,
1814 COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
1815 COL_WHEN_TEXT, _("Anytime"),
1818 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 2))
1819 gtk_tree_selection_select_iter (selection, &iter);
1824 populate_entities_from_search_hits (void)
1826 EmpathyAccountChooser *account_chooser;
1829 GtkTreeModel *model;
1830 GtkTreeSelection *selection;
1832 GtkListStore *store;
1835 view = GTK_TREE_VIEW (log_window->priv->treeview_who);
1836 model = gtk_tree_view_get_model (view);
1837 store = GTK_LIST_STORE (model);
1838 selection = gtk_tree_view_get_selection (view);
1840 gtk_list_store_clear (store);
1842 account_chooser = EMPATHY_ACCOUNT_CHOOSER (log_window->priv->account_chooser);
1843 account = empathy_account_chooser_get_account (account_chooser);
1845 for (l = log_window->priv->hits; l; l = l->next)
1847 TplLogSearchHit *hit = l->data;
1849 /* Protect against invalid data (corrupt or old log files). */
1850 if (hit->account == NULL || hit->target == NULL)
1853 /* Filter based on the selected account */
1854 if (account != NULL && !account_equal (account, hit->account))
1857 /* Add the entity if it's not already there */
1858 has_element = FALSE;
1859 gtk_tree_model_foreach (model, model_has_entity, hit);
1862 TplEntityType type = tpl_entity_get_entity_type (hit->target);
1863 EmpathyContact *contact;
1864 gboolean room = type == TPL_ENTITY_ROOM;
1866 contact = empathy_contact_from_tpl_contact (hit->account,
1869 gtk_list_store_append (store, &iter);
1870 gtk_list_store_set (store, &iter,
1871 COL_WHO_TYPE, COL_TYPE_NORMAL,
1872 COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
1873 : EMPATHY_IMAGE_AVATAR_DEFAULT,
1874 COL_WHO_NAME, empathy_contact_get_alias (contact),
1875 COL_WHO_ID, tpl_entity_get_identifier (hit->target),
1876 COL_WHO_ACCOUNT, hit->account,
1877 COL_WHO_TARGET, hit->target,
1880 g_object_unref (contact);
1884 if (gtk_tree_model_get_iter_first (model, &iter))
1886 gtk_list_store_prepend (store, &iter);
1887 gtk_list_store_set (store, &iter,
1888 COL_WHO_TYPE, COL_TYPE_SEPARATOR,
1889 COL_WHO_NAME, "separator",
1892 gtk_list_store_prepend (store, &iter);
1893 gtk_list_store_set (store, &iter,
1894 COL_WHO_TYPE, COL_TYPE_ANY,
1895 COL_WHO_NAME, _("Anyone"),
1899 /* Select 'Anyone' */
1900 if (gtk_tree_model_get_iter_first (model, &iter))
1901 gtk_tree_selection_select_iter (selection, &iter);
1905 log_manager_searched_new_cb (GObject *manager,
1906 GAsyncResult *result,
1911 GtkTreeSelection *selection;
1912 GError *error = NULL;
1914 if (log_window == NULL)
1917 if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager),
1918 result, &hits, &error))
1920 DEBUG ("%s. Aborting", error->message);
1921 g_error_free (error);
1925 tp_clear_pointer (&log_window->priv->hits, tpl_log_manager_search_free);
1926 log_window->priv->hits = hits;
1928 view = GTK_TREE_VIEW (log_window->priv->treeview_when);
1929 selection = gtk_tree_view_get_selection (view);
1931 g_signal_handlers_unblock_by_func (selection,
1932 log_window_when_changed_cb,
1935 populate_entities_from_search_hits ();
1939 log_window_find_populate (EmpathyLogWindow *self,
1940 const gchar *search_criteria)
1943 GtkTreeModel *model;
1944 GtkTreeSelection *selection;
1945 GtkListStore *store;
1947 gtk_tree_store_clear (self->priv->store_events);
1949 view = GTK_TREE_VIEW (self->priv->treeview_who);
1950 model = gtk_tree_view_get_model (view);
1951 store = GTK_LIST_STORE (model);
1953 gtk_list_store_clear (store);
1955 view = GTK_TREE_VIEW (self->priv->treeview_when);
1956 model = gtk_tree_view_get_model (view);
1957 store = GTK_LIST_STORE (model);
1958 selection = gtk_tree_view_get_selection (view);
1960 gtk_list_store_clear (store);
1962 if (EMP_STR_EMPTY (search_criteria))
1964 tp_clear_pointer (&self->priv->hits, tpl_log_manager_search_free);
1965 webkit_web_view_set_highlight_text_matches (
1966 WEBKIT_WEB_VIEW (self->priv->webview), FALSE);
1967 log_window_who_populate (self);
1971 g_signal_handlers_block_by_func (selection,
1972 log_window_when_changed_cb,
1975 /* highlight the search text */
1976 webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (self->priv->webview),
1977 search_criteria, FALSE, 0);
1979 tpl_log_manager_search_async (self->priv->log_manager,
1980 search_criteria, TPL_EVENT_MASK_ANY,
1981 log_manager_searched_new_cb, NULL);
1985 start_find_search (EmpathyLogWindow *self)
1989 str = gtk_entry_get_text (GTK_ENTRY (self->priv->search_entry));
1991 /* Don't find the same crap again */
1992 if (self->priv->last_find && !tp_strdiff (self->priv->last_find, str))
1995 g_free (self->priv->last_find);
1996 self->priv->last_find = g_strdup (str);
1998 log_window_find_populate (self, str);
2004 log_window_search_entry_changed_cb (GtkWidget *entry,
2005 EmpathyLogWindow *self)
2007 if (self->priv->source != 0)
2008 g_source_remove (self->priv->source);
2009 self->priv->source = g_timeout_add (500, (GSourceFunc) start_find_search,
2014 log_window_search_entry_activate_cb (GtkWidget *entry,
2015 EmpathyLogWindow *self)
2017 start_find_search (self);
2021 log_window_search_entry_icon_pressed_cb (GtkEntry *entry,
2022 GtkEntryIconPosition icon_pos,
2026 if (icon_pos != GTK_ENTRY_ICON_SECONDARY)
2029 gtk_entry_buffer_set_text (gtk_entry_get_buffer (entry),
2034 log_window_update_buttons_sensitivity (EmpathyLogWindow *self)
2037 GtkTreeModel *model;
2038 GtkTreeSelection *selection;
2039 EmpathyCapabilities capabilities;
2045 gboolean profile, chat, call, video;
2047 profile = chat = call = video = FALSE;
2049 tp_clear_object (&self->priv->button_video_binding);
2050 tp_clear_object (&self->priv->selected_contact);
2052 view = GTK_TREE_VIEW (self->priv->treeview_who);
2053 model = gtk_tree_view_get_model (view);
2054 selection = gtk_tree_view_get_selection (view);
2056 profile = chat = call = video = FALSE;
2058 if (!gtk_tree_model_get_iter_first (model, &iter))
2061 if (gtk_tree_selection_count_selected_rows (selection) != 1)
2064 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2067 paths = gtk_tree_selection_get_selected_rows (selection, &model);
2068 g_return_if_fail (paths != NULL);
2071 gtk_tree_model_get_iter (model, &iter, path);
2072 gtk_tree_model_get (model, &iter,
2073 COL_WHO_ACCOUNT, &account,
2074 COL_WHO_TARGET, &target,
2077 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
2079 self->priv->selected_contact = empathy_contact_from_tpl_contact (account,
2082 g_object_unref (account);
2083 g_object_unref (target);
2085 capabilities = empathy_contact_get_capabilities (self->priv->selected_contact);
2087 profile = chat = TRUE;
2088 call = capabilities & EMPATHY_CAPABILITIES_AUDIO;
2089 video = capabilities & EMPATHY_CAPABILITIES_VIDEO;
2094 /* If the Who pane doesn't contain a contact (e.g. it has many
2095 * selected, or has 'Anyone', let's try to get the contact from
2096 * the selected event. */
2098 if (self->priv->events_contact != NULL)
2099 self->priv->selected_contact = g_object_ref (self->priv->events_contact);
2103 capabilities = empathy_contact_get_capabilities (self->priv->selected_contact);
2105 profile = chat = TRUE;
2106 call = capabilities & EMPATHY_CAPABILITIES_AUDIO;
2107 video = capabilities & EMPATHY_CAPABILITIES_VIDEO;
2110 self->priv->button_video_binding = g_object_bind_property (
2111 self->priv->camera_monitor, "available",
2112 self->priv->button_video, "sensitive",
2113 G_BINDING_SYNC_CREATE);
2116 gtk_widget_set_sensitive (self->priv->button_profile, profile);
2117 gtk_widget_set_sensitive (self->priv->button_chat, chat);
2118 gtk_widget_set_sensitive (self->priv->button_call, call);
2120 /* Don't override the binding */
2122 gtk_widget_set_sensitive (self->priv->button_video, video);
2126 log_window_update_what_iter_sensitivity (GtkTreeModel *model,
2130 GtkTreeStore *store = GTK_TREE_STORE (model);
2134 gtk_tree_store_set (store, iter,
2135 COL_WHAT_SENSITIVE, sensitive,
2138 for (next = gtk_tree_model_iter_children (model, &child, iter);
2140 next = gtk_tree_model_iter_next (model, &child))
2142 gtk_tree_store_set (store, &child,
2143 COL_WHAT_SENSITIVE, sensitive,
2149 log_window_update_what_sensitivity (EmpathyLogWindow *self)
2152 GtkTreeModel *model;
2154 GList *accounts, *targets, *acc, *targ;
2157 if (!log_window_get_selected (self, &accounts, &targets, NULL, NULL,
2161 view = GTK_TREE_VIEW (self->priv->treeview_what);
2162 model = gtk_tree_view_get_model (view);
2164 /* For each event type... */
2165 for (next = gtk_tree_model_get_iter_first (model, &iter);
2167 next = gtk_tree_model_iter_next (model, &iter))
2169 TplEventTypeMask type;
2171 gtk_tree_model_get (model, &iter,
2172 COL_WHAT_TYPE, &type,
2175 /* ...we set the type and its subtypes (if any) unsensitive... */
2176 log_window_update_what_iter_sensitivity (model, &iter, FALSE);
2178 for (acc = accounts, targ = targets;
2179 acc != NULL && targ != NULL;
2180 acc = acc->next, targ = targ->next)
2182 TpAccount *account = acc->data;
2183 TplEntity *target = targ->data;
2185 if (tpl_log_manager_exists (self->priv->log_manager,
2186 account, target, type))
2188 /* And then we set it (and its subtypes, again, if any)
2189 * as sensitive if there are logs of that type. */
2190 log_window_update_what_iter_sensitivity (model, &iter, TRUE);
2196 g_list_free_full (accounts, g_object_unref);
2197 g_list_free_full (targets, g_object_unref);
2201 log_window_who_changed_cb (GtkTreeSelection *selection,
2202 EmpathyLogWindow *self)
2205 GtkTreeModel *model;
2208 DEBUG ("log_window_who_changed_cb");
2210 view = gtk_tree_selection_get_tree_view (selection);
2211 model = gtk_tree_view_get_model (view);
2213 if (gtk_tree_model_get_iter_first (model, &iter))
2215 /* If 'Anyone' is selected, everything else should be deselected */
2216 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2218 g_signal_handlers_block_by_func (selection,
2219 log_window_who_changed_cb,
2222 gtk_tree_selection_unselect_all (selection);
2223 gtk_tree_selection_select_iter (selection, &iter);
2225 g_signal_handlers_unblock_by_func (selection,
2226 log_window_who_changed_cb,
2231 log_window_update_what_sensitivity (self);
2232 log_window_update_buttons_sensitivity (self);
2234 /* The contact changed, so the dates need to be updated */
2235 log_window_chats_get_messages (self, TRUE);
2239 log_manager_got_entities_cb (GObject *manager,
2240 GAsyncResult *result,
2243 Ctx *ctx = user_data;
2247 GtkTreeModel *model;
2248 GtkTreeSelection *selection;
2249 GtkListStore *store;
2251 GError *error = NULL;
2252 gboolean select_account = FALSE;
2254 if (log_window == NULL)
2257 if (log_window->priv->count != ctx->count)
2260 if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
2261 result, &entities, &error))
2263 DEBUG ("%s. Aborting", error->message);
2264 g_error_free (error);
2268 view = GTK_TREE_VIEW (ctx->self->priv->treeview_who);
2269 model = gtk_tree_view_get_model (view);
2270 selection = gtk_tree_view_get_selection (view);
2271 store = GTK_LIST_STORE (model);
2273 /* Block signals to stop the logs being retrieved prematurely */
2274 g_signal_handlers_block_by_func (selection,
2275 log_window_who_changed_cb, ctx->self);
2277 for (l = entities; l; l = l->next)
2279 TplEntity *entity = TPL_ENTITY (l->data);
2280 TplEntityType type = tpl_entity_get_entity_type (entity);
2281 EmpathyContact *contact;
2282 gboolean room = type == TPL_ENTITY_ROOM;
2284 contact = empathy_contact_from_tpl_contact (ctx->account, entity);
2286 gtk_list_store_append (store, &iter);
2287 gtk_list_store_set (store, &iter,
2288 COL_WHO_TYPE, COL_TYPE_NORMAL,
2289 COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
2290 : EMPATHY_IMAGE_AVATAR_DEFAULT,
2291 COL_WHO_NAME, empathy_contact_get_alias (contact),
2292 COL_WHO_ID, tpl_entity_get_identifier (entity),
2293 COL_WHO_ACCOUNT, ctx->account,
2294 COL_WHO_TARGET, entity,
2297 g_object_unref (contact);
2299 if (ctx->self->priv->selected_account != NULL &&
2300 !tp_strdiff (tp_proxy_get_object_path (ctx->account),
2301 tp_proxy_get_object_path (ctx->self->priv->selected_account)))
2302 select_account = TRUE;
2304 g_list_free_full (entities, g_object_unref);
2306 if (gtk_tree_model_get_iter_first (model, &iter))
2310 gtk_tree_model_get (model, &iter,
2311 COL_WHO_TYPE, &type,
2314 if (type != COL_TYPE_ANY)
2316 gtk_list_store_prepend (store, &iter);
2317 gtk_list_store_set (store, &iter,
2318 COL_WHO_TYPE, COL_TYPE_SEPARATOR,
2319 COL_WHO_NAME, "separator",
2322 gtk_list_store_prepend (store, &iter);
2323 gtk_list_store_set (store, &iter,
2324 COL_WHO_TYPE, COL_TYPE_ANY,
2325 COL_WHO_NAME, _("Anyone"),
2330 /* Unblock signals */
2331 g_signal_handlers_unblock_by_func (selection,
2332 log_window_who_changed_cb,
2335 /* We display the selected account if we populate the model with chats from
2338 log_window_chats_set_selected (ctx->self);
2341 _tpl_action_chain_continue (log_window->priv->chain);
2346 get_entities_for_account (TplActionChain *chain, gpointer user_data)
2348 Ctx *ctx = user_data;
2350 tpl_log_manager_get_entities_async (ctx->self->priv->log_manager, ctx->account,
2351 log_manager_got_entities_cb, ctx);
2355 select_first_entity (TplActionChain *chain, gpointer user_data)
2357 EmpathyLogWindow *self = user_data;
2359 GtkTreeModel *model;
2360 GtkTreeSelection *selection;
2363 view = GTK_TREE_VIEW (self->priv->treeview_who);
2364 model = gtk_tree_view_get_model (view);
2365 selection = gtk_tree_view_get_selection (view);
2367 if (gtk_tree_model_get_iter_first (model, &iter))
2368 gtk_tree_selection_select_iter (selection, &iter);
2370 _tpl_action_chain_continue (self->priv->chain);
2374 log_window_who_populate (EmpathyLogWindow *self)
2376 EmpathyAccountChooser *account_chooser;
2378 gboolean all_accounts;
2380 GtkTreeModel *model;
2381 GtkTreeSelection *selection;
2382 GtkListStore *store;
2385 if (self->priv->hits != NULL)
2387 populate_entities_from_search_hits ();
2391 account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
2392 account = empathy_account_chooser_dup_account (account_chooser);
2393 all_accounts = empathy_account_chooser_has_all_selected (account_chooser);
2395 view = GTK_TREE_VIEW (self->priv->treeview_who);
2396 model = gtk_tree_view_get_model (view);
2397 selection = gtk_tree_view_get_selection (view);
2398 store = GTK_LIST_STORE (model);
2400 /* Block signals to stop the logs being retrieved prematurely */
2401 g_signal_handlers_block_by_func (selection,
2402 log_window_who_changed_cb,
2405 gtk_list_store_clear (store);
2407 /* Unblock signals */
2408 g_signal_handlers_unblock_by_func (selection,
2409 log_window_who_changed_cb,
2412 _tpl_action_chain_clear (self->priv->chain);
2413 self->priv->count++;
2415 if (!all_accounts && account == NULL)
2419 else if (!all_accounts)
2421 ctx = ctx_new (self, account, NULL, NULL, 0, 0, self->priv->count);
2422 _tpl_action_chain_append (self->priv->chain, get_entities_for_account, ctx);
2426 TpAccountManager *manager;
2427 GList *accounts, *l;
2429 manager = empathy_account_chooser_get_account_manager (account_chooser);
2430 accounts = tp_account_manager_get_valid_accounts (manager);
2432 for (l = accounts; l != NULL; l = l->next)
2436 ctx = ctx_new (self, account, NULL, NULL, 0, 0, self->priv->count);
2437 _tpl_action_chain_append (self->priv->chain,
2438 get_entities_for_account, ctx);
2441 g_list_free (accounts);
2443 _tpl_action_chain_append (self->priv->chain, select_first_entity, self);
2444 _tpl_action_chain_start (self->priv->chain);
2448 sort_by_name (GtkTreeModel *model,
2453 gchar *name1, *name2;
2457 gtk_tree_model_get (model, a,
2458 COL_WHO_TYPE, &type1,
2459 COL_WHO_NAME, &name1,
2462 gtk_tree_model_get (model, b,
2463 COL_WHO_TYPE, &type2,
2464 COL_WHO_NAME, &name2,
2467 if (type1 == COL_TYPE_ANY)
2469 else if (type2 == COL_TYPE_ANY)
2471 else if (type1 == COL_TYPE_SEPARATOR)
2473 else if (type2 == COL_TYPE_SEPARATOR)
2476 ret = g_strcmp0 (name1, name2);
2485 who_row_is_separator (GtkTreeModel *model,
2491 gtk_tree_model_get (model, iter,
2492 COL_WHO_TYPE, &type,
2495 return (type == COL_TYPE_SEPARATOR);
2499 log_window_find_row (EmpathyLogWindow *self,
2500 GdkEventButton *event)
2502 WebKitHitTestResult *hit = webkit_web_view_get_hit_test_result (
2503 WEBKIT_WEB_VIEW (self->priv->webview), event);
2504 WebKitDOMNode *inner_node;
2506 tp_clear_object (&self->priv->events_contact);
2509 "inner-node", &inner_node,
2512 if (inner_node != NULL)
2514 GtkTreeModel *model = GTK_TREE_MODEL (self->priv->store_events);
2515 WebKitDOMNode *node;
2516 const char *path = NULL;
2519 /* walk back up the DOM tree looking for a node with empathy:path set */
2520 for (node = inner_node; node != NULL;
2521 node = webkit_dom_node_get_parent_node (node))
2523 if (!WEBKIT_DOM_IS_ELEMENT (node))
2526 path = webkit_dom_element_get_attribute_ns (
2527 WEBKIT_DOM_ELEMENT (node), EMPATHY_NS, "path");
2529 if (!tp_str_empty (path))
2533 /* look up the contact for this path */
2534 if (!tp_str_empty (path) &&
2535 gtk_tree_model_get_iter_from_string (model, &iter, path))
2540 gtk_tree_model_get (model, &iter,
2541 COL_EVENTS_ACCOUNT, &account,
2542 COL_EVENTS_TARGET, &target,
2545 self->priv->events_contact = empathy_contact_from_tpl_contact (
2548 g_object_unref (account);
2549 g_object_unref (target);
2552 g_object_unref (inner_node);
2555 g_object_unref (hit);
2557 log_window_update_buttons_sensitivity (self);
2561 log_window_events_button_press_event (GtkWidget *webview,
2562 GdkEventButton *event,
2563 EmpathyLogWindow *self)
2565 switch (event->button)
2568 log_window_find_row (self, event);
2572 empathy_webkit_context_menu_for_event (
2573 WEBKIT_WEB_VIEW (webview), event, 0);
2584 log_window_events_setup (EmpathyLogWindow *self)
2586 GtkTreeSortable *sortable;
2587 GtkTreeStore *store;
2590 self->priv->store_events = store = gtk_tree_store_new (COL_EVENTS_COUNT,
2591 G_TYPE_INT, /* type */
2592 G_TYPE_INT64, /* timestamp */
2593 G_TYPE_STRING, /* stringified date */
2594 G_TYPE_STRING, /* icon */
2595 G_TYPE_STRING, /* name */
2596 TP_TYPE_ACCOUNT, /* account */
2597 TPL_TYPE_ENTITY, /* target */
2598 TPL_TYPE_EVENT); /* event */
2600 sortable = GTK_TREE_SORTABLE (store);
2602 gtk_tree_sortable_set_sort_column_id (sortable,
2604 GTK_SORT_ASCENDING);
2608 log_window_who_setup (EmpathyLogWindow *self)
2611 GtkTreeModel *model;
2612 GtkTreeSelection *selection;
2613 GtkTreeSortable *sortable;
2614 GtkTreeViewColumn *column;
2615 GtkListStore *store;
2616 GtkCellRenderer *cell;
2618 view = GTK_TREE_VIEW (self->priv->treeview_who);
2619 selection = gtk_tree_view_get_selection (view);
2622 store = gtk_list_store_new (COL_WHO_COUNT,
2623 G_TYPE_INT, /* type */
2624 G_TYPE_STRING, /* icon */
2625 G_TYPE_STRING, /* name */
2626 G_TYPE_STRING, /* id */
2627 TP_TYPE_ACCOUNT, /* account */
2628 TPL_TYPE_ENTITY); /* target */
2630 model = GTK_TREE_MODEL (store);
2631 sortable = GTK_TREE_SORTABLE (store);
2633 gtk_tree_view_set_model (view, model);
2636 column = gtk_tree_view_column_new ();
2637 gtk_tree_view_column_set_title (column, _("Who"));
2639 cell = gtk_cell_renderer_pixbuf_new ();
2640 gtk_tree_view_column_pack_start (column, cell, FALSE);
2641 gtk_tree_view_column_add_attribute (column, cell,
2645 cell = gtk_cell_renderer_text_new ();
2646 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2647 gtk_tree_view_column_pack_start (column, cell, TRUE);
2648 gtk_tree_view_column_add_attribute (column, cell,
2652 gtk_tree_view_append_column (view, column);
2654 /* set up treeview properties */
2655 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2656 gtk_tree_view_set_row_separator_func (view, who_row_is_separator,
2659 gtk_tree_sortable_set_sort_column_id (sortable,
2661 GTK_SORT_ASCENDING);
2662 gtk_tree_sortable_set_sort_func (sortable,
2663 COL_WHO_NAME, sort_by_name,
2666 gtk_tree_view_set_search_column (view, COL_WHO_NAME);
2667 gtk_tree_view_set_tooltip_column (view, COL_WHO_ID);
2669 /* set up signals */
2670 g_signal_connect (selection, "changed",
2671 G_CALLBACK (log_window_who_changed_cb), self);
2673 g_object_unref (store);
2677 log_window_chats_accounts_changed_cb (GtkWidget *combobox,
2678 EmpathyLogWindow *self)
2680 /* Clear all current messages shown in the textview */
2681 gtk_tree_store_clear (self->priv->store_events);
2683 log_window_who_populate (self);
2687 log_window_chats_set_selected (EmpathyLogWindow *self)
2690 GtkTreeModel *model;
2691 GtkTreeSelection *selection;
2696 view = GTK_TREE_VIEW (self->priv->treeview_who);
2697 model = gtk_tree_view_get_model (view);
2698 selection = gtk_tree_view_get_selection (view);
2700 for (next = gtk_tree_model_get_iter_first (model, &iter);
2702 next = gtk_tree_model_iter_next (model, &iter))
2704 TpAccount *this_account;
2705 TplEntity *this_target;
2706 const gchar *this_chat_id;
2707 gboolean this_is_chatroom;
2710 gtk_tree_model_get (model, &iter,
2711 COL_WHO_TYPE, &this_type,
2712 COL_WHO_ACCOUNT, &this_account,
2713 COL_WHO_TARGET, &this_target,
2716 if (this_type != COL_TYPE_NORMAL)
2719 this_chat_id = tpl_entity_get_identifier (this_target);
2720 this_is_chatroom = tpl_entity_get_entity_type (this_target)
2723 if (this_account == self->priv->selected_account &&
2724 !tp_strdiff (this_chat_id, self->priv->selected_chat_id) &&
2725 this_is_chatroom == self->priv->selected_is_chatroom)
2727 gtk_tree_selection_select_iter (selection, &iter);
2728 path = gtk_tree_model_get_path (model, &iter);
2729 gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 0.5, 0.0);
2730 gtk_tree_path_free (path);
2731 g_object_unref (this_account);
2732 g_object_unref (this_target);
2736 g_object_unref (this_account);
2737 g_object_unref (this_target);
2740 tp_clear_object (&self->priv->selected_account);
2741 tp_clear_pointer (&self->priv->selected_chat_id, g_free);
2745 sort_by_date (GtkTreeModel *model,
2750 GDate *date1, *date2;
2752 gtk_tree_model_get (model, a,
2753 COL_WHEN_DATE, &date1,
2756 gtk_tree_model_get (model, b,
2757 COL_WHEN_DATE, &date2,
2760 return g_date_compare (date1, date2);
2764 when_row_is_separator (GtkTreeModel *model,
2771 gtk_tree_model_get (model, iter,
2772 COL_WHEN_TEXT, &when,
2775 ret = !tp_strdiff (when, "separator");
2781 log_window_when_changed_cb (GtkTreeSelection *selection,
2782 EmpathyLogWindow *self)
2785 GtkTreeModel *model;
2788 DEBUG ("log_window_when_changed_cb");
2790 view = gtk_tree_selection_get_tree_view (selection);
2791 model = gtk_tree_view_get_model (view);
2793 /* If 'Anytime' is selected, everything else should be deselected */
2794 if (gtk_tree_model_get_iter_first (model, &iter))
2796 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2798 g_signal_handlers_block_by_func (selection,
2799 log_window_when_changed_cb,
2802 gtk_tree_selection_unselect_all (selection);
2803 gtk_tree_selection_select_iter (selection, &iter);
2805 g_signal_handlers_unblock_by_func (selection,
2806 log_window_when_changed_cb,
2811 log_window_chats_get_messages (self, FALSE);
2815 log_window_when_setup (EmpathyLogWindow *self)
2818 GtkTreeModel *model;
2819 GtkTreeSelection *selection;
2820 GtkTreeSortable *sortable;
2821 GtkTreeViewColumn *column;
2822 GtkListStore *store;
2823 GtkCellRenderer *cell;
2825 view = GTK_TREE_VIEW (self->priv->treeview_when);
2826 selection = gtk_tree_view_get_selection (view);
2829 store = gtk_list_store_new (COL_WHEN_COUNT,
2830 G_TYPE_DATE, /* date */
2831 G_TYPE_STRING, /* stringified date */
2832 G_TYPE_STRING); /* icon */
2834 model = GTK_TREE_MODEL (store);
2835 sortable = GTK_TREE_SORTABLE (store);
2837 gtk_tree_view_set_model (view, model);
2840 column = gtk_tree_view_column_new ();
2841 gtk_tree_view_column_set_title (column, _("When"));
2843 cell = gtk_cell_renderer_pixbuf_new ();
2844 gtk_tree_view_column_pack_start (column, cell, FALSE);
2845 gtk_tree_view_column_add_attribute (column, cell,
2846 "icon-name", COL_WHEN_ICON);
2848 cell = gtk_cell_renderer_text_new ();
2849 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2850 gtk_tree_view_column_pack_start (column, cell, TRUE);
2851 gtk_tree_view_column_add_attribute (column, cell,
2855 gtk_tree_view_append_column (view, column);
2857 /* set up treeview properties */
2858 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2859 gtk_tree_view_set_row_separator_func (view, when_row_is_separator,
2861 gtk_tree_sortable_set_sort_column_id (sortable,
2863 GTK_SORT_DESCENDING);
2864 gtk_tree_sortable_set_sort_func (sortable,
2865 COL_WHEN_DATE, sort_by_date,
2868 gtk_tree_view_set_search_column (view, COL_WHEN_TEXT);
2870 /* set up signals */
2871 g_signal_connect (selection, "changed",
2872 G_CALLBACK (log_window_when_changed_cb),
2875 g_object_unref (store);
2879 what_row_is_separator (GtkTreeModel *model,
2885 gtk_tree_model_get (model, iter,
2886 COL_WHAT_TYPE, &type,
2889 return (type == WHAT_TYPE_SEPARATOR);
2893 log_window_what_changed_cb (GtkTreeSelection *selection,
2894 EmpathyLogWindow *self)
2897 GtkTreeModel *model;
2900 DEBUG ("log_window_what_changed_cb");
2902 view = gtk_tree_selection_get_tree_view (selection);
2903 model = gtk_tree_view_get_model (view);
2905 /* If 'Anything' is selected, everything else should be deselected */
2906 if (gtk_tree_model_get_iter_first (model, &iter))
2908 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2910 g_signal_handlers_block_by_func (selection,
2911 log_window_what_changed_cb,
2914 gtk_tree_selection_unselect_all (selection);
2915 gtk_tree_selection_select_iter (selection, &iter);
2917 g_signal_handlers_unblock_by_func (selection,
2918 log_window_what_changed_cb,
2923 /* The dates need to be updated if we're not searching */
2924 log_window_chats_get_messages (self, self->priv->hits == NULL);
2928 log_window_what_collapse_row_cb (GtkTreeView *tree_view,
2933 /* Reject collapsing */
2940 EventSubtype subtype;
2946 log_window_what_setup (EmpathyLogWindow *self)
2949 GtkTreeModel *model;
2950 GtkTreeSelection *selection;
2951 GtkTreeViewColumn *column;
2953 GtkTreeStore *store;
2954 GtkCellRenderer *cell;
2956 struct event events [] = {
2957 { TPL_EVENT_MASK_ANY, 0, NULL, _("Anything") },
2958 { WHAT_TYPE_SEPARATOR, 0, NULL, "separator" },
2959 { TPL_EVENT_MASK_TEXT, 0, "stock_text_justify", _("Text chats") },
2960 #ifdef HAVE_CALL_LOGS
2961 { TPL_EVENT_MASK_CALL, EVENT_CALL_ALL, EMPATHY_IMAGE_CALL, _("Calls") },
2964 #ifdef HAVE_CALL_LOGS
2965 struct event call_events [] = {
2966 { TPL_EVENT_MASK_CALL, EVENT_CALL_INCOMING, EMPATHY_IMAGE_CALL_INCOMING, _("Incoming calls") },
2967 { TPL_EVENT_MASK_CALL, EVENT_CALL_OUTGOING, EMPATHY_IMAGE_CALL_OUTGOING, _("Outgoing calls") },
2968 { TPL_EVENT_MASK_CALL, EVENT_CALL_MISSED, EMPATHY_IMAGE_CALL_MISSED, _("Missed calls") }
2973 view = GTK_TREE_VIEW (self->priv->treeview_what);
2974 selection = gtk_tree_view_get_selection (view);
2977 store = gtk_tree_store_new (COL_WHAT_COUNT,
2978 G_TYPE_INT, /* history type */
2979 G_TYPE_INT, /* history subtype */
2980 G_TYPE_BOOLEAN, /* sensitive */
2981 G_TYPE_STRING, /* stringified history type */
2982 G_TYPE_STRING); /* icon */
2984 model = GTK_TREE_MODEL (store);
2986 gtk_tree_view_set_model (view, model);
2989 column = gtk_tree_view_column_new ();
2990 gtk_tree_view_column_set_title (column, _("What"));
2992 cell = gtk_cell_renderer_pixbuf_new ();
2993 gtk_tree_view_column_pack_start (column, cell, FALSE);
2994 gtk_tree_view_column_add_attribute (column, cell,
2995 "icon-name", COL_WHAT_ICON);
2997 cell = gtk_cell_renderer_text_new ();
2998 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2999 gtk_tree_view_column_pack_start (column, cell, TRUE);
3000 gtk_tree_view_column_add_attribute (column, cell,
3001 "text", COL_WHAT_TEXT);
3002 gtk_tree_view_column_add_attribute (column, cell,
3003 "sensitive", COL_WHAT_SENSITIVE);
3005 gtk_tree_view_append_column (view, column);
3006 gtk_tree_view_set_search_column (view, COL_WHAT_TEXT);
3008 /* set up treeview properties */
3009 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
3010 gtk_tree_view_set_show_expanders (view, FALSE);
3011 gtk_tree_view_set_level_indentation (view, 12);
3012 gtk_tree_view_expand_all (view);
3013 gtk_tree_view_set_row_separator_func (view, what_row_is_separator,
3017 for (i = 0; i < G_N_ELEMENTS (events); i++)
3019 gtk_tree_store_append (store, &iter, NULL);
3020 gtk_tree_store_set (store, &iter,
3021 COL_WHAT_TYPE, events[i].type,
3022 COL_WHAT_SUBTYPE, events[i].subtype,
3023 COL_WHAT_SENSITIVE, TRUE,
3024 COL_WHAT_TEXT, events[i].text,
3025 COL_WHAT_ICON, events[i].icon,
3029 #ifdef HAVE_CALL_LOGS
3030 gtk_tree_model_iter_nth_child (model, &parent, NULL, 3);
3031 for (i = 0; i < G_N_ELEMENTS (call_events); i++)
3033 gtk_tree_store_append (store, &iter, &parent);
3034 gtk_tree_store_set (store, &iter,
3035 COL_WHAT_TYPE, call_events[i].type,
3036 COL_WHAT_SUBTYPE, call_events[i].subtype,
3037 COL_WHAT_SENSITIVE, TRUE,
3038 COL_WHAT_TEXT, call_events[i].text,
3039 COL_WHAT_ICON, call_events[i].icon,
3044 gtk_tree_view_expand_all (view);
3046 /* select 'Anything' */
3047 if (gtk_tree_model_get_iter_first (model, &iter))
3048 gtk_tree_selection_select_iter (selection, &iter);
3050 /* set up signals */
3051 g_signal_connect (view, "test-collapse-row",
3052 G_CALLBACK (log_window_what_collapse_row_cb),
3054 g_signal_connect (selection, "changed",
3055 G_CALLBACK (log_window_what_changed_cb),
3058 g_object_unref (store);
3062 log_window_maybe_expand_events (void)
3064 GtkTreeModel *model = GTK_TREE_MODEL (log_window->priv->store_events);
3066 /* If there's only one result, expand it */
3067 if (gtk_tree_model_iter_n_children (model, NULL) == 1)
3068 webkit_web_view_execute_script (
3069 WEBKIT_WEB_VIEW (log_window->priv->webview),
3070 "javascript:expandAll()");
3074 show_spinner (gpointer data)
3078 if (log_window == NULL)
3081 g_object_get (log_window->priv->spinner, "active", &active, NULL);
3084 gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->priv->notebook),
3091 show_events (TplActionChain *chain,
3094 log_window_maybe_expand_events ();
3095 gtk_spinner_stop (GTK_SPINNER (log_window->priv->spinner));
3096 gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->priv->notebook),
3099 _tpl_action_chain_continue (chain);
3103 start_spinner (void)
3105 gtk_spinner_start (GTK_SPINNER (log_window->priv->spinner));
3106 gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->priv->notebook),
3109 g_timeout_add (1000, show_spinner, NULL);
3110 _tpl_action_chain_append (log_window->priv->chain, show_events, NULL);
3114 log_window_got_messages_for_date_cb (GObject *manager,
3115 GAsyncResult *result,
3118 Ctx *ctx = user_data;
3119 GtkTreeModel *model;
3123 GError *error = NULL;
3126 if (log_window == NULL)
3132 if (log_window->priv->count != ctx->count)
3135 if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager),
3136 result, &events, &error))
3138 DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
3140 g_error_free (error);
3144 for (l = events; l; l = l->next)
3146 TplEvent *event = l->data;
3147 gboolean append = TRUE;
3149 #ifdef HAVE_CALL_LOGS
3150 if (TPL_IS_CALL_EVENT (l->data)
3151 && ctx->event_mask & TPL_EVENT_MASK_CALL
3152 && ctx->event_mask != TPL_EVENT_MASK_ANY)
3154 TplCallEvent *call = l->data;
3158 if (ctx->subtype & EVENT_CALL_ALL)
3164 TplCallEndReason reason = tpl_call_event_get_end_reason (call);
3165 TplEntity *sender = tpl_event_get_sender (event);
3166 TplEntity *receiver = tpl_event_get_receiver (event);
3168 if (reason == TPL_CALL_END_REASON_NO_ANSWER)
3170 if (ctx->subtype & EVENT_CALL_MISSED)
3173 else if (ctx->subtype & EVENT_CALL_OUTGOING
3174 && tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
3178 else if (ctx->subtype & EVENT_CALL_INCOMING
3179 && tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
3189 EmpathyMessage *msg = empathy_message_from_tpl_log_event (event);
3190 log_window_append_message (event, msg);
3191 tp_clear_object (&msg);
3194 g_object_unref (event);
3196 g_list_free (events);
3198 model = GTK_TREE_MODEL (log_window->priv->store_events);
3199 n = gtk_tree_model_iter_n_children (model, NULL) - 1;
3201 if (n >= 0 && gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
3206 path = gtk_tree_model_get_path (model, &iter);
3207 str = gtk_tree_path_to_string (path);
3209 script = g_strdup_printf ("javascript:scrollToRow([%s]);",
3210 g_strdelimit (str, ":", ','));
3212 webkit_web_view_execute_script (
3213 WEBKIT_WEB_VIEW (log_window->priv->webview),
3216 gtk_tree_path_free (path);
3224 _tpl_action_chain_continue (log_window->priv->chain);
3228 get_events_for_date (TplActionChain *chain, gpointer user_data)
3230 Ctx *ctx = user_data;
3232 tpl_log_manager_get_events_for_date_async (ctx->self->priv->log_manager,
3233 ctx->account, ctx->entity, ctx->event_mask,
3235 log_window_got_messages_for_date_cb,
3240 log_window_get_messages_for_dates (EmpathyLogWindow *self,
3243 GList *accounts, *targets, *acc, *targ, *l;
3244 TplEventTypeMask event_mask;
3245 EventSubtype subtype;
3246 GDate *date, *anytime, *separator;
3248 if (!log_window_get_selected (self,
3249 &accounts, &targets, NULL, NULL, &event_mask, &subtype))
3252 anytime = g_date_new_dmy (2, 1, -1);
3253 separator = g_date_new_dmy (1, 1, -1);
3255 _tpl_action_chain_clear (self->priv->chain);
3256 self->priv->count++;
3258 for (acc = accounts, targ = targets;
3259 acc != NULL && targ != NULL;
3260 acc = acc->next, targ = targ->next)
3262 TpAccount *account = acc->data;
3263 TplEntity *target = targ->data;
3265 for (l = dates; l != NULL; l = l->next)
3270 if (g_date_compare (date, anytime) != 0)
3274 ctx = ctx_new (self, account, target, date, event_mask, subtype,
3276 _tpl_action_chain_append (self->priv->chain, get_events_for_date, ctx);
3280 GtkTreeView *view = GTK_TREE_VIEW (self->priv->treeview_when);
3281 GtkTreeModel *model = gtk_tree_view_get_model (view);
3286 for (next = gtk_tree_model_get_iter_first (model, &iter);
3288 next = gtk_tree_model_iter_next (model, &iter))
3292 gtk_tree_model_get (model, &iter,
3296 if (g_date_compare (d, anytime) != 0 &&
3297 g_date_compare (d, separator) != 0)
3299 ctx = ctx_new (self, account, target, d,
3300 event_mask, subtype, self->priv->count);
3301 _tpl_action_chain_append (self->priv->chain, get_events_for_date, ctx);
3309 _tpl_action_chain_start (self->priv->chain);
3311 g_list_free_full (accounts, g_object_unref);
3312 g_list_free_full (targets, g_object_unref);
3313 g_date_free (separator);
3314 g_date_free (anytime);
3318 log_manager_got_dates_cb (GObject *manager,
3319 GAsyncResult *result,
3322 Ctx *ctx = user_data;
3324 GtkTreeModel *model;
3325 GtkListStore *store;
3329 GError *error = NULL;
3331 if (log_window == NULL)
3337 if (log_window->priv->count != ctx->count)
3340 if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
3341 result, &dates, &error))
3343 DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
3348 view = GTK_TREE_VIEW (log_window->priv->treeview_when);
3349 model = gtk_tree_view_get_model (view);
3350 store = GTK_LIST_STORE (model);
3352 for (l = dates; l != NULL; l = l->next)
3354 GDate *date = l->data;
3356 /* Add the date if it's not already there */
3357 has_element = FALSE;
3358 gtk_tree_model_foreach (model, model_has_date, date);
3361 gchar *text = format_date_for_display (date);
3363 gtk_list_store_append (store, &iter);
3364 gtk_list_store_set (store, &iter,
3365 COL_WHEN_DATE, date,
3366 COL_WHEN_TEXT, text,
3367 COL_WHEN_ICON, CALENDAR_ICON,
3374 if (gtk_tree_model_get_iter_first (model, &iter))
3376 gchar *separator = NULL;
3378 if (gtk_tree_model_iter_next (model, &iter))
3380 gtk_tree_model_get (model, &iter,
3381 COL_WHEN_TEXT, &separator,
3385 if (g_strcmp0 (separator, "separator") != 0)
3387 gtk_list_store_prepend (store, &iter);
3388 gtk_list_store_set (store, &iter,
3389 COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
3390 COL_WHEN_TEXT, "separator",
3393 gtk_list_store_prepend (store, &iter);
3394 gtk_list_store_set (store, &iter,
3395 COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
3396 COL_WHEN_TEXT, _("Anytime"),
3401 g_list_free_full (dates, g_free);
3404 _tpl_action_chain_continue (log_window->priv->chain);
3408 select_date (TplActionChain *chain, gpointer user_data)
3411 GtkTreeModel *model;
3412 GtkTreeSelection *selection;
3415 gboolean selected = FALSE;
3417 view = GTK_TREE_VIEW (log_window->priv->treeview_when);
3418 model = gtk_tree_view_get_model (view);
3419 selection = gtk_tree_view_get_selection (view);
3421 if (log_window->priv->current_dates != NULL)
3423 for (next = gtk_tree_model_get_iter_first (model, &iter);
3425 next = gtk_tree_model_iter_next (model, &iter))
3429 gtk_tree_model_get (model, &iter,
3430 COL_WHEN_DATE, &date,
3433 if (g_list_find_custom (log_window->priv->current_dates, date,
3434 (GCompareFunc) g_date_compare) != NULL)
3438 gtk_tree_selection_select_iter (selection, &iter);
3439 path = gtk_tree_model_get_path (model, &iter);
3440 gtk_tree_view_scroll_to_cell (view, path, NULL, FALSE, 0, 0);
3443 gtk_tree_path_free (path);
3452 /* Show messages of the most recent date */
3453 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 2))
3454 gtk_tree_selection_select_iter (selection, &iter);
3457 _tpl_action_chain_continue (log_window->priv->chain);
3461 get_dates_for_entity (TplActionChain *chain, gpointer user_data)
3463 Ctx *ctx = user_data;
3465 tpl_log_manager_get_dates_async (ctx->self->priv->log_manager,
3466 ctx->account, ctx->entity, ctx->event_mask,
3467 log_manager_got_dates_cb, ctx);
3471 log_window_chats_get_messages (EmpathyLogWindow *self,
3472 gboolean force_get_dates)
3474 GList *accounts, *targets, *dates;
3475 TplEventTypeMask event_mask;
3477 GtkTreeModel *model;
3478 GtkListStore *store;
3479 GtkTreeSelection *selection;
3481 if (!log_window_get_selected (self, &accounts, &targets, NULL,
3482 &dates, &event_mask, NULL))
3485 view = GTK_TREE_VIEW (self->priv->treeview_when);
3486 selection = gtk_tree_view_get_selection (view);
3487 model = gtk_tree_view_get_model (view);
3488 store = GTK_LIST_STORE (model);
3490 /* Clear all current messages shown in the textview */
3491 gtk_tree_store_clear (self->priv->store_events);
3493 _tpl_action_chain_clear (self->priv->chain);
3494 self->priv->count++;
3496 /* If there's a search use the returned hits */
3497 if (self->priv->hits != NULL)
3499 if (force_get_dates)
3501 g_signal_handlers_block_by_func (selection,
3502 log_window_when_changed_cb,
3505 gtk_list_store_clear (store);
3507 g_signal_handlers_unblock_by_func (selection,
3508 log_window_when_changed_cb,
3511 populate_dates_from_search_hits (accounts, targets);
3515 populate_events_from_search_hits (accounts, targets, dates);
3518 /* Either use the supplied date or get the last */
3519 else if (force_get_dates || dates == NULL)
3523 if (self->priv->current_dates != NULL)
3525 g_list_free_full (self->priv->current_dates,
3526 (GDestroyNotify) g_date_free);
3527 self->priv->current_dates = NULL;
3530 if (gtk_tree_selection_count_selected_rows (selection) > 0)
3535 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
3537 for (l = paths; l != NULL; l = l->next)
3539 GtkTreePath *path = l->data;
3542 gtk_tree_model_get_iter (model, &iter, path);
3543 gtk_tree_model_get (model, &iter,
3544 COL_WHEN_DATE, &date,
3547 /* The list takes ownership of the date. */
3548 self->priv->current_dates =
3549 g_list_prepend (self->priv->current_dates, date);
3552 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
3555 g_signal_handlers_block_by_func (selection,
3556 log_window_when_changed_cb,
3559 gtk_list_store_clear (store);
3561 g_signal_handlers_unblock_by_func (selection,
3562 log_window_when_changed_cb,
3565 /* Get a list of dates and show them on the treeview */
3566 for (targ = targets, acc = accounts;
3567 targ != NULL && acc != NULL;
3568 targ = targ->next, acc = acc->next)
3570 TpAccount *account = acc->data;
3571 TplEntity *target = targ->data;
3572 Ctx *ctx = ctx_new (self, account, target, NULL, event_mask, 0,
3575 _tpl_action_chain_append (self->priv->chain, get_dates_for_entity, ctx);
3577 _tpl_action_chain_append (self->priv->chain, select_date, NULL);
3578 _tpl_action_chain_start (self->priv->chain);
3582 /* Show messages of the selected date */
3583 log_window_get_messages_for_dates (self, dates);
3586 g_list_free_full (accounts, g_object_unref);
3587 g_list_free_full (targets, g_object_unref);
3588 g_list_free_full (dates, (GFreeFunc) g_date_free);
3592 EmpathyAccountChooserFilterResultCallback callback;
3594 } FilterCallbackData;
3597 got_entities (GObject *manager,
3598 GAsyncResult *result,
3601 FilterCallbackData *data = user_data;
3603 GError *error = NULL;
3605 if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
3606 result, &entities, &error))
3608 DEBUG ("Could not get entities: %s", error->message);
3609 g_error_free (error);
3610 data->callback (FALSE, data->user_data);
3614 data->callback (entities != NULL, data->user_data);
3616 g_list_free_full (entities, g_object_unref);
3619 g_slice_free (FilterCallbackData, data);
3623 empathy_account_chooser_filter_has_logs (TpAccount *account,
3624 EmpathyAccountChooserFilterResultCallback callback,
3625 gpointer callback_data,
3628 TplLogManager *manager = tpl_log_manager_dup_singleton ();
3629 FilterCallbackData *cb_data = g_slice_new0 (FilterCallbackData);
3631 cb_data->callback = callback;
3632 cb_data->user_data = callback_data;
3634 tpl_log_manager_get_entities_async (manager, account, got_entities, cb_data);
3636 g_object_unref (manager);
3640 log_window_logger_clear_account_cb (TpProxy *proxy,
3641 const GError *error,
3643 GObject *weak_object)
3645 EmpathyLogWindow *self = EMPATHY_LOG_WINDOW (user_data);
3648 g_warning ("Error when clearing logs: %s", error->message);
3650 /* Refresh the log viewer so the logs are cleared if the account
3651 * has been deleted */
3652 gtk_tree_store_clear (self->priv->store_events);
3653 log_window_who_populate (self);
3655 /* Re-filter the account chooser so the accounts without logs get greyed out */
3656 empathy_account_chooser_set_filter (
3657 EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser),
3658 empathy_account_chooser_filter_has_logs, NULL);
3662 log_window_clear_logs_chooser_select_account (EmpathyAccountChooser *chooser,
3663 EmpathyLogWindow *self)
3665 EmpathyAccountChooser *account_chooser;
3667 account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
3669 empathy_account_chooser_set_account (chooser,
3670 empathy_account_chooser_get_account (account_chooser));
3674 log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
3675 EmpathyLogWindow *self)
3677 GtkWidget *dialog, *content_area, *hbox, *label;
3678 EmpathyAccountChooser *account_chooser;
3682 GError *error = NULL;
3684 account_chooser = (EmpathyAccountChooser *) empathy_account_chooser_new ();
3685 empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
3686 empathy_account_chooser_set_filter (account_chooser,
3687 empathy_account_chooser_filter_has_logs, NULL);
3689 /* Select the same account as in the history window */
3690 if (empathy_account_chooser_is_ready (account_chooser))
3691 log_window_clear_logs_chooser_select_account (account_chooser, self);
3693 g_signal_connect (account_chooser, "ready",
3694 G_CALLBACK (log_window_clear_logs_chooser_select_account), self);
3696 dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (self),
3697 GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING,
3699 _("Are you sure you want to delete all logs of previous conversations?"));
3701 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
3702 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3703 _("Clear All"), GTK_RESPONSE_APPLY,
3706 content_area = gtk_message_dialog_get_message_area (
3707 GTK_MESSAGE_DIALOG (dialog));
3709 hbox = gtk_hbox_new (FALSE, 6);
3710 label = gtk_label_new (_("Delete from:"));
3711 gtk_box_pack_start (GTK_BOX (hbox), label,
3713 gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (account_chooser),
3715 gtk_box_pack_start (GTK_BOX (content_area), hbox,
3718 gtk_widget_show_all (hbox);
3720 response_id = gtk_dialog_run (GTK_DIALOG (dialog));
3722 if (response_id != GTK_RESPONSE_APPLY)
3725 bus = tp_dbus_daemon_dup (&error);
3728 g_warning ("Could not delete logs: %s", error->message);
3729 g_error_free (error);
3733 logger = g_object_new (TP_TYPE_PROXY,
3734 "bus-name", "org.freedesktop.Telepathy.Logger",
3735 "object-path", "/org/freedesktop/Telepathy/Logger",
3738 g_object_unref (bus);
3740 tp_proxy_add_interface_by_id (logger, EMP_IFACE_QUARK_LOGGER);
3742 if (empathy_account_chooser_has_all_selected (account_chooser))
3744 DEBUG ("Deleting logs for all the accounts");
3746 emp_cli_logger_call_clear (logger, -1,
3747 log_window_logger_clear_account_cb,
3748 self, NULL, G_OBJECT (self));
3754 account = empathy_account_chooser_get_account (account_chooser);
3756 DEBUG ("Deleting logs for %s", tp_proxy_get_object_path (account));
3758 emp_cli_logger_call_clear_account (logger, -1,
3759 tp_proxy_get_object_path (account),
3760 log_window_logger_clear_account_cb,
3761 self, NULL, G_OBJECT (self));
3764 g_object_unref (logger);
3766 gtk_widget_destroy (dialog);