diff options
author | Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk> | 2011-04-20 16:32:56 +0800 |
---|---|---|
committer | Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk> | 2011-06-09 19:34:48 +0800 |
commit | 346d5c25eae1e9bab5ab36cd66885d6bfbe0d262 (patch) | |
tree | b80b8c03936ec0eed7ad58043ed011b8054e7c1c | |
parent | 04d3b8f34e0cab394ac143b9323bd3d00a8abee7 (diff) | |
download | gsoc2013-empathy-346d5c25eae1e9bab5ab36cd66885d6bfbe0d262.tar gsoc2013-empathy-346d5c25eae1e9bab5ab36cd66885d6bfbe0d262.tar.gz gsoc2013-empathy-346d5c25eae1e9bab5ab36cd66885d6bfbe0d262.tar.bz2 gsoc2013-empathy-346d5c25eae1e9bab5ab36cd66885d6bfbe0d262.tar.lz gsoc2013-empathy-346d5c25eae1e9bab5ab36cd66885d6bfbe0d262.tar.xz gsoc2013-empathy-346d5c25eae1e9bab5ab36cd66885d6bfbe0d262.tar.zst gsoc2013-empathy-346d5c25eae1e9bab5ab36cd66885d6bfbe0d262.zip |
Complete rewrite of the history window
https://bugzilla.gnome.org/show_bug.cgi?id=619866
Conflicts:
libempathy-gtk/empathy-log-window.c
libempathy-gtk/empathy-log-window.ui
-rw-r--r-- | libempathy-gtk/empathy-log-window.c | 2697 | ||||
-rw-r--r-- | libempathy-gtk/empathy-log-window.h | 12 | ||||
-rw-r--r-- | libempathy-gtk/empathy-log-window.ui | 430 |
3 files changed, 1855 insertions, 1284 deletions
diff --git a/libempathy-gtk/empathy-log-window.c b/libempathy-gtk/empathy-log-window.c index 6dc3b3f5e..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 <martyn@imendio.com> * Xavier Claessens <xclaesse@gmail.com> + * Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk> */ #include "config.h" @@ -30,9 +30,15 @@ #include <glib/gi18n-lib.h> #include <gtk/gtk.h> -#include <telepathy-glib/account-manager.h> -#include <telepathy-logger/log-manager.h> +#include <telepathy-glib/telepathy-glib.h> +#include <telepathy-glib/proxy-subclass.h> +#include <telepathy-logger/telepathy-logger.h> +#include <telepathy-logger/call-event.h> + +#include <extensions/extensions.h> + +#include <libempathy/action-chain-internal.h> #include <libempathy/empathy-chatroom-manager.h> #include <libempathy/empathy-chatroom.h> #include <libempathy/empathy-message.h> @@ -41,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 <libempathy/empathy-debug.h> -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 *vbox_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, - 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, - 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, +static void log_window_search_entry_changed_cb (GtkWidget *entry, EmpathyLogWindow *window); -static void log_window_button_close_clicked_cb (GtkWidget *widget, +static void log_window_search_entry_activate_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, + gboolean force_get_dates); +static void log_window_when_changed_cb (GtkTreeSelection *selection, EmpathyLogWindow *window); -static void log_window_entry_chats_activate_cb (GtkWidget *entry, +static void log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem, EmpathyLogWindow *window); -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 +static void +empathy_account_chooser_filter_has_logs (TpAccount *account, + 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; - if (account_num > 1) { - gtk_widget_show (window->vbox_chats); - gtk_widget_show (window->account_chooser_chats); - } else { - gtk_widget_hide (window->vbox_chats); - gtk_widget_hide (window->account_chooser_chats); - } +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); + + 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 @@ -191,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); @@ -209,742 +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) { - TpAccountManager *account_manager; - GtkBuilder *gui; - gchar *filename; - EmpathyLogWindow *window; - EmpathyThemeManager *theme_mgr; - - 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); - } + GtkTreeView *view; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + TpAccount *account; + TplEntity *target; + EmpathyContact *contact; + gint type; - return log_window->window; - } + g_return_if_fail (window != NULL); - 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, - "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, - "vbox_chats", &window->vbox_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_close", "clicked", log_window_button_close_clicked_cb, - "button_close2", "clicked", log_window_button_close_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, - 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 (); - - gtk_box_pack_start (GTK_BOX (window->vbox_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); - - /* Search List */ - log_window_find_setup (window); - - /* Contacts */ - log_window_chats_setup (window); - log_window_chats_populate (window); - - if (account != NULL && chat_id != NULL) - select_account_once_ready (window, account, chat_id, is_chatroom); - - if (parent != NULL) { - gtk_window_set_transient_for (GTK_WINDOW (window->window), - GTK_WINDOW (parent)); - } + view = GTK_TREE_VIEW (log_window->treeview_who); + selection = gtk_tree_view_get_selection (view); - gtk_widget_show (window->window); + 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); + } - return window->window; -} + g_return_if_fail (type == COL_TYPE_NORMAL); -static void -log_window_destroy_cb (GtkWidget *widget, - 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); + contact = empathy_contact_from_tpl_contact (account, target); + empathy_contact_information_dialog_show (contact, + GTK_WINDOW (window->window)); - g_free (window); + g_object_unref (contact); + g_object_unref (account); + g_object_unref (target); } -/* - * Search code. - */ static void -log_window_entry_find_changed_cb (GtkWidget *entry, - EmpathyLogWindow *window) +toolbutton_chat_clicked (GtkToolButton *toolbutton, + EmpathyLogWindow *window) { - const gchar *str; - gboolean is_sensitive = TRUE; + GtkTreeView *view; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + TpAccount *account; + TplEntity *target; + EmpathyContact *contact; + gint type; + + g_return_if_fail (window != NULL); + + 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); + + g_signal_connect (window->search_entry, "activate", + G_CALLBACK (log_window_search_entry_activate_cb), + window); - /* Turn off scrolling temporarily */ - empathy_chat_view_scroll (window->chatview_find, FALSE); + /* Contacts */ + log_window_who_setup (window); + log_window_what_setup (window); + log_window_when_setup (window); - /* Get messages */ - gdate = gdate_from_str (date); + log_window_who_populate (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); + if (account != NULL && chat_id != NULL) + select_account_once_ready (window, account, chat_id, is_chatroom); - g_date_free (gdate); - } + if (parent != NULL) + gtk_window_set_transient_for (GTK_WINDOW (window->window), + GTK_WINDOW (parent)); - g_object_unref (account); - g_object_unref (target); - g_free (date); -} + 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; - - if (log_window == NULL) - return; + 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 (!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; - 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); - 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; + + 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; + } + + tp_clear_pointer (&log_window->hits, tpl_log_manager_search_free); + log_window->hits = hits; + + populate_entities_from_search_hits (); + + /* FIXME: select old entity if still available, and populate the dates */ + //populate_dates_from_search_hits (NULL, 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); } -static void -log_window_button_close_clicked_cb (GtkWidget *widget, - EmpathyLogWindow *window) +#if 0 +static gboolean +search_results_filter_entities (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) { - gtk_widget_destroy (window->window); -} + TpAccount *account, *selected_account; + gboolean visible = FALSE; -/* - * Chats Code - */ + if (log_window->hits == NULL) + return TRUE; -static void -log_window_chats_changed_cb (GtkTreeSelection *selection, - EmpathyLogWindow *window) -{ - gboolean selected; + if (!log_window_chats_get_selected (log_window, &selected_account, + NULL, NULL, NULL, NULL)) + + gtk_tree_model_get (model, iter, + COL_WHO_ACCOUNT, &account, + -1); - /* 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); + if (selected_account == NULL || + account_equal (account, selected_account)) + visible = TRUE; - /* Use last date by default */ - gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats)); + g_object_unref (account); + g_object_unref (selected_account); - log_window_chats_get_messages (window, NULL); + 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) { - GList *entities; - GList *l; - GtkTreeView *view; - GtkTreeModel *model; - GtkTreeSelection *selection; - GtkListStore *store; - GtkTreeIter iter; - GError *error = NULL; - gboolean select_account = FALSE; - TpAccount *account = user_data; - - 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 (log_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, account, - COL_CHAT_TARGET, entity, - -1); + gtk_list_store_clear (store); - if (log_window->selected_account != NULL && - !tp_strdiff (tp_proxy_get_object_path (account), - tp_proxy_get_object_path (log_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); + + 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; - /* Unblock signals */ - g_signal_handlers_unblock_by_func (selection, - log_window_chats_changed_cb, - log_window); + str = gtk_entry_get_text (GTK_ENTRY (window->search_entry)); - /* We display the selected account if we populate the model with chats from - * this account. */ - if (select_account) - log_window_chats_set_selected (log_window); + /* Don't find the same crap again */ + if (window->last_find && !tp_strdiff (window->last_find, str)) + return; -out: - g_object_unref (account); + 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; +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; - /* Block signals to stop the logs being retrieved prematurely */ - g_signal_handlers_block_by_func (selection, - log_window_chats_changed_cb, - window); + if (log_window == NULL) + goto out; - gtk_list_store_clear (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; + } + + 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); - /* Pass the account reference to the callback */ - tpl_log_manager_get_entities_async (window->log_manager, account, - log_manager_got_entities_cb, account); +out: + _tpl_action_chain_continue (log_window->chain); + ctx_free (ctx); } static void -log_window_chats_setup (EmpathyLogWindow *window) +get_entities_for_account (TplActionChain *chain, gpointer user_data) { - 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); + Ctx *ctx = user_data; - /* 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 */ +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); +} - model = GTK_TREE_MODEL (store); - sortable = GTK_TREE_SORTABLE (store); +static void +log_window_who_populate (EmpathyLogWindow *window) +{ + 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); - gtk_tree_view_set_model (view, model); + 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); - /* new column */ - column = gtk_tree_view_column_new (); + /* Block signals to stop the logs being retrieved prematurely */ + g_signal_handlers_block_by_func (selection, + log_window_who_changed_cb, + window); + + gtk_list_store_clear (store); - 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); + /* Unblock signals */ + g_signal_handlers_unblock_by_func (selection, + log_window_who_changed_cb, + window); - 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); + 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); +} + +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 @@ -952,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 @@ -970,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); @@ -985,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); @@ -1012,43 +1369,257 @@ log_window_chats_set_selected (EmpathyLogWindow *window) tp_clear_pointer (&window->selected_chat_id, g_free); } +static gint +sort_by_date (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + GDate *date1, *date2; + + gtk_tree_model_get (model, a, + COL_WHEN_DATE, &date1, + -1); + + gtk_tree_model_get (model, b, + COL_WHEN_DATE, &date2, + -1); + + return g_date_compare (date1, date2); +} + static gboolean -log_window_chats_get_selected (EmpathyLogWindow *window, - TpAccount **account, - TplEntity **target) +when_row_is_separator (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer 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); + gchar *when; + gboolean ret; - if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { - return FALSE; - } + gtk_tree_model_get (model, iter, + COL_WHEN_TEXT, &when, + -1); - gtk_tree_model_get (model, &iter, - COL_CHAT_ACCOUNT, &acc, - COL_CHAT_TARGET, &targ, - -1); + ret = g_str_equal (when, "separator"); + g_free (when); + return ret; +} - if (account != NULL) { - *account = g_object_ref (acc); - } +static void +log_window_when_changed_cb (GtkTreeSelection *selection, + EmpathyLogWindow *window) +{ +g_print ("log_window_when_changed_cb\n"); + log_window_chats_get_messages (window, FALSE); +} - if (target != NULL) { - *target = g_object_ref (targ); - } +static void +log_window_when_setup (EmpathyLogWindow *window) +{ + 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); + + g_object_unref (store); +} + +static gboolean +what_row_is_separator (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gint type; + + gtk_tree_model_get (model, iter, + COL_WHAT_TYPE, &type, + -1); + + return (type == -1); +} - g_object_unref (acc); - g_object_unref (targ); +static void +log_window_what_changed_cb (GtkTreeSelection *selection, + EmpathyLogWindow *window) +{ +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); +} + +static gboolean +log_window_what_collapse_row_cb (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path, + gpointer user_data) +{ + /* Reject collapsing */ + return TRUE; +} + +struct event +{ + gint type; + EventSubtype subtype; + const gchar *icon; + const gchar *text; +}; + +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); - return TRUE; + g_object_unref (store); } static void @@ -1056,335 +1627,465 @@ log_window_got_messages_for_date_cb (GObject *manager, GAsyncResult *result, gpointer user_data) { - EmpathyLogWindow *window = user_data; + Ctx *ctx = user_data; GList *events; GList *l; GError *error = NULL; if (log_window == NULL) - return; + goto out; if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager), - result, &events, &error)) { + result, &events, &error)) + { DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting", error->message); - empathy_chat_view_append_event (window->chatview_find, + empathy_chat_view_append_event (log_window->chatview_events, "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); + 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); - /* Turn back on scrolling */ - empathy_chat_view_scroll (window->chatview_find, TRUE); + out: + ctx_free (ctx); - /* Give the search entry main focus */ - gtk_widget_grab_focus (window->entry_chats); + _tpl_action_chain_continue (log_window->chain); } +static void +get_events_for_date (TplActionChain *chain, gpointer user_data) +{ + Ctx *ctx = user_data; + +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_get_messages_for_date (EmpathyLogWindow *window, - GDate *date) + GDate *date) { TpAccount *account; TplEntity *target; + TplEventTypeMask event_mask; + EventSubtype subtype; + GDate *anytime, *separator; - if (!log_window_chats_get_selected (window, &account, &target)) { - return; - } - - /* Clear all current messages shown in the textview */ - empathy_chat_view_clear (window->chatview_chats); + if (!log_window_chats_get_selected (window, + &account, &target, NULL, &event_mask, &subtype)) + return; - /* Turn off scrolling temporarily */ - empathy_chat_view_scroll (window->chatview_find, FALSE); + anytime = g_date_new_dmy (2, 1, -1); + separator = g_date_new_dmy (1, 1, -1); /* 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); + 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); g_object_unref (account); g_object_unref (target); + g_date_free (separator); + g_date_free (anytime); } static void log_manager_got_dates_cb (GObject *manager, - GAsyncResult *result, - gpointer user_data) + GAsyncResult *result, + gpointer user_data) { - EmpathyLogWindow *window = user_data; - GList *dates; - GList *l; - guint year_selected; - guint month_selected; - gboolean day_selected = FALSE; - GDate *date = NULL; - GError *error = NULL; + Ctx *ctx = user_data; + GtkTreeView *view; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkListStore *store; + GtkTreeIter iter; + GList *dates; + GList *l; + GDate *date = NULL; + GError *error = NULL; - if (log_window == NULL) - return; + g_return_if_fail (log_window != NULL); 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; + 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); +} - gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), - g_date_get_day (d)); - } +static void +get_dates_for_entity (TplActionChain *chain, gpointer user_data) +{ + 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); +} - if (!day_selected) { - /* Unselect the day in the calendar */ - gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), 0); - } +static void +log_window_chats_get_messages (EmpathyLogWindow *window, + gboolean force_get_dates) +{ + 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; - g_signal_handlers_unblock_by_func (window->calendar_chats, - log_window_calendar_chats_day_selected_cb, - window); + 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 (date != NULL) { - /* Show messages of the most recent date */ + /* 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); - } + } - g_list_foreach (dates, (GFunc) g_free, NULL); - g_list_free (dates); + tp_clear_object (&account); + tp_clear_object (&target); } +typedef struct { + EmpathyAccountChooserFilterResultCallback callback; + gpointer user_data; +} FilterCallbackData; static void -log_window_chats_get_messages (EmpathyLogWindow *window, - GDate *date) +got_entities (GObject *manager, + GAsyncResult *result, + gpointer user_data) { - TpAccount *account; - TplEntity *target; - guint year_selected; - guint month_selected; - guint day; - + FilterCallbackData *data = user_data; + GList *entities; + GError *error; - 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 */ + if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager), result, &entities, &error)) { + DEBUG ("Could not get entities: %s", error->message); + g_error_free (error); + data->callback (FALSE, data->user_data); } else { - day = g_date_get_day (date); - gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats), - &year_selected, - &month_selected, - NULL); + data->callback (entities != NULL, data->user_data); - month_selected++; + g_list_free_full (entities, g_object_unref); + } - if (g_date_get_year (date) != year_selected && - g_date_get_month (date) != month_selected) { - day = 0; - } + g_slice_free (FilterCallbackData, data); +} - gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), day); +static void +empathy_account_chooser_filter_has_logs (TpAccount *account, + EmpathyAccountChooserFilterResultCallback callback, + gpointer callback_data, + gpointer user_data) +{ + TplLogManager *manager = tpl_log_manager_dup_singleton (); + FilterCallbackData *cb_data = g_slice_new0 (FilterCallbackData); - g_signal_handlers_unblock_by_func (window->calendar_chats, - log_window_calendar_chats_day_selected_cb, - window); - } + cb_data->callback = callback; + cb_data->user_data = callback_data; - if (date != NULL) { - /* Show messages of the selected date */ - log_window_get_messages_for_date (window, date); - } + tpl_log_manager_get_entities_async (manager, account, got_entities, cb_data); - g_object_unref (account); - g_object_unref (target); + g_object_unref (manager); } static void -log_window_calendar_chats_day_selected_cb (GtkWidget *calendar, - EmpathyLogWindow *window) +log_window_logger_clear_account_cb (TpProxy *proxy, + const GError *error, + gpointer user_data, + GObject *weak_object) { - 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); + EmpathyLogWindow *window = user_data; - DEBUG ("Currently selected date is: %04u-%02u-%02u", year, month, day); + if (error != NULL) + g_warning ("Error when clearing logs: %s", error->message); - log_window_chats_get_messages (window, date); + /* Refresh the log viewer so the logs are cleared if the account + * has been deleted */ + empathy_chat_view_clear (window->chatview_events); + log_window_who_populate (window); - g_date_free (date); + /* Re-filter the account chooser so the accounts without logs get greyed out */ + empathy_account_chooser_set_filter (EMPATHY_ACCOUNT_CHOOSER (window->account_chooser), + empathy_account_chooser_filter_has_logs, NULL); } static void -log_window_updating_calendar_month_cb (GObject *manager, - GAsyncResult *result, gpointer user_data) +log_window_clear_logs_chooser_select_account (EmpathyAccountChooser *chooser, + EmpathyLogWindow *window) { - EmpathyLogWindow *window = user_data; - GList *dates; - GList *l; - guint year_selected; - guint month_selected; - GError *error = NULL; + empathy_account_chooser_set_account (chooser, + empathy_account_chooser_get_account (EMPATHY_ACCOUNT_CHOOSER (window->account_chooser))); +} - if (log_window == NULL) - return; +static void +log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem, + EmpathyLogWindow *window) +{ + GtkWidget *dialog, *content_area, *hbox, *label; + EmpathyAccountChooser *account_chooser; + gint response_id; + TpDBusDaemon *bus; + TpProxy *logger; + GError *error = NULL; - 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; - } + account_chooser = (EmpathyAccountChooser *) empathy_account_chooser_new (); + empathy_account_chooser_set_has_all_option (account_chooser, TRUE); + empathy_account_chooser_set_filter (account_chooser, empathy_account_chooser_filter_has_logs, NULL); - gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats)); - g_object_get (window->calendar_chats, - "month", &month_selected, - "year", &year_selected, - NULL); + /* Select the same account as in the history window */ + if (empathy_account_chooser_is_ready (account_chooser)) + log_window_clear_logs_chooser_select_account (account_chooser, window); + else + g_signal_connect (account_chooser, "ready", + G_CALLBACK (log_window_clear_logs_chooser_select_account), window); - /* We need this here because it appears that the months start from 0 */ - month_selected++; + dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window->window), + GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, + GTK_BUTTONS_NONE, + _("Are you sure you want to delete all logs of previous conversations?")); - for (l = dates; l; l = l->next) { - GDate *date = l->data; + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("Clear All"), GTK_RESPONSE_APPLY, + NULL); - 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)); - } - } + content_area = gtk_message_dialog_get_message_area ( + GTK_MESSAGE_DIALOG (dialog)); - g_list_foreach (dates, (GFunc) g_free, NULL); - g_list_free (dates); + hbox = gtk_hbox_new (FALSE, 6); + label = gtk_label_new (_("Delete from:")); + gtk_box_pack_start (GTK_BOX (hbox), label, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (account_chooser), + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (content_area), hbox, + FALSE, FALSE, 0); - DEBUG ("Currently showing month %d and year %d", month_selected, - year_selected); -} + gtk_widget_show_all (hbox); -static void -log_window_calendar_chats_month_changed_cb (GtkWidget *calendar, - EmpathyLogWindow *window) -{ - TpAccount *account; - TplEntity *target; + response_id = gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_calendar_clear_marks (GTK_CALENDAR (calendar)); + if (response_id != GTK_RESPONSE_APPLY) + goto out; - if (!log_window_chats_get_selected (window, &account, &target)) { - DEBUG ("No chat selected to get dates for..."); - return; + bus = tp_dbus_daemon_dup (&error); + if (error != NULL) { + g_warning ("Could not delete logs: %s", error->message); + g_error_free (error); + goto out; } - /* 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); + logger = g_object_new (TP_TYPE_PROXY, + "bus-name", "org.freedesktop.Telepathy.Logger", + "object-path", "/org/freedesktop/Telepathy/Logger", + "dbus-daemon", bus, + NULL); + g_object_unref (bus); - g_object_unref (account); - g_object_unref (target); -} + tp_proxy_add_interface_by_id (logger, EMP_IFACE_QUARK_LOGGER); -static void -log_window_entry_chats_changed_cb (GtkWidget *entry, - EmpathyLogWindow *window) -{ - const gchar *str; + if (empathy_account_chooser_has_all_selected (account_chooser)) { + DEBUG ("Deleting logs for all the accounts"); - 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); - } -} - -static void -log_window_entry_chats_activate_cb (GtkWidget *entry, - EmpathyLogWindow *window) -{ - const gchar *str; + emp_cli_logger_call_clear (logger, -1, + log_window_logger_clear_account_cb, + window, NULL, G_OBJECT (window->window)); + } else { + TpAccount *account = empathy_account_chooser_get_account (account_chooser); - str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats)); + DEBUG ("Deleting logs for %s", tp_proxy_get_object_path (account)); - if (str != NULL) { - empathy_chat_view_find_next (window->chatview_chats, - str, - FALSE, - FALSE); + emp_cli_logger_call_clear_account (logger, -1, + tp_proxy_get_object_path (account), + log_window_logger_clear_account_cb, + window, NULL, G_OBJECT (window->window)); } + + g_object_unref (logger); + out: + gtk_widget_destroy (dialog); } diff --git a/libempathy-gtk/empathy-log-window.h b/libempathy-gtk/empathy-log-window.h index def0d846b..373f48c36 100644 --- a/libempathy-gtk/empathy-log-window.h +++ b/libempathy-gtk/empathy-log-window.h @@ -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 <martyn@imendio.com> * Xavier Claessens <xclaesse@gmail.com> + * Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk> */ #ifndef __EMPATHY_LOG_WINDOW_H__ @@ -29,10 +29,10 @@ G_BEGIN_DECLS -GtkWidget * empathy_log_window_show (TpAccount *account, - const gchar *chat_id, - gboolean chatroom, - GtkWindow *parent); +GtkWidget * empathy_log_window_show (TpAccount *account, + const gchar *chat_id, + gboolean chatroom, + GtkWindow *parent); G_END_DECLS diff --git a/libempathy-gtk/empathy-log-window.ui b/libempathy-gtk/empathy-log-window.ui index a0cbcdcfa..5acca02f5 100644 --- a/libempathy-gtk/empathy-log-window.ui +++ b/libempathy-gtk/empathy-log-window.ui @@ -1,266 +1,183 @@ <?xml version="1.0"?> <interface> - <!-- interface-requires gtk+ 2.12 --> - <!-- interface-naming-policy toplevel-contextual --> + <requires lib="gtk+" version="2.16"/> + <!-- interface-naming-policy project-wide --> <object class="GtkWindow" id="log_window"> - <property name="title" translatable="yes">Previous Conversations</property> - <property name="role">log</property> - <property name="default_width">640</property> - <property name="default_height">450</property> - <property name="icon_name">document-open-recent</property> + <property name="title" translatable="yes">History</property> + <property name="default_width">800</property> + <property name="default_height">600</property> <child> - <object class="GtkNotebook" id="notebook"> + <object class="GtkVBox" id="vbox1"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="border_width">2</property> + <property name="orientation">vertical</property> <child> - <object class="GtkVBox" id="vbox192"> + <object class="GtkMenuBar" id="menubar1"> <property name="visible">True</property> - <property name="border_width">12</property> - <property name="spacing">6</property> <child> - <object class="GtkHBox" id="hbox144"> + <object class="GtkMenuItem" id="menuitem1"> <property name="visible">True</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="label628"> - <property name="visible">True</property> - <property name="label" translatable="yes" comments="Searching *for* something">_For:</property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">entry_find</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry_find"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="has_focus">True</property> - <property name="activates_default">True</property> - </object> - <packing> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButton" id="button_find"> - <property name="label">gtk-find</property> + <property name="label" translatable="yes">_File</property> + <property name="use_underline">True</property> + <child type="submenu"> + <object class="GtkMenu" id="menu1"> <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="has_default">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - <property name="focus_on_click">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkVPaned" id="vpaned1"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="position">120</property> - <child> - <object class="GtkScrolledWindow" id="scrolledwindow14"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">never</property> - <property name="vscrollbar_policy">automatic</property> - <property name="shadow_type">in</property> <child> - <object class="GtkTreeView" id="treeview_find"> + <object class="GtkImageMenuItem" id="imagemenuitem_quit"> + <property name="label">gtk-quit</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="enable_search">False</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> </object> </child> </object> - <packing> - <property name="resize">False</property> - <property name="shrink">True</property> - </packing> </child> - <child> - <object class="GtkVBox" id="vbox215"> + </object> + </child> + <child> + <object class="GtkMenuItem" id="menuitem2"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Edit</property> + <property name="use_underline">True</property> + <child type="submenu"> + <object class="GtkMenu" id="menu2"> <property name="visible">True</property> - <property name="spacing">6</property> - <child> - <object class="GtkScrolledWindow" id="scrolledwindow_find"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">never</property> - <property name="shadow_type">in</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="position">0</property> - </packing> - </child> <child> - <object class="GtkHBox" id="hbox171"> + <object class="GtkImageMenuItem" id="imagemenuitem_delete"> + <property name="label">Delete All History...</property> <property name="visible">True</property> - <property name="spacing">12</property> - <child> - <placeholder/> - </child> - <child> - <object class="GtkButton" id="button_next"> - <property name="label" translatable="yes">Find Next</property> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="image">image1</property> - <property name="focus_on_click">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="pack_type">end</property> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkButton" id="button_previous"> - <property name="label" translatable="yes">Find Previous</property> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="image">image2</property> - <property name="focus_on_click">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="pack_type">end</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButton" id="button_close"> - <property name="label">gtk-close</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - <property name="focus_on_click">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> + <property name="image">image1</property> + <property name="use_stock">False</property> </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> </child> </object> - <packing> - <property name="resize">True</property> - <property name="shrink">True</property> - </packing> </child> </object> - <packing> - <property name="position">1</property> - </packing> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> </child> </object> - </child> - <child type="tab"> - <object class="GtkLabel" id="label595"> - <property name="visible">True</property> - <property name="label" translatable="yes" comments="Tab Label">Search</property> - </object> <packing> - <property name="tab_fill">False</property> + <property name="expand">False</property> + <property name="position">0</property> </packing> </child> <child> - <object class="GtkTable" id="table7"> + <object class="GtkToolbar" id="toolbar1"> <property name="visible">True</property> - <property name="border_width">12</property> - <property name="n_rows">3</property> - <property name="n_columns">2</property> - <property name="column_spacing">6</property> - <property name="row_spacing">6</property> + <property name="toolbar_style">both</property> + <child> + <object class="GtkToolButton" id="toolbutton_profile"> + <property name="visible">True</property> + <property name="label" translatable="yes">Profile</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-dialog-info</property> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + <child> + <object class="GtkToolButton" id="toolbutton_chat"> + <property name="visible">True</property> + <property name="label" translatable="yes">Chat</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-edit</property> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + <child> + <object class="GtkToolButton" id="toolbutton_call"> + <property name="visible">True</property> + <property name="label" translatable="yes">Call</property> + <property name="use_underline">True</property> + <property name="icon_name">audio-input-microphone</property> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + <child> + <object class="GtkToolButton" id="toolbutton_video"> + <property name="visible">True</property> + <property name="label" translatable="yes">Video</property> + <property name="use_underline">True</property> + <property name="icon_name">camera-video</property> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + <child> + <object class="GtkSeparatorToolItem" id="toolbutton_sep1"> + <property name="visible">True</property> + <property name="draw">False</property> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> <child> - <object class="GtkVBox" id="vbox_chats"> + <object class="GtkToolItem" id="toolbutton_accounts"> <property name="visible">True</property> - <property name="spacing">6</property> <child> <placeholder/> </child> </object> <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> + <property name="expand">False</property> + <property name="homogeneous">True</property> </packing> </child> <child> - <object class="GtkScrolledWindow" id="scrolledwindow_chats"> + <object class="GtkSeparatorToolItem" id="toolbutton_sep2"> + <property name="visible">True</property> + <property name="draw">False</property> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + <child> + <object class="GtkToolItem" id="toolbutton_search"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">never</property> - <property name="shadow_type">in</property> <child> <placeholder/> </child> </object> <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> + <property name="expand">False</property> + <property name="homogeneous">True</property> </packing> </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkVPaned" id="vpaned1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="orientation">vertical</property> <child> - <object class="GtkVBox" id="vbox191"> + <object class="GtkHBox" id="hbox1"> <property name="visible">True</property> - <property name="spacing">6</property> <child> - <object class="GtkScrolledWindow" id="scrolledwindow13"> - <property name="width_request">150</property> + <object class="GtkScrolledWindow" id="scrolledwindow_who"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="hscrollbar_policy">never</property> - <property name="vscrollbar_policy">automatic</property> - <property name="shadow_type">in</property> + <property name="hscrollbar_policy">automatic</property> <child> - <object class="GtkTreeView" id="treeview_chats"> + <object class="GtkTreeView" id="treeview_who"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="headers_visible">False</property> </object> </child> </object> @@ -269,104 +186,61 @@ </packing> </child> <child> - <object class="GtkCalendar" id="calendar_chats"> + <object class="GtkScrolledWindow" id="scrolledwindow_what"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="sensitive">False</property> + <property name="hscrollbar_policy">automatic</property> + <child> + <object class="GtkTreeView" id="treeview_what"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </object> + </child> </object> <packing> - <property name="expand">False</property> - <property name="fill">False</property> <property name="position">1</property> </packing> </child> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox143"> - <property name="visible">True</property> - <property name="spacing">6</property> <child> - <object class="GtkImage" id="image247"> - <property name="visible">True</property> - <property name="stock">gtk-find</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry_chats"> + <object class="GtkScrolledWindow" id="scrolledwindow_when"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="activates_default">True</property> + <property name="hscrollbar_policy">automatic</property> + <child> + <object class="GtkTreeView" id="treeview_when"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </object> + </child> </object> <packing> - <property name="position">1</property> + <property name="position">2</property> </packing> </child> </object> <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> + <property name="resize">False</property> + <property name="shrink">True</property> </packing> </child> <child> - <object class="GtkHBox" id="hbox2"> + <object class="GtkScrolledWindow" id="scrolledwindow_events"> <property name="visible">True</property> - <property name="spacing">12</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">automatic</property> + <property name="vscrollbar_policy">automatic</property> <child> <placeholder/> </child> - <child> - <object class="GtkButton" id="button_close2"> - <property name="label">gtk-close</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="pack_type">end</property> - <property name="position">1</property> - </packing> - </child> </object> <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="y_options"></property> + <property name="resize">True</property> + <property name="shrink">True</property> </packing> </child> - <child> - <placeholder/> - </child> </object> <packing> - <property name="position">1</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="label596"> - <property name="visible">True</property> - <property name="label" translatable="yes" comments="Tab Label">Conversations</property> - </object> - <packing> - <property name="position">1</property> - <property name="tab_fill">False</property> + <property name="position">2</property> </packing> </child> </object> @@ -374,10 +248,6 @@ </object> <object class="GtkImage" id="image1"> <property name="visible">True</property> - <property name="stock">gtk-go-forward</property> - </object> - <object class="GtkImage" id="image2"> - <property name="visible">True</property> - <property name="stock">gtk-go-back</property> + <property name="stock">gtk-missing-image</property> </object> </interface> |