LogWindow: Add live search support
[empathy-mirror.git] / libempathy-gtk / empathy-log-window.c
blobaeb097c8e0b2321ecff855b78a2592ea6406ee7c
1 /*
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>
25 #include "config.h"
27 #include <string.h>
28 #include <stdlib.h>
30 #include <glib/gi18n-lib.h>
31 #include <gtk/gtk.h>
33 #include <telepathy-glib/telepathy-glib.h>
34 #include <telepathy-glib/proxy-subclass.h>
36 #include <telepathy-logger/telepathy-logger.h>
37 #include <telepathy-logger/call-event.h>
39 #include <extensions/extensions.h>
41 #include <libempathy/action-chain-internal.h>
42 #include <libempathy/empathy-chatroom-manager.h>
43 #include <libempathy/empathy-chatroom.h>
44 #include <libempathy/empathy-message.h>
45 #include <libempathy/empathy-request-util.h>
46 #include <libempathy/empathy-utils.h>
47 #include <libempathy/empathy-time.h>
49 #include "empathy-log-window.h"
50 #include "empathy-account-chooser.h"
51 #include "empathy-call-utils.h"
52 #include "empathy-chat-view.h"
53 #include "empathy-contact-dialogs.h"
54 #include "empathy-images.h"
55 #include "empathy-theme-manager.h"
56 #include "empathy-ui-utils.h"
58 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
59 #include <libempathy/empathy-debug.h>
61 typedef struct
63 GtkWidget *window;
65 GtkWidget *button_profile;
66 GtkWidget *button_chat;
67 GtkWidget *button_call;
68 GtkWidget *button_video;
70 GtkWidget *search_entry;
72 GtkWidget *treeview_who;
73 GtkWidget *treeview_what;
74 GtkWidget *treeview_when;
75 GtkWidget *treeview_events;
77 GtkTreeStore *store_events;
79 GtkWidget *account_chooser;
81 gchar *last_find;
83 TplActionChain *chain;
84 TplLogManager *log_manager;
86 /* Used to cancel logger calls when no longer needed */
87 guint count;
89 /* List of owned TplLogSearchHits, free with tpl_log_search_hit_free */
90 GList *hits;
91 guint source;
93 /* Only used while waiting for the account chooser to be ready */
94 TpAccount *selected_account;
95 gchar *selected_chat_id;
96 gboolean selected_is_chatroom;
97 } EmpathyLogWindow;
99 static void log_window_destroy_cb (GtkWidget *widget,
100 EmpathyLogWindow *window);
101 static void log_window_search_entry_changed_cb (GtkWidget *entry,
102 EmpathyLogWindow *window);
103 static void log_window_search_entry_activate_cb (GtkWidget *widget,
104 EmpathyLogWindow *window);
105 static void log_window_who_populate (EmpathyLogWindow *window);
106 static void log_window_who_setup (EmpathyLogWindow *window);
107 static void log_window_when_setup (EmpathyLogWindow *window);
108 static void log_window_what_setup (EmpathyLogWindow *window);
109 static void log_window_events_setup (EmpathyLogWindow *window);
110 static void log_window_chats_accounts_changed_cb (GtkWidget *combobox,
111 EmpathyLogWindow *window);
112 static void log_window_chats_set_selected (EmpathyLogWindow *window);
113 static void log_window_chats_get_messages (EmpathyLogWindow *window,
114 gboolean force_get_dates);
115 static void log_window_when_changed_cb (GtkTreeSelection *selection,
116 EmpathyLogWindow *window);
117 static void log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
118 EmpathyLogWindow *window);
120 static void
121 empathy_account_chooser_filter_has_logs (TpAccount *account,
122 EmpathyAccountChooserFilterResultCallback callback,
123 gpointer callback_data,
124 gpointer user_data);
126 enum
128 COL_TYPE_ANY,
129 COL_TYPE_SEPARATOR,
130 COL_TYPE_NORMAL
133 enum
135 COL_WHO_TYPE,
136 COL_WHO_ICON,
137 COL_WHO_NAME,
138 COL_WHO_ACCOUNT,
139 COL_WHO_TARGET,
140 COL_WHO_COUNT
143 enum
145 COL_WHAT_TYPE,
146 COL_WHAT_SUBTYPE,
147 COL_WHAT_TEXT,
148 COL_WHAT_ICON,
149 COL_WHAT_EXPANDER,
150 COL_WHAT_COUNT
153 enum
155 COL_WHEN_DATE,
156 COL_WHEN_TEXT,
157 COL_WHEN_ICON,
158 COL_WHEN_COUNT
161 enum
163 COL_EVENTS_TYPE,
164 COL_EVENTS_TS,
165 COL_EVENTS_PRETTY_DATE,
166 COL_EVENTS_ICON,
167 COL_EVENTS_TEXT,
168 COL_EVENTS_ACCOUNT,
169 COL_EVENTS_TARGET,
170 COL_EVENTS_EVENT,
171 COL_EVENTS_COUNT
174 #define CALENDAR_ICON "stock_calendar"
176 typedef enum
178 EVENT_CALL_INCOMING = 1 << 0,
179 EVENT_CALL_OUTGOING = 1 << 1,
180 EVENT_CALL_MISSED = 1 << 2,
181 EVENT_CALL_ALL = 1 << 3,
182 } EventSubtype;
184 static EmpathyLogWindow *log_window = NULL;
186 static gboolean has_element;
188 #ifndef _date_copy
189 #define _date_copy(d) g_date_new_julian (g_date_get_julian (d))
190 #endif
192 typedef struct
194 EmpathyLogWindow *window;
195 TpAccount *account;
196 TplEntity *entity;
197 GDate *date;
198 TplEventTypeMask event_mask;
199 EventSubtype subtype;
200 guint count;
201 } Ctx;
203 static Ctx *
204 ctx_new (EmpathyLogWindow *window,
205 TpAccount *account,
206 TplEntity *entity,
207 GDate *date,
208 TplEventTypeMask event_mask,
209 EventSubtype subtype,
210 guint count)
212 Ctx *ctx = g_slice_new0 (Ctx);
214 ctx->window = window;
215 if (account != NULL)
216 ctx->account = g_object_ref (account);
217 if (entity != NULL)
218 ctx->entity = g_object_ref (entity);
219 if (date != NULL)
220 ctx->date = _date_copy (date);
221 ctx->event_mask = event_mask;
222 ctx->subtype = subtype;
223 ctx->count = count;
225 return ctx;
228 static void
229 ctx_free (Ctx *ctx)
231 tp_clear_object (&ctx->account);
232 tp_clear_object (&ctx->entity);
233 if (ctx->date != NULL)
234 g_date_free (ctx->date);
236 g_slice_free (Ctx, ctx);
239 static void
240 account_chooser_ready_cb (EmpathyAccountChooser *chooser,
241 EmpathyLogWindow *window)
243 /* We'll display the account once the model has been populate with the chats
244 * of this account. */
245 empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER (
246 window->account_chooser), window->selected_account);
249 static void
250 select_account_once_ready (EmpathyLogWindow *self,
251 TpAccount *account,
252 const gchar *chat_id,
253 gboolean is_chatroom)
255 EmpathyAccountChooser *account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->account_chooser);
257 tp_clear_object (&self->selected_account);
258 self->selected_account = g_object_ref (account);
260 g_free (self->selected_chat_id);
261 self->selected_chat_id = g_strdup (chat_id);
263 self->selected_is_chatroom = is_chatroom;
265 if (empathy_account_chooser_is_ready (account_chooser))
266 account_chooser_ready_cb (account_chooser, self);
267 else
268 /* Chat will be selected once the account chooser is ready */
269 g_signal_connect (account_chooser, "ready",
270 G_CALLBACK (account_chooser_ready_cb), self);
273 static void
274 toolbutton_profile_clicked (GtkToolButton *toolbutton,
275 EmpathyLogWindow *window)
277 GtkTreeView *view;
278 GtkTreeSelection *selection;
279 GtkTreeModel *model;
280 GtkTreeIter iter;
281 TpAccount *account;
282 TplEntity *target;
283 EmpathyContact *contact;
284 gint type;
286 g_return_if_fail (window != NULL);
288 view = GTK_TREE_VIEW (log_window->treeview_who);
289 selection = gtk_tree_view_get_selection (view);
291 if (gtk_tree_selection_get_selected (selection, &model, &iter))
293 gtk_tree_model_get (model, &iter,
294 COL_WHO_ACCOUNT, &account,
295 COL_WHO_TARGET, &target,
296 COL_WHO_TYPE, &type,
297 -1);
300 g_return_if_fail (type == COL_TYPE_NORMAL);
302 contact = empathy_contact_from_tpl_contact (account, target);
303 empathy_contact_information_dialog_show (contact,
304 GTK_WINDOW (window->window));
306 g_object_unref (contact);
307 g_object_unref (account);
308 g_object_unref (target);
311 static void
312 toolbutton_chat_clicked (GtkToolButton *toolbutton,
313 EmpathyLogWindow *window)
315 GtkTreeView *view;
316 GtkTreeSelection *selection;
317 GtkTreeModel *model;
318 GtkTreeIter iter;
319 TpAccount *account;
320 TplEntity *target;
321 EmpathyContact *contact;
322 gint type;
324 g_return_if_fail (window != NULL);
326 view = GTK_TREE_VIEW (log_window->treeview_who);
327 selection = gtk_tree_view_get_selection (view);
329 if (gtk_tree_selection_get_selected (selection, &model, &iter))
331 gtk_tree_model_get (model, &iter,
332 COL_WHO_ACCOUNT, &account,
333 COL_WHO_TARGET, &target,
334 COL_WHO_TYPE, &type,
335 -1);
338 g_return_if_fail (type == COL_TYPE_NORMAL);
340 contact = empathy_contact_from_tpl_contact (account, target);
341 empathy_chat_with_contact (contact,
342 gtk_get_current_event_time ());
344 g_object_unref (contact);
345 g_object_unref (account);
346 g_object_unref (target);
349 static void
350 toolbutton_av_clicked (GtkToolButton *toolbutton,
351 EmpathyLogWindow *window)
353 GtkTreeView *view;
354 GtkTreeSelection *selection;
355 GtkTreeModel *model;
356 GtkTreeIter iter;
357 TpAccount *account;
358 gchar *contact;
359 gint type;
360 gboolean video;
362 g_return_if_fail (window != NULL);
364 view = GTK_TREE_VIEW (log_window->treeview_who);
365 selection = gtk_tree_view_get_selection (view);
367 if (gtk_tree_selection_get_selected (selection, &model, &iter))
369 gtk_tree_model_get (model, &iter,
370 COL_WHO_ACCOUNT, &account,
371 COL_WHO_NAME, &contact,
372 COL_WHO_TYPE, &type,
373 -1);
376 g_return_if_fail (type == COL_TYPE_NORMAL);
378 video = (GTK_WIDGET (toolbutton) == window->button_video);
380 empathy_call_new_with_streams (contact, account,
381 TRUE, video, gtk_get_current_event_time ());
383 g_free (contact);
384 g_object_unref (account);
387 GtkWidget *
388 empathy_log_window_show (TpAccount *account,
389 const gchar *chat_id,
390 gboolean is_chatroom,
391 GtkWindow *parent)
393 EmpathyAccountChooser *account_chooser;
394 GtkBuilder *gui;
395 gchar *filename;
396 EmpathyLogWindow *window;
397 GtkWidget *vbox, *accounts, *search, *label, *quit;
399 if (log_window != NULL)
401 gtk_window_present (GTK_WINDOW (log_window->window));
403 if (account != NULL && chat_id != NULL)
404 select_account_once_ready (log_window, account, chat_id, is_chatroom);
406 return log_window->window;
409 log_window = g_new0 (EmpathyLogWindow, 1);
410 log_window->chain = _tpl_action_chain_new_async (NULL, NULL, NULL);
412 log_window->log_manager = tpl_log_manager_dup_singleton ();
414 window = log_window;
416 filename = empathy_file_lookup ("empathy-log-window.ui", "libempathy-gtk");
417 gui = empathy_builder_get_file (filename,
418 "log_window", &window->window,
419 "toolbutton_profile", &window->button_profile,
420 "toolbutton_chat", &window->button_chat,
421 "toolbutton_call", &window->button_call,
422 "toolbutton_video", &window->button_video,
423 "toolbutton_accounts", &accounts,
424 "toolbutton_search", &search,
425 "imagemenuitem_quit", &quit,
426 "treeview_who", &window->treeview_who,
427 "treeview_what", &window->treeview_what,
428 "treeview_when", &window->treeview_when,
429 "treeview_events", &window->treeview_events,
430 NULL);
431 g_free (filename);
433 empathy_builder_connect (gui, window,
434 "log_window", "destroy", log_window_destroy_cb,
435 "toolbutton_profile", "clicked", toolbutton_profile_clicked,
436 "toolbutton_chat", "clicked", toolbutton_chat_clicked,
437 "toolbutton_call", "clicked", toolbutton_av_clicked,
438 "toolbutton_video", "clicked", toolbutton_av_clicked,
439 "imagemenuitem_delete", "activate", log_window_delete_menu_clicked_cb,
440 NULL);
442 g_object_unref (gui);
444 g_object_add_weak_pointer (G_OBJECT (window->window),
445 (gpointer) &log_window);
447 g_signal_connect_swapped (quit, "activate",
448 G_CALLBACK (gtk_widget_destroy), window->window);
450 /* Account chooser for chats */
451 vbox = gtk_vbox_new (FALSE, 3);
453 window->account_chooser = empathy_account_chooser_new ();
454 account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser);
455 empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
456 empathy_account_chooser_set_filter (account_chooser,
457 empathy_account_chooser_filter_has_logs, NULL);
459 g_signal_connect (window->account_chooser, "changed",
460 G_CALLBACK (log_window_chats_accounts_changed_cb),
461 window);
463 label = gtk_label_new (_("Show"));
465 gtk_box_pack_start (GTK_BOX (vbox),
466 window->account_chooser,
467 FALSE, FALSE, 0);
469 gtk_box_pack_start (GTK_BOX (vbox),
470 label,
471 FALSE, FALSE, 0);
473 gtk_widget_show_all (vbox);
474 gtk_container_add (GTK_CONTAINER (accounts), vbox);
476 /* Search entry */
477 vbox = gtk_vbox_new (FALSE, 3);
479 window->search_entry = gtk_entry_new ();
480 gtk_entry_set_icon_from_stock (GTK_ENTRY (window->search_entry),
481 GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
483 label = gtk_label_new (_("Search"));
485 gtk_box_pack_start (GTK_BOX (vbox),
486 window->search_entry,
487 FALSE, FALSE, 0);
489 gtk_box_pack_start (GTK_BOX (vbox),
490 label,
491 FALSE, FALSE, 0);
493 gtk_widget_show_all (vbox);
494 gtk_container_add (GTK_CONTAINER (search), vbox);
496 g_signal_connect (window->search_entry, "changed",
497 G_CALLBACK (log_window_search_entry_changed_cb),
498 window);
500 g_signal_connect (window->search_entry, "activate",
501 G_CALLBACK (log_window_search_entry_activate_cb),
502 window);
504 /* Contacts */
505 log_window_events_setup (window);
506 log_window_who_setup (window);
507 log_window_what_setup (window);
508 log_window_when_setup (window);
510 log_window_who_populate (window);
512 if (account != NULL && chat_id != NULL)
513 select_account_once_ready (window, account, chat_id, is_chatroom);
515 if (parent != NULL)
516 gtk_window_set_transient_for (GTK_WINDOW (window->window),
517 GTK_WINDOW (parent));
519 gtk_widget_show (window->window);
521 return window->window;
524 static void
525 log_window_destroy_cb (GtkWidget *widget,
526 EmpathyLogWindow *window)
528 if (window->source != 0)
529 g_source_remove (window->source);
531 g_free (window->last_find);
532 _tpl_action_chain_free (window->chain);
533 g_object_unref (window->log_manager);
534 tp_clear_object (&window->selected_account);
535 g_free (window->selected_chat_id);
537 g_free (window);
540 static gboolean
541 account_equal (TpAccount *a,
542 TpAccount *b)
544 return g_str_equal (tp_proxy_get_object_path (a),
545 tp_proxy_get_object_path (b));
548 static gboolean
549 entity_equal (TplEntity *a,
550 TplEntity *b)
552 return g_str_equal (tpl_entity_get_identifier (a),
553 tpl_entity_get_identifier (b));
556 static TplEntity *
557 event_get_target (TplEvent *event)
559 TplEntity *sender = tpl_event_get_sender (event);
560 TplEntity *receiver = tpl_event_get_receiver (event);
562 if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
563 return receiver;
565 return sender;
568 static gboolean parent_found;
569 static GtkTreeIter model_parent;
571 static gboolean
572 model_is_parent (GtkTreeModel *model,
573 GtkTreePath *path,
574 GtkTreeIter *iter,
575 gpointer data)
577 TplEvent *event = data;
578 TplEvent *stored_event;
579 TplEntity *target;
580 TpAccount *account;
581 gint64 timestamp;
582 gboolean found = FALSE;
584 gtk_tree_model_get (model, iter,
585 COL_EVENTS_ACCOUNT, &account,
586 COL_EVENTS_TARGET, &target,
587 COL_EVENTS_TS, &timestamp,
588 COL_EVENTS_EVENT, &stored_event,
589 -1);
591 if (G_OBJECT_TYPE (event) == G_OBJECT_TYPE (stored_event) &&
592 account_equal (account, tpl_event_get_account (event)) &&
593 entity_equal (target, event_get_target (event)))
595 if (ABS (tpl_event_get_timestamp (event) - timestamp) < 1800)
597 /* The gap is smaller than 30 min */
598 model_parent = *iter;
599 parent_found = found = TRUE;
603 g_object_unref (stored_event);
604 g_object_unref (account);
605 g_object_unref (target);
607 return found;
610 static const gchar *
611 get_contact_alias_for_message (EmpathyMessage *message)
613 EmpathyContact *sender, *receiver;
615 sender = empathy_message_get_sender (message);
616 receiver = empathy_message_get_receiver (message);
618 if (empathy_contact_is_user (sender))
619 return empathy_contact_get_alias (receiver);
621 return empathy_contact_get_alias (sender);
624 static void
625 get_parent_iter_for_message (TplEvent *event,
626 EmpathyMessage *message,
627 GtkTreeIter *parent)
629 GtkTreeStore *store;
630 GtkTreeModel *model;
632 store = log_window->store_events;
633 model = GTK_TREE_MODEL (store);
635 parent_found = FALSE;
636 gtk_tree_model_foreach (model, model_is_parent, event);
638 if (parent_found)
639 *parent = model_parent;
640 else
642 GtkTreeIter iter;
643 GDateTime *date;
644 gchar *body, *pretty_date;
646 date = g_date_time_new_from_unix_utc (
647 tpl_event_get_timestamp (event));
649 pretty_date = g_date_time_format (date, "%x");
651 body = g_strdup_printf (_("Chat with %s"),
652 get_contact_alias_for_message (message));
654 gtk_tree_store_append (store, &iter, NULL);
655 gtk_tree_store_set (store, &iter,
656 COL_EVENTS_TS, tpl_event_get_timestamp (event),
657 COL_EVENTS_PRETTY_DATE, pretty_date,
658 COL_EVENTS_TEXT, body,
659 COL_EVENTS_ICON, "stock_text_justify",
660 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
661 COL_EVENTS_TARGET, event_get_target (event),
662 COL_EVENTS_EVENT, event,
663 -1);
665 *parent = iter;
667 g_free (body);
668 g_free (pretty_date);
669 g_date_time_unref (date);
673 static const gchar *
674 get_icon_for_event (TplEvent *event)
676 const gchar *icon = NULL;
678 if (TPL_IS_CALL_EVENT (event))
680 TplCallEvent *call = TPL_CALL_EVENT (event);
681 TplCallEndReason reason = tpl_call_event_get_end_reason (call);
682 TplEntity *sender = tpl_event_get_sender (event);
683 TplEntity *receiver = tpl_event_get_receiver (event);
685 if (reason == TPL_CALL_END_REASON_NO_ANSWER)
686 icon = EMPATHY_IMAGE_CALL_MISSED;
687 else if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
688 icon = EMPATHY_IMAGE_CALL_OUTGOING;
689 else if (tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
690 icon = EMPATHY_IMAGE_CALL_INCOMING;
693 return icon;
696 static void
697 log_window_append_chat_message (TplEvent *event,
698 EmpathyMessage *message)
700 GtkTreeStore *store = log_window->store_events;
701 GtkTreeIter iter, parent;
702 gchar *pretty_date, *body;
703 GDateTime *date;
705 date = g_date_time_new_from_unix_utc (
706 tpl_event_get_timestamp (event));
708 pretty_date = g_date_time_format (date, "%x");
710 get_parent_iter_for_message (event, message, &parent);
712 body = g_strdup_printf (
713 C_("First is a contact, second is what he said", "%s: %s"),
714 tpl_entity_get_alias (tpl_event_get_sender (event)),
715 empathy_message_get_body (message));
717 gtk_tree_store_append (store, &iter, &parent);
718 gtk_tree_store_set (store, &iter,
719 COL_EVENTS_TS, tpl_event_get_timestamp (event),
720 COL_EVENTS_PRETTY_DATE, pretty_date,
721 COL_EVENTS_TEXT, body,
722 COL_EVENTS_ICON, get_icon_for_event (event),
723 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
724 COL_EVENTS_TARGET, event_get_target (event),
725 COL_EVENTS_EVENT, event,
726 -1);
728 g_free (body);
729 g_free (pretty_date);
730 g_date_time_unref (date);
733 static void
734 log_window_append_call (TplEvent *event,
735 EmpathyMessage *message)
737 TplCallEvent *call = TPL_CALL_EVENT (event);
738 GtkTreeStore *store = log_window->store_events;
739 GtkTreeIter iter, child;
740 gchar *pretty_date, *duration, *finished;
741 GDateTime *started_date, *finished_date;
742 GTimeSpan span;
744 started_date = g_date_time_new_from_unix_utc (
745 tpl_event_get_timestamp (event));
747 pretty_date = g_date_time_format (started_date, "%x");
749 gtk_tree_store_append (store, &iter, NULL);
750 gtk_tree_store_set (store, &iter,
751 COL_EVENTS_TS, tpl_event_get_timestamp (event),
752 COL_EVENTS_PRETTY_DATE, pretty_date,
753 COL_EVENTS_TEXT, empathy_message_get_body (message),
754 COL_EVENTS_ICON, get_icon_for_event (event),
755 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
756 COL_EVENTS_TARGET, event_get_target (event),
757 COL_EVENTS_EVENT, event,
758 -1);
760 if (tpl_call_event_get_end_reason (call) != TPL_CALL_END_REASON_NO_ANSWER)
762 gchar *body;
764 span = tpl_call_event_get_duration (TPL_CALL_EVENT (event));
765 if (span < 60)
766 duration = g_strdup_printf (_("%" G_GINT64_FORMAT " seconds"), span);
767 else
768 duration = g_strdup_printf (_("%" G_GINT64_FORMAT " minutes"),
769 span / 60);
771 finished_date = g_date_time_add (started_date, -span);
772 finished = g_date_time_format (finished_date, "%X");
773 g_date_time_unref (finished_date);
775 body = g_strdup_printf (_("Call took %s, ended at %s"),
776 duration, finished);
778 g_free (duration);
779 g_free (finished);
781 gtk_tree_store_append (store, &child, &iter);
782 gtk_tree_store_set (store, &child,
783 COL_EVENTS_TS, tpl_event_get_timestamp (event),
784 COL_EVENTS_TEXT, body,
785 COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
786 COL_EVENTS_TARGET, event_get_target (event),
787 COL_EVENTS_EVENT, event,
788 -1);
790 g_free (body);
793 g_free (pretty_date);
794 g_date_time_unref (started_date);
797 static void
798 log_window_append_message (TplEvent *event,
799 EmpathyMessage *message)
801 if (TPL_IS_TEXT_EVENT (event))
802 log_window_append_chat_message (event, message);
803 else if (TPL_IS_CALL_EVENT (event))
804 log_window_append_call (event, message);
805 else
806 DEBUG ("Message type not handled");
809 static void
810 add_all_accounts_and_entities (GList **accounts,
811 GList **entities)
813 GtkTreeView *view;
814 GtkTreeModel *model;
815 GtkTreeIter iter;
817 view = GTK_TREE_VIEW (log_window->treeview_who);
818 model = gtk_tree_view_get_model (view);
820 if (!gtk_tree_model_get_iter_first (model, &iter))
821 return;
825 TpAccount *account;
826 TplEntity *entity;
827 gint type;
829 gtk_tree_model_get (model, &iter,
830 COL_WHO_ACCOUNT, &account,
831 COL_WHO_TARGET, &entity,
832 COL_WHO_TYPE, &type,
833 -1);
835 if (type != COL_TYPE_NORMAL)
836 continue;
838 if (accounts != NULL)
839 *accounts = g_list_append (*accounts, account);
841 if (entities != NULL)
842 *entities = g_list_append (*entities, entity);
844 while (gtk_tree_model_iter_next (model, &iter));
847 static gboolean
848 log_window_get_selected (EmpathyLogWindow *window,
849 GList **accounts,
850 GList **entities,
851 GList **dates,
852 TplEventTypeMask *event_mask,
853 EventSubtype *subtype)
855 GtkTreeView *view;
856 GtkTreeModel *model;
857 GtkTreeSelection *selection;
858 GtkTreeIter iter;
859 TplEventTypeMask ev = 0;
860 EventSubtype st = 0;
861 GList *paths, *l;
862 gint type;
864 view = GTK_TREE_VIEW (window->treeview_who);
865 model = gtk_tree_view_get_model (view);
866 selection = gtk_tree_view_get_selection (view);
868 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
869 if (paths == NULL)
870 return FALSE;
872 if (accounts)
873 *accounts = NULL;
874 if (entities)
875 *entities = NULL;
877 for (l = paths; l != NULL; l = l->next)
879 GtkTreePath *path = l->data;
880 TpAccount *account;
881 TplEntity *entity;
883 gtk_tree_model_get_iter (model, &iter, path);
884 gtk_tree_model_get (model, &iter,
885 COL_WHO_ACCOUNT, &account,
886 COL_WHO_TARGET, &entity,
887 COL_WHO_TYPE, &type,
888 -1);
890 if (type == COL_TYPE_ANY)
892 if (accounts != NULL || entities != NULL)
893 add_all_accounts_and_entities (accounts, entities);
894 break;
897 if (accounts != NULL)
898 *accounts = g_list_append (*accounts, g_object_ref (account));
900 if (entities != NULL)
901 *entities = g_list_append (*entities, g_object_ref (entity));
903 g_object_unref (account);
904 g_object_unref (entity);
906 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
908 view = GTK_TREE_VIEW (window->treeview_what);
909 model = gtk_tree_view_get_model (view);
910 selection = gtk_tree_view_get_selection (view);
912 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
913 for (l = paths; l != NULL; l = l->next)
915 GtkTreePath *path = l->data;
916 TplEventTypeMask mask;
917 EventSubtype submask;
919 gtk_tree_model_get_iter (model, &iter, path);
920 gtk_tree_model_get (model, &iter,
921 COL_WHAT_TYPE, &mask,
922 COL_WHAT_SUBTYPE, &submask,
923 -1);
925 ev |= mask;
926 st |= submask;
928 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
930 view = GTK_TREE_VIEW (window->treeview_when);
931 model = gtk_tree_view_get_model (view);
932 selection = gtk_tree_view_get_selection (view);
934 if (dates != NULL)
936 *dates = NULL;
938 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
939 for (l = paths; l != NULL; l = l->next)
941 GtkTreePath *path = l->data;
942 GDate *date;
944 gtk_tree_model_get_iter (model, &iter, path);
945 gtk_tree_model_get (model, &iter,
946 COL_WHEN_DATE, &date,
947 -1);
949 *dates = g_list_append (*dates, date);
953 if (event_mask != NULL)
954 *event_mask = ev;
956 if (subtype != NULL)
957 *subtype = st;
959 return TRUE;
962 static gboolean
963 model_has_entity (GtkTreeModel *model,
964 GtkTreePath *path,
965 GtkTreeIter *iter,
966 gpointer data)
968 TplLogSearchHit *hit = data;
969 TplEntity *e;
970 TpAccount *a;
972 gtk_tree_model_get (model, iter,
973 COL_WHO_TARGET, &e,
974 COL_WHO_ACCOUNT, &a,
975 -1);
977 if (e != NULL && entity_equal (hit->target, e) &&
978 a != NULL && account_equal (hit->account, a))
980 has_element = TRUE;
981 return TRUE;
984 return FALSE;
987 static gboolean
988 model_has_date (GtkTreeModel *model,
989 GtkTreePath *path,
990 GtkTreeIter *iter,
991 gpointer data)
993 GDate *date = data;
994 GDate *d;
996 gtk_tree_model_get (model, iter,
997 COL_WHEN_DATE, &d,
998 -1);
1000 if (!g_date_compare (date, d))
1002 has_element = TRUE;
1003 return TRUE;
1006 return FALSE;
1009 static void
1010 get_events_for_date (TplActionChain *chain, gpointer user_data);
1012 static void
1013 populate_events_from_search_hits (GList *accounts,
1014 GList *targets,
1015 GList *dates)
1017 TplEventTypeMask event_mask;
1018 EventSubtype subtype;
1019 GDate *anytime;
1020 GList *l;
1021 gboolean is_anytime = FALSE;
1023 if (!log_window_get_selected (log_window,
1024 NULL, NULL, NULL, &event_mask, &subtype))
1025 return;
1027 anytime = g_date_new_dmy (2, 1, -1);
1028 if (g_list_find_custom (dates, anytime, (GCompareFunc) g_date_compare))
1029 is_anytime = TRUE;
1031 for (l = log_window->hits; l != NULL; l = l->next)
1033 TplLogSearchHit *hit = l->data;
1034 GList *acc, *targ;
1035 gboolean found = FALSE;
1037 /* Protect against invalid data (corrupt or old log files). */
1038 if (hit->account == NULL || hit->target == NULL)
1039 continue;
1041 for (acc = accounts, targ = targets;
1042 acc != NULL && targ != NULL && !found;
1043 acc = acc->next, targ = targ->next)
1045 TpAccount *account = acc->data;
1046 TplEntity *target = targ->data;
1048 if (account_equal (hit->account, account) &&
1049 entity_equal (hit->target, target))
1050 found = TRUE;
1053 if (!found)
1054 continue;
1056 if (is_anytime ||
1057 g_list_find_custom (dates, hit->date, (GCompareFunc) g_date_compare)
1058 != NULL)
1060 Ctx *ctx;
1062 ctx = ctx_new (log_window, hit->account, hit->target, hit->date,
1063 event_mask, subtype, log_window->count);
1064 _tpl_action_chain_append (log_window->chain,
1065 get_events_for_date, ctx);
1069 _tpl_action_chain_start (log_window->chain);
1071 g_date_free (anytime);
1074 static gchar *
1075 format_date_for_display (GDate *date)
1077 gchar *text;
1078 GDate *now = NULL;
1079 gint days_elapsed;
1081 /* g_date_strftime sucks */
1083 now = g_date_new ();
1084 g_date_set_time_t (now, time (NULL));
1086 days_elapsed = g_date_days_between (date, now);
1088 if (days_elapsed < 0)
1089 text = NULL;
1090 else if (days_elapsed == 0)
1091 text = g_strdup (_("Today"));
1092 else if (days_elapsed == 1)
1093 text = g_strdup (_("Yesterday"));
1094 else
1096 GDateTime *dt;
1098 dt = g_date_time_new_utc (g_date_get_year (date),
1099 g_date_get_month (date), g_date_get_day (date),
1100 0, 0, 0);
1102 if (days_elapsed <= 7)
1103 text = g_date_time_format (dt, "%A");
1104 else
1105 text = g_date_time_format (dt,
1106 C_("A date such as '23 May 2010', "
1107 "%e is the day, %B the month and %Y the year",
1108 "%e %B %Y"));
1110 g_date_time_unref (dt);
1113 g_date_free (now);
1115 return text;
1118 static void
1119 populate_dates_from_search_hits (GList *accounts,
1120 GList *targets)
1122 GList *l;
1123 GtkTreeView *view;
1124 GtkTreeModel *model;
1125 GtkListStore *store;
1126 GtkTreeIter iter;
1128 if (log_window == NULL)
1129 return;
1131 view = GTK_TREE_VIEW (log_window->treeview_when);
1132 model = gtk_tree_view_get_model (view);
1133 store = GTK_LIST_STORE (model);
1135 for (l = log_window->hits; l != NULL; l = l->next)
1137 TplLogSearchHit *hit = l->data;
1138 GList *acc, *targ;
1139 gboolean found = FALSE;
1141 /* Protect against invalid data (corrupt or old log files). */
1142 if (hit->account == NULL || hit->target == NULL)
1143 continue;
1145 for (acc = accounts, targ = targets;
1146 acc != NULL && targ != NULL && !found;
1147 acc = acc->next, targ = targ->next)
1149 TpAccount *account = acc->data;
1150 TplEntity *target = targ->data;
1152 if (account_equal (hit->account, account) &&
1153 entity_equal (hit->target, target))
1154 found = TRUE;
1157 if (!found)
1158 continue;
1160 /* Add the date if it's not already there */
1161 has_element = FALSE;
1162 gtk_tree_model_foreach (model, model_has_date, hit->date);
1163 if (!has_element)
1165 gchar *text = format_date_for_display (hit->date);
1167 gtk_list_store_append (store, &iter);
1168 gtk_list_store_set (store, &iter,
1169 COL_WHEN_DATE, hit->date,
1170 COL_WHEN_TEXT, text,
1171 COL_WHEN_ICON, CALENDAR_ICON,
1172 -1);
1176 if (gtk_tree_model_get_iter_first (model, &iter))
1178 gtk_list_store_prepend (store, &iter);
1179 gtk_list_store_set (store, &iter,
1180 COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
1181 COL_WHEN_TEXT, "separator",
1182 -1);
1184 gtk_list_store_prepend (store, &iter);
1185 gtk_list_store_set (store, &iter,
1186 COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
1187 COL_WHEN_TEXT, _("Anytime"),
1188 -1);
1192 static void
1193 populate_entities_from_search_hits (void)
1195 EmpathyAccountChooser *account_chooser;
1196 TpAccount *account;
1197 GtkTreeView *view;
1198 GtkTreeModel *model;
1199 GtkTreeIter iter;
1200 GtkListStore *store;
1201 GList *l;
1203 view = GTK_TREE_VIEW (log_window->treeview_who);
1204 model = gtk_tree_view_get_model (view);
1205 store = GTK_LIST_STORE (model);
1207 gtk_list_store_clear (store);
1209 account_chooser = EMPATHY_ACCOUNT_CHOOSER (log_window->account_chooser);
1210 account = empathy_account_chooser_get_account (account_chooser);
1212 for (l = log_window->hits; l; l = l->next)
1214 TplLogSearchHit *hit = l->data;
1216 /* Protect against invalid data (corrupt or old log files). */
1217 if (hit->account == NULL || hit->target == NULL)
1218 continue;
1220 /* Filter based on the selected account */
1221 if (account != NULL && !account_equal (account, hit->account))
1222 continue;
1224 /* Add the entity if it's not already there */
1225 has_element = FALSE;
1226 gtk_tree_model_foreach (model, model_has_entity, hit);
1227 if (!has_element)
1229 TplEntityType type = tpl_entity_get_entity_type (hit->target);
1230 gboolean room = type == TPL_ENTITY_ROOM;
1232 gtk_list_store_append (store, &iter);
1233 gtk_list_store_set (store, &iter,
1234 COL_WHO_TYPE, COL_TYPE_NORMAL,
1235 COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
1236 : EMPATHY_IMAGE_AVATAR_DEFAULT,
1237 COL_WHO_NAME, tpl_entity_get_alias (hit->target),
1238 COL_WHO_ACCOUNT, hit->account,
1239 COL_WHO_TARGET, hit->target,
1240 -1);
1244 #if 0
1245 if (gtk_tree_model_get_iter_first (model, &iter))
1247 gtk_list_store_prepend (store, &iter);
1248 gtk_list_store_set (store, &iter,
1249 COL_WHO_TYPE, COL_TYPE_SEPARATOR,
1250 COL_WHO_NAME, "separator",
1251 -1);
1253 gtk_list_store_prepend (store, &iter);
1254 gtk_list_store_set (store, &iter,
1255 COL_WHO_TYPE, COL_TYPE_ANY,
1256 COL_WHO_NAME, _("Anyone"),
1257 -1);
1259 #endif
1262 static void
1263 log_manager_searched_new_cb (GObject *manager,
1264 GAsyncResult *result,
1265 gpointer user_data)
1267 GList *hits;
1268 GtkTreeView *view;
1269 GtkTreeSelection *selection;
1270 GError *error = NULL;
1272 if (log_window == NULL)
1273 return;
1275 if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager),
1276 result, &hits, &error))
1278 DEBUG ("%s. Aborting", error->message);
1279 g_error_free (error);
1280 return;
1283 tp_clear_pointer (&log_window->hits, tpl_log_manager_search_free);
1284 log_window->hits = hits;
1286 populate_entities_from_search_hits ();
1288 /* FIXME: select old entity if still available, and populate the dates */
1289 //populate_dates_from_search_hits (NULL, NULL);
1291 view = GTK_TREE_VIEW (log_window->treeview_when);
1292 selection = gtk_tree_view_get_selection (view);
1294 g_signal_handlers_unblock_by_func (selection,
1295 log_window_when_changed_cb,
1296 log_window);
1299 #if 0
1300 static gboolean
1301 search_results_filter_entities (GtkTreeModel *model,
1302 GtkTreeIter *iter,
1303 gpointer data)
1305 TpAccount *account, *selected_account;
1306 gboolean visible = FALSE;
1308 if (log_window->hits == NULL)
1309 return TRUE;
1311 if (!log_window_get_selected (log_window, &selected_account,
1312 NULL, NULL, NULL, NULL))
1314 gtk_tree_model_get (model, iter,
1315 COL_WHO_ACCOUNT, &account,
1316 -1);
1318 if (selected_account == NULL ||
1319 account_equal (account, selected_account))
1320 visible = TRUE;
1322 g_object_unref (account);
1323 g_object_unref (selected_account);
1325 return visible;
1327 #endif
1329 static void
1330 log_window_find_populate (EmpathyLogWindow *window,
1331 const gchar *search_criteria)
1333 GtkTreeView *view;
1334 GtkTreeModel *model;
1335 GtkTreeSelection *selection;
1336 GtkListStore *store;
1338 gtk_tree_store_clear (window->store_events);
1340 view = GTK_TREE_VIEW (window->treeview_who);
1341 model = gtk_tree_view_get_model (view);
1342 store = GTK_LIST_STORE (model);
1344 gtk_list_store_clear (store);
1346 view = GTK_TREE_VIEW (window->treeview_when);
1347 model = gtk_tree_view_get_model (view);
1348 store = GTK_LIST_STORE (model);
1349 selection = gtk_tree_view_get_selection (view);
1351 gtk_list_store_clear (store);
1353 if (EMP_STR_EMPTY (search_criteria))
1355 tp_clear_pointer (&window->hits, tpl_log_manager_search_free);
1356 log_window_who_populate (window);
1357 return;
1360 g_signal_handlers_block_by_func (selection,
1361 log_window_when_changed_cb,
1362 window);
1364 tpl_log_manager_search_async (window->log_manager,
1365 search_criteria, TPL_EVENT_MASK_ANY,
1366 log_manager_searched_new_cb, NULL);
1369 static gboolean
1370 start_find_search (EmpathyLogWindow *window)
1372 const gchar *str;
1374 str = gtk_entry_get_text (GTK_ENTRY (window->search_entry));
1376 /* Don't find the same crap again */
1377 if (window->last_find && !tp_strdiff (window->last_find, str))
1378 return FALSE;
1380 g_free (window->last_find);
1381 window->last_find = g_strdup (str);
1383 log_window_find_populate (window, str);
1385 return FALSE;
1388 static void
1389 log_window_search_entry_changed_cb (GtkWidget *entry,
1390 EmpathyLogWindow *window)
1392 if (window->source != 0)
1393 g_source_remove (window->source);
1394 window->source = g_timeout_add (500, (GSourceFunc) start_find_search,
1395 window);
1398 static void
1399 log_window_search_entry_activate_cb (GtkWidget *entry,
1400 EmpathyLogWindow *self)
1402 start_find_search (self);
1406 * Chats Code
1409 static void
1410 log_window_who_changed_cb (GtkTreeSelection *selection,
1411 EmpathyLogWindow *window)
1413 GtkTreeView *view;
1414 GtkTreeModel *model;
1415 GtkTreeIter iter;
1416 gboolean someone = FALSE;
1418 g_print ("log_window_who_changed_cb\n");
1420 view = gtk_tree_selection_get_tree_view (selection);
1421 model = gtk_tree_view_get_model (view);
1423 if (gtk_tree_model_get_iter_first (model, &iter))
1425 /* If 'Anyone' is selected, everything else should be deselected */
1426 if (gtk_tree_selection_iter_is_selected (selection, &iter))
1428 g_signal_handlers_block_by_func (selection,
1429 log_window_who_changed_cb,
1430 window);
1432 gtk_tree_selection_unselect_all (selection);
1433 gtk_tree_selection_select_iter (selection, &iter);
1435 g_signal_handlers_unblock_by_func (selection,
1436 log_window_who_changed_cb,
1437 window);
1439 else if (gtk_tree_selection_count_selected_rows (selection) == 1)
1440 someone = TRUE;
1443 gtk_widget_set_sensitive (window->button_profile, someone);
1444 gtk_widget_set_sensitive (window->button_chat, someone);
1445 gtk_widget_set_sensitive (window->button_call, someone);
1446 gtk_widget_set_sensitive (window->button_video, someone);
1448 /* The contact changed, so the dates need to be updated */
1449 log_window_chats_get_messages (window, TRUE);
1452 static void
1453 log_manager_got_entities_cb (GObject *manager,
1454 GAsyncResult *result,
1455 gpointer user_data)
1457 Ctx *ctx = user_data;
1458 GList *entities;
1459 GList *l;
1460 GtkTreeView *view;
1461 GtkTreeModel *model;
1462 GtkTreeSelection *selection;
1463 GtkListStore *store;
1464 GtkTreeIter iter;
1465 GError *error = NULL;
1466 gboolean select_account = FALSE;
1468 if (log_window == NULL)
1469 goto out;
1471 if (log_window->count != ctx->count)
1472 goto out;
1474 if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
1475 result, &entities, &error))
1477 DEBUG ("%s. Aborting", error->message);
1478 g_error_free (error);
1479 goto out;
1482 view = GTK_TREE_VIEW (ctx->window->treeview_who);
1483 model = gtk_tree_view_get_model (view);
1484 selection = gtk_tree_view_get_selection (view);
1485 store = GTK_LIST_STORE (model);
1487 /* Block signals to stop the logs being retrieved prematurely */
1488 g_signal_handlers_block_by_func (selection,
1489 log_window_who_changed_cb, ctx->window);
1491 for (l = entities; l; l = l->next)
1493 TplEntity *entity = TPL_ENTITY (l->data);
1494 TplEntityType type = tpl_entity_get_entity_type (entity);
1495 gboolean room = type == TPL_ENTITY_ROOM;
1497 gtk_list_store_append (store, &iter);
1498 gtk_list_store_set (store, &iter,
1499 COL_WHO_TYPE, COL_TYPE_NORMAL,
1500 COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE
1501 : EMPATHY_IMAGE_AVATAR_DEFAULT,
1502 COL_WHO_NAME, tpl_entity_get_alias (entity),
1503 COL_WHO_ACCOUNT, ctx->account,
1504 COL_WHO_TARGET, entity,
1505 -1);
1507 if (ctx->window->selected_account != NULL &&
1508 !tp_strdiff (tp_proxy_get_object_path (ctx->account),
1509 tp_proxy_get_object_path (ctx->window->selected_account)))
1510 select_account = TRUE;
1512 g_list_free_full (entities, g_object_unref);
1514 if (gtk_tree_model_get_iter_first (model, &iter))
1516 gint type;
1518 gtk_tree_model_get (model, &iter,
1519 COL_WHO_TYPE, &type,
1520 -1);
1522 if (type != COL_TYPE_ANY)
1524 gtk_list_store_prepend (store, &iter);
1525 gtk_list_store_set (store, &iter,
1526 COL_WHO_TYPE, COL_TYPE_SEPARATOR,
1527 COL_WHO_NAME, "separator",
1528 -1);
1530 gtk_list_store_prepend (store, &iter);
1531 gtk_list_store_set (store, &iter,
1532 COL_WHO_TYPE, COL_TYPE_ANY,
1533 COL_WHO_NAME, _("Anyone"),
1534 -1);
1538 /* Unblock signals */
1539 g_signal_handlers_unblock_by_func (selection,
1540 log_window_who_changed_cb,
1541 ctx->window);
1543 /* We display the selected account if we populate the model with chats from
1544 * this account. */
1545 if (select_account)
1546 log_window_chats_set_selected (ctx->window);
1548 out:
1549 _tpl_action_chain_continue (log_window->chain);
1550 ctx_free (ctx);
1553 static void
1554 get_entities_for_account (TplActionChain *chain, gpointer user_data)
1556 Ctx *ctx = user_data;
1558 g_print ("get_entities_for_account\n");
1559 tpl_log_manager_get_entities_async (ctx->window->log_manager, ctx->account,
1560 log_manager_got_entities_cb, ctx);
1563 static void
1564 select_first_entity (TplActionChain *chain, gpointer user_data)
1566 GtkTreeView *view;
1567 GtkTreeModel *model;
1568 GtkTreeSelection *selection;
1569 GtkTreeIter iter;
1571 view = GTK_TREE_VIEW (log_window->treeview_who);
1572 model = gtk_tree_view_get_model (view);
1573 selection = gtk_tree_view_get_selection (view);
1575 if (gtk_tree_model_get_iter_first (model, &iter))
1576 gtk_tree_selection_select_iter (selection, &iter);
1578 _tpl_action_chain_continue (log_window->chain);
1581 static void
1582 log_window_who_populate (EmpathyLogWindow *window)
1584 EmpathyAccountChooser *account_chooser;
1585 TpAccount *account;
1586 gboolean all_accounts;
1587 GtkTreeView *view;
1588 GtkTreeModel *model;
1589 GtkTreeSelection *selection;
1590 GtkListStore *store;
1591 Ctx *ctx;
1593 if (window->hits != NULL)
1595 populate_entities_from_search_hits ();
1596 return;
1599 account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser);
1600 account = empathy_account_chooser_dup_account (account_chooser);
1601 all_accounts = empathy_account_chooser_has_all_selected (account_chooser);
1603 view = GTK_TREE_VIEW (window->treeview_who);
1604 model = gtk_tree_view_get_model (view);
1605 selection = gtk_tree_view_get_selection (view);
1606 store = GTK_LIST_STORE (model);
1608 /* Block signals to stop the logs being retrieved prematurely */
1609 g_signal_handlers_block_by_func (selection,
1610 log_window_who_changed_cb,
1611 window);
1613 gtk_list_store_clear (store);
1615 /* Unblock signals */
1616 g_signal_handlers_unblock_by_func (selection,
1617 log_window_who_changed_cb,
1618 window);
1620 _tpl_action_chain_clear (window->chain);
1621 window->count++;
1623 if (!all_accounts && account == NULL)
1625 return;
1627 else if (!all_accounts)
1629 ctx = ctx_new (window, account, NULL, NULL, 0, 0, window->count);
1630 _tpl_action_chain_append (window->chain, get_entities_for_account, ctx);
1632 else
1634 TpAccountManager *manager;
1635 GList *accounts, *l;
1637 manager = empathy_account_chooser_get_account_manager (account_chooser);
1638 accounts = tp_account_manager_get_valid_accounts (manager);
1640 for (l = accounts; l != NULL; l = l->next)
1642 account = l->data;
1644 ctx = ctx_new (window, account, NULL, NULL, 0, 0, window->count);
1645 _tpl_action_chain_append (window->chain,
1646 get_entities_for_account, ctx);
1649 g_list_free (accounts);
1651 _tpl_action_chain_append (window->chain, select_first_entity, NULL);
1652 _tpl_action_chain_start (window->chain);
1655 static gint
1656 sort_by_name (GtkTreeModel *model,
1657 GtkTreeIter *a,
1658 GtkTreeIter *b,
1659 gpointer user_data)
1661 gchar *name1, *name2;
1662 gint type1, type2;
1663 gint ret;
1665 gtk_tree_model_get (model, a,
1666 COL_WHO_TYPE, &type1,
1667 COL_WHO_NAME, &name1,
1668 -1);
1670 gtk_tree_model_get (model, b,
1671 COL_WHO_TYPE, &type2,
1672 COL_WHO_NAME, &name2,
1673 -1);
1675 if (type1 == COL_TYPE_ANY)
1676 ret = -1;
1677 else if (type2 == COL_TYPE_ANY)
1678 ret = 1;
1679 else if (type1 == COL_TYPE_SEPARATOR)
1680 ret = -1;
1681 else if (type2 == COL_TYPE_SEPARATOR)
1682 ret = 1;
1683 else
1684 ret = g_strcmp0 (name1, name2);
1686 g_free (name1);
1687 g_free (name2);
1689 return ret;
1692 static gboolean
1693 who_row_is_separator (GtkTreeModel *model,
1694 GtkTreeIter *iter,
1695 gpointer data)
1697 gint type;
1699 gtk_tree_model_get (model, iter,
1700 COL_WHO_TYPE, &type,
1701 -1);
1703 return (type == COL_TYPE_SEPARATOR);
1706 static void
1707 log_window_events_setup (EmpathyLogWindow *window)
1709 GtkTreeView *view;
1710 GtkTreeModel *model;
1711 GtkTreeSelection *selection;
1712 GtkTreeSortable *sortable;
1713 GtkTreeViewColumn *column;
1714 GtkTreeStore *store;
1715 GtkCellRenderer *cell;
1717 view = GTK_TREE_VIEW (window->treeview_events);
1718 selection = gtk_tree_view_get_selection (view);
1720 /* new store */
1721 window->store_events = store = gtk_tree_store_new (COL_EVENTS_COUNT,
1722 G_TYPE_INT, /* type */
1723 G_TYPE_INT64, /* timestamp */
1724 G_TYPE_STRING, /* stringified date */
1725 G_TYPE_STRING, /* icon */
1726 G_TYPE_STRING, /* name */
1727 TP_TYPE_ACCOUNT, /* account */
1728 TPL_TYPE_ENTITY, /* target */
1729 TPL_TYPE_EVENT); /* event */
1731 model = GTK_TREE_MODEL (store);
1732 sortable = GTK_TREE_SORTABLE (store);
1734 gtk_tree_view_set_model (view, model);
1736 /* new column */
1737 column = gtk_tree_view_column_new ();
1739 cell = gtk_cell_renderer_pixbuf_new ();
1740 gtk_tree_view_column_pack_start (column, cell, FALSE);
1741 gtk_tree_view_column_add_attribute (column, cell,
1742 "icon-name", COL_EVENTS_ICON);
1744 cell = gtk_cell_renderer_text_new ();
1745 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1746 gtk_tree_view_column_pack_start (column, cell, TRUE);
1747 gtk_tree_view_column_add_attribute (column, cell,
1748 "text", COL_EVENTS_TEXT);
1750 cell = gtk_cell_renderer_text_new ();
1751 gtk_tree_view_column_pack_end (column, cell, FALSE);
1752 gtk_tree_view_column_add_attribute (column, cell,
1753 "text", COL_EVENTS_PRETTY_DATE);
1755 gtk_tree_view_append_column (view, column);
1757 /* set up treeview properties */
1758 gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
1759 gtk_tree_view_set_headers_visible (view, FALSE);
1761 gtk_tree_sortable_set_sort_column_id (sortable,
1762 COL_EVENTS_TS,
1763 GTK_SORT_ASCENDING);
1764 /* FIXME gtk_tree_sortable_set_sort_func (sortable,
1765 COL_EVENTS_NAME, sort_by_event_date,
1766 NULL, NULL);*/
1768 g_object_unref (store);
1771 static void
1772 log_window_who_setup (EmpathyLogWindow *window)
1774 GtkTreeView *view;
1775 GtkTreeModel *model;
1776 GtkTreeSelection *selection;
1777 GtkTreeSortable *sortable;
1778 GtkTreeViewColumn *column;
1779 GtkListStore *store;
1780 GtkCellRenderer *cell;
1782 view = GTK_TREE_VIEW (window->treeview_who);
1783 selection = gtk_tree_view_get_selection (view);
1785 /* new store */
1786 store = gtk_list_store_new (COL_WHO_COUNT,
1787 G_TYPE_INT, /* type */
1788 G_TYPE_STRING, /* icon */
1789 G_TYPE_STRING, /* name */
1790 TP_TYPE_ACCOUNT, /* account */
1791 TPL_TYPE_ENTITY); /* target */
1793 model = GTK_TREE_MODEL (store);
1794 sortable = GTK_TREE_SORTABLE (store);
1796 gtk_tree_view_set_model (view, model);
1798 /* new column */
1799 column = gtk_tree_view_column_new ();
1800 gtk_tree_view_column_set_title (column, _("Who"));
1802 cell = gtk_cell_renderer_pixbuf_new ();
1803 gtk_tree_view_column_pack_start (column, cell, FALSE);
1804 gtk_tree_view_column_add_attribute (column, cell,
1805 "icon-name",
1806 COL_WHO_ICON);
1808 cell = gtk_cell_renderer_text_new ();
1809 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1810 gtk_tree_view_column_pack_start (column, cell, TRUE);
1811 gtk_tree_view_column_add_attribute (column, cell,
1812 "text",
1813 COL_WHO_NAME);
1815 gtk_tree_view_append_column (view, column);
1817 /* set up treeview properties */
1818 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
1819 gtk_tree_view_set_row_separator_func (view, who_row_is_separator,
1820 NULL, NULL);
1822 gtk_tree_sortable_set_sort_column_id (sortable,
1823 COL_WHO_NAME,
1824 GTK_SORT_ASCENDING);
1825 gtk_tree_sortable_set_sort_func (sortable,
1826 COL_WHO_NAME, sort_by_name,
1827 NULL, NULL);
1829 /* set up signals */
1830 g_signal_connect (selection, "changed",
1831 G_CALLBACK (log_window_who_changed_cb), window);
1833 g_object_unref (store);
1836 static void
1837 log_window_chats_accounts_changed_cb (GtkWidget *combobox,
1838 EmpathyLogWindow *window)
1840 /* Clear all current messages shown in the textview */
1841 gtk_tree_store_clear (window->store_events);
1843 log_window_who_populate (window);
1846 static void
1847 log_window_chats_set_selected (EmpathyLogWindow *window)
1849 GtkTreeView *view;
1850 GtkTreeModel *model;
1851 GtkTreeSelection *selection;
1852 GtkTreeIter iter;
1853 GtkTreePath *path;
1854 gboolean ok;
1856 view = GTK_TREE_VIEW (window->treeview_who);
1857 model = gtk_tree_view_get_model (view);
1858 selection = gtk_tree_view_get_selection (view);
1860 if (!gtk_tree_model_get_iter_first (model, &iter)) {
1861 return;
1864 for (ok = TRUE; ok; ok = gtk_tree_model_iter_next (model, &iter)) {
1865 TpAccount *this_account;
1866 TplEntity *this_target;
1867 const gchar *this_chat_id;
1868 gboolean this_is_chatroom;
1870 gtk_tree_model_get (model, &iter,
1871 COL_WHO_ACCOUNT, &this_account,
1872 COL_WHO_TARGET, &this_target,
1873 -1);
1875 this_chat_id = tpl_entity_get_identifier (this_target);
1876 this_is_chatroom = tpl_entity_get_entity_type (this_target) == TPL_ENTITY_ROOM;
1878 if (this_account == window->selected_account &&
1879 !tp_strdiff (this_chat_id, window->selected_chat_id) &&
1880 this_is_chatroom == window->selected_is_chatroom) {
1881 gtk_tree_selection_select_iter (selection, &iter);
1882 path = gtk_tree_model_get_path (model, &iter);
1883 gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 0.5, 0.0);
1884 gtk_tree_path_free (path);
1885 g_object_unref (this_account);
1886 g_object_unref (this_target);
1887 break;
1890 g_object_unref (this_account);
1891 g_object_unref (this_target);
1894 tp_clear_object (&window->selected_account);
1895 tp_clear_pointer (&window->selected_chat_id, g_free);
1898 static gint
1899 sort_by_date (GtkTreeModel *model,
1900 GtkTreeIter *a,
1901 GtkTreeIter *b,
1902 gpointer user_data)
1904 GDate *date1, *date2;
1906 gtk_tree_model_get (model, a,
1907 COL_WHEN_DATE, &date1,
1908 -1);
1910 gtk_tree_model_get (model, b,
1911 COL_WHEN_DATE, &date2,
1912 -1);
1914 return g_date_compare (date1, date2);
1917 static gboolean
1918 when_row_is_separator (GtkTreeModel *model,
1919 GtkTreeIter *iter,
1920 gpointer data)
1922 gchar *when;
1923 gboolean ret;
1925 gtk_tree_model_get (model, iter,
1926 COL_WHEN_TEXT, &when,
1927 -1);
1929 ret = g_str_equal (when, "separator");
1930 g_free (when);
1931 return ret;
1934 static void
1935 log_window_when_changed_cb (GtkTreeSelection *selection,
1936 EmpathyLogWindow *window)
1938 GtkTreeView *view;
1939 GtkTreeModel *model;
1940 GtkTreeIter iter;
1942 g_print ("log_window_when_changed_cb\n");
1944 view = gtk_tree_selection_get_tree_view (selection);
1945 model = gtk_tree_view_get_model (view);
1947 /* If 'Anytime' is selected, everything else should be deselected */
1948 if (gtk_tree_model_get_iter_first (model, &iter))
1950 if (gtk_tree_selection_iter_is_selected (selection, &iter))
1952 g_signal_handlers_block_by_func (selection,
1953 log_window_when_changed_cb,
1954 window);
1956 gtk_tree_selection_unselect_all (selection);
1957 gtk_tree_selection_select_iter (selection, &iter);
1959 g_signal_handlers_unblock_by_func (selection,
1960 log_window_when_changed_cb,
1961 window);
1965 log_window_chats_get_messages (window, FALSE);
1968 static void
1969 log_window_when_setup (EmpathyLogWindow *window)
1971 GtkTreeView *view;
1972 GtkTreeModel *model;
1973 GtkTreeSelection *selection;
1974 GtkTreeSortable *sortable;
1975 GtkTreeViewColumn *column;
1976 GtkListStore *store;
1977 GtkCellRenderer *cell;
1979 view = GTK_TREE_VIEW (window->treeview_when);
1980 selection = gtk_tree_view_get_selection (view);
1982 /* new store */
1983 store = gtk_list_store_new (COL_WHEN_COUNT,
1984 G_TYPE_DATE, /* date */
1985 G_TYPE_STRING, /* stringified date */
1986 G_TYPE_STRING); /* icon */
1988 model = GTK_TREE_MODEL (store);
1989 sortable = GTK_TREE_SORTABLE (store);
1991 gtk_tree_view_set_model (view, model);
1993 /* new column */
1994 column = gtk_tree_view_column_new ();
1995 gtk_tree_view_column_set_title (column, _("When"));
1997 cell = gtk_cell_renderer_pixbuf_new ();
1998 gtk_tree_view_column_pack_start (column, cell, FALSE);
1999 gtk_tree_view_column_add_attribute (column, cell,
2000 "icon-name", COL_WHEN_ICON);
2002 cell = gtk_cell_renderer_text_new ();
2003 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2004 gtk_tree_view_column_pack_start (column, cell, TRUE);
2005 gtk_tree_view_column_add_attribute (column, cell,
2006 "text",
2007 COL_WHEN_TEXT);
2009 gtk_tree_view_append_column (view, column);
2011 /* set up treeview properties */
2012 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2013 gtk_tree_view_set_row_separator_func (view, when_row_is_separator,
2014 NULL, NULL);
2015 gtk_tree_sortable_set_sort_column_id (sortable,
2016 COL_WHEN_DATE,
2017 GTK_SORT_DESCENDING);
2018 gtk_tree_sortable_set_sort_func (sortable,
2019 COL_WHEN_DATE, sort_by_date,
2020 NULL, NULL);
2022 /* set up signals */
2023 g_signal_connect (selection, "changed",
2024 G_CALLBACK (log_window_when_changed_cb),
2025 window);
2027 g_object_unref (store);
2030 static gboolean
2031 what_row_is_separator (GtkTreeModel *model,
2032 GtkTreeIter *iter,
2033 gpointer data)
2035 gint type;
2037 gtk_tree_model_get (model, iter,
2038 COL_WHAT_TYPE, &type,
2039 -1);
2041 return (type == -1);
2044 static void
2045 log_window_what_changed_cb (GtkTreeSelection *selection,
2046 EmpathyLogWindow *window)
2048 GtkTreeView *view;
2049 GtkTreeModel *model;
2050 GtkTreeIter iter;
2052 g_print ("log_window_what_changed_cb\n");
2054 view = gtk_tree_selection_get_tree_view (selection);
2055 model = gtk_tree_view_get_model (view);
2057 /* If 'Anything' is selected, everything else should be deselected */
2058 if (gtk_tree_model_get_iter_first (model, &iter))
2060 if (gtk_tree_selection_iter_is_selected (selection, &iter))
2062 g_signal_handlers_block_by_func (selection,
2063 log_window_what_changed_cb,
2064 window);
2066 gtk_tree_selection_unselect_all (selection);
2067 gtk_tree_selection_select_iter (selection, &iter);
2069 g_signal_handlers_unblock_by_func (selection,
2070 log_window_what_changed_cb,
2071 window);
2075 /* The dates need to be updated if we're not searching */
2076 log_window_chats_get_messages (window, window->hits == NULL);
2079 static gboolean
2080 log_window_what_collapse_row_cb (GtkTreeView *tree_view,
2081 GtkTreeIter *iter,
2082 GtkTreePath *path,
2083 gpointer user_data)
2085 /* Reject collapsing */
2086 return TRUE;
2089 struct event
2091 gint type;
2092 EventSubtype subtype;
2093 const gchar *icon;
2094 const gchar *text;
2097 static void
2098 log_window_what_setup (EmpathyLogWindow *window)
2100 GtkTreeView *view;
2101 GtkTreeModel *model;
2102 GtkTreeSelection *selection;
2103 GtkTreeSortable *sortable;
2104 GtkTreeViewColumn *column;
2105 GtkTreeIter iter, parent;
2106 GtkTreeStore *store;
2107 GtkCellRenderer *cell;
2108 guint i;
2109 struct event events [] = {
2110 { TPL_EVENT_MASK_ANY, 0, NULL, _("Anything") },
2111 { -1, 0, NULL, "separator" },
2112 { TPL_EVENT_MASK_TEXT, 0, "stock_text_justify", _("Text chats") },
2113 { TPL_EVENT_MASK_CALL, EVENT_CALL_ALL, "call-start", _("Calls") }
2115 struct event call_events [] = {
2116 { TPL_EVENT_MASK_CALL, EVENT_CALL_INCOMING, "call-start", _("Incoming calls") },
2117 { TPL_EVENT_MASK_CALL, EVENT_CALL_OUTGOING, "call-start", _("Outgoing calls") },
2118 { TPL_EVENT_MASK_CALL, EVENT_CALL_MISSED, "call-stop", _("Missed calls") }
2121 view = GTK_TREE_VIEW (window->treeview_what);
2122 selection = gtk_tree_view_get_selection (view);
2124 /* new store */
2125 store = gtk_tree_store_new (COL_WHAT_COUNT,
2126 G_TYPE_INT, /* history type */
2127 G_TYPE_INT, /* history subtype */
2128 G_TYPE_STRING, /* stringified history type */
2129 G_TYPE_STRING, /* icon */
2130 G_TYPE_BOOLEAN); /* expander (hidden) */
2132 model = GTK_TREE_MODEL (store);
2133 sortable = GTK_TREE_SORTABLE (store);
2135 gtk_tree_view_set_model (view, model);
2137 /* new column */
2138 column = gtk_tree_view_column_new ();
2139 gtk_tree_view_column_set_title (column, _("What"));
2141 cell = gtk_cell_renderer_pixbuf_new ();
2142 gtk_tree_view_column_pack_start (column, cell, FALSE);
2143 gtk_tree_view_column_add_attribute (column, cell,
2144 "icon-name", COL_WHAT_ICON);
2146 cell = gtk_cell_renderer_text_new ();
2147 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2148 gtk_tree_view_column_pack_start (column, cell, TRUE);
2149 gtk_tree_view_column_add_attribute (column, cell,
2150 "text", COL_WHAT_TEXT);
2152 gtk_tree_view_append_column (view, column);
2154 /* set up treeview properties */
2155 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2156 gtk_tree_view_set_show_expanders (view, FALSE);
2157 gtk_tree_view_set_level_indentation (view, 12);
2158 gtk_tree_view_expand_all (view);
2159 gtk_tree_view_set_row_separator_func (view, what_row_is_separator,
2160 NULL, NULL);
2162 /* populate */
2163 for (i = 0; i < G_N_ELEMENTS (events); i++)
2165 gtk_tree_store_append (store, &iter, NULL);
2166 gtk_tree_store_set (store, &iter,
2167 COL_WHAT_TYPE, events[i].type,
2168 COL_WHAT_SUBTYPE, events[i].subtype,
2169 COL_WHAT_TEXT, events[i].text,
2170 COL_WHAT_ICON, events[i].icon,
2171 -1);
2174 gtk_tree_model_iter_nth_child (model, &parent, NULL, 3);
2175 for (i = 0; i < G_N_ELEMENTS (call_events); i++)
2177 gtk_tree_store_append (store, &iter, &parent);
2178 gtk_tree_store_set (store, &iter,
2179 COL_WHAT_TYPE, call_events[i].type,
2180 COL_WHAT_SUBTYPE, call_events[i].subtype,
2181 COL_WHAT_TEXT, call_events[i].text,
2182 COL_WHAT_ICON, call_events[i].icon,
2183 -1);
2186 gtk_tree_view_expand_all (view);
2188 /* select 'Anything' */
2189 if (gtk_tree_model_get_iter_first (model, &iter))
2190 gtk_tree_selection_select_iter (selection, &iter);
2192 /* set up signals */
2193 g_signal_connect (view, "test-collapse-row",
2194 G_CALLBACK (log_window_what_collapse_row_cb),
2195 NULL);
2196 g_signal_connect (selection, "changed",
2197 G_CALLBACK (log_window_what_changed_cb),
2198 window);
2200 g_object_unref (store);
2203 static void
2204 log_window_got_messages_for_date_cb (GObject *manager,
2205 GAsyncResult *result,
2206 gpointer user_data)
2208 Ctx *ctx = user_data;
2209 GList *events;
2210 GList *l;
2211 GError *error = NULL;
2213 if (log_window == NULL)
2214 goto out;
2216 if (log_window->count != ctx->count)
2217 goto out;
2219 if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager),
2220 result, &events, &error))
2222 DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
2223 error->message);
2224 /* FIXME
2225 empathy_chat_view_append_event (log_window->chatview_events,
2226 "Unable to retrieve messages for the selected date");*/
2227 g_error_free (error);
2228 goto out;
2231 for (l = events; l; l = l->next)
2233 TplEvent *event = l->data;
2234 gboolean append = TRUE;
2236 if (TPL_IS_CALL_EVENT (l->data)
2237 && ctx->event_mask & TPL_EVENT_MASK_CALL
2238 && ctx->event_mask != TPL_EVENT_MASK_ANY)
2240 TplCallEvent *call = l->data;
2242 append = FALSE;
2244 if (ctx->subtype & EVENT_CALL_ALL)
2245 append = TRUE;
2246 else
2248 TplCallEndReason reason = tpl_call_event_get_end_reason (call);
2249 TplEntity *sender = tpl_event_get_sender (event);
2250 TplEntity *receiver = tpl_event_get_receiver (event);
2252 if (reason == TPL_CALL_END_REASON_NO_ANSWER)
2254 if (ctx->subtype & EVENT_CALL_MISSED)
2255 append = TRUE;
2257 else if (ctx->subtype & EVENT_CALL_OUTGOING
2258 && tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
2259 append = TRUE;
2260 else if (ctx->subtype & EVENT_CALL_INCOMING
2261 && tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
2262 append = TRUE;
2266 if (append)
2268 EmpathyMessage *msg = empathy_message_from_tpl_log_event (event);
2269 log_window_append_message (event, msg);
2270 g_object_unref (msg);
2273 g_object_unref (event);
2275 g_list_free (events);
2277 out:
2278 ctx_free (ctx);
2280 _tpl_action_chain_continue (log_window->chain);
2283 static void
2284 get_events_for_date (TplActionChain *chain, gpointer user_data)
2286 Ctx *ctx = user_data;
2288 g_print ("get_events_for_date\n");
2289 tpl_log_manager_get_events_for_date_async (ctx->window->log_manager,
2290 ctx->account, ctx->entity, ctx->event_mask,
2291 ctx->date,
2292 log_window_got_messages_for_date_cb,
2293 ctx);
2296 static void
2297 log_window_get_messages_for_dates (EmpathyLogWindow *window,
2298 GList *dates)
2300 GList *accounts, *targets, *acc, *targ, *l;
2301 TplEventTypeMask event_mask;
2302 EventSubtype subtype;
2303 GDate *date, *anytime, *separator;
2305 if (!log_window_get_selected (window,
2306 &accounts, &targets, NULL, &event_mask, &subtype))
2307 return;
2309 anytime = g_date_new_dmy (2, 1, -1);
2310 separator = g_date_new_dmy (1, 1, -1);
2312 _tpl_action_chain_clear (window->chain);
2313 window->count++;
2315 for (acc = accounts, targ = targets;
2316 acc != NULL && targ != NULL;
2317 acc = acc->next, targ = targ->next)
2319 TpAccount *account = acc->data;
2320 TplEntity *target = targ->data;
2322 for (l = dates; l != NULL; l = l->next)
2324 date = l->data;
2326 /* Get events */
2327 if (g_date_compare (date, anytime) != 0)
2329 Ctx *ctx;
2331 ctx = ctx_new (window, account, target, date, event_mask, subtype,
2332 window->count);
2333 _tpl_action_chain_append (window->chain, get_events_for_date, ctx);
2335 else
2337 GtkTreeView *view = GTK_TREE_VIEW (window->treeview_when);
2338 GtkTreeModel *model = gtk_tree_view_get_model (view);
2339 GtkTreeIter iter;
2340 gboolean next;
2341 GDate *d;
2343 for (next = gtk_tree_model_get_iter_first (model, &iter);
2344 next;
2345 next = gtk_tree_model_iter_next (model, &iter))
2347 Ctx *ctx;
2349 gtk_tree_model_get (model, &iter,
2350 COL_WHEN_DATE, &d,
2351 -1);
2353 if (g_date_compare (d, anytime) != 0 &&
2354 g_date_compare (d, separator) != 0)
2356 ctx = ctx_new (window, account, target, d,
2357 event_mask, subtype, window->count);
2358 _tpl_action_chain_append (window->chain, get_events_for_date, ctx);
2365 _tpl_action_chain_start (window->chain);
2367 g_list_free_full (accounts, g_object_unref);
2368 g_list_free_full (targets, g_object_unref);
2369 g_date_free (separator);
2370 g_date_free (anytime);
2373 static void
2374 log_manager_got_dates_cb (GObject *manager,
2375 GAsyncResult *result,
2376 gpointer user_data)
2378 Ctx *ctx = user_data;
2379 GtkTreeView *view;
2380 GtkTreeModel *model;
2381 GtkTreeSelection *selection;
2382 GtkListStore *store;
2383 GtkTreeIter iter;
2384 GList *dates;
2385 GList *l;
2386 GDate *date = NULL;
2387 GError *error = NULL;
2389 if (log_window == NULL)
2390 goto out;
2392 if (log_window->count != ctx->count)
2393 goto out;
2395 if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
2396 result, &dates, &error))
2398 DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
2399 error->message);
2400 /* FIXME
2401 empathy_chat_view_append_event (log_window->chatview_events,
2402 _("Unable to retrieve messages' dates"));*/
2403 goto out;
2406 view = GTK_TREE_VIEW (log_window->treeview_when);
2407 model = gtk_tree_view_get_model (view);
2408 store = GTK_LIST_STORE (model);
2409 selection = gtk_tree_view_get_selection (view);
2411 for (l = dates; l != NULL; l = l->next)
2413 gchar *text;
2415 date = l->data;
2416 text = format_date_for_display (date);
2418 gtk_list_store_append (store, &iter);
2419 gtk_list_store_set (store, &iter,
2420 COL_WHEN_DATE, date,
2421 COL_WHEN_TEXT, text,
2422 COL_WHEN_ICON, CALENDAR_ICON,
2423 -1);
2425 g_free (text);
2428 if (gtk_tree_model_get_iter_first (model, &iter))
2430 gchar *separator = NULL;
2432 if (gtk_tree_model_iter_next (model, &iter))
2434 gtk_tree_model_get (model, &iter,
2435 COL_WHEN_TEXT, &separator,
2436 -1);
2439 if (g_strcmp0 (separator, "separator") != 0)
2441 gtk_list_store_prepend (store, &iter);
2442 gtk_list_store_set (store, &iter,
2443 COL_WHEN_DATE, g_date_new_dmy (1, 1, -1),
2444 COL_WHEN_TEXT, "separator",
2445 -1);
2447 gtk_list_store_prepend (store, &iter);
2448 gtk_list_store_set (store, &iter,
2449 COL_WHEN_DATE, g_date_new_dmy (2, 1, -1),
2450 COL_WHEN_TEXT, _("Anytime"),
2451 -1);
2455 g_list_free_full (dates, g_free);
2456 out:
2457 ctx_free (ctx);
2458 _tpl_action_chain_continue (log_window->chain);
2461 static void
2462 select_first_date (TplActionChain *chain, gpointer user_data)
2464 GtkTreeView *view;
2465 GtkTreeModel *model;
2466 GtkTreeSelection *selection;
2467 GtkTreeIter iter;
2469 view = GTK_TREE_VIEW (log_window->treeview_when);
2470 model = gtk_tree_view_get_model (view);
2471 selection = gtk_tree_view_get_selection (view);
2473 /* Show messages of the most recent date */
2474 if (gtk_tree_model_get_iter_first (model, &iter))
2475 gtk_tree_selection_select_iter (selection, &iter);
2477 _tpl_action_chain_continue (log_window->chain);
2480 static void
2481 get_dates_for_entity (TplActionChain *chain, gpointer user_data)
2483 Ctx *ctx = user_data;
2484 g_print ("get_dates_for_entity\n");
2485 tpl_log_manager_get_dates_async (ctx->window->log_manager,
2486 ctx->account, ctx->entity, ctx->event_mask,
2487 log_manager_got_dates_cb, ctx);
2490 static void
2491 log_window_chats_get_messages (EmpathyLogWindow *window,
2492 gboolean force_get_dates)
2494 GList *accounts, *targets, *dates;
2495 TplEventTypeMask event_mask;
2496 GtkTreeView *view;
2497 GtkTreeModel *model;
2498 GtkListStore *store;
2499 GtkTreeSelection *selection;
2501 if (!log_window_get_selected (window, &accounts, &targets,
2502 &dates, &event_mask, NULL))
2503 return;
2505 view = GTK_TREE_VIEW (window->treeview_when);
2506 selection = gtk_tree_view_get_selection (view);
2507 model = gtk_tree_view_get_model (view);
2508 store = GTK_LIST_STORE (model);
2510 /* Clear all current messages shown in the textview */
2511 gtk_tree_store_clear (window->store_events);
2513 _tpl_action_chain_clear (window->chain);
2514 window->count++;
2516 /* If there's a search use the returned hits */
2517 if (window->hits != NULL)
2519 if (force_get_dates)
2521 gtk_list_store_clear (store);
2522 populate_dates_from_search_hits (accounts, targets);
2524 else
2525 populate_events_from_search_hits (accounts, targets, dates);
2527 /* Either use the supplied date or get the last */
2528 else if (force_get_dates || dates == NULL)
2530 GList *acc, *targ;
2532 g_signal_handlers_block_by_func (selection,
2533 log_window_when_changed_cb,
2534 window);
2536 gtk_list_store_clear (store);
2538 g_signal_handlers_unblock_by_func (selection,
2539 log_window_when_changed_cb,
2540 window);
2542 /* Get a list of dates and show them on the treeview */
2543 for (targ = targets, acc = accounts;
2544 targ != NULL && acc != NULL;
2545 targ = targ->next, acc = acc->next)
2547 TpAccount *account = acc->data;
2548 TplEntity *target = targ->data;
2549 Ctx *ctx = ctx_new (window, account, target, NULL, event_mask, 0,
2550 window->count);
2552 _tpl_action_chain_append (window->chain, get_dates_for_entity, ctx);
2554 _tpl_action_chain_append (window->chain, select_first_date, NULL);
2555 _tpl_action_chain_start (window->chain);
2557 else
2559 /* Show messages of the selected date */
2560 log_window_get_messages_for_dates (window, dates);
2563 g_list_free_full (accounts, g_object_unref);
2564 g_list_free_full (targets, g_object_unref);
2565 g_list_free_full (dates, (GFreeFunc) g_date_free);
2568 typedef struct {
2569 EmpathyAccountChooserFilterResultCallback callback;
2570 gpointer user_data;
2571 } FilterCallbackData;
2573 static void
2574 got_entities (GObject *manager,
2575 GAsyncResult *result,
2576 gpointer user_data)
2578 FilterCallbackData *data = user_data;
2579 GList *entities;
2580 GError *error;
2582 if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager), result, &entities, &error)) {
2583 DEBUG ("Could not get entities: %s", error->message);
2584 g_error_free (error);
2585 data->callback (FALSE, data->user_data);
2586 } else {
2587 data->callback (entities != NULL, data->user_data);
2589 g_list_free_full (entities, g_object_unref);
2592 g_slice_free (FilterCallbackData, data);
2595 static void
2596 empathy_account_chooser_filter_has_logs (TpAccount *account,
2597 EmpathyAccountChooserFilterResultCallback callback,
2598 gpointer callback_data,
2599 gpointer user_data)
2601 TplLogManager *manager = tpl_log_manager_dup_singleton ();
2602 FilterCallbackData *cb_data = g_slice_new0 (FilterCallbackData);
2604 cb_data->callback = callback;
2605 cb_data->user_data = callback_data;
2607 tpl_log_manager_get_entities_async (manager, account, got_entities, cb_data);
2609 g_object_unref (manager);
2612 static void
2613 log_window_logger_clear_account_cb (TpProxy *proxy,
2614 const GError *error,
2615 gpointer user_data,
2616 GObject *weak_object)
2618 EmpathyLogWindow *window = user_data;
2620 if (error != NULL)
2621 g_warning ("Error when clearing logs: %s", error->message);
2623 /* Refresh the log viewer so the logs are cleared if the account
2624 * has been deleted */
2625 gtk_tree_store_clear (window->store_events);
2626 log_window_who_populate (window);
2628 /* Re-filter the account chooser so the accounts without logs get greyed out */
2629 empathy_account_chooser_set_filter (EMPATHY_ACCOUNT_CHOOSER (window->account_chooser),
2630 empathy_account_chooser_filter_has_logs, NULL);
2633 static void
2634 log_window_clear_logs_chooser_select_account (EmpathyAccountChooser *chooser,
2635 EmpathyLogWindow *window)
2637 empathy_account_chooser_set_account (chooser,
2638 empathy_account_chooser_get_account (EMPATHY_ACCOUNT_CHOOSER (window->account_chooser)));
2641 static void
2642 log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
2643 EmpathyLogWindow *window)
2645 GtkWidget *dialog, *content_area, *hbox, *label;
2646 EmpathyAccountChooser *account_chooser;
2647 gint response_id;
2648 TpDBusDaemon *bus;
2649 TpProxy *logger;
2650 GError *error = NULL;
2652 account_chooser = (EmpathyAccountChooser *) empathy_account_chooser_new ();
2653 empathy_account_chooser_set_has_all_option (account_chooser, TRUE);
2654 empathy_account_chooser_set_filter (account_chooser, empathy_account_chooser_filter_has_logs, NULL);
2656 /* Select the same account as in the history window */
2657 if (empathy_account_chooser_is_ready (account_chooser))
2658 log_window_clear_logs_chooser_select_account (account_chooser, window);
2659 else
2660 g_signal_connect (account_chooser, "ready",
2661 G_CALLBACK (log_window_clear_logs_chooser_select_account), window);
2663 dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window->window),
2664 GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING,
2665 GTK_BUTTONS_NONE,
2666 _("Are you sure you want to delete all logs of previous conversations?"));
2668 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
2669 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2670 _("Clear All"), GTK_RESPONSE_APPLY,
2671 NULL);
2673 content_area = gtk_message_dialog_get_message_area (
2674 GTK_MESSAGE_DIALOG (dialog));
2676 hbox = gtk_hbox_new (FALSE, 6);
2677 label = gtk_label_new (_("Delete from:"));
2678 gtk_box_pack_start (GTK_BOX (hbox), label,
2679 FALSE, FALSE, 0);
2680 gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (account_chooser),
2681 FALSE, FALSE, 0);
2682 gtk_box_pack_start (GTK_BOX (content_area), hbox,
2683 FALSE, FALSE, 0);
2685 gtk_widget_show_all (hbox);
2687 response_id = gtk_dialog_run (GTK_DIALOG (dialog));
2689 if (response_id != GTK_RESPONSE_APPLY)
2690 goto out;
2692 bus = tp_dbus_daemon_dup (&error);
2693 if (error != NULL) {
2694 g_warning ("Could not delete logs: %s", error->message);
2695 g_error_free (error);
2696 goto out;
2699 logger = g_object_new (TP_TYPE_PROXY,
2700 "bus-name", "org.freedesktop.Telepathy.Logger",
2701 "object-path", "/org/freedesktop/Telepathy/Logger",
2702 "dbus-daemon", bus,
2703 NULL);
2704 g_object_unref (bus);
2706 tp_proxy_add_interface_by_id (logger, EMP_IFACE_QUARK_LOGGER);
2708 if (empathy_account_chooser_has_all_selected (account_chooser)) {
2709 DEBUG ("Deleting logs for all the accounts");
2711 emp_cli_logger_call_clear (logger, -1,
2712 log_window_logger_clear_account_cb,
2713 window, NULL, G_OBJECT (window->window));
2714 } else {
2715 TpAccount *account = empathy_account_chooser_get_account (account_chooser);
2717 DEBUG ("Deleting logs for %s", tp_proxy_get_object_path (account));
2719 emp_cli_logger_call_clear_account (logger, -1,
2720 tp_proxy_get_object_path (account),
2721 log_window_logger_clear_account_cb,
2722 window, NULL, G_OBJECT (window->window));
2725 g_object_unref (logger);
2726 out:
2727 gtk_widget_destroy (dialog);