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-message.h>
51 #include <libempathy/empathy-request-util.h>
52 #include <libempathy/empathy-utils.h>
53 #include <libempathy/empathy-time.h>
55 #include "empathy-log-window.h"
56 #include "empathy-account-chooser.h"
57 #include "empathy-call-utils.h"
58 #include "empathy-chat-view.h"
59 #include "empathy-contact-dialogs.h"
60 #include "empathy-images.h"
61 #include "empathy-theme-manager.h"
62 #include "empathy-ui-utils.h"
64 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
65 #include <libempathy/empathy-debug.h>
67 G_DEFINE_TYPE (EmpathyLogWindow, empathy_log_window, GTK_TYPE_WINDOW);
69 struct _EmpathyLogWindowPriv
73 GtkWidget *button_profile;
74 GtkWidget *button_chat;
75 GtkWidget *button_call;
76 GtkWidget *button_video;
78 GtkWidget *search_entry;
83 GtkWidget *treeview_who;
84 GtkWidget *treeview_what;
85 GtkWidget *treeview_when;
86 GtkWidget *treeview_events;
89 GtkTreeStore *store_events;
91 GtkWidget *account_chooser;
95 /* List of selected GDates, free with g_list_free_full (l, g_date_free) */
98 TplActionChain *chain;
99 TplLogManager *log_manager;
101 /* Hash of TpChannel<->TpAccount for use by the observer until we can
102 * get a TpAccount from a TpConnection or wherever */
103 GHashTable *channels;
104 TpBaseClient *observer;
106 EmpathyContact *selected_contact;
108 EmpathyCameraMonitor *camera_monitor;
109 GBinding *button_video_binding;
111 /* Used to cancel logger calls when no longer needed */
114 /* List of owned TplLogSearchHits, free with tpl_log_search_hit_free */
118 /* Only used while waiting for the account chooser to be ready */
119 TpAccount *selected_account;
120 gchar *selected_chat_id;
121 gboolean selected_is_chatroom;
124 static void log_window_search_entry_changed_cb (GtkWidget *entry,
125 EmpathyLogWindow *self);
126 static void log_window_search_entry_activate_cb (GtkWidget *widget,
127 EmpathyLogWindow *self);
128 static void log_window_search_entry_icon_pressed_cb (GtkEntry *entry,
129 GtkEntryIconPosition icon_pos,
132 static void log_window_who_populate (EmpathyLogWindow *self);
133 static void log_window_who_setup (EmpathyLogWindow *self);
134 static void log_window_when_setup (EmpathyLogWindow *self);
135 static void log_window_what_setup (EmpathyLogWindow *self);
136 static void log_window_events_setup (EmpathyLogWindow *self);
137 static void log_window_chats_accounts_changed_cb (GtkWidget *combobox,
138 EmpathyLogWindow *self);
139 static void log_window_chats_set_selected (EmpathyLogWindow *self);
140 static void log_window_chats_get_messages (EmpathyLogWindow *self,
141 gboolean force_get_dates);
142 static void log_window_when_changed_cb (GtkTreeSelection *selection,
143 EmpathyLogWindow *self);
144 static void log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
145 EmpathyLogWindow *self);
146 static void start_spinner (void);
148 static void log_window_create_observer (EmpathyLogWindow *window);
151 empathy_account_chooser_filter_has_logs (TpAccount *account,
152 EmpathyAccountChooserFilterResultCallback callback,
153 gpointer callback_data,
203 COL_EVENTS_PRETTY_DATE,
212 #define CALENDAR_ICON "stock_calendar"
214 /* Seconds between two messages to be considered one conversation */
215 #define MAX_GAP 30*60
217 #define WHAT_TYPE_SEPARATOR -1
221 EVENT_CALL_INCOMING = 1 << 0,
222 EVENT_CALL_OUTGOING = 1 << 1,
223 EVENT_CALL_MISSED = 1 << 2,
224 EVENT_CALL_ALL = 1 << 3,
228 log_window_get_selected (EmpathyLogWindow *window,
233 TplEventTypeMask *event_mask,
234 EventSubtype *subtype);
236 static EmpathyLogWindow *log_window = NULL;
238 static gboolean has_element;
241 #define _date_copy(d) g_date_new_julian (g_date_get_julian (d))
246 EmpathyLogWindow *self;
250 TplEventTypeMask event_mask;
251 EventSubtype subtype;
256 ctx_new (EmpathyLogWindow *self,
260 TplEventTypeMask event_mask,
261 EventSubtype subtype,
264 Ctx *ctx = g_slice_new0 (Ctx);
268 ctx->account = g_object_ref (account);
270 ctx->entity = g_object_ref (entity);
272 ctx->date = _date_copy (date);
273 ctx->event_mask = event_mask;
274 ctx->subtype = subtype;
283 tp_clear_object (&ctx->account);
284 tp_clear_object (&ctx->entity);
285 tp_clear_pointer (&ctx->date, g_date_free);
287 g_slice_free (Ctx, ctx);
291 account_chooser_ready_cb (EmpathyAccountChooser *chooser,
292 EmpathyLogWindow *self)
294 /* We'll display the account once the model has been populate with the chats
295 * of this account. */
296 empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER (
297 self->priv->account_chooser), self->priv->selected_account);
301 select_account_once_ready (EmpathyLogWindow *self,
303 const gchar *chat_id,
304 gboolean is_chatroom)
306 EmpathyAccountChooser *account_chooser;
308 account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
310 tp_clear_object (&self->priv->selected_account);
311 self->priv->selected_account = g_object_ref (account);
313 g_free (self->priv->selected_chat_id);
314 self->priv->selected_chat_id = g_strdup (chat_id);
316 self->priv->selected_is_chatroom = is_chatroom;
318 if (empathy_account_chooser_is_ready (account_chooser))
319 account_chooser_ready_cb (account_chooser, self);
321 /* Chat will be selected once the account chooser is ready */
322 g_signal_connect (account_chooser, "ready",
323 G_CALLBACK (account_chooser_ready_cb), self);
327 toolbutton_profile_clicked (GtkToolButton *toolbutton,
328 EmpathyLogWindow *self)
330 g_return_if_fail (self != NULL);
331 g_return_if_fail (EMPATHY_IS_CONTACT (self->priv->selected_contact));
333 empathy_contact_information_dialog_show (self->priv->selected_contact,
338 toolbutton_chat_clicked (GtkToolButton *toolbutton,
339 EmpathyLogWindow *self)
341 g_return_if_fail (self != NULL);
342 g_return_if_fail (EMPATHY_IS_CONTACT (self->priv->selected_contact));
344 empathy_chat_with_contact (self->priv->selected_contact,
345 gtk_get_current_event_time ());
349 toolbutton_av_clicked (GtkToolButton *toolbutton,
350 EmpathyLogWindow *self)
354 g_return_if_fail (self != NULL);
355 g_return_if_fail (EMPATHY_IS_CONTACT (self->priv->selected_contact));
357 video = (GTK_WIDGET (toolbutton) == self->priv->button_video);
359 empathy_call_new_with_streams (
360 empathy_contact_get_id (self->priv->selected_contact),
361 empathy_contact_get_account (self->priv->selected_contact),
362 TRUE, video, gtk_get_current_event_time ());
366 insert_or_change_row (EmpathyLogWindow *self,
372 char *str = gtk_tree_path_to_string (path);
375 gtk_tree_model_get (model, iter,
376 COL_EVENTS_TEXT, &text,
379 script = g_strdup_printf ("javascript:%s([%s], '%s');",
381 g_strdelimit (str, ":", ','),
384 // g_print ("%s\n", script);
385 webkit_web_view_execute_script (WEBKIT_WEB_VIEW (self->priv->webview),
394 store_events_row_inserted (GtkTreeModel *model,
397 EmpathyLogWindow *self)
399 insert_or_change_row (self, "insertRow", model, path, iter);
403 store_events_row_changed (GtkTreeModel *model,
406 EmpathyLogWindow *self)
408 insert_or_change_row (self, "changeRow", model, path, iter);
412 store_events_row_deleted (GtkTreeModel *model,
414 EmpathyLogWindow *self)
416 char *str = gtk_tree_path_to_string (path);
419 script = g_strdup_printf ("javascript:deleteRow([%s]);",
420 g_strdelimit (str, ":", ','));
422 // g_print ("%s\n", script);
423 webkit_web_view_execute_script (WEBKIT_WEB_VIEW (self->priv->webview),
431 store_events_rows_reordered (GtkTreeModel *model,
435 EmpathyLogWindow *self)
437 char *str = gtk_tree_path_to_string (path);
438 int i, children = gtk_tree_model_iter_n_children (model, iter);
439 char **new_order_strv, *new_order_s;
442 new_order_strv = g_new0 (char *, children + 1);
444 for (i = 0; i < children; i++)
445 new_order_strv[i] = g_strdup_printf ("%i", new_order[i]);
447 new_order_s = g_strjoinv (",", new_order_strv);
449 script = g_strdup_printf ("javascript:reorderRows([%s], [%s]);",
450 str == NULL ? "" : g_strdelimit (str, ":", ','),
453 webkit_web_view_execute_script (WEBKIT_WEB_VIEW (self->priv->webview),
458 g_free (new_order_s);
459 g_strfreev (new_order_strv);
463 empathy_log_window_constructor (GType type,
465 GObjectConstructParam *props)
469 if (log_window != NULL)
471 retval = (GObject *) log_window;
475 retval = G_OBJECT_CLASS (empathy_log_window_parent_class)
476 ->constructor (type, n_props, props);
478 log_window = EMPATHY_LOG_WINDOW (retval);
479 g_object_add_weak_pointer (retval, (gpointer) &log_window);
486 empathy_log_window_dispose (GObject *object)
488 EmpathyLogWindow *self = EMPATHY_LOG_WINDOW (object);
490 if (self->priv->source != 0)
492 g_source_remove (self->priv->source);
493 self->priv->source = 0;
496 if (self->priv->current_dates != NULL)
498 g_list_free_full (self->priv->current_dates,
499 (GDestroyNotify) g_date_free);
500 self->priv->current_dates = NULL;
503 tp_clear_pointer (&self->priv->chain, _tpl_action_chain_free);
504 tp_clear_pointer (&self->priv->channels, g_hash_table_unref);
506 tp_clear_object (&self->priv->observer);
507 tp_clear_object (&self->priv->log_manager);
508 tp_clear_object (&self->priv->selected_account);
509 tp_clear_object (&self->priv->selected_contact);
510 tp_clear_object (&self->priv->camera_monitor);
512 G_OBJECT_CLASS (empathy_log_window_parent_class)->dispose (object);
516 empathy_log_window_finalize (GObject *object)
518 EmpathyLogWindow *self = EMPATHY_LOG_WINDOW (object);
520 g_free (self->priv->last_find);
521 g_free (self->priv->selected_chat_id);
523 G_OBJECT_CLASS (empathy_log_window_parent_class)->finalize (object);
527 empathy_log_window_class_init (
528 EmpathyLogWindowClass *empathy_log_window_class)
530 GObjectClass *object_class = G_OBJECT_CLASS (empathy_log_window_class);
532 g_type_class_add_private (empathy_log_window_class,
533 sizeof (EmpathyLogWindowPriv));
535 object_class->constructor = empathy_log_window_constructor;
536 object_class->dispose = empathy_log_window_dispose;
537 object_class->finalize = empathy_log_window_finalize;
541 empathy_log_window_init (EmpathyLogWindow *self)
543 EmpathyAccountChooser *account_chooser;
547 GtkWidget *vbox, *accounts, *search, *label, *quit;
550 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
551 EMPATHY_TYPE_LOG_WINDOW, EmpathyLogWindowPriv);
553 self->priv->chain = _tpl_action_chain_new_async (NULL, NULL, NULL);
555 self->priv->camera_monitor = empathy_camera_monitor_dup_singleton ();
557 self->priv->log_manager = tpl_log_manager_dup_singleton ();
559 gtk_window_set_title (GTK_WINDOW (self), _("History"));
560 gtk_widget_set_can_focus (GTK_WIDGET (self), FALSE);
561 gtk_window_set_default_size (GTK_WINDOW (self), 800, 600);
563 filename = empathy_file_lookup ("empathy-log-window.ui", "libempathy-gtk");
564 gui = empathy_builder_get_file (filename,
565 "vbox1", &self->priv->vbox,
566 "toolbutton_profile", &self->priv->button_profile,
567 "toolbutton_chat", &self->priv->button_chat,
568 "toolbutton_call", &self->priv->button_call,
569 "toolbutton_video", &self->priv->button_video,
570 "toolbutton_accounts", &accounts,
571 "toolbutton_search", &search,
572 "imagemenuitem_quit", &quit,
573 "treeview_who", &self->priv->treeview_who,
574 "treeview_what", &self->priv->treeview_what,
575 "treeview_when", &self->priv->treeview_when,
576 "treeview_events", &self->priv->treeview_events,
577 "notebook", &self->priv->notebook,
578 "spinner", &self->priv->spinner,
582 empathy_builder_connect (gui, self,
583 "toolbutton_profile", "clicked", toolbutton_profile_clicked,
584 "toolbutton_chat", "clicked", toolbutton_chat_clicked,
585 "toolbutton_call", "clicked", toolbutton_av_clicked,
586 "toolbutton_video", "clicked", toolbutton_av_clicked,
587 "imagemenuitem_delete", "activate", log_window_delete_menu_clicked_cb,
590 gtk_container_add (GTK_CONTAINER (self), self->priv->vbox);
592 g_object_unref (gui);
594 g_signal_connect_swapped (quit, "activate",
595 G_CALLBACK (gtk_widget_destroy), self);
597 /* Account chooser for chats */
598 vbox = gtk_vbox_new (FALSE, 3);
600 self->priv->account_chooser = empathy_account_chooser_new ();
601 account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
602 empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
603 empathy_account_chooser_set_filter (account_chooser,
604 empathy_account_chooser_filter_has_logs, NULL);
605 empathy_account_chooser_set_all (account_chooser);
607 g_signal_connect (self->priv->account_chooser, "changed",
608 G_CALLBACK (log_window_chats_accounts_changed_cb),
611 label = gtk_label_new (_("Show"));
613 gtk_box_pack_start (GTK_BOX (vbox),
614 self->priv->account_chooser,
617 gtk_box_pack_start (GTK_BOX (vbox),
621 gtk_widget_show_all (vbox);
622 gtk_container_add (GTK_CONTAINER (accounts), vbox);
625 vbox = gtk_vbox_new (FALSE, 3);
627 self->priv->search_entry = gtk_entry_new ();
628 gtk_entry_set_icon_from_stock (GTK_ENTRY (self->priv->search_entry),
629 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
630 gtk_entry_set_icon_from_stock (GTK_ENTRY (self->priv->search_entry),
631 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
633 label = gtk_label_new (_("Search"));
635 gtk_box_pack_start (GTK_BOX (vbox),
636 self->priv->search_entry,
639 gtk_box_pack_start (GTK_BOX (vbox),
643 gtk_widget_show_all (vbox);
644 gtk_container_add (GTK_CONTAINER (search), vbox);
646 g_signal_connect (self->priv->search_entry, "changed",
647 G_CALLBACK (log_window_search_entry_changed_cb),
650 g_signal_connect (self->priv->search_entry, "activate",
651 G_CALLBACK (log_window_search_entry_activate_cb),
654 g_signal_connect (self->priv->search_entry, "icon-press",
655 G_CALLBACK (log_window_search_entry_icon_pressed_cb),
659 log_window_events_setup (self);
660 log_window_who_setup (self);
661 log_window_what_setup (self);
662 log_window_when_setup (self);
664 log_window_create_observer (self);
666 log_window_who_populate (self);
669 sw = gtk_scrolled_window_new (NULL, NULL);
670 self->priv->webview = webkit_web_view_new ();
671 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
672 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
673 gtk_container_add (GTK_CONTAINER (sw), self->priv->webview);
675 filename = empathy_file_lookup ("empathy-log-window.html", "data");
676 gfile = g_file_new_for_path (filename);
679 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (self->priv->webview),
680 g_file_get_uri (gfile));
681 g_object_unref (gfile);
683 gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
684 sw, gtk_label_new ("webview"));
685 gtk_widget_show_all (sw);
687 /* listen to changes to the treemodel */
688 g_signal_connect (self->priv->store_events, "row-inserted",
689 G_CALLBACK (store_events_row_inserted), self);
690 g_signal_connect (self->priv->store_events, "row-changed",
691 G_CALLBACK (store_events_row_changed), self);
692 g_signal_connect (self->priv->store_events, "row-deleted",
693 G_CALLBACK (store_events_row_deleted), self);
694 g_signal_connect (self->priv->store_events, "rows-reordered",
695 G_CALLBACK (store_events_rows_reordered), self);
698 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (self->priv->notebook), TRUE);
700 gtk_widget_show (GTK_WIDGET (self));
704 empathy_log_window_show (TpAccount *account,
705 const gchar *chat_id,
706 gboolean is_chatroom,
709 log_window = g_object_new (EMPATHY_TYPE_LOG_WINDOW, NULL);
711 gtk_window_present (GTK_WINDOW (log_window));
713 if (account != NULL && chat_id != NULL)
714 select_account_once_ready (log_window, account, chat_id, is_chatroom);
717 gtk_window_set_transient_for (GTK_WINDOW (log_window),
718 GTK_WINDOW (parent));
720 return GTK_WIDGET (log_window);
724 account_equal (TpAccount *a,
727 return g_str_equal (tp_proxy_get_object_path (a),
728 tp_proxy_get_object_path (b));
732 entity_equal (TplEntity *a,
735 return g_str_equal (tpl_entity_get_identifier (a),
736 tpl_entity_get_identifier (b));
740 is_same_confroom (TplEvent *e1,
743 TplEntity *sender1 = tpl_event_get_sender (e1);
744 TplEntity *receiver1 = tpl_event_get_receiver (e1);
745 TplEntity *sender2 = tpl_event_get_sender (e2);
746 TplEntity *receiver2 = tpl_event_get_receiver (e2);
747 TplEntity *room1, *room2;
749 if (receiver1 == NULL || receiver2 == NULL)
752 if (tpl_entity_get_entity_type (sender1) == TPL_ENTITY_ROOM)
754 else if (tpl_entity_get_entity_type (receiver1) == TPL_ENTITY_ROOM)
759 if (tpl_entity_get_entity_type (sender2) == TPL_ENTITY_ROOM)
761 else if (tpl_entity_get_entity_type (receiver2) == TPL_ENTITY_ROOM)
766 return g_str_equal (tpl_entity_get_identifier (room1),
767 tpl_entity_get_identifier (room2));
771 maybe_refresh_logs (TpChannel *channel,
774 GList *accounts = NULL, *entities = NULL, *dates = NULL;
776 TplEventTypeMask event_mask;
777 GDate *anytime = NULL, *today = NULL;
778 GDateTime *now = NULL;
779 gboolean refresh = FALSE;
783 if (!log_window_get_selected (log_window,
784 &accounts, &entities, &anyone, &dates, &event_mask, NULL))
786 DEBUG ("Could not get selected rows");
790 type = tp_channel_get_channel_type (channel);
792 /* If the channel type is not in the What pane, whatever has happened
793 * won't be displayed in the events pane. */
794 if (!tp_strdiff (type, TP_IFACE_CHANNEL_TYPE_TEXT) &&
795 !(event_mask & TPL_EVENT_MASK_TEXT))
797 if ((!tp_strdiff (type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA) ||
798 !tp_strdiff (type, TPY_IFACE_CHANNEL_TYPE_CALL)) &&
799 !(event_mask & TPL_EVENT_MASK_CALL))
802 anytime = g_date_new_dmy (2, 1, -1);
803 now = g_date_time_new_now_local ();
804 today = g_date_new_dmy (g_date_time_get_day_of_month (now),
805 g_date_time_get_month (now),
806 g_date_time_get_year (now));
808 /* If Today (or anytime) isn't selected, anything that has happened now
809 * won't be displayed. */
810 if (!g_list_find_custom (dates, anytime, (GCompareFunc) g_date_compare) &&
811 !g_list_find_custom (dates, today, (GCompareFunc) g_date_compare))
820 for (acc = accounts, ent = entities;
821 acc != NULL && ent != NULL;
822 acc = g_list_next (acc), ent = g_list_next (ent))
824 if (!account_equal (account, acc->data))
827 if (!tp_strdiff (tp_channel_get_identifier (channel),
828 tpl_entity_get_identifier (ent->data)))
836 tp_clear_pointer (&anytime, g_date_free);
837 tp_clear_pointer (&today, g_date_free);
838 tp_clear_pointer (&now, g_date_time_unref);
839 g_list_free_full (accounts, g_object_unref);
840 g_list_free_full (entities, g_object_unref);
841 g_list_free_full (dates, (GFreeFunc) g_date_free);
845 DEBUG ("Refreshing logs after received event");
847 /* FIXME: We need to populate the entities in case we
848 * didn't have any previous logs with this contact. */
849 log_window_chats_get_messages (log_window, FALSE);
854 on_msg_sent (TpTextChannel *channel,
855 TpSignalledMessage *message,
858 EmpathyLogWindow *self)
860 TpAccount *account = g_hash_table_lookup (self->priv->channels, channel);
862 maybe_refresh_logs (TP_CHANNEL (channel), account);
866 on_msg_received (TpTextChannel *channel,
867 TpSignalledMessage *message,
868 EmpathyLogWindow *self)
870 TpMessage *msg = TP_MESSAGE (message);
871 TpChannelTextMessageType type = tp_message_get_message_type (msg);
872 TpAccount *account = g_hash_table_lookup (self->priv->channels, channel);
874 if (type != TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL &&
875 type != TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)
878 maybe_refresh_logs (TP_CHANNEL (channel), account);
882 on_channel_ended (TpChannel *channel,
886 EmpathyLogWindow *self)
888 if (self->priv->channels != NULL)
889 g_hash_table_remove (self->priv->channels, channel);
893 on_call_ended (TpChannel *channel,
897 EmpathyLogWindow *self)
899 TpAccount *account = g_hash_table_lookup (self->priv->channels, channel);
901 maybe_refresh_logs (channel, account);
903 if (self->priv->channels != NULL)
904 g_hash_table_remove (self->priv->channels, channel);
908 observe_channels (TpSimpleObserver *observer,
910 TpConnection *connection,
912 TpChannelDispatchOperation *dispatch_operation,
914 TpObserveChannelsContext *context,
917 EmpathyLogWindow *self = user_data;
921 for (l = channels; l != NULL; l = g_list_next (l))
923 TpChannel *channel = l->data;
924 const gchar *type = tp_channel_get_channel_type (channel);
926 if (!tp_strdiff (type, TP_IFACE_CHANNEL_TYPE_TEXT))
928 TpTextChannel *text_channel = TP_TEXT_CHANNEL (channel);
930 g_hash_table_insert (self->priv->channels,
931 g_object_ref (channel), g_object_ref (account));
933 tp_g_signal_connect_object (text_channel, "message-sent",
934 G_CALLBACK (on_msg_sent), self, 0);
935 tp_g_signal_connect_object (text_channel, "message-received",
936 G_CALLBACK (on_msg_received), self, 0);
937 tp_g_signal_connect_object (channel, "invalidated",
938 G_CALLBACK (on_channel_ended), self, 0);
940 else if (!tp_strdiff (type, TPY_IFACE_CHANNEL_TYPE_CALL) ||
941 !tp_strdiff (type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA))
943 g_hash_table_insert (self->priv->channels,
944 g_object_ref (channel), g_object_ref (account));
946 tp_g_signal_connect_object (channel, "invalidated",
947 G_CALLBACK (on_call_ended), self, 0);
951 g_warning ("Unknown channel type: %s", type);
955 tp_observe_channels_context_accept (context);
959 log_window_create_observer (EmpathyLogWindow *self)
962 GError *error = NULL;
964 dbus = tp_dbus_daemon_dup (&error);
968 DEBUG ("Could not connect to the bus: %s", error->message);
969 g_error_free (error);
973 self->priv->observer = tp_simple_observer_new (dbus, TRUE, "LogWindow",
974 TRUE, observe_channels,
975 g_object_ref (self), g_object_unref);
976 self->priv->channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
977 g_object_unref, g_object_unref);
979 tp_base_client_take_observer_filter (self->priv->observer,
981 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
982 TP_IFACE_CHANNEL_TYPE_TEXT,
984 tp_base_client_take_observer_filter (self->priv->observer,
986 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
987 TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
989 tp_base_client_take_observer_filter (self->priv->observer,
991 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
992 TPY_IFACE_CHANNEL_TYPE_CALL,
995 tp_base_client_register (self->priv->observer, NULL);
997 g_object_unref (dbus);
1001 event_get_target (TplEvent *event)
1003 TplEntity *sender = tpl_event_get_sender (event);
1004 TplEntity *receiver = tpl_event_get_receiver (event);
1006 if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
1013 model_is_parent (GtkTreeModel *model,
1017 TplEvent *stored_event;
1020 gboolean found = FALSE;
1023 if (gtk_tree_model_iter_parent (model, &parent, iter))
1026 gtk_tree_model_get (model, iter,
1027 COL_EVENTS_ACCOUNT, &account,
1028 COL_EVENTS_TARGET, &target,
1029 COL_EVENTS_EVENT, &stored_event,
1032 if (G_OBJECT_TYPE (event) == G_OBJECT_TYPE (stored_event) &&
1033 account_equal (account, tpl_event_get_account (event)) &&
1034 (entity_equal (target, event_get_target (event)) ||
1035 is_same_confroom (event, stored_event)))
1040 gtk_tree_model_iter_nth_child (model, &child, iter,
1041 gtk_tree_model_iter_n_children (model, iter) - 1);
1043 gtk_tree_model_get (model, &child,
1044 COL_EVENTS_TS, ×tamp,
1047 if (ABS (tpl_event_get_timestamp (event) - timestamp) < MAX_GAP)
1049 /* The gap is smaller than 30 min */
1054 g_object_unref (stored_event);
1055 g_object_unref (account);
1056 g_object_unref (target);
1062 get_display_string_for_chat_message (EmpathyMessage *message,
1065 EmpathyContact *sender, *receiver, *target;
1066 TplEntity *ent_sender, *ent_receiver;
1067 const gchar *format;
1069 sender = empathy_message_get_sender (message);
1070 receiver = empathy_message_get_receiver (message);
1072 ent_sender = tpl_event_get_sender (event);
1073 ent_receiver = tpl_event_get_receiver (event);
1075 /* If this is a MUC, we want to show "Chat in <room>". */
1076 if (tpl_entity_get_entity_type (ent_sender) == TPL_ENTITY_ROOM ||
1077 (ent_receiver != NULL &&
1078 tpl_entity_get_entity_type (ent_receiver) == TPL_ENTITY_ROOM))
1079 format = _("Chat in %s");
1081 format = _("Chat with %s");
1083 if (tpl_entity_get_entity_type (ent_sender) == TPL_ENTITY_ROOM)
1085 else if (ent_receiver != NULL &&
1086 tpl_entity_get_entity_type (ent_receiver) == TPL_ENTITY_ROOM)
1088 else if (empathy_contact_is_user (sender))
1093 return g_markup_printf_escaped (format, empathy_contact_get_alias (target));
1097 get_parent_iter_for_message (TplEvent *event,
1098 EmpathyMessage *message,
1099 GtkTreeIter *parent)
1101 GtkTreeStore *store;
1102 GtkTreeModel *model;
1104 gboolean parent_found = FALSE;
1107 store = log_window->priv->store_events;
1108 model = GTK_TREE_MODEL (store);
1110 for (next = gtk_tree_model_get_iter_first (model, &iter);
1112 next = gtk_tree_model_iter_next (model, &iter))
1114 if ((parent_found = model_is_parent (model, &iter, event)))
1125 gchar *body, *pretty_date;
1127 date = g_date_time_new_from_unix_utc (
1128 tpl_event_get_timestamp (event));
1130 pretty_date = g_date_time_format (date,
1131 C_("A date with the time", "%A, %e %B %Y %X"));
1133 body = get_display_string_for_chat_message (message, event);
1135 gtk_tree_store_append (store, &iter, NULL);
1136 gtk_tree_store_set (store, &iter,
1137 COL_EVENTS_TS, tpl_event_get_timestamp (event),
1138 COL_EVENTS_PRETTY_DATE, pretty_date,
1139 COL_EVENTS_TEXT, body,
1140 COL_EVENTS_ICON, "stock_text_justify",
1141 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
1142 COL_EVENTS_TARGET, event_get_target (event),
1143 COL_EVENTS_EVENT, event,
1149 g_free (pretty_date);
1150 g_date_time_unref (date);
1154 static const gchar *
1155 get_icon_for_event (TplEvent *event)
1157 const gchar *icon = NULL;
1159 if (TPL_IS_TEXT_EVENT (event))
1161 TplTextEvent *text = TPL_TEXT_EVENT (event);
1163 if (!tp_str_empty (tpl_text_event_get_supersedes_token (text)))
1164 icon = EMPATHY_IMAGE_EDIT_MESSAGE;
1166 #ifdef HAVE_CALL_LOGS
1167 else if (TPL_IS_CALL_EVENT (event))
1169 TplCallEvent *call = TPL_CALL_EVENT (event);
1170 TplCallEndReason reason = tpl_call_event_get_end_reason (call);
1171 TplEntity *sender = tpl_event_get_sender (event);
1172 TplEntity *receiver = tpl_event_get_receiver (event);
1174 if (reason == TPL_CALL_END_REASON_NO_ANSWER)
1175 icon = EMPATHY_IMAGE_CALL_MISSED;
1176 else if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
1177 icon = EMPATHY_IMAGE_CALL_OUTGOING;
1178 else if (tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
1179 icon = EMPATHY_IMAGE_CALL_INCOMING;
1187 log_window_append_chat_message (TplEvent *event,
1188 EmpathyMessage *message)
1190 GtkTreeStore *store = log_window->priv->store_events;
1191 GtkTreeIter iter, parent;
1192 gchar *pretty_date, *alias, *body, *msg;
1195 date = g_date_time_new_from_unix_utc (
1196 tpl_event_get_timestamp (event));
1198 pretty_date = g_date_time_format (date, "%X");
1200 get_parent_iter_for_message (event, message, &parent);
1202 msg = g_markup_escape_text (empathy_message_get_body (message), -1);
1203 alias = g_markup_escape_text (
1204 tpl_entity_get_alias (tpl_event_get_sender (event)), -1);
1206 /* If the user is searching, highlight the matched text */
1207 if (!EMP_STR_EMPTY (log_window->priv->last_find))
1209 gchar *str = g_regex_escape_string (log_window->priv->last_find, -1);
1210 gchar *replacement = g_markup_printf_escaped (
1211 "<span background=\"yellow\">%s</span>",
1212 log_window->priv->last_find);
1213 GError *error = NULL;
1214 GRegex *regex = g_regex_new (str, 0, 0, &error);
1218 DEBUG ("Could not create regex: %s", error->message);
1219 g_error_free (error);
1223 gchar *new_msg = g_regex_replace_literal (regex,
1224 empathy_message_get_body (message), -1, 0, replacement,
1227 if (new_msg != NULL)
1229 /* We pass ownership of new_msg to msg, which is freed later */
1235 DEBUG ("Error while performing string substitution: %s",
1237 g_error_free (error);
1242 g_free (replacement);
1244 tp_clear_pointer (®ex, g_regex_unref);
1247 if (tpl_text_event_get_message_type (TPL_TEXT_EVENT (event))
1248 == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)
1250 /* Translators: this is an emote: '* Danielle waves' */
1251 body = g_strdup_printf (_("<i>* %s %s</i>"), alias, msg);
1255 /* Translators: this is a message: 'Danielle: hello'
1256 * The string in bold is the sender's name */
1257 body = g_strdup_printf (_("<b>%s:</b> %s"), alias, msg);
1260 gtk_tree_store_append (store, &iter, &parent);
1261 gtk_tree_store_set (store, &iter,
1262 COL_EVENTS_TS, tpl_event_get_timestamp (event),
1263 COL_EVENTS_PRETTY_DATE, pretty_date,
1264 COL_EVENTS_TEXT, body,
1265 COL_EVENTS_ICON, get_icon_for_event (event),
1266 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
1267 COL_EVENTS_TARGET, event_get_target (event),
1268 COL_EVENTS_EVENT, event,
1274 g_free (pretty_date);
1275 g_date_time_unref (date);
1278 #ifdef HAVE_CALL_LOGS
1280 log_window_append_call (TplEvent *event,
1281 EmpathyMessage *message)
1283 TplCallEvent *call = TPL_CALL_EVENT (event);
1284 GtkTreeStore *store = log_window->priv->store_events;
1285 GtkTreeIter iter, child;
1286 gchar *pretty_date, *duration, *finished;
1287 GDateTime *started_date, *finished_date;
1290 /* If searching, only add the call if the search string appears anywhere */
1291 if (!EMP_STR_EMPTY (log_window->priv->last_find))
1293 if (strstr (tpl_entity_get_identifier (tpl_event_get_sender (event)),
1294 log_window->priv->last_find) == NULL &&
1295 strstr (tpl_entity_get_identifier (tpl_event_get_receiver (event)),
1296 log_window->priv->last_find) == NULL &&
1297 strstr (tpl_call_event_get_detailed_end_reason (call),
1298 log_window->priv->last_find) == NULL)
1300 DEBUG ("TplCallEvent doesn't match search string, ignoring");
1305 started_date = g_date_time_new_from_unix_utc (
1306 tpl_event_get_timestamp (event));
1308 pretty_date = g_date_time_format (started_date,
1309 C_("A date with the time", "%A, %e %B %Y %X"));
1311 gtk_tree_store_append (store, &iter, NULL);
1312 gtk_tree_store_set (store, &iter,
1313 COL_EVENTS_TS, tpl_event_get_timestamp (event),
1314 COL_EVENTS_PRETTY_DATE, pretty_date,
1315 COL_EVENTS_TEXT, empathy_message_get_body (message),
1316 COL_EVENTS_ICON, get_icon_for_event (event),
1317 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
1318 COL_EVENTS_TARGET, event_get_target (event),
1319 COL_EVENTS_EVENT, event,
1322 if (tpl_call_event_get_end_reason (call) != TPL_CALL_END_REASON_NO_ANSWER)
1326 span = tpl_call_event_get_duration (TPL_CALL_EVENT (event));
1328 duration = g_strdup_printf (_("%" G_GINT64_FORMAT " seconds"), span);
1330 duration = g_strdup_printf (_("%" G_GINT64_FORMAT " minutes"),
1333 finished_date = g_date_time_add (started_date, -span);
1334 finished = g_date_time_format (finished_date, "%X");
1335 g_date_time_unref (finished_date);
1337 body = g_strdup_printf (_("Call took %s, ended at %s"),
1338 duration, finished);
1343 gtk_tree_store_append (store, &child, &iter);
1344 gtk_tree_store_set (store, &child,
1345 COL_EVENTS_TS, tpl_event_get_timestamp (event),
1346 COL_EVENTS_TEXT, body,
1347 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
1348 COL_EVENTS_TARGET, event_get_target (event),
1349 COL_EVENTS_EVENT, event,
1355 g_free (pretty_date);
1356 g_date_time_unref (started_date);
1361 log_window_append_message (TplEvent *event,
1362 EmpathyMessage *message)
1364 if (TPL_IS_TEXT_EVENT (event))
1365 log_window_append_chat_message (event, message);
1366 #ifdef HAVE_CALL_LOGS
1367 else if (TPL_IS_CALL_EVENT (event))
1368 log_window_append_call (event, message);
1371 DEBUG ("Message type not handled");
1375 add_all_accounts_and_entities (GList **accounts,
1379 GtkTreeModel *model;
1382 view = GTK_TREE_VIEW (log_window->priv->treeview_who);
1383 model = gtk_tree_view_get_model (view);
1385 if (!gtk_tree_model_get_iter_first (model, &iter))
1394 gtk_tree_model_get (model, &iter,
1395 COL_WHO_ACCOUNT, &account,
1396 COL_WHO_TARGET, &entity,
1397 COL_WHO_TYPE, &type,
1400 if (type != COL_TYPE_NORMAL)
1403 if (accounts != NULL)
1404 *accounts = g_list_append (*accounts, account);
1406 if (entities != NULL)
1407 *entities = g_list_append (*entities, entity);
1409 while (gtk_tree_model_iter_next (model, &iter));
1413 log_window_get_selected (EmpathyLogWindow *self,
1418 TplEventTypeMask *event_mask,
1419 EventSubtype *subtype)
1422 GtkTreeModel *model;
1423 GtkTreeSelection *selection;
1425 TplEventTypeMask ev = 0;
1426 EventSubtype st = 0;
1430 view = GTK_TREE_VIEW (self->priv->treeview_who);
1431 model = gtk_tree_view_get_model (view);
1432 selection = gtk_tree_view_get_selection (view);
1434 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1438 if (accounts != NULL)
1440 if (entities != NULL)
1445 for (l = paths; l != NULL; l = l->next)
1447 GtkTreePath *path = l->data;
1451 gtk_tree_model_get_iter (model, &iter, path);
1452 gtk_tree_model_get (model, &iter,
1453 COL_WHO_ACCOUNT, &account,
1454 COL_WHO_TARGET, &entity,
1455 COL_WHO_TYPE, &type,
1458 if (type == COL_TYPE_ANY)
1460 if (accounts != NULL || entities != NULL)
1461 add_all_accounts_and_entities (accounts, entities);
1467 if (accounts != NULL)
1468 *accounts = g_list_append (*accounts, g_object_ref (account));
1470 if (entities != NULL)
1471 *entities = g_list_append (*entities, g_object_ref (entity));
1473 g_object_unref (account);
1474 g_object_unref (entity);
1476 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1478 view = GTK_TREE_VIEW (self->priv->treeview_what);
1479 model = gtk_tree_view_get_model (view);
1480 selection = gtk_tree_view_get_selection (view);
1482 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1483 for (l = paths; l != NULL; l = l->next)
1485 GtkTreePath *path = l->data;
1486 TplEventTypeMask mask;
1487 EventSubtype submask;
1489 gtk_tree_model_get_iter (model, &iter, path);
1490 gtk_tree_model_get (model, &iter,
1491 COL_WHAT_TYPE, &mask,
1492 COL_WHAT_SUBTYPE, &submask,
1498 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1500 view = GTK_TREE_VIEW (self->priv->treeview_when);
1501 model = gtk_tree_view_get_model (view);
1502 selection = gtk_tree_view_get_selection (view);
1508 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1509 for (l = paths; l != NULL; l = l->next)
1511 GtkTreePath *path = l->data;
1514 gtk_tree_model_get_iter (model, &iter, path);
1515 gtk_tree_model_get (model, &iter,
1516 COL_WHEN_DATE, &date,
1519 *dates = g_list_append (*dates, date);
1521 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
1524 if (event_mask != NULL)
1527 if (subtype != NULL)
1534 model_has_entity (GtkTreeModel *model,
1539 TplLogSearchHit *hit = data;
1542 gboolean ret = FALSE;
1544 gtk_tree_model_get (model, iter,
1546 COL_WHO_ACCOUNT, &a,
1549 if (e != NULL && entity_equal (hit->target, e) &&
1550 a != NULL && account_equal (hit->account, a))
1552 ret = has_element = TRUE;
1555 tp_clear_object (&e);
1556 tp_clear_object (&a);
1562 model_has_date (GtkTreeModel *model,
1570 gtk_tree_model_get (model, iter,
1574 if (!g_date_compare (date, d))
1584 get_events_for_date (TplActionChain *chain, gpointer user_data);
1587 populate_events_from_search_hits (GList *accounts,
1591 TplEventTypeMask event_mask;
1592 EventSubtype subtype;
1595 gboolean is_anytime = FALSE;
1597 if (!log_window_get_selected (log_window,
1598 NULL, NULL, NULL, NULL, &event_mask, &subtype))
1601 anytime = g_date_new_dmy (2, 1, -1);
1602 if (g_list_find_custom (dates, anytime, (GCompareFunc) g_date_compare))
1605 for (l = log_window->priv->hits; l != NULL; l = l->next)
1607 TplLogSearchHit *hit = l->data;
1609 gboolean found = FALSE;
1611 /* Protect against invalid data (corrupt or old log files). */
1612 if (hit->account == NULL || hit->target == NULL)
1615 for (acc = accounts, targ = targets;
1616 acc != NULL && targ != NULL && !found;
1617 acc = acc->next, targ = targ->next)
1619 TpAccount *account = acc->data;
1620 TplEntity *target = targ->data;
1622 if (account_equal (hit->account, account) &&
1623 entity_equal (hit->target, target))
1631 g_list_find_custom (dates, hit->date, (GCompareFunc) g_date_compare)
1636 ctx = ctx_new (log_window, hit->account, hit->target, hit->date,
1637 event_mask, subtype, log_window->priv->count);
1638 _tpl_action_chain_append (log_window->priv->chain,
1639 get_events_for_date, ctx);
1644 _tpl_action_chain_start (log_window->priv->chain);
1646 g_date_free (anytime);
1650 format_date_for_display (GDate *date)
1656 /* g_date_strftime sucks */
1658 now = g_date_new ();
1659 g_date_set_time_t (now, time (NULL));
1661 days_elapsed = g_date_days_between (date, now);
1663 if (days_elapsed < 0)
1667 else if (days_elapsed == 0)
1669 text = g_strdup (_("Today"));
1671 else if (days_elapsed == 1)
1673 text = g_strdup (_("Yesterday"));
1679 dt = g_date_time_new_utc (g_date_get_year (date),
1680 g_date_get_month (date), g_date_get_day (date),
1683 if (days_elapsed <= 7)
1684 text = g_date_time_format (dt, "%A");
1686 text = g_date_time_format (dt,
1687 C_("A date such as '23 May 2010', "
1688 "%e is the day, %B the month and %Y the year",
1691 g_date_time_unref (dt);
1700 populate_dates_from_search_hits (GList *accounts,
1705 GtkTreeModel *model;
1706 GtkListStore *store;
1707 GtkTreeSelection *selection;
1710 if (log_window == NULL)
1713 view = GTK_TREE_VIEW (log_window->priv->treeview_when);
1714 model = gtk_tree_view_get_model (view);
1715 store = GTK_LIST_STORE (model);
1716 selection = gtk_tree_view_get_selection (view);
1718 for (l = log_window->priv->hits; l != NULL; l = l->next)
1720 TplLogSearchHit *hit = l->data;
1722 gboolean found = FALSE;
1724 /* Protect against invalid data (corrupt or old log files). */
1725 if (hit->account == NULL || hit->target == NULL)
1728 for (acc = accounts, targ = targets;
1729 acc != NULL && targ != NULL && !found;
1730 acc = acc->next, targ = targ->next)
1732 TpAccount *account = acc->data;
1733 TplEntity *target = targ->data;
1735 if (account_equal (hit->account, account) &&
1736 entity_equal (hit->target, target))
1743 /* Add the date if it's not already there */
1744 has_element = FALSE;
1745 gtk_tree_model_foreach (model, model_has_date, hit->date);
1748 gchar *text = format_date_for_display (hit->date);
1750 gtk_list_store_append (store, &iter);
1751 gtk_list_store_set (store, &iter,
1752 COL_WHEN_DATE, hit->date,
1753 COL_WHEN_TEXT, text,
1754 COL_WHEN_ICON, CALENDAR_ICON,
1759 if (gtk_tree_model_get_iter_first (model, &iter))
1761 gtk_list_store_prepend (store, &iter);
1762 gtk_list_store_set (store, &iter,
1763 COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
1764 COL_WHEN_TEXT, "separator",
1767 gtk_list_store_prepend (store, &iter);
1768 gtk_list_store_set (store, &iter,
1769 COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
1770 COL_WHEN_TEXT, _("Anytime"),
1773 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 2))
1774 gtk_tree_selection_select_iter (selection, &iter);
1779 populate_entities_from_search_hits (void)
1781 EmpathyAccountChooser *account_chooser;
1784 GtkTreeModel *model;
1785 GtkTreeSelection *selection;
1787 GtkListStore *store;
1790 view = GTK_TREE_VIEW (log_window->priv->treeview_who);
1791 model = gtk_tree_view_get_model (view);
1792 store = GTK_LIST_STORE (model);
1793 selection = gtk_tree_view_get_selection (view);
1795 gtk_list_store_clear (store);
1797 account_chooser = EMPATHY_ACCOUNT_CHOOSER (log_window->priv->account_chooser);
1798 account = empathy_account_chooser_get_account (account_chooser);
1800 for (l = log_window->priv->hits; l; l = l->next)
1802 TplLogSearchHit *hit = l->data;
1804 /* Protect against invalid data (corrupt or old log files). */
1805 if (hit->account == NULL || hit->target == NULL)
1808 /* Filter based on the selected account */
1809 if (account != NULL && !account_equal (account, hit->account))
1812 /* Add the entity if it's not already there */
1813 has_element = FALSE;
1814 gtk_tree_model_foreach (model, model_has_entity, hit);
1817 TplEntityType type = tpl_entity_get_entity_type (hit->target);
1818 EmpathyContact *contact;
1819 gboolean room = type == TPL_ENTITY_ROOM;
1821 contact = empathy_contact_from_tpl_contact (hit->account,
1824 gtk_list_store_append (store, &iter);
1825 gtk_list_store_set (store, &iter,
1826 COL_WHO_TYPE, COL_TYPE_NORMAL,
1827 COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
1828 : EMPATHY_IMAGE_AVATAR_DEFAULT,
1829 COL_WHO_NAME, empathy_contact_get_alias (contact),
1830 COL_WHO_ID, tpl_entity_get_identifier (hit->target),
1831 COL_WHO_ACCOUNT, hit->account,
1832 COL_WHO_TARGET, hit->target,
1835 g_object_unref (contact);
1839 if (gtk_tree_model_get_iter_first (model, &iter))
1841 gtk_list_store_prepend (store, &iter);
1842 gtk_list_store_set (store, &iter,
1843 COL_WHO_TYPE, COL_TYPE_SEPARATOR,
1844 COL_WHO_NAME, "separator",
1847 gtk_list_store_prepend (store, &iter);
1848 gtk_list_store_set (store, &iter,
1849 COL_WHO_TYPE, COL_TYPE_ANY,
1850 COL_WHO_NAME, _("Anyone"),
1854 /* Select 'Anyone' */
1855 if (gtk_tree_model_get_iter_first (model, &iter))
1856 gtk_tree_selection_select_iter (selection, &iter);
1860 log_manager_searched_new_cb (GObject *manager,
1861 GAsyncResult *result,
1866 GtkTreeSelection *selection;
1867 GError *error = NULL;
1869 if (log_window == NULL)
1872 if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager),
1873 result, &hits, &error))
1875 DEBUG ("%s. Aborting", error->message);
1876 g_error_free (error);
1880 tp_clear_pointer (&log_window->priv->hits, tpl_log_manager_search_free);
1881 log_window->priv->hits = hits;
1883 view = GTK_TREE_VIEW (log_window->priv->treeview_when);
1884 selection = gtk_tree_view_get_selection (view);
1886 g_signal_handlers_unblock_by_func (selection,
1887 log_window_when_changed_cb,
1890 populate_entities_from_search_hits ();
1894 log_window_find_populate (EmpathyLogWindow *self,
1895 const gchar *search_criteria)
1898 GtkTreeModel *model;
1899 GtkTreeSelection *selection;
1900 GtkListStore *store;
1902 gtk_tree_store_clear (self->priv->store_events);
1904 view = GTK_TREE_VIEW (self->priv->treeview_who);
1905 model = gtk_tree_view_get_model (view);
1906 store = GTK_LIST_STORE (model);
1908 gtk_list_store_clear (store);
1910 view = GTK_TREE_VIEW (self->priv->treeview_when);
1911 model = gtk_tree_view_get_model (view);
1912 store = GTK_LIST_STORE (model);
1913 selection = gtk_tree_view_get_selection (view);
1915 gtk_list_store_clear (store);
1917 if (EMP_STR_EMPTY (search_criteria))
1919 tp_clear_pointer (&self->priv->hits, tpl_log_manager_search_free);
1920 log_window_who_populate (self);
1924 g_signal_handlers_block_by_func (selection,
1925 log_window_when_changed_cb,
1928 tpl_log_manager_search_async (self->priv->log_manager,
1929 search_criteria, TPL_EVENT_MASK_ANY,
1930 log_manager_searched_new_cb, NULL);
1934 start_find_search (EmpathyLogWindow *self)
1938 str = gtk_entry_get_text (GTK_ENTRY (self->priv->search_entry));
1940 /* Don't find the same crap again */
1941 if (self->priv->last_find && !tp_strdiff (self->priv->last_find, str))
1944 g_free (self->priv->last_find);
1945 self->priv->last_find = g_strdup (str);
1947 log_window_find_populate (self, str);
1953 log_window_search_entry_changed_cb (GtkWidget *entry,
1954 EmpathyLogWindow *self)
1956 if (self->priv->source != 0)
1957 g_source_remove (self->priv->source);
1958 self->priv->source = g_timeout_add (500, (GSourceFunc) start_find_search,
1963 log_window_search_entry_activate_cb (GtkWidget *entry,
1964 EmpathyLogWindow *self)
1966 start_find_search (self);
1970 log_window_search_entry_icon_pressed_cb (GtkEntry *entry,
1971 GtkEntryIconPosition icon_pos,
1975 if (icon_pos != GTK_ENTRY_ICON_SECONDARY)
1978 gtk_entry_buffer_set_text (gtk_entry_get_buffer (entry),
1983 log_window_update_buttons_sensitivity (EmpathyLogWindow *self)
1986 GtkTreeModel *model;
1987 GtkTreeSelection *selection;
1988 EmpathyCapabilities capabilities;
1994 gboolean profile, chat, call, video;
1996 tp_clear_object (&self->priv->selected_contact);
1997 tp_clear_object (&self->priv->button_video_binding);
1999 view = GTK_TREE_VIEW (self->priv->treeview_who);
2000 model = gtk_tree_view_get_model (view);
2001 selection = gtk_tree_view_get_selection (view);
2003 profile = chat = call = video = FALSE;
2005 if (!gtk_tree_model_get_iter_first (model, &iter))
2008 if (gtk_tree_selection_count_selected_rows (selection) != 1)
2011 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2014 paths = gtk_tree_selection_get_selected_rows (selection, &model);
2015 g_return_if_fail (paths != NULL);
2018 gtk_tree_model_get_iter (model, &iter, path);
2019 gtk_tree_model_get (model, &iter,
2020 COL_WHO_ACCOUNT, &account,
2021 COL_WHO_TARGET, &target,
2024 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
2026 self->priv->selected_contact = empathy_contact_from_tpl_contact (account,
2029 g_object_unref (account);
2030 g_object_unref (target);
2032 capabilities = empathy_contact_get_capabilities (self->priv->selected_contact);
2034 profile = chat = TRUE;
2035 call = capabilities & EMPATHY_CAPABILITIES_AUDIO;
2036 video = capabilities & EMPATHY_CAPABILITIES_VIDEO;
2041 /* If the Who pane doesn't contain a contact (e.g. it has many
2042 * selected, or has 'Anyone', let's try to get the contact from
2043 * the selected event. */
2044 view = GTK_TREE_VIEW (self->priv->treeview_events);
2045 model = gtk_tree_view_get_model (view);
2046 selection = gtk_tree_view_get_selection (view);
2048 if (gtk_tree_selection_count_selected_rows (selection) != 1)
2051 if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
2054 gtk_tree_model_get (model, &iter,
2055 COL_EVENTS_ACCOUNT, &account,
2056 COL_EVENTS_TARGET, &target,
2059 self->priv->selected_contact = empathy_contact_from_tpl_contact (account,
2062 g_object_unref (account);
2063 g_object_unref (target);
2065 capabilities = empathy_contact_get_capabilities (self->priv->selected_contact);
2067 profile = chat = TRUE;
2068 call = capabilities & EMPATHY_CAPABILITIES_AUDIO;
2069 video = capabilities & EMPATHY_CAPABILITIES_VIDEO;
2072 self->priv->button_video_binding = g_object_bind_property (
2073 self->priv->camera_monitor, "available",
2074 self->priv->button_video, "sensitive",
2075 G_BINDING_SYNC_CREATE);
2078 gtk_widget_set_sensitive (self->priv->button_profile, profile);
2079 gtk_widget_set_sensitive (self->priv->button_chat, chat);
2080 gtk_widget_set_sensitive (self->priv->button_call, call);
2082 /* Don't override the binding */
2084 gtk_widget_set_sensitive (self->priv->button_video, video);
2088 log_window_update_what_iter_sensitivity (GtkTreeModel *model,
2092 GtkTreeStore *store = GTK_TREE_STORE (model);
2096 gtk_tree_store_set (store, iter,
2097 COL_WHAT_SENSITIVE, sensitive,
2100 for (next = gtk_tree_model_iter_children (model, &child, iter);
2102 next = gtk_tree_model_iter_next (model, &child))
2104 gtk_tree_store_set (store, &child,
2105 COL_WHAT_SENSITIVE, sensitive,
2111 log_window_update_what_sensitivity (EmpathyLogWindow *self)
2114 GtkTreeModel *model;
2116 GList *accounts, *targets, *acc, *targ;
2119 if (!log_window_get_selected (self, &accounts, &targets, NULL, NULL,
2123 view = GTK_TREE_VIEW (self->priv->treeview_what);
2124 model = gtk_tree_view_get_model (view);
2126 /* For each event type... */
2127 for (next = gtk_tree_model_get_iter_first (model, &iter);
2129 next = gtk_tree_model_iter_next (model, &iter))
2131 TplEventTypeMask type;
2133 gtk_tree_model_get (model, &iter,
2134 COL_WHAT_TYPE, &type,
2137 /* ...we set the type and its subtypes (if any) unsensitive... */
2138 log_window_update_what_iter_sensitivity (model, &iter, FALSE);
2140 for (acc = accounts, targ = targets;
2141 acc != NULL && targ != NULL;
2142 acc = acc->next, targ = targ->next)
2144 TpAccount *account = acc->data;
2145 TplEntity *target = targ->data;
2147 if (tpl_log_manager_exists (self->priv->log_manager,
2148 account, target, type))
2150 /* And then we set it (and its subtypes, again, if any)
2151 * as sensitive if there are logs of that type. */
2152 log_window_update_what_iter_sensitivity (model, &iter, TRUE);
2158 g_list_free_full (accounts, g_object_unref);
2159 g_list_free_full (targets, g_object_unref);
2163 log_window_who_changed_cb (GtkTreeSelection *selection,
2164 EmpathyLogWindow *self)
2167 GtkTreeModel *model;
2170 DEBUG ("log_window_who_changed_cb");
2172 view = gtk_tree_selection_get_tree_view (selection);
2173 model = gtk_tree_view_get_model (view);
2175 if (gtk_tree_model_get_iter_first (model, &iter))
2177 /* If 'Anyone' is selected, everything else should be deselected */
2178 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2180 g_signal_handlers_block_by_func (selection,
2181 log_window_who_changed_cb,
2184 gtk_tree_selection_unselect_all (selection);
2185 gtk_tree_selection_select_iter (selection, &iter);
2187 g_signal_handlers_unblock_by_func (selection,
2188 log_window_who_changed_cb,
2193 log_window_update_what_sensitivity (self);
2194 log_window_update_buttons_sensitivity (self);
2196 /* The contact changed, so the dates need to be updated */
2197 log_window_chats_get_messages (self, TRUE);
2201 log_manager_got_entities_cb (GObject *manager,
2202 GAsyncResult *result,
2205 Ctx *ctx = user_data;
2209 GtkTreeModel *model;
2210 GtkTreeSelection *selection;
2211 GtkListStore *store;
2213 GError *error = NULL;
2214 gboolean select_account = FALSE;
2216 if (log_window == NULL)
2219 if (log_window->priv->count != ctx->count)
2222 if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
2223 result, &entities, &error))
2225 DEBUG ("%s. Aborting", error->message);
2226 g_error_free (error);
2230 view = GTK_TREE_VIEW (ctx->self->priv->treeview_who);
2231 model = gtk_tree_view_get_model (view);
2232 selection = gtk_tree_view_get_selection (view);
2233 store = GTK_LIST_STORE (model);
2235 /* Block signals to stop the logs being retrieved prematurely */
2236 g_signal_handlers_block_by_func (selection,
2237 log_window_who_changed_cb, ctx->self);
2239 for (l = entities; l; l = l->next)
2241 TplEntity *entity = TPL_ENTITY (l->data);
2242 TplEntityType type = tpl_entity_get_entity_type (entity);
2243 EmpathyContact *contact;
2244 gboolean room = type == TPL_ENTITY_ROOM;
2246 contact = empathy_contact_from_tpl_contact (ctx->account, entity);
2248 gtk_list_store_append (store, &iter);
2249 gtk_list_store_set (store, &iter,
2250 COL_WHO_TYPE, COL_TYPE_NORMAL,
2251 COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
2252 : EMPATHY_IMAGE_AVATAR_DEFAULT,
2253 COL_WHO_NAME, empathy_contact_get_alias (contact),
2254 COL_WHO_ID, tpl_entity_get_identifier (entity),
2255 COL_WHO_ACCOUNT, ctx->account,
2256 COL_WHO_TARGET, entity,
2259 g_object_unref (contact);
2261 if (ctx->self->priv->selected_account != NULL &&
2262 !tp_strdiff (tp_proxy_get_object_path (ctx->account),
2263 tp_proxy_get_object_path (ctx->self->priv->selected_account)))
2264 select_account = TRUE;
2266 g_list_free_full (entities, g_object_unref);
2268 if (gtk_tree_model_get_iter_first (model, &iter))
2272 gtk_tree_model_get (model, &iter,
2273 COL_WHO_TYPE, &type,
2276 if (type != COL_TYPE_ANY)
2278 gtk_list_store_prepend (store, &iter);
2279 gtk_list_store_set (store, &iter,
2280 COL_WHO_TYPE, COL_TYPE_SEPARATOR,
2281 COL_WHO_NAME, "separator",
2284 gtk_list_store_prepend (store, &iter);
2285 gtk_list_store_set (store, &iter,
2286 COL_WHO_TYPE, COL_TYPE_ANY,
2287 COL_WHO_NAME, _("Anyone"),
2292 /* Unblock signals */
2293 g_signal_handlers_unblock_by_func (selection,
2294 log_window_who_changed_cb,
2297 /* We display the selected account if we populate the model with chats from
2300 log_window_chats_set_selected (ctx->self);
2303 _tpl_action_chain_continue (log_window->priv->chain);
2308 get_entities_for_account (TplActionChain *chain, gpointer user_data)
2310 Ctx *ctx = user_data;
2312 tpl_log_manager_get_entities_async (ctx->self->priv->log_manager, ctx->account,
2313 log_manager_got_entities_cb, ctx);
2317 select_first_entity (TplActionChain *chain, gpointer user_data)
2319 EmpathyLogWindow *self = user_data;
2321 GtkTreeModel *model;
2322 GtkTreeSelection *selection;
2325 view = GTK_TREE_VIEW (self->priv->treeview_who);
2326 model = gtk_tree_view_get_model (view);
2327 selection = gtk_tree_view_get_selection (view);
2329 if (gtk_tree_model_get_iter_first (model, &iter))
2330 gtk_tree_selection_select_iter (selection, &iter);
2332 _tpl_action_chain_continue (self->priv->chain);
2336 log_window_who_populate (EmpathyLogWindow *self)
2338 EmpathyAccountChooser *account_chooser;
2340 gboolean all_accounts;
2342 GtkTreeModel *model;
2343 GtkTreeSelection *selection;
2344 GtkListStore *store;
2347 if (self->priv->hits != NULL)
2349 populate_entities_from_search_hits ();
2353 account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
2354 account = empathy_account_chooser_dup_account (account_chooser);
2355 all_accounts = empathy_account_chooser_has_all_selected (account_chooser);
2357 view = GTK_TREE_VIEW (self->priv->treeview_who);
2358 model = gtk_tree_view_get_model (view);
2359 selection = gtk_tree_view_get_selection (view);
2360 store = GTK_LIST_STORE (model);
2362 /* Block signals to stop the logs being retrieved prematurely */
2363 g_signal_handlers_block_by_func (selection,
2364 log_window_who_changed_cb,
2367 gtk_list_store_clear (store);
2369 /* Unblock signals */
2370 g_signal_handlers_unblock_by_func (selection,
2371 log_window_who_changed_cb,
2374 _tpl_action_chain_clear (self->priv->chain);
2375 self->priv->count++;
2377 if (!all_accounts && account == NULL)
2381 else if (!all_accounts)
2383 ctx = ctx_new (self, account, NULL, NULL, 0, 0, self->priv->count);
2384 _tpl_action_chain_append (self->priv->chain, get_entities_for_account, ctx);
2388 TpAccountManager *manager;
2389 GList *accounts, *l;
2391 manager = empathy_account_chooser_get_account_manager (account_chooser);
2392 accounts = tp_account_manager_get_valid_accounts (manager);
2394 for (l = accounts; l != NULL; l = l->next)
2398 ctx = ctx_new (self, account, NULL, NULL, 0, 0, self->priv->count);
2399 _tpl_action_chain_append (self->priv->chain,
2400 get_entities_for_account, ctx);
2403 g_list_free (accounts);
2405 _tpl_action_chain_append (self->priv->chain, select_first_entity, self);
2406 _tpl_action_chain_start (self->priv->chain);
2410 sort_by_name (GtkTreeModel *model,
2415 gchar *name1, *name2;
2419 gtk_tree_model_get (model, a,
2420 COL_WHO_TYPE, &type1,
2421 COL_WHO_NAME, &name1,
2424 gtk_tree_model_get (model, b,
2425 COL_WHO_TYPE, &type2,
2426 COL_WHO_NAME, &name2,
2429 if (type1 == COL_TYPE_ANY)
2431 else if (type2 == COL_TYPE_ANY)
2433 else if (type1 == COL_TYPE_SEPARATOR)
2435 else if (type2 == COL_TYPE_SEPARATOR)
2438 ret = g_strcmp0 (name1, name2);
2447 who_row_is_separator (GtkTreeModel *model,
2453 gtk_tree_model_get (model, iter,
2454 COL_WHO_TYPE, &type,
2457 return (type == COL_TYPE_SEPARATOR);
2461 log_window_events_changed_cb (GtkTreeSelection *selection,
2462 EmpathyLogWindow *self)
2464 DEBUG ("log_window_events_changed_cb");
2466 log_window_update_buttons_sensitivity (self);
2470 log_window_events_row_activated_cb (GtkTreeView *view,
2472 GtkTreeViewColumn *column,
2473 EmpathyLogWindow *self)
2475 if (gtk_tree_view_row_expanded (view, path))
2476 gtk_tree_view_collapse_row (view, path);
2478 gtk_tree_view_expand_row (view, path, FALSE);
2482 log_window_events_setup (EmpathyLogWindow *self)
2485 GtkTreeModel *model;
2486 GtkTreeSelection *selection;
2487 GtkTreeSortable *sortable;
2488 GtkTreeViewColumn *column;
2489 GtkTreeStore *store;
2490 GtkCellRenderer *cell;
2492 view = GTK_TREE_VIEW (self->priv->treeview_events);
2493 selection = gtk_tree_view_get_selection (view);
2496 self->priv->store_events = store = gtk_tree_store_new (COL_EVENTS_COUNT,
2497 G_TYPE_INT, /* type */
2498 G_TYPE_INT64, /* timestamp */
2499 G_TYPE_STRING, /* stringified date */
2500 G_TYPE_STRING, /* icon */
2501 G_TYPE_STRING, /* name */
2502 TP_TYPE_ACCOUNT, /* account */
2503 TPL_TYPE_ENTITY, /* target */
2504 TPL_TYPE_EVENT); /* event */
2506 model = GTK_TREE_MODEL (store);
2507 sortable = GTK_TREE_SORTABLE (store);
2509 gtk_tree_view_set_model (view, model);
2512 column = gtk_tree_view_column_new ();
2514 cell = gtk_cell_renderer_pixbuf_new ();
2515 gtk_tree_view_column_pack_start (column, cell, FALSE);
2516 gtk_tree_view_column_add_attribute (column, cell,
2517 "icon-name", COL_EVENTS_ICON);
2519 cell = gtk_cell_renderer_text_new ();
2520 gtk_tree_view_column_pack_start (column, cell, TRUE);
2521 gtk_tree_view_column_add_attribute (column, cell,
2522 "markup", COL_EVENTS_TEXT);
2524 cell = gtk_cell_renderer_text_new ();
2525 g_object_set (cell, "xalign", 1.0, NULL);
2526 gtk_tree_view_column_pack_end (column, cell, FALSE);
2527 gtk_tree_view_column_add_attribute (column, cell,
2528 "text", COL_EVENTS_PRETTY_DATE);
2530 gtk_tree_view_append_column (view, column);
2532 /* set up treeview properties */
2533 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
2534 gtk_tree_view_set_headers_visible (view, FALSE);
2536 gtk_tree_sortable_set_sort_column_id (sortable,
2538 GTK_SORT_ASCENDING);
2540 gtk_tree_view_set_enable_search (view, FALSE);
2542 /* set up signals */
2543 g_signal_connect (selection, "changed",
2544 G_CALLBACK (log_window_events_changed_cb),
2547 g_signal_connect (view, "row-activated",
2548 G_CALLBACK (log_window_events_row_activated_cb),
2551 g_object_unref (store);
2555 log_window_who_setup (EmpathyLogWindow *self)
2558 GtkTreeModel *model;
2559 GtkTreeSelection *selection;
2560 GtkTreeSortable *sortable;
2561 GtkTreeViewColumn *column;
2562 GtkListStore *store;
2563 GtkCellRenderer *cell;
2565 view = GTK_TREE_VIEW (self->priv->treeview_who);
2566 selection = gtk_tree_view_get_selection (view);
2569 store = gtk_list_store_new (COL_WHO_COUNT,
2570 G_TYPE_INT, /* type */
2571 G_TYPE_STRING, /* icon */
2572 G_TYPE_STRING, /* name */
2573 G_TYPE_STRING, /* id */
2574 TP_TYPE_ACCOUNT, /* account */
2575 TPL_TYPE_ENTITY); /* target */
2577 model = GTK_TREE_MODEL (store);
2578 sortable = GTK_TREE_SORTABLE (store);
2580 gtk_tree_view_set_model (view, model);
2583 column = gtk_tree_view_column_new ();
2584 gtk_tree_view_column_set_title (column, _("Who"));
2586 cell = gtk_cell_renderer_pixbuf_new ();
2587 gtk_tree_view_column_pack_start (column, cell, FALSE);
2588 gtk_tree_view_column_add_attribute (column, cell,
2592 cell = gtk_cell_renderer_text_new ();
2593 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2594 gtk_tree_view_column_pack_start (column, cell, TRUE);
2595 gtk_tree_view_column_add_attribute (column, cell,
2599 gtk_tree_view_append_column (view, column);
2601 /* set up treeview properties */
2602 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2603 gtk_tree_view_set_row_separator_func (view, who_row_is_separator,
2606 gtk_tree_sortable_set_sort_column_id (sortable,
2608 GTK_SORT_ASCENDING);
2609 gtk_tree_sortable_set_sort_func (sortable,
2610 COL_WHO_NAME, sort_by_name,
2613 gtk_tree_view_set_search_column (view, COL_WHO_NAME);
2614 gtk_tree_view_set_tooltip_column (view, COL_WHO_ID);
2616 /* set up signals */
2617 g_signal_connect (selection, "changed",
2618 G_CALLBACK (log_window_who_changed_cb), self);
2620 g_object_unref (store);
2624 log_window_chats_accounts_changed_cb (GtkWidget *combobox,
2625 EmpathyLogWindow *self)
2627 /* Clear all current messages shown in the textview */
2628 gtk_tree_store_clear (self->priv->store_events);
2630 log_window_who_populate (self);
2634 log_window_chats_set_selected (EmpathyLogWindow *self)
2637 GtkTreeModel *model;
2638 GtkTreeSelection *selection;
2643 view = GTK_TREE_VIEW (self->priv->treeview_who);
2644 model = gtk_tree_view_get_model (view);
2645 selection = gtk_tree_view_get_selection (view);
2647 for (next = gtk_tree_model_get_iter_first (model, &iter);
2649 next = gtk_tree_model_iter_next (model, &iter))
2651 TpAccount *this_account;
2652 TplEntity *this_target;
2653 const gchar *this_chat_id;
2654 gboolean this_is_chatroom;
2657 gtk_tree_model_get (model, &iter,
2658 COL_WHO_TYPE, &this_type,
2659 COL_WHO_ACCOUNT, &this_account,
2660 COL_WHO_TARGET, &this_target,
2663 if (this_type != COL_TYPE_NORMAL)
2666 this_chat_id = tpl_entity_get_identifier (this_target);
2667 this_is_chatroom = tpl_entity_get_entity_type (this_target)
2670 if (this_account == self->priv->selected_account &&
2671 !tp_strdiff (this_chat_id, self->priv->selected_chat_id) &&
2672 this_is_chatroom == self->priv->selected_is_chatroom)
2674 gtk_tree_selection_select_iter (selection, &iter);
2675 path = gtk_tree_model_get_path (model, &iter);
2676 gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 0.5, 0.0);
2677 gtk_tree_path_free (path);
2678 g_object_unref (this_account);
2679 g_object_unref (this_target);
2683 g_object_unref (this_account);
2684 g_object_unref (this_target);
2687 tp_clear_object (&self->priv->selected_account);
2688 tp_clear_pointer (&self->priv->selected_chat_id, g_free);
2692 sort_by_date (GtkTreeModel *model,
2697 GDate *date1, *date2;
2699 gtk_tree_model_get (model, a,
2700 COL_WHEN_DATE, &date1,
2703 gtk_tree_model_get (model, b,
2704 COL_WHEN_DATE, &date2,
2707 return g_date_compare (date1, date2);
2711 when_row_is_separator (GtkTreeModel *model,
2718 gtk_tree_model_get (model, iter,
2719 COL_WHEN_TEXT, &when,
2722 ret = g_str_equal (when, "separator");
2728 log_window_when_changed_cb (GtkTreeSelection *selection,
2729 EmpathyLogWindow *self)
2732 GtkTreeModel *model;
2735 DEBUG ("log_window_when_changed_cb");
2737 view = gtk_tree_selection_get_tree_view (selection);
2738 model = gtk_tree_view_get_model (view);
2740 /* If 'Anytime' is selected, everything else should be deselected */
2741 if (gtk_tree_model_get_iter_first (model, &iter))
2743 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2745 g_signal_handlers_block_by_func (selection,
2746 log_window_when_changed_cb,
2749 gtk_tree_selection_unselect_all (selection);
2750 gtk_tree_selection_select_iter (selection, &iter);
2752 g_signal_handlers_unblock_by_func (selection,
2753 log_window_when_changed_cb,
2758 log_window_chats_get_messages (self, FALSE);
2762 log_window_when_setup (EmpathyLogWindow *self)
2765 GtkTreeModel *model;
2766 GtkTreeSelection *selection;
2767 GtkTreeSortable *sortable;
2768 GtkTreeViewColumn *column;
2769 GtkListStore *store;
2770 GtkCellRenderer *cell;
2772 view = GTK_TREE_VIEW (self->priv->treeview_when);
2773 selection = gtk_tree_view_get_selection (view);
2776 store = gtk_list_store_new (COL_WHEN_COUNT,
2777 G_TYPE_DATE, /* date */
2778 G_TYPE_STRING, /* stringified date */
2779 G_TYPE_STRING); /* icon */
2781 model = GTK_TREE_MODEL (store);
2782 sortable = GTK_TREE_SORTABLE (store);
2784 gtk_tree_view_set_model (view, model);
2787 column = gtk_tree_view_column_new ();
2788 gtk_tree_view_column_set_title (column, _("When"));
2790 cell = gtk_cell_renderer_pixbuf_new ();
2791 gtk_tree_view_column_pack_start (column, cell, FALSE);
2792 gtk_tree_view_column_add_attribute (column, cell,
2793 "icon-name", COL_WHEN_ICON);
2795 cell = gtk_cell_renderer_text_new ();
2796 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2797 gtk_tree_view_column_pack_start (column, cell, TRUE);
2798 gtk_tree_view_column_add_attribute (column, cell,
2802 gtk_tree_view_append_column (view, column);
2804 /* set up treeview properties */
2805 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2806 gtk_tree_view_set_row_separator_func (view, when_row_is_separator,
2808 gtk_tree_sortable_set_sort_column_id (sortable,
2810 GTK_SORT_DESCENDING);
2811 gtk_tree_sortable_set_sort_func (sortable,
2812 COL_WHEN_DATE, sort_by_date,
2815 gtk_tree_view_set_search_column (view, COL_WHEN_TEXT);
2817 /* set up signals */
2818 g_signal_connect (selection, "changed",
2819 G_CALLBACK (log_window_when_changed_cb),
2822 g_object_unref (store);
2826 what_row_is_separator (GtkTreeModel *model,
2832 gtk_tree_model_get (model, iter,
2833 COL_WHAT_TYPE, &type,
2836 return (type == WHAT_TYPE_SEPARATOR);
2840 log_window_what_changed_cb (GtkTreeSelection *selection,
2841 EmpathyLogWindow *self)
2844 GtkTreeModel *model;
2847 DEBUG ("log_window_what_changed_cb");
2849 view = gtk_tree_selection_get_tree_view (selection);
2850 model = gtk_tree_view_get_model (view);
2852 /* If 'Anything' is selected, everything else should be deselected */
2853 if (gtk_tree_model_get_iter_first (model, &iter))
2855 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2857 g_signal_handlers_block_by_func (selection,
2858 log_window_what_changed_cb,
2861 gtk_tree_selection_unselect_all (selection);
2862 gtk_tree_selection_select_iter (selection, &iter);
2864 g_signal_handlers_unblock_by_func (selection,
2865 log_window_what_changed_cb,
2870 /* The dates need to be updated if we're not searching */
2871 log_window_chats_get_messages (self, self->priv->hits == NULL);
2875 log_window_what_collapse_row_cb (GtkTreeView *tree_view,
2880 /* Reject collapsing */
2887 EventSubtype subtype;
2893 log_window_what_setup (EmpathyLogWindow *self)
2896 GtkTreeModel *model;
2897 GtkTreeSelection *selection;
2898 GtkTreeViewColumn *column;
2900 GtkTreeStore *store;
2901 GtkCellRenderer *cell;
2903 struct event events [] = {
2904 { TPL_EVENT_MASK_ANY, 0, NULL, _("Anything") },
2905 { WHAT_TYPE_SEPARATOR, 0, NULL, "separator" },
2906 { TPL_EVENT_MASK_TEXT, 0, "stock_text_justify", _("Text chats") },
2907 #ifdef HAVE_CALL_LOGS
2908 { TPL_EVENT_MASK_CALL, EVENT_CALL_ALL, EMPATHY_IMAGE_CALL, _("Calls") },
2911 #ifdef HAVE_CALL_LOGS
2912 struct event call_events [] = {
2913 { TPL_EVENT_MASK_CALL, EVENT_CALL_INCOMING, EMPATHY_IMAGE_CALL_INCOMING, _("Incoming calls") },
2914 { TPL_EVENT_MASK_CALL, EVENT_CALL_OUTGOING, EMPATHY_IMAGE_CALL_OUTGOING, _("Outgoing calls") },
2915 { TPL_EVENT_MASK_CALL, EVENT_CALL_MISSED, EMPATHY_IMAGE_CALL_MISSED, _("Missed calls") }
2920 view = GTK_TREE_VIEW (self->priv->treeview_what);
2921 selection = gtk_tree_view_get_selection (view);
2924 store = gtk_tree_store_new (COL_WHAT_COUNT,
2925 G_TYPE_INT, /* history type */
2926 G_TYPE_INT, /* history subtype */
2927 G_TYPE_BOOLEAN, /* sensitive */
2928 G_TYPE_STRING, /* stringified history type */
2929 G_TYPE_STRING); /* icon */
2931 model = GTK_TREE_MODEL (store);
2933 gtk_tree_view_set_model (view, model);
2936 column = gtk_tree_view_column_new ();
2937 gtk_tree_view_column_set_title (column, _("What"));
2939 cell = gtk_cell_renderer_pixbuf_new ();
2940 gtk_tree_view_column_pack_start (column, cell, FALSE);
2941 gtk_tree_view_column_add_attribute (column, cell,
2942 "icon-name", COL_WHAT_ICON);
2944 cell = gtk_cell_renderer_text_new ();
2945 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2946 gtk_tree_view_column_pack_start (column, cell, TRUE);
2947 gtk_tree_view_column_add_attribute (column, cell,
2948 "text", COL_WHAT_TEXT);
2949 gtk_tree_view_column_add_attribute (column, cell,
2950 "sensitive", COL_WHAT_SENSITIVE);
2952 gtk_tree_view_append_column (view, column);
2953 gtk_tree_view_set_search_column (view, COL_WHAT_TEXT);
2955 /* set up treeview properties */
2956 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2957 gtk_tree_view_set_show_expanders (view, FALSE);
2958 gtk_tree_view_set_level_indentation (view, 12);
2959 gtk_tree_view_expand_all (view);
2960 gtk_tree_view_set_row_separator_func (view, what_row_is_separator,
2964 for (i = 0; i < G_N_ELEMENTS (events); i++)
2966 gtk_tree_store_append (store, &iter, NULL);
2967 gtk_tree_store_set (store, &iter,
2968 COL_WHAT_TYPE, events[i].type,
2969 COL_WHAT_SUBTYPE, events[i].subtype,
2970 COL_WHAT_SENSITIVE, TRUE,
2971 COL_WHAT_TEXT, events[i].text,
2972 COL_WHAT_ICON, events[i].icon,
2976 #ifdef HAVE_CALL_LOGS
2977 gtk_tree_model_iter_nth_child (model, &parent, NULL, 3);
2978 for (i = 0; i < G_N_ELEMENTS (call_events); i++)
2980 gtk_tree_store_append (store, &iter, &parent);
2981 gtk_tree_store_set (store, &iter,
2982 COL_WHAT_TYPE, call_events[i].type,
2983 COL_WHAT_SUBTYPE, call_events[i].subtype,
2984 COL_WHAT_SENSITIVE, TRUE,
2985 COL_WHAT_TEXT, call_events[i].text,
2986 COL_WHAT_ICON, call_events[i].icon,
2991 gtk_tree_view_expand_all (view);
2993 /* select 'Anything' */
2994 if (gtk_tree_model_get_iter_first (model, &iter))
2995 gtk_tree_selection_select_iter (selection, &iter);
2997 /* set up signals */
2998 g_signal_connect (view, "test-collapse-row",
2999 G_CALLBACK (log_window_what_collapse_row_cb),
3001 g_signal_connect (selection, "changed",
3002 G_CALLBACK (log_window_what_changed_cb),
3005 g_object_unref (store);
3009 log_window_maybe_expand_events (void)
3012 GtkTreeModel *model;
3014 view = GTK_TREE_VIEW (log_window->priv->treeview_events);
3015 model = gtk_tree_view_get_model (view);
3017 /* If there's only one result, expand it */
3018 if (gtk_tree_model_iter_n_children (model, NULL) == 1)
3019 gtk_tree_view_expand_all (view);
3023 show_spinner (gpointer data)
3027 if (log_window == NULL)
3030 g_object_get (log_window->priv->spinner, "active", &active, NULL);
3033 gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->priv->notebook),
3040 show_events (TplActionChain *chain,
3043 log_window_maybe_expand_events ();
3044 gtk_spinner_stop (GTK_SPINNER (log_window->priv->spinner));
3045 gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->priv->notebook),
3048 _tpl_action_chain_continue (chain);
3052 start_spinner (void)
3054 gtk_spinner_start (GTK_SPINNER (log_window->priv->spinner));
3055 gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->priv->notebook),
3058 g_timeout_add (1000, show_spinner, NULL);
3059 _tpl_action_chain_append (log_window->priv->chain, show_events, NULL);
3063 log_window_got_messages_for_date_cb (GObject *manager,
3064 GAsyncResult *result,
3067 Ctx *ctx = user_data;
3069 GtkTreeModel *model;
3073 GError *error = NULL;
3076 if (log_window == NULL)
3082 if (log_window->priv->count != ctx->count)
3085 if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager),
3086 result, &events, &error))
3088 DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
3090 g_error_free (error);
3094 for (l = events; l; l = l->next)
3096 TplEvent *event = l->data;
3097 gboolean append = TRUE;
3099 #ifdef HAVE_CALL_LOGS
3100 if (TPL_IS_CALL_EVENT (l->data)
3101 && ctx->event_mask & TPL_EVENT_MASK_CALL
3102 && ctx->event_mask != TPL_EVENT_MASK_ANY)
3104 TplCallEvent *call = l->data;
3108 if (ctx->subtype & EVENT_CALL_ALL)
3114 TplCallEndReason reason = tpl_call_event_get_end_reason (call);
3115 TplEntity *sender = tpl_event_get_sender (event);
3116 TplEntity *receiver = tpl_event_get_receiver (event);
3118 if (reason == TPL_CALL_END_REASON_NO_ANSWER)
3120 if (ctx->subtype & EVENT_CALL_MISSED)
3123 else if (ctx->subtype & EVENT_CALL_OUTGOING
3124 && tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
3128 else if (ctx->subtype & EVENT_CALL_INCOMING
3129 && tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
3139 EmpathyMessage *msg = empathy_message_from_tpl_log_event (event);
3140 log_window_append_message (event, msg);
3141 tp_clear_object (&msg);
3144 g_object_unref (event);
3146 g_list_free (events);
3148 view = GTK_TREE_VIEW (log_window->priv->treeview_events);
3149 model = gtk_tree_view_get_model (view);
3150 n = gtk_tree_model_iter_n_children (model, NULL) - 1;
3152 if (n >= 0 && gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
3156 path = gtk_tree_model_get_path (model, &iter);
3157 gtk_tree_view_scroll_to_cell (view, path, NULL, FALSE, 0, 0);
3158 gtk_tree_path_free (path);
3164 _tpl_action_chain_continue (log_window->priv->chain);
3168 get_events_for_date (TplActionChain *chain, gpointer user_data)
3170 Ctx *ctx = user_data;
3172 tpl_log_manager_get_events_for_date_async (ctx->self->priv->log_manager,
3173 ctx->account, ctx->entity, ctx->event_mask,
3175 log_window_got_messages_for_date_cb,
3180 log_window_get_messages_for_dates (EmpathyLogWindow *self,
3183 GList *accounts, *targets, *acc, *targ, *l;
3184 TplEventTypeMask event_mask;
3185 EventSubtype subtype;
3186 GDate *date, *anytime, *separator;
3188 if (!log_window_get_selected (self,
3189 &accounts, &targets, NULL, NULL, &event_mask, &subtype))
3192 anytime = g_date_new_dmy (2, 1, -1);
3193 separator = g_date_new_dmy (1, 1, -1);
3195 _tpl_action_chain_clear (self->priv->chain);
3196 self->priv->count++;
3198 for (acc = accounts, targ = targets;
3199 acc != NULL && targ != NULL;
3200 acc = acc->next, targ = targ->next)
3202 TpAccount *account = acc->data;
3203 TplEntity *target = targ->data;
3205 for (l = dates; l != NULL; l = l->next)
3210 if (g_date_compare (date, anytime) != 0)
3214 ctx = ctx_new (self, account, target, date, event_mask, subtype,
3216 _tpl_action_chain_append (self->priv->chain, get_events_for_date, ctx);
3220 GtkTreeView *view = GTK_TREE_VIEW (self->priv->treeview_when);
3221 GtkTreeModel *model = gtk_tree_view_get_model (view);
3226 for (next = gtk_tree_model_get_iter_first (model, &iter);
3228 next = gtk_tree_model_iter_next (model, &iter))
3232 gtk_tree_model_get (model, &iter,
3236 if (g_date_compare (d, anytime) != 0 &&
3237 g_date_compare (d, separator) != 0)
3239 ctx = ctx_new (self, account, target, d,
3240 event_mask, subtype, self->priv->count);
3241 _tpl_action_chain_append (self->priv->chain, get_events_for_date, ctx);
3249 _tpl_action_chain_start (self->priv->chain);
3251 g_list_free_full (accounts, g_object_unref);
3252 g_list_free_full (targets, g_object_unref);
3253 g_date_free (separator);
3254 g_date_free (anytime);
3258 log_manager_got_dates_cb (GObject *manager,
3259 GAsyncResult *result,
3262 Ctx *ctx = user_data;
3264 GtkTreeModel *model;
3265 GtkListStore *store;
3269 GError *error = NULL;
3271 if (log_window == NULL)
3277 if (log_window->priv->count != ctx->count)
3280 if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
3281 result, &dates, &error))
3283 DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
3288 view = GTK_TREE_VIEW (log_window->priv->treeview_when);
3289 model = gtk_tree_view_get_model (view);
3290 store = GTK_LIST_STORE (model);
3292 for (l = dates; l != NULL; l = l->next)
3294 GDate *date = l->data;
3296 /* Add the date if it's not already there */
3297 has_element = FALSE;
3298 gtk_tree_model_foreach (model, model_has_date, date);
3301 gchar *text = format_date_for_display (date);
3303 gtk_list_store_append (store, &iter);
3304 gtk_list_store_set (store, &iter,
3305 COL_WHEN_DATE, date,
3306 COL_WHEN_TEXT, text,
3307 COL_WHEN_ICON, CALENDAR_ICON,
3314 if (gtk_tree_model_get_iter_first (model, &iter))
3316 gchar *separator = NULL;
3318 if (gtk_tree_model_iter_next (model, &iter))
3320 gtk_tree_model_get (model, &iter,
3321 COL_WHEN_TEXT, &separator,
3325 if (g_strcmp0 (separator, "separator") != 0)
3327 gtk_list_store_prepend (store, &iter);
3328 gtk_list_store_set (store, &iter,
3329 COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
3330 COL_WHEN_TEXT, "separator",
3333 gtk_list_store_prepend (store, &iter);
3334 gtk_list_store_set (store, &iter,
3335 COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
3336 COL_WHEN_TEXT, _("Anytime"),
3341 g_list_free_full (dates, g_free);
3344 _tpl_action_chain_continue (log_window->priv->chain);
3348 select_date (TplActionChain *chain, gpointer user_data)
3351 GtkTreeModel *model;
3352 GtkTreeSelection *selection;
3355 gboolean selected = FALSE;
3357 view = GTK_TREE_VIEW (log_window->priv->treeview_when);
3358 model = gtk_tree_view_get_model (view);
3359 selection = gtk_tree_view_get_selection (view);
3361 if (log_window->priv->current_dates != NULL)
3363 for (next = gtk_tree_model_get_iter_first (model, &iter);
3365 next = gtk_tree_model_iter_next (model, &iter))
3369 gtk_tree_model_get (model, &iter,
3370 COL_WHEN_DATE, &date,
3373 if (g_list_find_custom (log_window->priv->current_dates, date,
3374 (GCompareFunc) g_date_compare) != NULL)
3378 gtk_tree_selection_select_iter (selection, &iter);
3379 path = gtk_tree_model_get_path (model, &iter);
3380 gtk_tree_view_scroll_to_cell (view, path, NULL, FALSE, 0, 0);
3383 gtk_tree_path_free (path);
3392 /* Show messages of the most recent date */
3393 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 2))
3394 gtk_tree_selection_select_iter (selection, &iter);
3397 _tpl_action_chain_continue (log_window->priv->chain);
3401 get_dates_for_entity (TplActionChain *chain, gpointer user_data)
3403 Ctx *ctx = user_data;
3405 tpl_log_manager_get_dates_async (ctx->self->priv->log_manager,
3406 ctx->account, ctx->entity, ctx->event_mask,
3407 log_manager_got_dates_cb, ctx);
3411 log_window_chats_get_messages (EmpathyLogWindow *self,
3412 gboolean force_get_dates)
3414 GList *accounts, *targets, *dates;
3415 TplEventTypeMask event_mask;
3417 GtkTreeModel *model;
3418 GtkListStore *store;
3419 GtkTreeSelection *selection;
3421 if (!log_window_get_selected (self, &accounts, &targets, NULL,
3422 &dates, &event_mask, NULL))
3425 view = GTK_TREE_VIEW (self->priv->treeview_when);
3426 selection = gtk_tree_view_get_selection (view);
3427 model = gtk_tree_view_get_model (view);
3428 store = GTK_LIST_STORE (model);
3430 /* Clear all current messages shown in the textview */
3431 gtk_tree_store_clear (self->priv->store_events);
3433 _tpl_action_chain_clear (self->priv->chain);
3434 self->priv->count++;
3436 /* If there's a search use the returned hits */
3437 if (self->priv->hits != NULL)
3439 if (force_get_dates)
3441 g_signal_handlers_block_by_func (selection,
3442 log_window_when_changed_cb,
3445 gtk_list_store_clear (store);
3447 g_signal_handlers_unblock_by_func (selection,
3448 log_window_when_changed_cb,
3451 populate_dates_from_search_hits (accounts, targets);
3455 populate_events_from_search_hits (accounts, targets, dates);
3458 /* Either use the supplied date or get the last */
3459 else if (force_get_dates || dates == NULL)
3463 if (self->priv->current_dates != NULL)
3465 g_list_free_full (self->priv->current_dates,
3466 (GDestroyNotify) g_date_free);
3467 self->priv->current_dates = NULL;
3470 if (gtk_tree_selection_count_selected_rows (selection) > 0)
3475 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
3477 for (l = paths; l != NULL; l = l->next)
3479 GtkTreePath *path = l->data;
3482 gtk_tree_model_get_iter (model, &iter, path);
3483 gtk_tree_model_get (model, &iter,
3484 COL_WHEN_DATE, &date,
3487 /* The list takes ownership of the date. */
3488 self->priv->current_dates =
3489 g_list_prepend (self->priv->current_dates, date);
3492 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
3495 g_signal_handlers_block_by_func (selection,
3496 log_window_when_changed_cb,
3499 gtk_list_store_clear (store);
3501 g_signal_handlers_unblock_by_func (selection,
3502 log_window_when_changed_cb,
3505 /* Get a list of dates and show them on the treeview */
3506 for (targ = targets, acc = accounts;
3507 targ != NULL && acc != NULL;
3508 targ = targ->next, acc = acc->next)
3510 TpAccount *account = acc->data;
3511 TplEntity *target = targ->data;
3512 Ctx *ctx = ctx_new (self, account, target, NULL, event_mask, 0,
3515 _tpl_action_chain_append (self->priv->chain, get_dates_for_entity, ctx);
3517 _tpl_action_chain_append (self->priv->chain, select_date, NULL);
3518 _tpl_action_chain_start (self->priv->chain);
3522 /* Show messages of the selected date */
3523 log_window_get_messages_for_dates (self, dates);
3526 g_list_free_full (accounts, g_object_unref);
3527 g_list_free_full (targets, g_object_unref);
3528 g_list_free_full (dates, (GFreeFunc) g_date_free);
3532 EmpathyAccountChooserFilterResultCallback callback;
3534 } FilterCallbackData;
3537 got_entities (GObject *manager,
3538 GAsyncResult *result,
3541 FilterCallbackData *data = user_data;
3543 GError *error = NULL;
3545 if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
3546 result, &entities, &error))
3548 DEBUG ("Could not get entities: %s", error->message);
3549 g_error_free (error);
3550 data->callback (FALSE, data->user_data);
3554 data->callback (entities != NULL, data->user_data);
3556 g_list_free_full (entities, g_object_unref);
3559 g_slice_free (FilterCallbackData, data);
3563 empathy_account_chooser_filter_has_logs (TpAccount *account,
3564 EmpathyAccountChooserFilterResultCallback callback,
3565 gpointer callback_data,
3568 TplLogManager *manager = tpl_log_manager_dup_singleton ();
3569 FilterCallbackData *cb_data = g_slice_new0 (FilterCallbackData);
3571 cb_data->callback = callback;
3572 cb_data->user_data = callback_data;
3574 tpl_log_manager_get_entities_async (manager, account, got_entities, cb_data);
3576 g_object_unref (manager);
3580 log_window_logger_clear_account_cb (TpProxy *proxy,
3581 const GError *error,
3583 GObject *weak_object)
3585 EmpathyLogWindow *self = EMPATHY_LOG_WINDOW (user_data);
3588 g_warning ("Error when clearing logs: %s", error->message);
3590 /* Refresh the log viewer so the logs are cleared if the account
3591 * has been deleted */
3592 gtk_tree_store_clear (self->priv->store_events);
3593 log_window_who_populate (self);
3595 /* Re-filter the account chooser so the accounts without logs get greyed out */
3596 empathy_account_chooser_set_filter (
3597 EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser),
3598 empathy_account_chooser_filter_has_logs, NULL);
3602 log_window_clear_logs_chooser_select_account (EmpathyAccountChooser *chooser,
3603 EmpathyLogWindow *self)
3605 EmpathyAccountChooser *account_chooser;
3607 account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->priv->account_chooser);
3609 empathy_account_chooser_set_account (chooser,
3610 empathy_account_chooser_get_account (account_chooser));
3614 log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
3615 EmpathyLogWindow *self)
3617 GtkWidget *dialog, *content_area, *hbox, *label;
3618 EmpathyAccountChooser *account_chooser;
3622 GError *error = NULL;
3624 account_chooser = (EmpathyAccountChooser *) empathy_account_chooser_new ();
3625 empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
3626 empathy_account_chooser_set_filter (account_chooser,
3627 empathy_account_chooser_filter_has_logs, NULL);
3629 /* Select the same account as in the history window */
3630 if (empathy_account_chooser_is_ready (account_chooser))
3631 log_window_clear_logs_chooser_select_account (account_chooser, self);
3633 g_signal_connect (account_chooser, "ready",
3634 G_CALLBACK (log_window_clear_logs_chooser_select_account), self);
3636 dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (self),
3637 GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING,
3639 _("Are you sure you want to delete all logs of previous conversations?"));
3641 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
3642 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3643 _("Clear All"), GTK_RESPONSE_APPLY,
3646 content_area = gtk_message_dialog_get_message_area (
3647 GTK_MESSAGE_DIALOG (dialog));
3649 hbox = gtk_hbox_new (FALSE, 6);
3650 label = gtk_label_new (_("Delete from:"));
3651 gtk_box_pack_start (GTK_BOX (hbox), label,
3653 gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (account_chooser),
3655 gtk_box_pack_start (GTK_BOX (content_area), hbox,
3658 gtk_widget_show_all (hbox);
3660 response_id = gtk_dialog_run (GTK_DIALOG (dialog));
3662 if (response_id != GTK_RESPONSE_APPLY)
3665 bus = tp_dbus_daemon_dup (&error);
3668 g_warning ("Could not delete logs: %s", error->message);
3669 g_error_free (error);
3673 logger = g_object_new (TP_TYPE_PROXY,
3674 "bus-name", "org.freedesktop.Telepathy.Logger",
3675 "object-path", "/org/freedesktop/Telepathy/Logger",
3678 g_object_unref (bus);
3680 tp_proxy_add_interface_by_id (logger, EMP_IFACE_QUARK_LOGGER);
3682 if (empathy_account_chooser_has_all_selected (account_chooser))
3684 DEBUG ("Deleting logs for all the accounts");
3686 emp_cli_logger_call_clear (logger, -1,
3687 log_window_logger_clear_account_cb,
3688 self, NULL, G_OBJECT (self));
3694 account = empathy_account_chooser_get_account (account_chooser);
3696 DEBUG ("Deleting logs for %s", tp_proxy_get_object_path (account));
3698 emp_cli_logger_call_clear_account (logger, -1,
3699 tp_proxy_get_object_path (account),
3700 log_window_logger_clear_account_cb,
3701 self, NULL, G_OBJECT (self));
3704 g_object_unref (logger);
3706 gtk_widget_destroy (dialog);