From 989e0d8be1747b8345af1f4d310436124335e36b Mon Sep 17 00:00:00 2001 From: Emilio Pozuelo Monfort Date: Wed, 20 Apr 2011 09:32:56 +0100 Subject: Complete rewrite of the history window https://bugzilla.gnome.org/show_bug.cgi?id=619866 --- libempathy-gtk/empathy-log-window.c | 2623 +++++++++++++++++++++-------------- 1 file changed, 1565 insertions(+), 1058 deletions(-) (limited to 'libempathy-gtk/empathy-log-window.c') diff --git a/libempathy-gtk/empathy-log-window.c b/libempathy-gtk/empathy-log-window.c index 8bb92b82e..3af0729a4 100644 --- a/libempathy-gtk/empathy-log-window.c +++ b/libempathy-gtk/empathy-log-window.c @@ -1,7 +1,6 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 2006-2007 Imendio AB - * Copyright (C) 2007-2008 Collabora Ltd. + * Copyright (C) 2007-2011 Collabora Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -20,6 +19,7 @@ * * Authors: Martyn Russell * Xavier Claessens + * Emilio Pozuelo Monfort */ #include "config.h" @@ -30,13 +30,15 @@ #include #include -#include +#include #include -#include +#include +#include #include +#include #include #include #include @@ -45,148 +47,181 @@ #include "empathy-log-window.h" #include "empathy-account-chooser.h" +#include "empathy-call-utils.h" #include "empathy-chat-view.h" +#include "empathy-contact-dialogs.h" +#include "empathy-images.h" #include "empathy-theme-manager.h" #include "empathy-ui-utils.h" #define DEBUG_FLAG EMPATHY_DEBUG_OTHER #include -typedef struct { - GtkWidget *window; - - GtkWidget *notebook; - - GtkWidget *entry_find; - GtkWidget *button_find; - GtkWidget *treeview_find; - GtkWidget *scrolledwindow_find; - EmpathyChatView *chatview_find; - GtkWidget *button_previous; - GtkWidget *button_next; - - GtkWidget *hbox_chats; - GtkWidget *account_chooser_chats; - GtkWidget *entry_chats; - GtkWidget *calendar_chats; - GtkWidget *treeview_chats; - GtkWidget *scrolledwindow_chats; - EmpathyChatView *chatview_chats; - - gchar *last_find; - - TplLogManager *log_manager; - - /* Those are only used while waiting for the account chooser to be ready */ - TpAccount *selected_account; - gchar *selected_chat_id; - gboolean selected_is_chatroom; +typedef struct +{ + GtkWidget *window; + + GtkWidget *button_profile; + GtkWidget *button_chat; + GtkWidget *button_call; + GtkWidget *button_video; + + GtkWidget *search_entry; + + GtkWidget *treeview_who; + GtkWidget *treeview_what; + GtkWidget *treeview_when; + + GtkWidget *scrolledwindow_events; + EmpathyChatView *chatview_events; + + GtkWidget *account_chooser; + + gchar *last_find; + + TplActionChain *chain; + TplLogManager *log_manager; + + /* List of owned TplLogSearchHits, free with tpl_log_search_hit_free */ + GList *hits; + + /* Only used while waiting for the account chooser to be ready */ + TpAccount *selected_account; + gchar *selected_chat_id; + gboolean selected_is_chatroom; } EmpathyLogWindow; static void log_window_destroy_cb (GtkWidget *widget, EmpathyLogWindow *window); -static void log_window_entry_find_changed_cb (GtkWidget *entry, - EmpathyLogWindow *window); -static void log_window_find_changed_cb (GtkTreeSelection *selection, +static void log_window_search_entry_changed_cb (GtkWidget *entry, EmpathyLogWindow *window); -static void log_window_find_populate (EmpathyLogWindow *window, - const gchar *search_criteria); -static void log_window_find_setup (EmpathyLogWindow *window); -static void log_window_button_find_clicked_cb (GtkWidget *widget, +static void log_window_search_entry_activate_cb (GtkWidget *widget, EmpathyLogWindow *window); -static void log_window_entry_find_activate_cb (GtkWidget *widget, - EmpathyLogWindow *window); -static void log_window_button_next_clicked_cb (GtkWidget *widget, - EmpathyLogWindow *window); -static void log_window_button_previous_clicked_cb (GtkWidget *widget, - EmpathyLogWindow *window); -static void log_window_chats_changed_cb (GtkTreeSelection *selection, - EmpathyLogWindow *window); -static void log_window_chats_populate (EmpathyLogWindow *window); -static void log_window_chats_setup (EmpathyLogWindow *window); +static void log_window_who_populate (EmpathyLogWindow *window); +static void log_window_who_setup (EmpathyLogWindow *window); +static void log_window_when_setup (EmpathyLogWindow *window); +static void log_window_what_setup (EmpathyLogWindow *window); static void log_window_chats_accounts_changed_cb (GtkWidget *combobox, EmpathyLogWindow *window); static void log_window_chats_set_selected (EmpathyLogWindow *window); -static gboolean log_window_chats_get_selected (EmpathyLogWindow *window, - TpAccount **account, - TplEntity **target); static void log_window_chats_get_messages (EmpathyLogWindow *window, - GDate *date_to_show); -static void log_window_calendar_chats_day_selected_cb (GtkWidget *calendar, - EmpathyLogWindow *window); -static void log_window_calendar_chats_month_changed_cb (GtkWidget *calendar, - EmpathyLogWindow *window); -static void log_window_entry_chats_changed_cb (GtkWidget *entry, - EmpathyLogWindow *window); -static void log_window_entry_chats_activate_cb (GtkWidget *entry, + gboolean force_get_dates); +static void log_window_when_changed_cb (GtkTreeSelection *selection, EmpathyLogWindow *window); static void log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem, EmpathyLogWindow *window); static void empathy_account_chooser_filter_has_logs (TpAccount *account, - EmpathyAccountChooserFilterResultCallback callback, - gpointer callback_data, - gpointer user_data); - -enum { - COL_FIND_ACCOUNT_ICON, - COL_FIND_ACCOUNT_NAME, - COL_FIND_ACCOUNT, - COL_FIND_CHAT_NAME, - COL_FIND_TARGET, - COL_FIND_DATE, - COL_FIND_DATE_READABLE, - COL_FIND_COUNT + EmpathyAccountChooserFilterResultCallback callback, + gpointer callback_data, + gpointer user_data); + +enum +{ + COL_TYPE_ANY, + COL_TYPE_SEPARATOR, + COL_TYPE_NORMAL }; -enum { - COL_CHAT_ICON, - COL_CHAT_NAME, - COL_CHAT_ACCOUNT, - COL_CHAT_TARGET, - COL_CHAT_COUNT +enum +{ + COL_WHO_TYPE, + COL_WHO_ICON, + COL_WHO_NAME, + COL_WHO_ACCOUNT, + COL_WHO_TARGET, + COL_WHO_COUNT }; -static EmpathyLogWindow *log_window = NULL; +enum +{ + COL_WHAT_TYPE, + COL_WHAT_SUBTYPE, + COL_WHAT_TEXT, + COL_WHAT_ICON, + COL_WHAT_EXPANDER, + COL_WHAT_COUNT +}; -static void -account_manager_prepared_cb (GObject *source_object, - GAsyncResult *result, - gpointer user_data) +enum { - TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object); - EmpathyLogWindow *window = user_data; - guint account_num; - GList *accounts; - GError *error = NULL; + COL_WHEN_DATE, + COL_WHEN_TEXT, + COL_WHEN_ICON, + COL_WHEN_COUNT +}; - if (log_window == NULL) - return; +#define CALENDAR_ICON "stock_calendar" - if (!tp_account_manager_prepare_finish (account_manager, result, &error)) { - DEBUG ("Failed to prepare account manager: %s", error->message); - g_error_free (error); - return; - } +typedef enum +{ + EVENT_CALL_INCOMING = 1 << 0, + EVENT_CALL_OUTGOING = 1 << 1, + EVENT_CALL_MISSED = 1 << 2, + EVENT_CALL_ALL = 1 << 3, +} EventSubtype; - accounts = tp_account_manager_get_valid_accounts (account_manager); - account_num = g_list_length (accounts); - g_list_free (accounts); +static EmpathyLogWindow *log_window = NULL; + +static gboolean has_element; + +#ifndef _date_copy +#define _date_copy(d) g_date_new_julian (g_date_get_julian (d)) +#endif + +typedef struct +{ + EmpathyLogWindow *window; + TpAccount *account; + TplEntity *entity; + GDate *date; + TplEventTypeMask event_mask; + EventSubtype subtype; +} Ctx; + +static Ctx * +ctx_new (EmpathyLogWindow *window, + TpAccount *account, + TplEntity *entity, + GDate *date, + TplEventTypeMask event_mask, + EventSubtype subtype) +{ + Ctx *ctx = g_slice_new0 (Ctx); + + ctx->window = window; + if (account != NULL) + ctx->account = g_object_ref (account); + if (entity != NULL) + ctx->entity = g_object_ref (entity); + if (date != NULL) + ctx->date = _date_copy (date); + ctx->event_mask = event_mask; + ctx->subtype = subtype; + + return ctx; +} + +static void +ctx_free (Ctx *ctx) +{ + tp_clear_object (&ctx->account); + tp_clear_object (&ctx->entity); + if (ctx->date != NULL) + g_date_free (ctx->date); - gtk_widget_show_all (window->hbox_chats); + g_slice_free (Ctx, ctx); } static void account_chooser_ready_cb (EmpathyAccountChooser *chooser, EmpathyLogWindow *window) { - gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), 1); - /* We'll display the account once the model has been populate with the chats * of this account. */ empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER ( - window->account_chooser_chats), window->selected_account); + window->account_chooser), window->selected_account); } static void @@ -195,7 +230,7 @@ select_account_once_ready (EmpathyLogWindow *self, const gchar *chat_id, gboolean is_chatroom) { - EmpathyAccountChooser *account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->account_chooser_chats); + EmpathyAccountChooser *account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->account_chooser); tp_clear_object (&self->selected_account); self->selected_account = g_object_ref (account); @@ -213,770 +248,1063 @@ select_account_once_ready (EmpathyLogWindow *self, G_CALLBACK (account_chooser_ready_cb), self); } -GtkWidget * -empathy_log_window_show (TpAccount *account, - const gchar *chat_id, - gboolean is_chatroom, - GtkWindow *parent) +static void +toolbutton_profile_clicked (GtkToolButton *toolbutton, + EmpathyLogWindow *window) { - EmpathyAccountChooser *account_chooser; - TpAccountManager *account_manager; - GtkBuilder *gui; - gchar *filename; - EmpathyLogWindow *window; - EmpathyThemeManager *theme_mgr; - GtkWidget *menu_delete; - - if (log_window != NULL) { - gtk_window_present (GTK_WINDOW (log_window->window)); - - if (account != NULL && chat_id != NULL) { - gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook), 1); - select_account_once_ready (log_window, account, chat_id, is_chatroom); - } - - return log_window->window; - } - - log_window = g_new0 (EmpathyLogWindow, 1); - log_window->log_manager = tpl_log_manager_dup_singleton (); - - window = log_window; - - filename = empathy_file_lookup ("empathy-log-window.ui", - "libempathy-gtk"); - gui = empathy_builder_get_file (filename, - "log_window", &window->window, - "menu_delete", &menu_delete, - "notebook", &window->notebook, - "entry_find", &window->entry_find, - "button_find", &window->button_find, - "treeview_find", &window->treeview_find, - "scrolledwindow_find", &window->scrolledwindow_find, - "button_previous", &window->button_previous, - "button_next", &window->button_next, - "entry_chats", &window->entry_chats, - "calendar_chats", &window->calendar_chats, - "hbox_chats", &window->hbox_chats, - "treeview_chats", &window->treeview_chats, - "scrolledwindow_chats", &window->scrolledwindow_chats, - NULL); - g_free (filename); - - empathy_builder_connect (gui, window, - "log_window", "destroy", log_window_destroy_cb, - "entry_find", "changed", log_window_entry_find_changed_cb, - "entry_find", "activate", log_window_entry_find_activate_cb, - "button_previous", "clicked", log_window_button_previous_clicked_cb, - "button_next", "clicked", log_window_button_next_clicked_cb, - "button_find", "clicked", log_window_button_find_clicked_cb, - "entry_chats", "changed", log_window_entry_chats_changed_cb, - "entry_chats", "activate", log_window_entry_chats_activate_cb, - "menu_delete", "activate", log_window_delete_menu_clicked_cb, - NULL); - - g_object_unref (gui); - - g_object_add_weak_pointer (G_OBJECT (window->window), - (gpointer) &log_window); - - /* We set this up here so we can block it when needed. */ - g_signal_connect (window->calendar_chats, "day-selected", - G_CALLBACK (log_window_calendar_chats_day_selected_cb), - window); - g_signal_connect (window->calendar_chats, "month-changed", - G_CALLBACK (log_window_calendar_chats_month_changed_cb), - window); - - /* Configure Search EmpathyChatView */ - theme_mgr = empathy_theme_manager_dup_singleton (); - window->chatview_find = empathy_theme_manager_create_view (theme_mgr); - gtk_container_add (GTK_CONTAINER (window->scrolledwindow_find), - GTK_WIDGET (window->chatview_find)); - gtk_widget_show (GTK_WIDGET (window->chatview_find)); - - /* Configure Contacts EmpathyChatView */ - window->chatview_chats = empathy_theme_manager_create_view (theme_mgr); - gtk_container_add (GTK_CONTAINER (window->scrolledwindow_chats), - GTK_WIDGET (window->chatview_chats)); - gtk_widget_show (GTK_WIDGET (window->chatview_chats)); - g_object_unref (theme_mgr); - - /* Account chooser for chats */ - window->account_chooser_chats = empathy_account_chooser_new (); - account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats); - empathy_account_chooser_set_filter (account_chooser, empathy_account_chooser_filter_has_logs, NULL); - - gtk_box_pack_start (GTK_BOX (window->hbox_chats), - window->account_chooser_chats, - FALSE, TRUE, 0); - - g_signal_connect (window->account_chooser_chats, "changed", - G_CALLBACK (log_window_chats_accounts_changed_cb), - window); - - /* Populate */ - account_manager = tp_account_manager_dup (); - tp_account_manager_prepare_async (account_manager, NULL, - account_manager_prepared_cb, window); - g_object_unref (account_manager); + GtkTreeView *view; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + TpAccount *account; + TplEntity *target; + EmpathyContact *contact; + gint type; - /* Search List */ - log_window_find_setup (window); + g_return_if_fail (window != NULL); - /* Contacts */ - log_window_chats_setup (window); - log_window_chats_populate (window); + view = GTK_TREE_VIEW (log_window->treeview_who); + selection = gtk_tree_view_get_selection (view); - if (account != NULL && chat_id != NULL) - select_account_once_ready (window, account, chat_id, is_chatroom); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_model_get (model, &iter, + COL_WHO_ACCOUNT, &account, + COL_WHO_TARGET, &target, + COL_WHO_TYPE, &type, + -1); + } - if (parent != NULL) { - gtk_window_set_transient_for (GTK_WINDOW (window->window), - GTK_WINDOW (parent)); - } + g_return_if_fail (type == COL_TYPE_NORMAL); - gtk_widget_show (window->window); + contact = empathy_contact_from_tpl_contact (account, target); + empathy_contact_information_dialog_show (contact, + GTK_WINDOW (window->window)); - return window->window; + g_object_unref (contact); + g_object_unref (account); + g_object_unref (target); } static void -log_window_destroy_cb (GtkWidget *widget, - EmpathyLogWindow *window) +toolbutton_chat_clicked (GtkToolButton *toolbutton, + EmpathyLogWindow *window) { - g_free (window->last_find); - g_object_unref (window->log_manager); - tp_clear_object (&window->selected_account); - g_free (window->selected_chat_id); + GtkTreeView *view; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + TpAccount *account; + TplEntity *target; + EmpathyContact *contact; + gint type; - g_free (window); -} + g_return_if_fail (window != NULL); -/* - * Search code. - */ -static void -log_window_entry_find_changed_cb (GtkWidget *entry, - EmpathyLogWindow *window) -{ - const gchar *str; - gboolean is_sensitive = TRUE; + view = GTK_TREE_VIEW (log_window->treeview_who); + selection = gtk_tree_view_get_selection (view); - str = gtk_entry_get_text (GTK_ENTRY (window->entry_find)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_model_get (model, &iter, + COL_WHO_ACCOUNT, &account, + COL_WHO_TARGET, &target, + COL_WHO_TYPE, &type, + -1); + } - is_sensitive &= !EMP_STR_EMPTY (str); - is_sensitive &= - !window->last_find || - (window->last_find && tp_strdiff (window->last_find, str)); + g_return_if_fail (type == COL_TYPE_NORMAL); - gtk_widget_set_sensitive (window->button_find, is_sensitive); + contact = empathy_contact_from_tpl_contact (account, target); + empathy_dispatcher_chat_with_contact (contact, + gtk_get_current_event_time ()); + + g_object_unref (contact); + g_object_unref (account); + g_object_unref (target); } static void -got_events_for_date_cb (GObject *manager, - GAsyncResult *result, - gpointer user_data) +toolbutton_av_clicked (GtkToolButton *toolbutton, + EmpathyLogWindow *window) { - EmpathyLogWindow *window = user_data; - GList *messages; - GList *l; - gboolean can_do_previous; - gboolean can_do_next; - GError *error = NULL; + GtkTreeView *view; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + TpAccount *account; + gchar *contact; + gint type; + gboolean video; - if (log_window == NULL) - return; + g_return_if_fail (window != NULL); - if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager), - result, &messages, &error)) { - DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting", - error->message); - empathy_chat_view_append_event (window->chatview_find, - "Unable to retrieve messages for the selected date"); - g_error_free (error); - return; - } + view = GTK_TREE_VIEW (log_window->treeview_who); + selection = gtk_tree_view_get_selection (view); - for (l = messages; l; l = l->next) { - EmpathyMessage *message; + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_model_get (model, &iter, + COL_WHO_ACCOUNT, &account, + COL_WHO_NAME, &contact, + COL_WHO_TYPE, &type, + -1); + } - g_assert (TPL_IS_EVENT (l->data)); + g_return_if_fail (type == COL_TYPE_NORMAL); - message = empathy_message_from_tpl_log_event (l->data); - g_object_unref (l->data); - empathy_chat_view_append_message (window->chatview_find, message); - g_object_unref (message); - } - g_list_free (messages); - - /* Scroll to the most recent messages */ - empathy_chat_view_scroll (window->chatview_find, TRUE); - - /* Highlight and find messages */ - empathy_chat_view_highlight (window->chatview_find, - window->last_find, - FALSE); - empathy_chat_view_find_next (window->chatview_find, - window->last_find, - TRUE, - FALSE); - empathy_chat_view_find_abilities (window->chatview_find, - window->last_find, - FALSE, - &can_do_previous, - &can_do_next); - gtk_widget_set_sensitive (window->button_previous, can_do_previous); - gtk_widget_set_sensitive (window->button_next, can_do_next); - gtk_widget_set_sensitive (window->button_find, FALSE); + video = (GTK_WIDGET (toolbutton) == window->button_video); + + empathy_call_new_with_streams (contact, account, + TRUE, video, gtk_get_current_event_time ()); + + g_free (contact); + g_object_unref (account); } -static GDate * -gdate_from_str (const gchar *str) +GtkWidget * +empathy_log_window_show (TpAccount *account, + const gchar *chat_id, + gboolean is_chatroom, + GtkWindow *parent) { - guint u; - guint day, month, year; + EmpathyAccountChooser *account_chooser; + GtkBuilder *gui; + gchar *filename; + EmpathyLogWindow *window; + EmpathyThemeManager *theme_mgr; + GtkWidget *vbox, *accounts, *search, *label, *quit; + + if (log_window != NULL) + { + gtk_window_present (GTK_WINDOW (log_window->window)); + + if (account != NULL && chat_id != NULL) + select_account_once_ready (log_window, account, chat_id, is_chatroom); + + return log_window->window; + } + + log_window = g_new0 (EmpathyLogWindow, 1); + log_window->chain = _tpl_action_chain_new_async (NULL, NULL, NULL); + + log_window->log_manager = tpl_log_manager_dup_singleton (); + + window = log_window; + + filename = empathy_file_lookup ("empathy-log-window.ui", "libempathy-gtk"); + gui = empathy_builder_get_file (filename, + "log_window", &window->window, + "toolbutton_profile", &window->button_profile, + "toolbutton_chat", &window->button_chat, + "toolbutton_call", &window->button_call, + "toolbutton_video", &window->button_video, + "toolbutton_accounts", &accounts, + "toolbutton_search", &search, + "imagemenuitem_quit", &quit, + "treeview_who", &window->treeview_who, + "treeview_what", &window->treeview_what, + "treeview_when", &window->treeview_when, + "scrolledwindow_events", &window->scrolledwindow_events, + NULL); + g_free (filename); + + empathy_builder_connect (gui, window, + "log_window", "destroy", log_window_destroy_cb, + "toolbutton_profile", "clicked", toolbutton_profile_clicked, + "toolbutton_chat", "clicked", toolbutton_chat_clicked, + "toolbutton_call", "clicked", toolbutton_av_clicked, + "toolbutton_video", "clicked", toolbutton_av_clicked, + "imagemenuitem_delete", "activate", log_window_delete_menu_clicked_cb, + NULL); + + g_object_unref (gui); + + g_object_add_weak_pointer (G_OBJECT (window->window), + (gpointer) &log_window); + + g_signal_connect_swapped (quit, "activate", + G_CALLBACK (gtk_widget_destroy), window->window); + + /* Configure Contacts EmpathyChatView */ + theme_mgr = empathy_theme_manager_dup_singleton (); + window->chatview_events = empathy_theme_manager_create_view (theme_mgr); + gtk_container_add (GTK_CONTAINER (window->scrolledwindow_events), + GTK_WIDGET (window->chatview_events)); + gtk_widget_show (GTK_WIDGET (window->chatview_events)); + g_object_unref (theme_mgr); + + /* Account chooser for chats */ + vbox = gtk_vbox_new (FALSE, 3); + + window->account_chooser = empathy_account_chooser_new (); + account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser); + empathy_account_chooser_set_has_all_option (account_chooser, TRUE); + empathy_account_chooser_set_filter (account_chooser, + empathy_account_chooser_filter_has_logs, NULL); + + g_signal_connect (window->account_chooser, "changed", + G_CALLBACK (log_window_chats_accounts_changed_cb), + window); - if (sscanf (str, "%u", &u) != 1) - return NULL; + label = gtk_label_new (_("Show")); - day = (u % 100); - month = ((u / 100) % 100); - year = (u / 10000); + gtk_box_pack_start (GTK_BOX (vbox), + window->account_chooser, + FALSE, FALSE, 0); - if (!g_date_valid_dmy (day, month, year)) - return NULL; + gtk_box_pack_start (GTK_BOX (vbox), + label, + FALSE, FALSE, 0); - return g_date_new_dmy (day, month, year); -} + gtk_widget_show_all (vbox); + gtk_container_add (GTK_CONTAINER (accounts), vbox); -static void -log_window_find_changed_cb (GtkTreeSelection *selection, - EmpathyLogWindow *window) -{ - GtkTreeView *view; - GtkTreeModel *model; - GtkTreeIter iter; - TpAccount *account; - TplEntity *target; - gchar *date; - GDate *gdate; - - /* Get selected information */ - view = GTK_TREE_VIEW (window->treeview_find); - model = gtk_tree_view_get_model (view); + /* Search entry */ + vbox = gtk_vbox_new (FALSE, 3); - if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { - gtk_widget_set_sensitive (window->button_previous, FALSE); - gtk_widget_set_sensitive (window->button_next, FALSE); + window->search_entry = gtk_entry_new (); + gtk_entry_set_icon_from_stock (GTK_ENTRY (window->search_entry), + GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND); - empathy_chat_view_clear (window->chatview_find); + label = gtk_label_new (_("Search")); - return; - } + gtk_box_pack_start (GTK_BOX (vbox), + window->search_entry, + FALSE, FALSE, 0); - gtk_widget_set_sensitive (window->button_previous, TRUE); - gtk_widget_set_sensitive (window->button_next, TRUE); + gtk_box_pack_start (GTK_BOX (vbox), + label, + FALSE, FALSE, 0); - gtk_tree_model_get (model, &iter, - COL_FIND_ACCOUNT, &account, - COL_FIND_TARGET, &target, - COL_FIND_DATE, &date, - -1); + gtk_widget_show_all (vbox); + gtk_container_add (GTK_CONTAINER (search), vbox); - /* Clear all current messages shown in the textview */ - empathy_chat_view_clear (window->chatview_find); + g_signal_connect (window->search_entry, "changed", + G_CALLBACK (log_window_search_entry_changed_cb), + window); - /* Turn off scrolling temporarily */ - empathy_chat_view_scroll (window->chatview_find, FALSE); + g_signal_connect (window->search_entry, "activate", + G_CALLBACK (log_window_search_entry_activate_cb), + window); - /* Get messages */ - gdate = gdate_from_str (date); + /* Contacts */ + log_window_who_setup (window); + log_window_what_setup (window); + log_window_when_setup (window); - if (gdate != NULL) { - tpl_log_manager_get_events_for_date_async (window->log_manager, - account, - target, - TPL_EVENT_MASK_TEXT, - gdate, - got_events_for_date_cb, - window); + log_window_who_populate (window); - g_date_free (gdate); - } + if (account != NULL && chat_id != NULL) + select_account_once_ready (window, account, chat_id, is_chatroom); - g_object_unref (account); - g_object_unref (target); - g_free (date); -} + if (parent != NULL) + gtk_window_set_transient_for (GTK_WINDOW (window->window), + GTK_WINDOW (parent)); + + gtk_widget_show (window->window); + return window->window; +} static void -log_manager_searched_new_cb (GObject *manager, - GAsyncResult *result, - gpointer user_data) +log_window_destroy_cb (GtkWidget *widget, + EmpathyLogWindow *window) { - GList *hits; - GList *l; - GtkTreeIter iter; - GtkListStore *store = user_data; - GError *error = NULL; + g_free (window->last_find); + _tpl_action_chain_free (window->chain); + g_object_unref (window->log_manager); + tp_clear_object (&window->selected_account); + g_free (window->selected_chat_id); - if (log_window == NULL) - return; - - if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager), result, - &hits, &error)) { - DEBUG ("%s. Aborting", error->message); - g_error_free (error); - return; - } + g_free (window); +} - for (l = hits; l; l = l->next) { - TplLogSearchHit *hit; - const gchar *account_name; - const gchar *account_icon; - gchar date_readable[255]; - gchar tmp[255]; - - hit = l->data; - - /* Protect against invalid data (corrupt or old log files. */ - if (hit->account == NULL || hit->target == NULL) { - continue; - } - - g_date_strftime (date_readable, sizeof (date_readable), - EMPATHY_DATE_FORMAT_DISPLAY_SHORT, hit->date); - - g_date_strftime (tmp, sizeof (tmp), - "%Y%m%d", hit->date); - - account_name = tp_account_get_display_name (hit->account); - account_icon = tp_account_get_icon_name (hit->account); - - gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, - COL_FIND_ACCOUNT_ICON, account_icon, - COL_FIND_ACCOUNT_NAME, account_name, - COL_FIND_ACCOUNT, hit->account, - COL_FIND_CHAT_NAME, tpl_entity_get_alias (hit->target), - COL_FIND_TARGET, hit->target, - COL_FIND_DATE, tmp, - COL_FIND_DATE_READABLE, date_readable, - -1); - - /* FIXME: Update COL_FIND_CHAT_NAME */ - if (tpl_entity_get_entity_type (hit->target) == TPL_ENTITY_ROOM) { - } else { - } - } +static gboolean +log_window_chats_get_selected (EmpathyLogWindow *window, + TpAccount **account, + TplEntity **target, + GDate **date, + TplEventTypeMask *event_mask, + EventSubtype *subtype) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + TplEntity *targ; + TpAccount *acc = NULL; + TplEventTypeMask ev = 0; + EventSubtype st = 0; + GDate *d = NULL; + + view = GTK_TREE_VIEW (window->treeview_who); + model = gtk_tree_view_get_model (view); + selection = gtk_tree_view_get_selection (view); + + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) + return FALSE; + + gtk_tree_model_get (model, &iter, + COL_WHO_ACCOUNT, &acc, + COL_WHO_TARGET, &targ, + -1); + + view = GTK_TREE_VIEW (window->treeview_what); + model = gtk_tree_view_get_model (view); + selection = gtk_tree_view_get_selection (view); + + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) + { + gtk_tree_model_get (model, &iter, + COL_WHAT_TYPE, &ev, + COL_WHAT_SUBTYPE, &st, + -1); + } + + view = GTK_TREE_VIEW (window->treeview_when); + model = gtk_tree_view_get_model (view); + selection = gtk_tree_view_get_selection (view); + + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) + { + gtk_tree_model_get (model, &iter, + COL_WHEN_DATE, &d, + -1); + } + + if (account != NULL) + *account = acc ? g_object_ref (acc) : NULL; + + if (target != NULL) + *target = targ ? g_object_ref (targ) : NULL; + + if (date != NULL) + *date = d; + + if (event_mask != NULL) + *event_mask = ev; + + if (subtype != NULL) + *subtype = st; + + tp_clear_object (&acc); + tp_clear_object (&targ); + + return TRUE; +} - if (hits != NULL) { - tpl_log_manager_search_free (hits); - } +static gboolean +account_equal (TpAccount *a, + TpAccount *b) +{ + return g_str_equal (tp_proxy_get_object_path (a), + tp_proxy_get_object_path (b)); } -static void -log_window_find_populate (EmpathyLogWindow *window, - const gchar *search_criteria) +static gboolean +entity_equal (TplEntity *a, + TplEntity *b) { - GtkTreeView *view; - GtkTreeModel *model; - GtkTreeSelection *selection; - GtkListStore *store; + return g_str_equal (tpl_entity_get_identifier (a), + tpl_entity_get_identifier (b)); +} - view = GTK_TREE_VIEW (window->treeview_find); - model = gtk_tree_view_get_model (view); - selection = gtk_tree_view_get_selection (view); - store = GTK_LIST_STORE (model); +static gboolean +model_has_entity (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + TplLogSearchHit *hit = data; + TplEntity *e; + TpAccount *a; + + gtk_tree_model_get (model, iter, + COL_WHO_TARGET, &e, + COL_WHO_ACCOUNT, &a, + -1); + + if (e != NULL && entity_equal (hit->target, e) && + a != NULL && account_equal (hit->account, a)) + { + has_element = TRUE; + return TRUE; + } + + return FALSE; +} - empathy_chat_view_clear (window->chatview_find); +static gboolean +model_has_date (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GDate *date = data; + GDate *d; - gtk_list_store_clear (store); + gtk_tree_model_get (model, iter, + COL_WHEN_DATE, &d, + -1); - if (EMP_STR_EMPTY (search_criteria)) { - /* Just clear the search. */ - return; - } + if (!g_date_compare (date, d)) + { + has_element = TRUE; + return TRUE; + } - tpl_log_manager_search_async (window->log_manager, - search_criteria, TPL_EVENT_MASK_TEXT, - log_manager_searched_new_cb, (gpointer) store); + return FALSE; } static void -log_window_find_setup (EmpathyLogWindow *window) -{ - GtkTreeView *view; - GtkTreeModel *model; - GtkTreeSelection *selection; - GtkTreeSortable *sortable; - GtkTreeViewColumn *column; - GtkListStore *store; - GtkCellRenderer *cell; - gint offset; - - view = GTK_TREE_VIEW (window->treeview_find); - selection = gtk_tree_view_get_selection (view); - - /* New store */ - store = gtk_list_store_new (COL_FIND_COUNT, - G_TYPE_STRING, /* account icon name */ - G_TYPE_STRING, /* account name */ - TP_TYPE_ACCOUNT, /* account */ - G_TYPE_STRING, /* chat name */ - TPL_TYPE_ENTITY, /* target */ - G_TYPE_STRING, /* date */ - G_TYPE_STRING); /* date_readable */ - - model = GTK_TREE_MODEL (store); - sortable = GTK_TREE_SORTABLE (store); - - gtk_tree_view_set_model (view, model); - - /* New column */ - column = gtk_tree_view_column_new (); - - cell = gtk_cell_renderer_pixbuf_new (); - gtk_tree_view_column_pack_start (column, cell, FALSE); - gtk_tree_view_column_add_attribute (column, cell, - "icon-name", - COL_FIND_ACCOUNT_ICON); - - cell = gtk_cell_renderer_text_new (); - gtk_tree_view_column_pack_start (column, cell, TRUE); - gtk_tree_view_column_add_attribute (column, cell, - "text", - COL_FIND_ACCOUNT_NAME); - - gtk_tree_view_column_set_title (column, _("Account")); - gtk_tree_view_append_column (view, column); - - gtk_tree_view_column_set_resizable (column, TRUE); - gtk_tree_view_column_set_clickable (column, TRUE); - - cell = gtk_cell_renderer_text_new (); - offset = gtk_tree_view_insert_column_with_attributes (view, -1, _("Conversation"), - cell, "text", COL_FIND_CHAT_NAME, - NULL); - - column = gtk_tree_view_get_column (view, offset - 1); - gtk_tree_view_column_set_sort_column_id (column, COL_FIND_CHAT_NAME); - gtk_tree_view_column_set_resizable (column, TRUE); - gtk_tree_view_column_set_clickable (column, TRUE); - - cell = gtk_cell_renderer_text_new (); - offset = gtk_tree_view_insert_column_with_attributes (view, -1, _("Date"), - cell, "text", COL_FIND_DATE_READABLE, - NULL); - - column = gtk_tree_view_get_column (view, offset - 1); - gtk_tree_view_column_set_sort_column_id (column, COL_FIND_DATE); - gtk_tree_view_column_set_resizable (column, TRUE); - gtk_tree_view_column_set_clickable (column, TRUE); - - /* Set up treeview properties */ - gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); - gtk_tree_sortable_set_sort_column_id (sortable, - COL_FIND_DATE, - GTK_SORT_ASCENDING); - - /* Set up signals */ - g_signal_connect (selection, "changed", - G_CALLBACK (log_window_find_changed_cb), - window); - - g_object_unref (store); -} +get_events_for_date (TplActionChain *chain, gpointer user_data); static void -start_find_search (EmpathyLogWindow *window) +populate_events_from_search_hits (TpAccount *account, + TplEntity *target, + GDate *date) { - const gchar *str; + TplEventTypeMask event_mask; + EventSubtype subtype; + GDate *anytime; + GList *l; - str = gtk_entry_get_text (GTK_ENTRY (window->entry_find)); + if (!log_window_chats_get_selected (log_window, + NULL, NULL, NULL, &event_mask, &subtype)) + return; - /* Don't find the same crap again */ - if (window->last_find && !tp_strdiff (window->last_find, str)) { - return; - } + anytime = g_date_new_dmy (2, 1, -1); - g_free (window->last_find); - window->last_find = g_strdup (str); + for (l = log_window->hits; l != NULL; l = l->next) + { + TplLogSearchHit *hit = l->data; - log_window_find_populate (window, str); -} + /* Protect against invalid data (corrupt or old log files). */ + if (hit->account == NULL || hit->target == NULL) + continue; -static void -log_window_button_find_clicked_cb (GtkWidget *widget, - EmpathyLogWindow *window) -{ - start_find_search (window); + if (!account_equal (hit->account, account) || + !entity_equal (hit->target, target)) + continue; + + if (g_date_compare (date, anytime) == 0 || + g_date_compare (date, hit->date) == 0) + { + Ctx *ctx; + + ctx = ctx_new (log_window, hit->account, hit->target, hit->date, + event_mask, subtype); + _tpl_action_chain_append (log_window->chain, + get_events_for_date, ctx); + } + } + + _tpl_action_chain_start (log_window->chain); + + g_date_free (anytime); } static void -log_window_entry_find_activate_cb (GtkWidget *entry, - EmpathyLogWindow *self) +populate_dates_from_search_hits (TpAccount *account, + TplEntity *target) { - start_find_search (self); + GList *l; + GtkTreeView *view; + GtkTreeModel *model; + GtkListStore *store; + GtkTreeIter iter; + + if (log_window == NULL) + return; + + view = GTK_TREE_VIEW (log_window->treeview_when); + model = gtk_tree_view_get_model (view); + store = GTK_LIST_STORE (model); + + for (l = log_window->hits; l != NULL; l = l->next) + { + TplLogSearchHit *hit = l->data; + + /* Protect against invalid data (corrupt or old log files). */ + if (hit->account == NULL || hit->target == NULL) + continue; + + if (!account_equal (hit->account, account) || + !entity_equal (hit->target, target)) + continue; + + /* Add the date if it's not already there */ + has_element = FALSE; + gtk_tree_model_foreach (model, model_has_date, hit->date); + if (!has_element) + { + gchar *text = g_strdup_printf ("%02d/%02d/%d", + g_date_get_day (hit->date), + g_date_get_month (hit->date), + g_date_get_year (hit->date)); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_WHEN_DATE, hit->date, + COL_WHEN_TEXT, text, + COL_WHEN_ICON, CALENDAR_ICON, + -1); + } + } + + if (gtk_tree_model_get_iter_first (model, &iter)) + { + gtk_list_store_prepend (store, &iter); + gtk_list_store_set (store, &iter, + COL_WHEN_DATE, g_date_new_dmy (1, 1, -1), + COL_WHEN_TEXT, "separator", + -1); + + gtk_list_store_prepend (store, &iter); + gtk_list_store_set (store, &iter, + COL_WHEN_DATE, g_date_new_dmy (2, 1, -1), + COL_WHEN_TEXT, _("Anytime"), + -1); + } } static void -log_window_button_next_clicked_cb (GtkWidget *widget, - EmpathyLogWindow *window) +populate_entities_from_search_hits (void) { - if (window->last_find) { - gboolean can_do_previous; - gboolean can_do_next; - - empathy_chat_view_find_next (window->chatview_find, - window->last_find, - FALSE, - FALSE); - empathy_chat_view_find_abilities (window->chatview_find, - window->last_find, - FALSE, - &can_do_previous, - &can_do_next); - gtk_widget_set_sensitive (window->button_previous, can_do_previous); - gtk_widget_set_sensitive (window->button_next, can_do_next); - } + EmpathyAccountChooser *account_chooser; + TpAccount *account; + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeIter iter; + GtkListStore *store; + GList *l; + + view = GTK_TREE_VIEW (log_window->treeview_who); + model = gtk_tree_view_get_model (view); + store = GTK_LIST_STORE (model); + + gtk_list_store_clear (store); + + account_chooser = EMPATHY_ACCOUNT_CHOOSER (log_window->account_chooser); + account = empathy_account_chooser_get_account (account_chooser); + + for (l = log_window->hits; l; l = l->next) + { + TplLogSearchHit *hit = l->data; + + /* Protect against invalid data (corrupt or old log files). */ + if (hit->account == NULL || hit->target == NULL) + continue; + + /* Filter based on the selected account */ + if (account != NULL && !account_equal (account, hit->account)) + continue; + + /* Add the entity if it's not already there */ + has_element = FALSE; + gtk_tree_model_foreach (model, model_has_entity, hit); + if (!has_element) + { + TplEntityType type = tpl_entity_get_entity_type (hit->target); + gboolean room = type == TPL_ENTITY_ROOM; + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_WHO_TYPE, COL_TYPE_NORMAL, + COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE + : EMPATHY_IMAGE_AVATAR_DEFAULT, + COL_WHO_NAME, tpl_entity_get_alias (hit->target), + COL_WHO_ACCOUNT, hit->account, + COL_WHO_TARGET, hit->target, + -1); + } + } + +#if 0 + if (gtk_tree_model_get_iter_first (model, &iter)) + { + gtk_list_store_prepend (store, &iter); + gtk_list_store_set (store, &iter, + COL_WHO_TYPE, COL_TYPE_SEPARATOR, + COL_WHO_NAME, "separator", + -1); + + gtk_list_store_prepend (store, &iter); + gtk_list_store_set (store, &iter, + COL_WHO_TYPE, COL_TYPE_ANY, + COL_WHO_NAME, _("Anyone"), + -1); + } +#endif } static void -log_window_button_previous_clicked_cb (GtkWidget *widget, - EmpathyLogWindow *window) +log_manager_searched_new_cb (GObject *manager, + GAsyncResult *result, + gpointer user_data) { - if (window->last_find) { - gboolean can_do_previous; - gboolean can_do_next; - - empathy_chat_view_find_previous (window->chatview_find, - window->last_find, - FALSE, - FALSE); - empathy_chat_view_find_abilities (window->chatview_find, - window->last_find, - FALSE, - &can_do_previous, - &can_do_next); - gtk_widget_set_sensitive (window->button_previous, can_do_previous); - gtk_widget_set_sensitive (window->button_next, can_do_next); - } -} + GList *hits; + GtkTreeView *view; + GtkTreeSelection *selection; + GError *error = NULL; -/* - * Chats Code - */ + if (log_window == NULL) + return; -static void -log_window_chats_changed_cb (GtkTreeSelection *selection, - EmpathyLogWindow *window) -{ - gboolean selected; + if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager), + result, &hits, &error)) + { + DEBUG ("%s. Aborting", error->message); + g_error_free (error); + return; + } + + tp_clear_pointer (&log_window->hits, tpl_log_manager_search_free); + log_window->hits = hits; - /* The calendar has to be sensitive only if there is something selected */ - selected = log_window_chats_get_selected (window, NULL, NULL); - gtk_widget_set_sensitive (window->calendar_chats, selected); + populate_entities_from_search_hits (); - /* Use last date by default */ - gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats)); + /* FIXME: select old entity if still available, and populate the dates */ + //populate_dates_from_search_hits (NULL, NULL); - log_window_chats_get_messages (window, NULL); + view = GTK_TREE_VIEW (log_window->treeview_when); + selection = gtk_tree_view_get_selection (view); + + g_signal_handlers_unblock_by_func (selection, + log_window_when_changed_cb, + log_window); } -typedef struct +#if 0 +static gboolean +search_results_filter_entities (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) { - EmpathyLogWindow *window; - TpAccount *account; -} GetEntitiesCtx; + TpAccount *account, *selected_account; + gboolean visible = FALSE; -static GetEntitiesCtx * -get_entities_ctx_new (EmpathyLogWindow *window, - TpAccount *account) -{ - GetEntitiesCtx *ctx = g_slice_new (GetEntitiesCtx); + if (log_window->hits == NULL) + return TRUE; - /* EmpathyLogWindow isn't a proper GObject so we can't ref it */ - ctx->window = window; - ctx->account = g_object_ref (account); - return ctx; -} + if (!log_window_chats_get_selected (log_window, &selected_account, + NULL, NULL, NULL, NULL)) -static void -get_entities_ctx_free (GetEntitiesCtx *ctx) -{ - g_object_unref (ctx->account); - g_slice_free (GetEntitiesCtx, ctx); + gtk_tree_model_get (model, iter, + COL_WHO_ACCOUNT, &account, + -1); + + if (selected_account == NULL || + account_equal (account, selected_account)) + visible = TRUE; + + g_object_unref (account); + g_object_unref (selected_account); + + return visible; } +#endif static void -log_manager_got_entities_cb (GObject *manager, - GAsyncResult *result, - gpointer user_data) +log_window_find_populate (EmpathyLogWindow *window, + const gchar *search_criteria) { - GetEntitiesCtx *ctx = user_data; - GList *entities; - GList *l; - GtkTreeView *view; - GtkTreeModel *model; - GtkTreeSelection *selection; - GtkListStore *store; - GtkTreeIter iter; - GError *error = NULL; - gboolean select_account = FALSE; - - view = GTK_TREE_VIEW (ctx->window->treeview_chats); - - if (log_window == NULL) - goto out; + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkListStore *store; - if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager), - result, &entities, &error)) { - DEBUG ("%s. Aborting", error->message); - g_error_free (error); - goto out; - } + empathy_chat_view_clear (window->chatview_events); - view = GTK_TREE_VIEW (ctx->window->treeview_chats); - model = gtk_tree_view_get_model (view); - selection = gtk_tree_view_get_selection (view); - store = GTK_LIST_STORE (model); + view = GTK_TREE_VIEW (window->treeview_who); + model = gtk_tree_view_get_model (view); + store = GTK_LIST_STORE (model); - for (l = entities; l; l = l->next) { - TplEntity *entity; + gtk_list_store_clear (store); - entity = TPL_ENTITY (l->data); + view = GTK_TREE_VIEW (window->treeview_when); + model = gtk_tree_view_get_model (view); + store = GTK_LIST_STORE (model); + selection = gtk_tree_view_get_selection (view); - gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, - COL_CHAT_ICON, "empathy-available", /* FIXME */ - COL_CHAT_NAME, tpl_entity_get_alias (entity), - COL_CHAT_ACCOUNT, ctx->account, - COL_CHAT_TARGET, entity, - -1); + gtk_list_store_clear (store); - if (ctx->window->selected_account != NULL && - !tp_strdiff (tp_proxy_get_object_path (ctx->account), - tp_proxy_get_object_path (ctx->window->selected_account))) - select_account = TRUE; + if (EMP_STR_EMPTY (search_criteria)) + { + tp_clear_pointer (&window->hits, tpl_log_manager_search_free); + log_window_who_populate (window); + return; + } - /* FIXME: Update COL_CHAT_ICON/NAME */ - if (tpl_entity_get_entity_type (entity) == TPL_ENTITY_ROOM) { - } else { - } - } - g_list_free_full (entities, g_object_unref); + g_signal_handlers_block_by_func (selection, + log_window_when_changed_cb, + window); - /* Unblock signals */ - g_signal_handlers_unblock_by_func (selection, - log_window_chats_changed_cb, - ctx->window); + tpl_log_manager_search_async (window->log_manager, + search_criteria, TPL_EVENT_MASK_ANY, + log_manager_searched_new_cb, NULL); +} + +static void +start_find_search (EmpathyLogWindow *window) +{ + const gchar *str; - /* We display the selected account if we populate the model with chats from - * this account. */ - if (select_account) - log_window_chats_set_selected (ctx->window); + str = gtk_entry_get_text (GTK_ENTRY (window->search_entry)); -out: - get_entities_ctx_free (ctx); + /* Don't find the same crap again */ + if (window->last_find && !tp_strdiff (window->last_find, str)) + return; + + g_free (window->last_find); + window->last_find = g_strdup (str); + + log_window_find_populate (window, str); } static void -log_window_chats_populate (EmpathyLogWindow *window) +log_window_search_entry_changed_cb (GtkWidget *entry, + EmpathyLogWindow *window) { - EmpathyAccountChooser *account_chooser; - TpAccount *account; + /* FIXME: live search ? */ +} - GtkTreeView *view; - GtkTreeModel *model; - GtkTreeSelection *selection; - GtkListStore *store; - GetEntitiesCtx *ctx; +static void +log_window_search_entry_activate_cb (GtkWidget *entry, + EmpathyLogWindow *self) +{ + start_find_search (self); +} - account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats); - account = empathy_account_chooser_dup_account (account_chooser); +/* + * Chats Code + */ - view = GTK_TREE_VIEW (window->treeview_chats); - model = gtk_tree_view_get_model (view); - selection = gtk_tree_view_get_selection (view); - store = GTK_LIST_STORE (model); +static void +log_window_who_changed_cb (GtkTreeSelection *selection, + EmpathyLogWindow *window) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gint type; + gboolean someone = FALSE; + +g_print ("log_window_who_changed_cb\n"); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_model_get (model, &iter, + COL_WHO_TYPE, &type, + -1); + + someone = (type == COL_TYPE_NORMAL); + } + + gtk_widget_set_sensitive (window->button_profile, someone); + gtk_widget_set_sensitive (window->button_chat, someone); + gtk_widget_set_sensitive (window->button_call, someone); + gtk_widget_set_sensitive (window->button_video, someone); + + /* The contact changed, so the dates need to be updated */ + log_window_chats_get_messages (window, TRUE); +} - if (account == NULL) { - gtk_list_store_clear (store); - return; - } +static void +log_manager_got_entities_cb (GObject *manager, + GAsyncResult *result, + gpointer user_data) +{ + Ctx *ctx = user_data; + GList *entities; + GList *l; + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkListStore *store; + GtkTreeIter iter; + GError *error = NULL; + gboolean select_account = FALSE; + + if (log_window == NULL) + goto out; - /* Block signals to stop the logs being retrieved prematurely */ - g_signal_handlers_block_by_func (selection, - log_window_chats_changed_cb, - window); + if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager), + result, &entities, &error)) + { + DEBUG ("%s. Aborting", error->message); + g_error_free (error); + goto out; + } + + view = GTK_TREE_VIEW (ctx->window->treeview_who); + model = gtk_tree_view_get_model (view); + selection = gtk_tree_view_get_selection (view); + store = GTK_LIST_STORE (model); + + /* Block signals to stop the logs being retrieved prematurely */ + g_signal_handlers_block_by_func (selection, + log_window_who_changed_cb, ctx->window); + + for (l = entities; l; l = l->next) + { + TplEntity *entity = TPL_ENTITY (l->data); + TplEntityType type = tpl_entity_get_entity_type (entity); + gboolean room = type == TPL_ENTITY_ROOM; + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_WHO_TYPE, COL_TYPE_NORMAL, + COL_WHO_ICON, room ? EMPATHY_IMAGE_GROUP_MESSAGE + : EMPATHY_IMAGE_AVATAR_DEFAULT, + COL_WHO_NAME, tpl_entity_get_alias (entity), + COL_WHO_ACCOUNT, ctx->account, + COL_WHO_TARGET, entity, + -1); + + if (ctx->window->selected_account != NULL && + !tp_strdiff (tp_proxy_get_object_path (ctx->account), + tp_proxy_get_object_path (ctx->window->selected_account))) + select_account = TRUE; + } + g_list_free_full (entities, g_object_unref); + + if (gtk_tree_model_get_iter_first (model, &iter)) + { + gint type; + + gtk_tree_model_get (model, &iter, + COL_WHO_TYPE, &type, + -1); + + if (type != COL_TYPE_ANY) + { + gtk_list_store_prepend (store, &iter); + gtk_list_store_set (store, &iter, + COL_WHO_TYPE, COL_TYPE_SEPARATOR, + COL_WHO_NAME, "separator", + -1); + + gtk_list_store_prepend (store, &iter); + gtk_list_store_set (store, &iter, + COL_WHO_TYPE, COL_TYPE_ANY, + COL_WHO_NAME, _("Anyone"), + -1); + } + } + + /* Select 'Anyone' by default */ + if (gtk_tree_model_get_iter_first (model, &iter)) + gtk_tree_selection_select_iter (selection, &iter); + + /* Unblock signals */ + g_signal_handlers_unblock_by_func (selection, + log_window_who_changed_cb, + ctx->window); + + /* We display the selected account if we populate the model with chats from + * this account. */ + if (select_account) + log_window_chats_set_selected (ctx->window); - gtk_list_store_clear (store); +out: + _tpl_action_chain_continue (log_window->chain); + ctx_free (ctx); +} - ctx = get_entities_ctx_new (window, account); +static void +get_entities_for_account (TplActionChain *chain, gpointer user_data) +{ + Ctx *ctx = user_data; - tpl_log_manager_get_entities_async (window->log_manager, account, - log_manager_got_entities_cb, ctx); +g_print ("get_entities_for_account\n"); + tpl_log_manager_get_entities_async (ctx->window->log_manager, ctx->account, + log_manager_got_entities_cb, ctx); } static void -log_window_chats_setup (EmpathyLogWindow *window) +log_window_who_populate (EmpathyLogWindow *window) { - GtkTreeView *view; - GtkTreeModel *model; - GtkTreeSelection *selection; - GtkTreeSortable *sortable; - GtkTreeViewColumn *column; - GtkListStore *store; - GtkCellRenderer *cell; - - view = GTK_TREE_VIEW (window->treeview_chats); - selection = gtk_tree_view_get_selection (view); + EmpathyAccountChooser *account_chooser; + TpAccount *account; + gboolean all_accounts; + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkListStore *store; + Ctx *ctx; + + if (window->hits != NULL) + { + populate_entities_from_search_hits (); + return; + } + + account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser); + account = empathy_account_chooser_dup_account (account_chooser); + all_accounts = empathy_account_chooser_has_all_selected (account_chooser); - /* new store */ - store = gtk_list_store_new (COL_CHAT_COUNT, - G_TYPE_STRING, /* icon */ - G_TYPE_STRING, /* name */ - TP_TYPE_ACCOUNT, /* account */ - TPL_TYPE_ENTITY); /* target */ + view = GTK_TREE_VIEW (window->treeview_who); + model = gtk_tree_view_get_model (view); + selection = gtk_tree_view_get_selection (view); + store = GTK_LIST_STORE (model); - model = GTK_TREE_MODEL (store); - sortable = GTK_TREE_SORTABLE (store); + /* Block signals to stop the logs being retrieved prematurely */ + g_signal_handlers_block_by_func (selection, + log_window_who_changed_cb, + window); - gtk_tree_view_set_model (view, model); + gtk_list_store_clear (store); - /* new column */ - column = gtk_tree_view_column_new (); + /* Unblock signals */ + g_signal_handlers_unblock_by_func (selection, + log_window_who_changed_cb, + window); - cell = gtk_cell_renderer_pixbuf_new (); - gtk_tree_view_column_pack_start (column, cell, FALSE); - gtk_tree_view_column_add_attribute (column, cell, - "icon-name", - COL_CHAT_ICON); + if (!all_accounts && account == NULL) + { + return; + } + else if (!all_accounts) + { + ctx = ctx_new (window, account, NULL, NULL, 0, 0); + _tpl_action_chain_append (window->chain, get_entities_for_account, ctx); + } + else + { + TpAccountManager *manager; + GList *accounts, *l; + + manager = empathy_account_chooser_get_account_manager (account_chooser); + accounts = tp_account_manager_get_valid_accounts (manager); + + for (l = accounts; l != NULL; l = l->next) + { + account = l->data; + + ctx = ctx_new (window, account, NULL, NULL, 0, 0); + _tpl_action_chain_append (window->chain, + get_entities_for_account, ctx); + } + + g_list_free (accounts); + } + _tpl_action_chain_start (window->chain); +} - cell = gtk_cell_renderer_text_new (); - g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL); - gtk_tree_view_column_pack_start (column, cell, TRUE); - gtk_tree_view_column_add_attribute (column, cell, - "text", - COL_CHAT_NAME); +static gint +sort_by_name (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gchar *name1, *name2; + gint type1, type2; + gint ret; + + gtk_tree_model_get (model, a, + COL_WHO_TYPE, &type1, + COL_WHO_NAME, &name1, + -1); + + gtk_tree_model_get (model, b, + COL_WHO_TYPE, &type2, + COL_WHO_NAME, &name2, + -1); + + if (type1 == COL_TYPE_ANY) + ret = -1; + else if (type2 == COL_TYPE_ANY) + ret = 1; + else if (type1 == COL_TYPE_SEPARATOR) + ret = -1; + else if (type2 == COL_TYPE_SEPARATOR) + ret = 1; + else + ret = g_strcmp0 (name1, name2); + + g_free (name1); + g_free (name2); + + return ret; +} - gtk_tree_view_append_column (view, column); +static gboolean +who_row_is_separator (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gint type; - /* set up treeview properties */ - gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); - gtk_tree_sortable_set_sort_column_id (sortable, - COL_CHAT_NAME, - GTK_SORT_ASCENDING); + gtk_tree_model_get (model, iter, + COL_WHO_TYPE, &type, + -1); - /* set up signals */ - g_signal_connect (selection, "changed", - G_CALLBACK (log_window_chats_changed_cb), - window); + return (type == COL_TYPE_SEPARATOR); +} - g_object_unref (store); +static void +log_window_who_setup (EmpathyLogWindow *window) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeSortable *sortable; + GtkTreeViewColumn *column; + GtkListStore *store; + GtkCellRenderer *cell; + + view = GTK_TREE_VIEW (window->treeview_who); + selection = gtk_tree_view_get_selection (view); + + /* new store */ + store = gtk_list_store_new (COL_WHO_COUNT, + G_TYPE_INT, /* type */ + G_TYPE_STRING, /* icon */ + G_TYPE_STRING, /* name */ + TP_TYPE_ACCOUNT, /* account */ + TPL_TYPE_ENTITY); /* target */ + + model = GTK_TREE_MODEL (store); + sortable = GTK_TREE_SORTABLE (store); + + gtk_tree_view_set_model (view, model); + + /* new column */ + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, _("Who")); + + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, cell, FALSE); + gtk_tree_view_column_add_attribute (column, cell, + "icon-name", + COL_WHO_ICON); + + cell = gtk_cell_renderer_text_new (); + g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_column_pack_start (column, cell, TRUE); + gtk_tree_view_column_add_attribute (column, cell, + "text", + COL_WHO_NAME); + + gtk_tree_view_append_column (view, column); + + /* set up treeview properties */ + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + gtk_tree_view_set_row_separator_func (view, who_row_is_separator, + NULL, NULL); + + gtk_tree_sortable_set_sort_column_id (sortable, + COL_WHO_NAME, + GTK_SORT_ASCENDING); + gtk_tree_sortable_set_sort_func (sortable, + COL_WHO_NAME, sort_by_name, + NULL, NULL); + + /* set up signals */ + g_signal_connect (selection, "changed", + G_CALLBACK (log_window_who_changed_cb), window); + + g_object_unref (store); } static void @@ -984,12 +1312,9 @@ log_window_chats_accounts_changed_cb (GtkWidget *combobox, EmpathyLogWindow *window) { /* Clear all current messages shown in the textview */ - empathy_chat_view_clear (window->chatview_chats); + empathy_chat_view_clear (window->chatview_events); - log_window_chats_populate (window); - - /* No chat is selected as we just changed the account */ - gtk_widget_set_sensitive (window->calendar_chats, FALSE); + log_window_who_populate (window); } static void @@ -1002,7 +1327,7 @@ log_window_chats_set_selected (EmpathyLogWindow *window) GtkTreePath *path; gboolean ok; - view = GTK_TREE_VIEW (window->treeview_chats); + view = GTK_TREE_VIEW (window->treeview_who); model = gtk_tree_view_get_model (view); selection = gtk_tree_view_get_selection (view); @@ -1017,8 +1342,8 @@ log_window_chats_set_selected (EmpathyLogWindow *window) gboolean this_is_chatroom; gtk_tree_model_get (model, &iter, - COL_CHAT_ACCOUNT, &this_account, - COL_CHAT_TARGET, &this_target, + COL_WHO_ACCOUNT, &this_account, + COL_WHO_TARGET, &this_target, -1); this_chat_id = tpl_entity_get_identifier (this_target); @@ -1044,381 +1369,563 @@ log_window_chats_set_selected (EmpathyLogWindow *window) tp_clear_pointer (&window->selected_chat_id, g_free); } -static gboolean -log_window_chats_get_selected (EmpathyLogWindow *window, - TpAccount **account, - TplEntity **target) +static gint +sort_by_date (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) { - GtkTreeView *view; - GtkTreeModel *model; - GtkTreeSelection *selection; - GtkTreeIter iter; - TplEntity *targ; - TpAccount *acc = NULL; - - view = GTK_TREE_VIEW (window->treeview_chats); - model = gtk_tree_view_get_model (view); - selection = gtk_tree_view_get_selection (view); + GDate *date1, *date2; - if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { - return FALSE; - } + gtk_tree_model_get (model, a, + COL_WHEN_DATE, &date1, + -1); - gtk_tree_model_get (model, &iter, - COL_CHAT_ACCOUNT, &acc, - COL_CHAT_TARGET, &targ, - -1); + gtk_tree_model_get (model, b, + COL_WHEN_DATE, &date2, + -1); - if (account != NULL) { - *account = g_object_ref (acc); - } + return g_date_compare (date1, date2); +} - if (target != NULL) { - *target = g_object_ref (targ); - } +static gboolean +when_row_is_separator (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gchar *when; + gboolean ret; - g_object_unref (acc); - g_object_unref (targ); + gtk_tree_model_get (model, iter, + COL_WHEN_TEXT, &when, + -1); - return TRUE; + ret = g_str_equal (when, "separator"); + g_free (when); + return ret; } static void -log_window_got_messages_for_date_cb (GObject *manager, - GAsyncResult *result, - gpointer user_data) +log_window_when_changed_cb (GtkTreeSelection *selection, + EmpathyLogWindow *window) { - EmpathyLogWindow *window = user_data; - GList *events; - GList *l; - GError *error = NULL; - - if (log_window == NULL) - return; - - if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager), - result, &events, &error)) { - DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting", - error->message); - empathy_chat_view_append_event (window->chatview_find, - "Unable to retrieve messages for the selected date"); - g_error_free (error); - return; - } - - for (l = events; l; l = l->next) { - EmpathyMessage *message = empathy_message_from_tpl_log_event (l->data); - g_object_unref (l->data); - empathy_chat_view_append_message (window->chatview_chats, - message); - g_object_unref (message); - } - g_list_free (events); - - /* Turn back on scrolling */ - empathy_chat_view_scroll (window->chatview_find, TRUE); - - /* Give the search entry main focus */ - gtk_widget_grab_focus (window->entry_chats); +g_print ("log_window_when_changed_cb\n"); + log_window_chats_get_messages (window, FALSE); } - static void -log_window_get_messages_for_date (EmpathyLogWindow *window, - GDate *date) +log_window_when_setup (EmpathyLogWindow *window) { - TpAccount *account; - TplEntity *target; - - if (!log_window_chats_get_selected (window, &account, &target)) { - return; - } + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeSortable *sortable; + GtkTreeViewColumn *column; + GtkListStore *store; + GtkCellRenderer *cell; + + view = GTK_TREE_VIEW (window->treeview_when); + selection = gtk_tree_view_get_selection (view); + + /* new store */ + store = gtk_list_store_new (COL_WHEN_COUNT, + G_TYPE_DATE, /* date */ + G_TYPE_STRING, /* stringified date */ + G_TYPE_STRING); /* icon */ + + model = GTK_TREE_MODEL (store); + sortable = GTK_TREE_SORTABLE (store); + + gtk_tree_view_set_model (view, model); + + /* new column */ + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, _("When")); + + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, cell, FALSE); + gtk_tree_view_column_add_attribute (column, cell, + "icon-name", COL_WHEN_ICON); + + cell = gtk_cell_renderer_text_new (); + g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_column_pack_start (column, cell, TRUE); + gtk_tree_view_column_add_attribute (column, cell, + "text", + COL_WHEN_TEXT); + + gtk_tree_view_append_column (view, column); + + /* set up treeview properties */ + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + gtk_tree_view_set_row_separator_func (view, when_row_is_separator, + NULL, NULL); + gtk_tree_sortable_set_sort_column_id (sortable, + COL_WHEN_DATE, + GTK_SORT_DESCENDING); + gtk_tree_sortable_set_sort_func (sortable, + COL_WHEN_DATE, sort_by_date, + NULL, NULL); + + /* set up signals */ + g_signal_connect (selection, "changed", + G_CALLBACK (log_window_when_changed_cb), + window); - /* Clear all current messages shown in the textview */ - empathy_chat_view_clear (window->chatview_chats); + g_object_unref (store); +} - /* Turn off scrolling temporarily */ - empathy_chat_view_scroll (window->chatview_find, FALSE); +static gboolean +what_row_is_separator (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gint type; - /* Get events */ - tpl_log_manager_get_events_for_date_async (window->log_manager, - account, target, TPL_EVENT_MASK_TEXT, - date, - log_window_got_messages_for_date_cb, - (gpointer) window); + gtk_tree_model_get (model, iter, + COL_WHAT_TYPE, &type, + -1); - g_object_unref (account); - g_object_unref (target); + return (type == -1); } static void -log_manager_got_dates_cb (GObject *manager, - GAsyncResult *result, - gpointer user_data) +log_window_what_changed_cb (GtkTreeSelection *selection, + EmpathyLogWindow *window) { - EmpathyLogWindow *window = user_data; - GList *dates; - GList *l; - guint year_selected; - guint month_selected; - gboolean day_selected = FALSE; - GDate *date = NULL; - GError *error = NULL; - - if (log_window == NULL) - return; - - if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager), - result, &dates, &error)) { - DEBUG ("Unable to retrieve messages' dates: %s. Aborting", - error->message); - empathy_chat_view_append_event (window->chatview_find, - "Unable to retrieve messages' dates"); - return; - } - - for (l = dates; l; l = l->next) { - GDate *d = l->data; - - gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats), - &year_selected, - &month_selected, - NULL); - - month_selected++; - - if (!l->next) { - date = d; - } - - if (g_date_get_year (d) != year_selected || - g_date_get_month (d) != month_selected) { - continue; - } - - DEBUG ("Marking date: %04u-%02u-%02u", g_date_get_year (d), - g_date_get_month (d), g_date_get_day (d)); - - gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats), - g_date_get_day (d)); - - if (l->next) { - continue; - } - - day_selected = TRUE; +g_print ("log_window_what_changed_cb\n"); + /* The dates need to be updated if we're not searching */ + log_window_chats_get_messages (window, window->hits == NULL); +} - gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), - g_date_get_day (d)); - } +static gboolean +log_window_what_collapse_row_cb (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path, + gpointer user_data) +{ + /* Reject collapsing */ + return TRUE; +} - if (!day_selected) { - /* Unselect the day in the calendar */ - gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), 0); - } +struct event +{ + gint type; + EventSubtype subtype; + const gchar *icon; + const gchar *text; +}; - g_signal_handlers_unblock_by_func (window->calendar_chats, - log_window_calendar_chats_day_selected_cb, +static void +log_window_what_setup (EmpathyLogWindow *window) +{ + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeSortable *sortable; + GtkTreeViewColumn *column; + GtkTreeIter iter, parent; + GtkTreeStore *store; + GtkCellRenderer *cell; + guint i; + struct event events [] = { + { TPL_EVENT_MASK_ANY, 0, NULL, _("Anything") }, + { -1, 0, NULL, "separator" }, + { TPL_EVENT_MASK_TEXT, 0, "stock_text_justify", _("Text chats") }, + { TPL_EVENT_MASK_CALL, EVENT_CALL_ALL, "call-start", _("Calls") } + }; + struct event call_events [] = { + { TPL_EVENT_MASK_CALL, EVENT_CALL_INCOMING, "call-start", _("Incoming calls") }, + { TPL_EVENT_MASK_CALL, EVENT_CALL_OUTGOING, "call-start", _("Outgoing calls") }, + { TPL_EVENT_MASK_CALL, EVENT_CALL_MISSED, "call-stop", _("Missed calls") } + }; + + view = GTK_TREE_VIEW (window->treeview_what); + selection = gtk_tree_view_get_selection (view); + + /* new store */ + store = gtk_tree_store_new (COL_WHAT_COUNT, + G_TYPE_INT, /* history type */ + G_TYPE_INT, /* history subtype */ + G_TYPE_STRING, /* stringified history type */ + G_TYPE_STRING, /* icon */ + G_TYPE_BOOLEAN); /* expander (hidden) */ + + model = GTK_TREE_MODEL (store); + sortable = GTK_TREE_SORTABLE (store); + + gtk_tree_view_set_model (view, model); + + /* new column */ + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, _("What")); + + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, cell, FALSE); + gtk_tree_view_column_add_attribute (column, cell, + "icon-name", COL_WHAT_ICON); + + cell = gtk_cell_renderer_text_new (); + g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL); + gtk_tree_view_column_pack_start (column, cell, TRUE); + gtk_tree_view_column_add_attribute (column, cell, + "text", COL_WHAT_TEXT); + + gtk_tree_view_append_column (view, column); + + /* set up treeview properties */ + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + gtk_tree_view_set_show_expanders (view, FALSE); + gtk_tree_view_set_level_indentation (view, 12); + gtk_tree_view_expand_all (view); + gtk_tree_view_set_row_separator_func (view, what_row_is_separator, + NULL, NULL); + + /* populate */ + for (i = 0; i < G_N_ELEMENTS (events); i++) + { + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, + COL_WHAT_TYPE, events[i].type, + COL_WHAT_SUBTYPE, events[i].subtype, + COL_WHAT_TEXT, events[i].text, + COL_WHAT_ICON, events[i].icon, + -1); + } + + gtk_tree_model_iter_nth_child (model, &parent, NULL, 3); + for (i = 0; i < G_N_ELEMENTS (call_events); i++) + { + gtk_tree_store_append (store, &iter, &parent); + gtk_tree_store_set (store, &iter, + COL_WHAT_TYPE, call_events[i].type, + COL_WHAT_SUBTYPE, call_events[i].subtype, + COL_WHAT_TEXT, call_events[i].text, + COL_WHAT_ICON, call_events[i].icon, + -1); + } + + gtk_tree_view_expand_all (view); + + /* select 'Anything' */ + if (gtk_tree_model_get_iter_first (model, &iter)) + gtk_tree_selection_select_iter (selection, &iter); + + /* set up signals */ + g_signal_connect (view, "test-collapse-row", + G_CALLBACK (log_window_what_collapse_row_cb), + NULL); + g_signal_connect (selection, "changed", + G_CALLBACK (log_window_what_changed_cb), window); - if (date != NULL) { - /* Show messages of the most recent date */ - log_window_get_messages_for_date (window, date); - } - - g_list_foreach (dates, (GFunc) g_free, NULL); - g_list_free (dates); + g_object_unref (store); } - static void -log_window_chats_get_messages (EmpathyLogWindow *window, - GDate *date) +log_window_got_messages_for_date_cb (GObject *manager, + GAsyncResult *result, + gpointer user_data) { - TpAccount *account; - TplEntity *target; - guint year_selected; - guint month_selected; - guint day; - - - if (!log_window_chats_get_selected (window, &account, &target)) { - return; - } - - g_signal_handlers_block_by_func (window->calendar_chats, - log_window_calendar_chats_day_selected_cb, - window); - - /* Either use the supplied date or get the last */ - if (date == NULL) { - /* Get a list of dates and show them on the calendar */ - tpl_log_manager_get_dates_async (window->log_manager, - account, target, TPL_EVENT_MASK_TEXT, - log_manager_got_dates_cb, (gpointer) window); - /* signal unblocked at the end of the CB flow */ - } else { - day = g_date_get_day (date); - gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats), - &year_selected, - &month_selected, - NULL); - - month_selected++; - - if (g_date_get_year (date) != year_selected && - g_date_get_month (date) != month_selected) { - day = 0; - } + Ctx *ctx = user_data; + GList *events; + GList *l; + GError *error = NULL; - gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), day); + if (log_window == NULL) + goto out; - g_signal_handlers_unblock_by_func (window->calendar_chats, - log_window_calendar_chats_day_selected_cb, - window); - } + if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager), + result, &events, &error)) + { + DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting", + error->message); + empathy_chat_view_append_event (log_window->chatview_events, + "Unable to retrieve messages for the selected date"); + g_error_free (error); + goto out; + } + + for (l = events; l; l = l->next) + { + TplEvent *event = l->data; + EmpathyMessage *message = empathy_message_from_tpl_log_event (event); + gboolean append = TRUE; + + if (TPL_IS_CALL_EVENT (l->data) + && ctx->event_mask & TPL_EVENT_MASK_CALL) + { + TplCallEvent *call = l->data; + + append = FALSE; + + if (ctx->subtype & EVENT_CALL_ALL) + append = TRUE; + else + { + TplCallEndReason reason = tpl_call_event_get_end_reason (call); + TplEntity *sender = tpl_event_get_sender (event); + TplEntity *receiver = tpl_event_get_receiver (event); + + if (reason == TPL_CALL_END_REASON_NO_ANSWER) + { + if (ctx->subtype & EVENT_CALL_MISSED) + append = TRUE; + } + else if (ctx->subtype & EVENT_CALL_OUTGOING + && tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF) + append = TRUE; + else if (ctx->subtype & EVENT_CALL_INCOMING + && tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF) + append = TRUE; + } + } + + if (append) + empathy_chat_view_append_message (log_window->chatview_events, + message); + + g_object_unref (event); + g_object_unref (message); + } + g_list_free (events); - if (date != NULL) { - /* Show messages of the selected date */ - log_window_get_messages_for_date (window, date); - } + out: + ctx_free (ctx); - g_object_unref (account); - g_object_unref (target); + _tpl_action_chain_continue (log_window->chain); } static void -log_window_calendar_chats_day_selected_cb (GtkWidget *calendar, - EmpathyLogWindow *window) +get_events_for_date (TplActionChain *chain, gpointer user_data) { - guint year; - guint month; - guint day; - GDate *date; - - gtk_calendar_get_date (GTK_CALENDAR (calendar), &year, &month, &day); - if (day == 0) - /* No date selected */ - return; - - /* We need this hear because it appears that the months start from 0 */ - month++; - - date = g_date_new_dmy (day, month, year); + Ctx *ctx = user_data; - DEBUG ("Currently selected date is: %04u-%02u-%02u", year, month, day); - - log_window_chats_get_messages (window, date); - - g_date_free (date); +g_print ("get_events_for_date\n"); + tpl_log_manager_get_events_for_date_async (ctx->window->log_manager, + ctx->account, ctx->entity, ctx->event_mask, + ctx->date, + log_window_got_messages_for_date_cb, + ctx); } static void -log_window_updating_calendar_month_cb (GObject *manager, - GAsyncResult *result, gpointer user_data) +log_window_get_messages_for_date (EmpathyLogWindow *window, + GDate *date) { - EmpathyLogWindow *window = user_data; - GList *dates; - GList *l; - guint year_selected; - guint month_selected; - GError *error = NULL; - - if (log_window == NULL) - return; - - if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager), - result, &dates, &error)) { - DEBUG ("Unable to retrieve messages' dates: %s. Aborting", - error->message); - empathy_chat_view_append_event (window->chatview_find, - "Unable to retrieve messages' dates"); - g_error_free (error); - return; - } - - gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats)); - g_object_get (window->calendar_chats, - "month", &month_selected, - "year", &year_selected, - NULL); - - /* We need this here because it appears that the months start from 0 */ - month_selected++; + TpAccount *account; + TplEntity *target; + TplEventTypeMask event_mask; + EventSubtype subtype; + GDate *anytime, *separator; - for (l = dates; l; l = l->next) { - GDate *date = l->data; + if (!log_window_chats_get_selected (window, + &account, &target, NULL, &event_mask, &subtype)) + return; - if (g_date_get_year (date) == year_selected && - g_date_get_month (date) == month_selected) { - DEBUG ("Marking date: %04u-%02u-%02u", g_date_get_year (date), - g_date_get_month (date), g_date_get_day (date)); - gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats), g_date_get_day (date)); - } - } + anytime = g_date_new_dmy (2, 1, -1); + separator = g_date_new_dmy (1, 1, -1); - g_list_foreach (dates, (GFunc) g_free, NULL); - g_list_free (dates); + /* Get events */ + if (g_date_compare (date, anytime) != 0) + { + Ctx *ctx; + + ctx = ctx_new (window, account, target, date, event_mask, subtype); + _tpl_action_chain_append (window->chain, get_events_for_date, ctx); + } + else + { + GtkTreeView *view = GTK_TREE_VIEW (window->treeview_when); + GtkTreeModel *model = gtk_tree_view_get_model (view); + GtkTreeIter iter; + gboolean next; + GDate *d; + + for (next = gtk_tree_model_get_iter_first (model, &iter); + next; + next = gtk_tree_model_iter_next (model, &iter)) + { + Ctx *ctx; + + gtk_tree_model_get (model, &iter, + COL_WHEN_DATE, &d, + -1); + + if (g_date_compare (d, anytime) == 0 || + g_date_compare (d, separator) == 0) + continue; + + ctx = ctx_new (window, account, target, d, event_mask, subtype); + _tpl_action_chain_append (window->chain, get_events_for_date, ctx); + } + } + + _tpl_action_chain_start (window->chain); - DEBUG ("Currently showing month %d and year %d", month_selected, - year_selected); + g_object_unref (account); + g_object_unref (target); + g_date_free (separator); + g_date_free (anytime); } static void -log_window_calendar_chats_month_changed_cb (GtkWidget *calendar, - EmpathyLogWindow *window) +log_manager_got_dates_cb (GObject *manager, + GAsyncResult *result, + gpointer user_data) { - TpAccount *account; - TplEntity *target; - - gtk_calendar_clear_marks (GTK_CALENDAR (calendar)); - - if (!log_window_chats_get_selected (window, &account, &target)) { - DEBUG ("No chat selected to get dates for..."); - return; - } + Ctx *ctx = user_data; + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkListStore *store; + GtkTreeIter iter; + GList *dates; + GList *l; + GDate *date = NULL; + GError *error = NULL; - /* Get the log object for this contact */ - tpl_log_manager_get_dates_async (window->log_manager, account, target, - TPL_EVENT_MASK_TEXT, - log_window_updating_calendar_month_cb, - (gpointer) window); + g_return_if_fail (log_window != NULL); - g_object_unref (account); - g_object_unref (target); + if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager), + result, &dates, &error)) + { + DEBUG ("Unable to retrieve messages' dates: %s. Aborting", + error->message); + empathy_chat_view_append_event (log_window->chatview_events, + _("Unable to retrieve messages' dates")); + goto out; + } + + view = GTK_TREE_VIEW (log_window->treeview_when); + model = gtk_tree_view_get_model (view); + store = GTK_LIST_STORE (model); + selection = gtk_tree_view_get_selection (view); + + for (l = dates; l != NULL; l = l->next) + { + gchar *text; + + date = l->data; + text = g_strdup_printf ("%02d/%02d/%d", + g_date_get_day (date), + g_date_get_month (date), + g_date_get_year (date)); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_WHEN_DATE, date, + COL_WHEN_TEXT, text, + COL_WHEN_ICON, CALENDAR_ICON, + -1); + + g_free (text); + } + + if (gtk_tree_model_get_iter_first (model, &iter)) + { + gtk_list_store_prepend (store, &iter); + gtk_list_store_set (store, &iter, + COL_WHEN_DATE, g_date_new_dmy (1, 1, -1), + COL_WHEN_TEXT, "separator", + -1); + + gtk_list_store_prepend (store, &iter); + gtk_list_store_set (store, &iter, + COL_WHEN_DATE, g_date_new_dmy (2, 1, -1), + COL_WHEN_TEXT, _("Anytime"), + -1); + } + + /* Show messages of the most recent date */ + if (gtk_tree_model_get_iter_first (model, &iter)) + gtk_tree_selection_select_iter (selection, &iter); + + g_list_free_full (dates, g_free); + out: + ctx_free (ctx); + _tpl_action_chain_continue (log_window->chain); } static void -log_window_entry_chats_changed_cb (GtkWidget *entry, - EmpathyLogWindow *window) +get_dates_for_entity (TplActionChain *chain, gpointer user_data) { - const gchar *str; - - str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats)); - empathy_chat_view_highlight (window->chatview_chats, str, FALSE); - - if (str != NULL) { - empathy_chat_view_find_next (window->chatview_chats, - str, - TRUE, - FALSE); - } + Ctx *ctx = user_data; +g_print ("get_dates_for_entity\n"); + tpl_log_manager_get_dates_async (ctx->window->log_manager, + ctx->account, ctx->entity, ctx->event_mask, + log_manager_got_dates_cb, ctx); } static void -log_window_entry_chats_activate_cb (GtkWidget *entry, - EmpathyLogWindow *window) +log_window_chats_get_messages (EmpathyLogWindow *window, + gboolean force_get_dates) { - const gchar *str; + TpAccount *account; + TplEntity *target; + TplEventTypeMask event_mask; + GtkTreeView *view; + GtkTreeModel *model; + GtkListStore *store; + GtkTreeSelection *selection; + GDate *date; + + if (!log_window_chats_get_selected (window, &account, &target, + &date, &event_mask, NULL)) + return; - str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats)); + view = GTK_TREE_VIEW (window->treeview_when); + selection = gtk_tree_view_get_selection (view); + model = gtk_tree_view_get_model (view); + store = GTK_LIST_STORE (model); - if (str != NULL) { - empathy_chat_view_find_next (window->chatview_chats, - str, - FALSE, - FALSE); - } + /* Clear all current messages shown in the textview */ + empathy_chat_view_clear (window->chatview_events); + + /* If there's a search use the returned hits */ + if (window->hits != NULL) + { + if (force_get_dates) + { + gtk_list_store_clear (store); + populate_dates_from_search_hits (account, target); + } + else + populate_events_from_search_hits (account, target, date); + } + /* Either use the supplied date or get the last */ + else if (force_get_dates || date == NULL) + { + g_signal_handlers_block_by_func (selection, + log_window_when_changed_cb, + window); + + gtk_list_store_clear (store); + + g_signal_handlers_unblock_by_func (selection, + log_window_when_changed_cb, + window); + + /* Get a list of dates and show them on the treeview */ + if (target != NULL) + { + Ctx *ctx = ctx_new (window, account, target, NULL, event_mask, 0); + _tpl_action_chain_append (window->chain, get_dates_for_entity, ctx); + _tpl_action_chain_start (window->chain); + } + else + { + /* FIXME: get dates for all entities ? */ + } + } + else + { + /* Show messages of the selected date */ + log_window_get_messages_for_date (window, date); + } + + tp_clear_object (&account); + tp_clear_object (&target); } typedef struct { @@ -1478,11 +1985,11 @@ log_window_logger_clear_account_cb (TpProxy *proxy, /* Refresh the log viewer so the logs are cleared if the account * has been deleted */ - empathy_chat_view_clear (window->chatview_chats); - log_window_chats_populate (window); + empathy_chat_view_clear (window->chatview_events); + log_window_who_populate (window); /* Re-filter the account chooser so the accounts without logs get greyed out */ - empathy_account_chooser_set_filter (EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats), + empathy_account_chooser_set_filter (EMPATHY_ACCOUNT_CHOOSER (window->account_chooser), empathy_account_chooser_filter_has_logs, NULL); } @@ -1491,7 +1998,7 @@ log_window_clear_logs_chooser_select_account (EmpathyAccountChooser *chooser, EmpathyLogWindow *window) { empathy_account_chooser_set_account (chooser, - empathy_account_chooser_get_account (EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats))); + empathy_account_chooser_get_account (EMPATHY_ACCOUNT_CHOOSER (window->account_chooser))); } static void -- cgit v1.2.3