/* -*- 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.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* Authors: Martyn Russell <martyn@imendio.com>
* Xavier Claessens <xclaesse@gmail.com>
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
#include <telepathy-glib/account-manager.h>
#include <telepathy-logger/log-manager.h>
#include <libempathy/empathy-chatroom-manager.h>
#include <libempathy/empathy-chatroom.h>
#include <libempathy/empathy-message.h>
#include <libempathy/empathy-utils.h>
#include <libempathy/empathy-time.h>
#include "empathy-log-window.h"
#include "empathy-account-chooser.h"
#include "empathy-chat-view.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;
} 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,
EmpathyLogWindow *window);
static void log_window_button_close_clicked_cb (GtkWidget *widget,
EmpathyLogWindow *window);
static void log_window_chats_changed_cb (GtkTreeSelection *selection,
EmpathyLogWindow *window);
static void log_window_chats_populate (EmpathyLogWindow *window);
static void log_window_chats_setup (EmpathyLogWindow *window);
static void log_window_chats_accounts_changed_cb (GtkWidget *combobox,
EmpathyLogWindow *window);
static void log_window_chats_set_selected (EmpathyLogWindow *window);
static gboolean log_window_chats_get_selected (EmpathyLogWindow *window,
TpAccount **account,
TplEntity **target);
static void log_window_chats_get_messages (EmpathyLogWindow *window,
GDate *date_to_show);
static void log_window_calendar_chats_day_selected_cb (GtkWidget *calendar,
EmpathyLogWindow *window);
static void log_window_calendar_chats_month_changed_cb (GtkWidget *calendar,
EmpathyLogWindow *window);
static void log_window_entry_chats_changed_cb (GtkWidget *entry,
EmpathyLogWindow *window);
static void log_window_entry_chats_activate_cb (GtkWidget *entry,
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
};
enum {
COL_CHAT_ICON,
COL_CHAT_NAME,
COL_CHAT_ACCOUNT,
COL_CHAT_TARGET,
COL_CHAT_COUNT
};
static EmpathyLogWindow *log_window = NULL;
static void
account_manager_prepared_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
EmpathyLogWindow *window = user_data;
guint account_num;
GList *accounts;
GError *error = NULL;
if (log_window == NULL)
return;
if (!tp_account_manager_prepare_finish (account_manager, result, &error)) {
DEBUG ("Failed to prepare account manager: %s", error->message);
g_error_free (error);
return;
}
accounts = tp_account_manager_get_valid_accounts (account_manager);
account_num = g_list_length (accounts);
g_list_free (accounts);
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 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);
}
static void
select_account_once_ready (EmpathyLogWindow *self,
TpAccount *account,
const gchar *chat_id,
gboolean is_chatroom)
{
EmpathyAccountChooser *account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->account_chooser_chats);
tp_clear_object (&self->selected_account);
self->selected_account = g_object_ref (account);
g_free (self->selected_chat_id);
self->selected_chat_id = g_strdup (chat_id);
self->selected_is_chatroom = is_chatroom;
if (empathy_account_chooser_is_ready (account_chooser))
account_chooser_ready_cb (account_chooser, self);
else
/* Chat will be selected once the account chooser is ready */
g_signal_connect (account_chooser, "ready",
G_CALLBACK (account_chooser_ready_cb), self);
}
GtkWidget *
empathy_log_window_show (TpAccount *account,
const gchar *chat_id,
gboolean is_chatroom,
GtkWindow *parent)
{
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);
}
return log_window->window;
}
log_window = g_new0 (EmpathyLogWindow, 1);
log_window->log_manager = tpl_log_manager_dup_singleton ();
window = log_window;
filename = empathy_file_lookup ("empathy-log-window.ui",
"libempathy-gtk");
gui = empathy_builder_get_file (filename,
"log_window", &window->window,
"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));
}
gtk_widget_show (window->window);
return window->window;
}
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);
g_free (window);
}
/*
* Search code.
*/
static void
log_window_entry_find_changed_cb (GtkWidget *entry,
EmpathyLogWindow *window)
{
const gchar *str;
gboolean is_sensitive = TRUE;
str = gtk_entry_get_text (GTK_ENTRY (window->entry_find));
is_sensitive &= !EMP_STR_EMPTY (str);
is_sensitive &=
!window->last_find ||
(window->last_find && tp_strdiff (window->last_find, str));
gtk_widget_set_sensitive (window->button_find, is_sensitive);
}
static void
got_events_for_date_cb (GObject *manager,
GAsyncResult *result,
gpointer user_data)
{
EmpathyLogWindow *window = user_data;
GList *messages;
GList *l;
gboolean can_do_previous;
gboolean can_do_next;
GError *error = NULL;
if (log_window == NULL)
return;
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;
}
for (l = messages; l; l = l->next) {
EmpathyMessage *message;
g_assert (TPL_IS_EVENT (l->data));
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);
}
static GDate *
gdate_from_str (const gchar *str)
{
guint u;
guint day, month, year;
if (sscanf (str, "%u", &u) != 1)
return NULL;
day = (u % 100);
month = ((u / 100) % 100);
year = (u / 10000);
if (!g_date_valid_dmy (day, month, year))
return NULL;
return g_date_new_dmy (day, month, year);
}
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);
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);
empathy_chat_view_clear (window->chatview_find);
return;
}
gtk_widget_set_sensitive (window->button_previous, TRUE);
gtk_widget_set_sensitive (window->button_next, TRUE);
gtk_tree_model_get (model, &iter,
COL_FIND_ACCOUNT, &account,
COL_FIND_TARGET, &target,
COL_FIND_DATE, &date,
-1);
/* Clear all current messages shown in the textview */
empathy_chat_view_clear (window->chatview_find);
/* Turn off scrolling temporarily */
empathy_chat_view_scroll (window->chatview_find, FALSE);
/* Get messages */
gdate = gdate_from_str (date);
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);
g_date_free (gdate);
}
g_object_unref (account);
g_object_unref (target);
g_free (date);
}
static void
log_manager_searched_new_cb (GObject *manager,
GAsyncResult *result,
gpointer user_data)
{
GList *hits;
GList *l;
GtkTreeIter iter;
GtkListStore *store = user_data;
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;
}
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 {
}
}
if (hits != NULL) {
tpl_log_manager_search_free (hits);
}
}
static void
log_window_find_populate (EmpathyLogWindow *window,
const gchar *search_criteria)
{
GtkTreeView *view;
GtkTreeModel *model;
GtkListStore *store;
view = GTK_TREE_VIEW (window->treeview_find);
model = gtk_tree_view_get_model (view);
store = GTK_LIST_STORE (model);
empathy_chat_view_clear (window->chatview_find);
gtk_list_store_clear (store);
if (EMP_STR_EMPTY (search_criteria)) {
/* Just clear the search. */
return;
}
tpl_log_manager_search_async (window->log_manager,
search_criteria, TPL_EVENT_MASK_TEXT,
log_manager_searched_new_cb, (gpointer) store);
}
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);
}
static void
start_find_search (EmpathyLogWindow *window)
{
const gchar *str;
str = gtk_entry_get_text (GTK_ENTRY (window->entry_find));
/* Don't find the same crap again */
if (window->last_find && !tp_strdiff (window->last_find, str)) {
return;
}
g_free (window->last_find);
window->last_find = g_strdup (str);
log_window_find_populate (window, str);
}
static void
log_window_button_find_clicked_cb (GtkWidget *widget,
EmpathyLogWindow *window)
{
start_find_search (window);
}
static void
log_window_entry_find_activate_cb (GtkWidget *entry,
EmpathyLogWindow *self)
{
start_find_search (self);
}
static void
log_window_button_next_clicked_cb (GtkWidget *widget,
EmpathyLogWindow *window)
{
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);
}
}
static void
log_window_button_previous_clicked_cb (GtkWidget *widget,
EmpathyLogWindow *window)
{
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);
}
}
static void
log_window_button_close_clicked_cb (GtkWidget *widget,
EmpathyLogWindow *window)
{
gtk_widget_destroy (window->window);
}
/*
* Chats Code
*/
static void
log_window_chats_changed_cb (GtkTreeSelection *selection,
EmpathyLogWindow *window)
{
/* Use last date by default */
gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats));
log_window_chats_get_messages (window, NULL);
}
static void
log_manager_got_entities_cb (GObject *manager,
GAsyncResult *result,
gpointer user_data)
{
EmpathyLogWindow *window = user_data;
EmpathyAccountChooser *account_chooser;
TpAccount *account;
GList *entities;
GList *l;
GtkTreeView *view;
GtkTreeModel *model;
GtkTreeSelection *selection;
GtkListStore *store;
GtkTreeIter iter;
GError *error = NULL;
gboolean select_account = FALSE;
account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats);
account = empathy_account_chooser_get_account (account_chooser);
view = GTK_TREE_VIEW (window->treeview_chats);
if (log_window == NULL)
return;
if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
result, &entities, &error)) {
DEBUG ("%s. Aborting", error->message);
g_error_free (error);
return;
}
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);
for (l = entities; l; l = l->next) {
TplEntity *entity;
entity = TPL_ENTITY (l->data);
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);
if (window->selected_account != NULL &&
!tp_strdiff (tp_proxy_get_object_path (account),
tp_proxy_get_object_path (window->selected_account)))
select_account = TRUE;
/* 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);
/* Unblock signals */
g_signal_handlers_unblock_by_func (selection,
log_window_chats_changed_cb,
window);
/* We display the selected account if we populate the model with chats from
* this account. */
if (select_account)
log_window_chats_set_selected (window);
}
static void
log_window_chats_populate (EmpathyLogWindow *window)
{
EmpathyAccountChooser *account_chooser;
TpAccount *account;
GtkTreeView *view;
GtkTreeModel *model;
GtkTreeSelection *selection;
GtkListStore *store;
account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats);
account = empathy_account_chooser_dup_account (account_chooser);
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);
if (account == NULL) {
gtk_list_store_clear (store);
return;
}
/* Block signals to stop the logs being retrieved prematurely */
g_signal_handlers_block_by_func (selection,
log_window_chats_changed_cb,
window);
gtk_list_store_clear (store);
tpl_log_manager_get_entities_async (window->log_manager, account,
log_manager_got_entities_cb, (gpointer) window);
}
static void
log_window_chats_setup (EmpathyLogWindow *window)
{
GtkTreeView *view;
GtkTreeModel *model;
GtkTreeSelection *selection;
GtkTreeSortable *sortable;
GtkTreeViewColumn *column;
GtkListStore *store;
GtkCellRenderer *cell;
view = GTK_TREE_VIEW (window->treeview_chats);
selection = gtk_tree_view_get_selection (view);
/* 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 */
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_CHAT_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_CHAT_NAME);
gtk_tree_view_append_column (view, column);
/* 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);
/* set up signals */
g_signal_connect (selection, "changed",
G_CALLBACK (log_window_chats_changed_cb),
window);
g_object_unref (store);
}
static void
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);
log_window_chats_populate (window);
}
static void
log_window_chats_set_selected (EmpathyLogWindow *window)
{
GtkTreeView *view;
GtkTreeModel *model;
GtkTreeSelection *selection;
GtkTreeIter iter;
GtkTreePath *path;
gboolean ok;
view = GTK_TREE_VIEW (window->treeview_chats);
model = gtk_tree_view_get_model (view);
selection = gtk_tree_view_get_selection (view);
if (!gtk_tree_model_get_iter_first (model, &iter)) {
return;
}
for (ok = TRUE; ok; ok = gtk_tree_model_iter_next (model, &iter)) {
TpAccount *this_account;
TplEntity *this_target;
const gchar *this_chat_id;
gboolean this_is_chatroom;
gtk_tree_model_get (model, &iter,
COL_CHAT_ACCOUNT, &this_account,
COL_CHAT_TARGET, &this_target,
-1);
this_chat_id = tpl_entity_get_identifier (this_target);
this_is_chatroom = tpl_entity_get_entity_type (this_target) == TPL_ENTITY_ROOM;
if (this_account == window->selected_account &&
!tp_strdiff (this_chat_id, window->selected_chat_id) &&
this_is_chatroom == window->selected_is_chatroom) {
gtk_tree_selection_select_iter (selection, &iter);
path = gtk_tree_model_get_path (model, &iter);
gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 0.5, 0.0);
gtk_tree_path_free (path);
g_object_unref (this_account);
g_object_unref (this_target);
break;
}
g_object_unref (this_account);
g_object_unref (this_target);
}
tp_clear_object (&window->selected_account);
tp_clear_pointer (&window->selected_chat_id, g_free);
}
static gboolean
log_window_chats_get_selected (EmpathyLogWindow *window,
TpAccount **account,
TplEntity **target)
{
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);
if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
return FALSE;
}
gtk_tree_model_get (model, &iter,
COL_CHAT_ACCOUNT, &acc,
COL_CHAT_TARGET, &targ,
-1);
if (account != NULL) {
*account = g_object_ref (acc);
}
if (target != NULL) {
*target = g_object_ref (targ);
}
g_object_unref (acc);
g_object_unref (targ);
return TRUE;
}
static void
log_window_got_messages_for_date_cb (GObject *manager,
GAsyncResult *result,
gpointer user_data)
{
EmpathyLogWindow *window = user_data;
GList *events;
GList *l;
GError *error = NULL;
if (log_window == NULL)
return;
if (!tpl_log_manager_get_events_for_date_finish (TPL_LOG_MANAGER (manager),
result, &events, &error)) {
DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
error->message);
empathy_chat_view_append_event (window->chatview_find,
"Unable to retrieve messages for the selected date");
g_error_free (error);
return;
}
for (l = events; l; l = l->next) {
EmpathyMessage *message = empathy_message_from_tpl_log_event (l->data);
g_object_unref (l->data);
empathy_chat_view_append_message (window->chatview_chats,
message);
g_object_unref (message);
}
g_list_free (events);
/* Turn back on scrolling */
empathy_chat_view_scroll (window->chatview_find, TRUE);
/* Give the search entry main focus */
gtk_widget_grab_focus (window->entry_chats);
}
static void
log_window_get_messages_for_date (EmpathyLogWindow *window,
GDate *date)
{
TpAccount *account;
TplEntity *target;
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);
/* Turn off scrolling temporarily */
empathy_chat_view_scroll (window->chatview_find, FALSE);
/* 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);
g_object_unref (account);
g_object_unref (target);
}
static void
log_manager_got_dates_cb (GObject *manager,
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;
if (log_window == NULL)
return;
if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
result, &dates, &error)) {
DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
error->message);
empathy_chat_view_append_event (window->chatview_find,
"Unable to retrieve messages' dates");
return;
}
for (l = dates; l; l = l->next) {
GDate *d = l->data;
gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats),
&year_selected,
&month_selected,
NULL);
month_selected++;
if (!l->next) {
date = d;
}
if (g_date_get_year (d) != year_selected ||
g_date_get_month (d) != month_selected) {
continue;
}
DEBUG ("Marking date: %04u-%02u-%02u", g_date_get_year (d),
g_date_get_month (d), g_date_get_day (d));
gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats),
g_date_get_day (d));
if (l->next) {
continue;
}
day_selected = TRUE;
gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats),
g_date_get_day (d));
}
if (!day_selected) {
/* Unselect the day in the calendar */
gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), 0);
}
g_signal_handlers_unblock_by_func (window->calendar_chats,
log_window_calendar_chats_day_selected_cb,
window);
if (date != NULL) {
/* Show messages of the most recent date */
log_window_get_messages_for_date (window, date);
}
g_list_foreach (dates, (GFunc) g_free, NULL);
g_list_free (dates);
}
static void
log_window_chats_get_messages (EmpathyLogWindow *window,
GDate *date)
{
TpAccount *account;
TplEntity *target;
guint year_selected;
guint month_selected;
guint day;
if (!log_window_chats_get_selected (window, &account, &target)) {
return;
}
g_signal_handlers_block_by_func (window->calendar_chats,
log_window_calendar_chats_day_selected_cb,
window);
/* Either use the supplied date or get the last */
if (date == NULL) {
/* Get a list of dates and show them on the calendar */
tpl_log_manager_get_dates_async (window->log_manager,
account, target, TPL_EVENT_MASK_TEXT,
log_manager_got_dates_cb, (gpointer) window);
/* signal unblocked at the end of the CB flow */
} else {
day = g_date_get_day (date);
gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats),
&year_selected,
&month_selected,
NULL);
month_selected++;
if (g_date_get_year (date) != year_selected &&
g_date_get_month (date) != month_selected) {
day = 0;
}
gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), day);
g_signal_handlers_unblock_by_func (window->calendar_chats,
log_window_calendar_chats_day_selected_cb,
window);
}
if (date != NULL) {
/* Show messages of the selected date */
log_window_get_messages_for_date (window, date);
}
g_object_unref (account);
g_object_unref (target);
}
static void
log_window_calendar_chats_day_selected_cb (GtkWidget *calendar,
EmpathyLogWindow *window)
{
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);
DEBUG ("Currently selected date is: %04u-%02u-%02u", year, month, day);
log_window_chats_get_messages (window, date);
g_date_free (date);
}
static void
log_window_updating_calendar_month_cb (GObject *manager,
GAsyncResult *result, gpointer user_data)
{
EmpathyLogWindow *window = user_data;
GList *dates;
GList *l;
guint year_selected;
guint month_selected;
GError *error = NULL;
if (log_window == NULL)
return;
if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
result, &dates, &error)) {
DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
error->message);
empathy_chat_view_append_event (window->chatview_find,
"Unable to retrieve messages' dates");
g_error_free (error);
return;
}
gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats));
g_object_get (window->calendar_chats,
"month", &month_selected,
"year", &year_selected,
NULL);
/* We need this here because it appears that the months start from 0 */
month_selected++;
for (l = dates; l; l = l->next) {
GDate *date = l->data;
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));
}
}
g_list_foreach (dates, (GFunc) g_free, NULL);
g_list_free (dates);
DEBUG ("Currently showing month %d and year %d", month_selected,
year_selected);
}
static void
log_window_calendar_chats_month_changed_cb (GtkWidget *calendar,
EmpathyLogWindow *window)
{
TpAccount *account;
TplEntity *target;
gtk_calendar_clear_marks (GTK_CALENDAR (calendar));
if (!log_window_chats_get_selected (window, &account, &target)) {
DEBUG ("No chat selected to get dates for...");
return;
}
/* Get the log object for this contact */
tpl_log_manager_get_dates_async (window->log_manager, account, target,
TPL_EVENT_MASK_TEXT,
log_window_updating_calendar_month_cb,
(gpointer) window);
g_object_unref (account);
g_object_unref (target);
}
static void
log_window_entry_chats_changed_cb (GtkWidget *entry,
EmpathyLogWindow *window)
{
const gchar *str;
str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats));
empathy_chat_view_highlight (window->chatview_chats, str, FALSE);
if (str != NULL) {
empathy_chat_view_find_next (window->chatview_chats,
str,
TRUE,
FALSE);
}
}
static void
log_window_entry_chats_activate_cb (GtkWidget *entry,
EmpathyLogWindow *window)
{
const gchar *str;
str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats));
if (str != NULL) {
empathy_chat_view_find_next (window->chatview_chats,
str,
FALSE,
FALSE);
}
}