aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac37
-rw-r--r--libempathy-gtk/empathy-account-chooser.c67
-rw-r--r--libempathy-gtk/empathy-account-chooser.h5
-rw-r--r--libempathy-gtk/empathy-images.h4
-rw-r--r--libempathy-gtk/empathy-log-window.c3792
-rw-r--r--libempathy-gtk/empathy-log-window.h12
-rw-r--r--libempathy-gtk/empathy-log-window.ui514
-rw-r--r--libempathy/Makefile.am2
-rw-r--r--libempathy/action-chain-internal.h51
-rw-r--r--libempathy/action-chain.c192
-rw-r--r--libempathy/empathy-contact.c96
-rw-r--r--libempathy/empathy-message.c42
-rw-r--r--po/POTFILES.in1
13 files changed, 3403 insertions, 1412 deletions
diff --git a/configure.ac b/configure.ac
index 1e95f2f66..b192e5f6b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,7 +42,7 @@ LIBCANBERRA_GTK_REQUIRED=0.25
LIBNOTIFY_REQUIRED=0.7.0
TELEPATHY_FARSIGHT_REQUIRED=0.0.14
TELEPATHY_GLIB_REQUIRED=0.15.1
-TELEPATHY_LOGGER=0.2.0
+TELEPATHY_LOGGER=0.2.8
# Optional deps
CLUTTER_GTK_REQUIRED=0.90.3
@@ -210,6 +210,38 @@ if test "x$with_call" = "xyes" -a "x$have_farstream" != "xyes"; then
fi
AM_CONDITIONAL(HAVE_CALL, test "x$have_farstream" = "xyes")
+
+# -----------------------------------------------------------
+# Call support in tp-logger
+# -----------------------------------------------------------
+AC_ARG_WITH(call-logs,
+ AC_HELP_STRING([--enable-call-logs=@<:@no/yes/auto@:>@],
+ [build with call logs support]),,
+ [with_call_logs=auto])
+if test "x$with_call_logs" != "xno" ; then
+ SAVE_CFLAGS=$CFLAGS
+ SAVE_CPPFLAGS=$CPPFLAGS
+ CFLAGS="$CFLAGS $EMPATHY_CFLAGS"
+ CPPFLAGS="$CPPFLAGS $EMPATHY_CFLAGS"
+
+ AC_CHECK_HEADER(telepathy-logger/call-event.h,
+ have_call_logs="yes", have_call_logs="no")
+
+ CFLAGS=$SAVE_CFLAGS
+ CPPFLAGS=$SAVE_CPPFLAGS
+
+ if test "x$have_call_logs" = "xyes"; then
+ AC_DEFINE(HAVE_CALL_LOGS, 1, [Define if you have call log support])
+ fi
+else
+ have_call_logs=no
+fi
+
+if test "x$with_call_logs" = "xyes" -a "x$have_call_logs" != "xyes"; then
+ AC_MSG_ERROR([Call logs support requested but telepathy-logger wasn't
+ built with --enable-call])
+fi
+
# -----------------------------------------------------------
# evolution-data-server (about-me)
# -----------------------------------------------------------
@@ -592,7 +624,7 @@ Configure summary:
Spell checking (enchant)....: ${have_enchant}
Display maps (libchamplain).: ${have_libchamplain}
Location awareness (Geoclue): ${have_geoclue}
- Geocode support (Geoclue): ${have_geocode}
+ Geocode support (Geoclue)...: ${have_geocode}
Adium themes (Webkit).......: ${have_webkit}
Meego widgets...............: ${have_meego}
Control center embedding....: ${have_control_center_embedding}
@@ -606,4 +638,5 @@ Configure summary:
Nautilus-sendto plugin......: ${have_nst}
Salut E-D-S support.........: ${with_eds}
Exp. Call channel handler...: ${have_farstream}
+ Exp. Call log support.......: ${have_call_logs}
"
diff --git a/libempathy-gtk/empathy-account-chooser.c b/libempathy-gtk/empathy-account-chooser.c
index 2df4a153a..32b314be1 100644
--- a/libempathy-gtk/empathy-account-chooser.c
+++ b/libempathy-gtk/empathy-account-chooser.c
@@ -312,6 +312,30 @@ empathy_account_chooser_new (void)
return chooser;
}
+gboolean
+empathy_account_chooser_has_all_selected (EmpathyAccountChooser *chooser)
+{
+ EmpathyAccountChooserPriv *priv;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ RowType type;
+
+ g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser), FALSE);
+
+ priv = GET_PRIV (chooser);
+
+ g_return_val_if_fail (priv->has_all_option == TRUE, FALSE);
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser));
+ if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (chooser), &iter)) {
+ return FALSE;
+ }
+
+ gtk_tree_model_get (model, &iter, COL_ACCOUNT_ROW_TYPE, &type, -1);
+
+ return type == ROW_ALL;
+}
+
/**
* empathy_account_chooser_dup_account:
* @chooser: an #EmpathyAccountChooser
@@ -414,6 +438,30 @@ empathy_account_chooser_set_account (EmpathyAccountChooser *chooser,
return data.set;
}
+void
+empathy_account_chooser_set_all (EmpathyAccountChooser *chooser)
+{
+ EmpathyAccountChooserPriv *priv;
+ GtkComboBox *combobox;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser));
+
+ priv = GET_PRIV (chooser);
+
+ g_return_if_fail (priv->has_all_option);
+
+ combobox = GTK_COMBO_BOX (chooser);
+ model = gtk_combo_box_get_model (combobox);
+
+ if (gtk_tree_model_get_iter_first (model, &iter)) {
+ /* 'All accounts' is the first row */
+ gtk_combo_box_set_active_iter (combobox, &iter);
+ priv->account_manually_set = TRUE;
+ }
+}
+
/**
* empathy_account_chooser_get_has_all_option:
* @chooser: an #EmpathyAccountChooser
@@ -488,7 +536,7 @@ empathy_account_chooser_set_has_all_option (EmpathyAccountChooser *chooser,
gtk_list_store_prepend (store, &iter);
gtk_list_store_set (store, &iter,
- COL_ACCOUNT_TEXT, _("All"),
+ COL_ACCOUNT_TEXT, _("All accounts"),
COL_ACCOUNT_ENABLED, TRUE,
COL_ACCOUNT_POINTER, NULL,
COL_ACCOUNT_ROW_TYPE, ROW_ALL,
@@ -697,8 +745,15 @@ account_chooser_find_account_foreach (GtkTreeModel *model,
{
FindAccountData *data = user_data;
TpAccount *account;
+ RowType type;
- gtk_tree_model_get (model, iter, COL_ACCOUNT_POINTER, &account, -1);
+ gtk_tree_model_get (model, iter,
+ COL_ACCOUNT_POINTER, &account,
+ COL_ACCOUNT_ROW_TYPE, &type,
+ -1);
+
+ if (type != ROW_ACCOUNT)
+ return FALSE;
if (account == data->account) {
data->found = TRUE;
@@ -1053,3 +1108,11 @@ empathy_account_chooser_get_account (EmpathyAccountChooser *chooser)
return account;
}
+
+TpAccountManager *
+empathy_account_chooser_get_account_manager (EmpathyAccountChooser *self)
+{
+ EmpathyAccountChooserPriv *priv = GET_PRIV (self);
+
+ return priv->manager;
+}
diff --git a/libempathy-gtk/empathy-account-chooser.h b/libempathy-gtk/empathy-account-chooser.h
index 62854b959..7ed920e9f 100644
--- a/libempathy-gtk/empathy-account-chooser.h
+++ b/libempathy-gtk/empathy-account-chooser.h
@@ -27,7 +27,7 @@
#include <gtk/gtk.h>
-#include <telepathy-glib/account.h>
+#include <telepathy-glib/telepathy-glib.h>
G_BEGIN_DECLS
@@ -81,9 +81,12 @@ TpAccount * empathy_account_chooser_get_account (EmpathyAccountChooser
TpConnection * empathy_account_chooser_get_connection (EmpathyAccountChooser *chooser);
gboolean empathy_account_chooser_set_account (EmpathyAccountChooser *chooser,
TpAccount *account);
+void empathy_account_chooser_set_all (EmpathyAccountChooser *chooser);
+TpAccountManager * empathy_account_chooser_get_account_manager (EmpathyAccountChooser *self);
gboolean empathy_account_chooser_get_has_all_option (EmpathyAccountChooser *chooser);
void empathy_account_chooser_set_has_all_option (EmpathyAccountChooser *chooser,
gboolean has_all_option);
+gboolean empathy_account_chooser_has_all_selected (EmpathyAccountChooser *chooser);
void empathy_account_chooser_set_filter (EmpathyAccountChooser *chooser,
EmpathyAccountChooserFilterFunc filter,
gpointer user_data);
diff --git a/libempathy-gtk/empathy-images.h b/libempathy-gtk/empathy-images.h
index 86f1db641..e2512d495 100644
--- a/libempathy-gtk/empathy-images.h
+++ b/libempathy-gtk/empathy-images.h
@@ -47,6 +47,10 @@ G_BEGIN_DECLS
#define EMPATHY_IMAGE_DOCUMENT_SEND "document-send"
#define EMPATHY_IMAGE_AVATAR_DEFAULT "avatar-default"
+#define EMPATHY_IMAGE_CALL_MISSED "call-start"
+#define EMPATHY_IMAGE_CALL_INCOMING "call-start"
+#define EMPATHY_IMAGE_CALL_OUTGOING "call-stop"
+
G_END_DECLS
#endif /* __EMPATHY_IMAGES_ICONS_H__ */
diff --git a/libempathy-gtk/empathy-log-window.c b/libempathy-gtk/empathy-log-window.c
index 6dc3b3f5e..4ff658834 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,1361 +30,2953 @@
#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>
+#ifdef HAVE_CALL_LOGS
+# include <telepathy-logger/call-event.h>
+#endif
+
+#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>
+#include <libempathy/empathy-request-util.h>
#include <libempathy/empathy-utils.h>
#include <libempathy/empathy-time.h>
#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 *notebook;
+ GtkWidget *spinner;
+
+ GtkWidget *treeview_who;
+ GtkWidget *treeview_what;
+ GtkWidget *treeview_when;
+ GtkWidget *treeview_events;
+
+ GtkTreeStore *store_events;
+
+ GtkWidget *account_chooser;
+
+ gchar *last_find;
+
+ TplActionChain *chain;
+ TplLogManager *log_manager;
+
+ /* Used to cancel logger calls when no longer needed */
+ guint count;
+
+ /* List of owned TplLogSearchHits, free with tpl_log_search_hit_free */
+ GList *hits;
+ guint source;
+
+ /* 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
+static void log_window_destroy_cb (GtkWidget *widget,
+ EmpathyLogWindow *window);
+static void log_window_search_entry_changed_cb (GtkWidget *entry,
+ EmpathyLogWindow *window);
+static void log_window_search_entry_activate_cb (GtkWidget *widget,
+ EmpathyLogWindow *window);
+static void log_window_search_entry_icon_pressed_cb (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos,
+ GdkEvent *event,
+ gpointer user_data);
+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_events_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 void log_window_chats_get_messages (EmpathyLogWindow *window,
+ gboolean force_get_dates);
+static void log_window_when_changed_cb (GtkTreeSelection *selection,
+ EmpathyLogWindow *window);
+static void log_window_delete_menu_clicked_cb (GtkMenuItem *menuitem,
+ EmpathyLogWindow *window);
+
+static void
+empathy_account_chooser_filter_has_logs (TpAccount *account,
+ EmpathyAccountChooserFilterResultCallback callback,
+ gpointer callback_data,
+ gpointer user_data);
+
+enum
+{
+ PAGE_EVENTS,
+ PAGE_SPINNER,
+ PAGE_EMPTY
+};
+
+enum
+{
+ COL_TYPE_ANY,
+ COL_TYPE_SEPARATOR,
+ COL_TYPE_NORMAL
+};
+
+enum
+{
+ COL_WHO_TYPE,
+ COL_WHO_ICON,
+ COL_WHO_NAME,
+ COL_WHO_ACCOUNT,
+ COL_WHO_TARGET,
+ COL_WHO_COUNT
+};
+
+enum
+{
+ COL_WHAT_TYPE,
+ COL_WHAT_SUBTYPE,
+ COL_WHAT_TEXT,
+ COL_WHAT_ICON,
+ COL_WHAT_COUNT
};
-enum {
- COL_CHAT_ICON,
- COL_CHAT_NAME,
- COL_CHAT_ACCOUNT,
- COL_CHAT_TARGET,
- COL_CHAT_COUNT
+enum
+{
+ COL_WHEN_DATE,
+ COL_WHEN_TEXT,
+ COL_WHEN_ICON,
+ COL_WHEN_COUNT
};
+enum
+{
+ COL_EVENTS_TYPE,
+ COL_EVENTS_TS,
+ COL_EVENTS_PRETTY_DATE,
+ COL_EVENTS_ICON,
+ COL_EVENTS_TEXT,
+ COL_EVENTS_ACCOUNT,
+ COL_EVENTS_TARGET,
+ COL_EVENTS_EVENT,
+ COL_EVENTS_COUNT
+};
+
+#define CALENDAR_ICON "stock_calendar"
+
+/* Seconds between two messages to be considered one conversation */
+#define MAX_GAP 30*60
+
+#define WHAT_TYPE_SEPARATOR -1
+
+typedef enum
+{
+ EVENT_CALL_INCOMING = 1 << 0,
+ EVENT_CALL_OUTGOING = 1 << 1,
+ EVENT_CALL_MISSED = 1 << 2,
+ EVENT_CALL_ALL = 1 << 3,
+} EventSubtype;
+
static EmpathyLogWindow *log_window = NULL;
+static gboolean has_element;
+
+#ifndef _date_copy
+#define _date_copy(d) g_date_new_julian (g_date_get_julian (d))
+#endif
+
+typedef struct
+{
+ EmpathyLogWindow *window;
+ TpAccount *account;
+ TplEntity *entity;
+ GDate *date;
+ TplEventTypeMask event_mask;
+ EventSubtype subtype;
+ guint count;
+} Ctx;
+
+static Ctx *
+ctx_new (EmpathyLogWindow *window,
+ TpAccount *account,
+ TplEntity *entity,
+ GDate *date,
+ TplEventTypeMask event_mask,
+ EventSubtype subtype,
+ guint count)
+{
+ 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;
+ ctx->count = count;
+
+ return ctx;
+}
+
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);
- }
+ctx_free (Ctx *ctx)
+{
+ tp_clear_object (&ctx->account);
+ tp_clear_object (&ctx->entity);
+ tp_clear_pointer (&ctx->date, g_date_free);
+
+ g_slice_free (Ctx, ctx);
}
static void
account_chooser_ready_cb (EmpathyAccountChooser *chooser,
- EmpathyLogWindow *window)
+ 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);
+ /* 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), window->selected_account);
}
static void
select_account_once_ready (EmpathyLogWindow *self,
- TpAccount *account,
- const gchar *chat_id,
- gboolean is_chatroom)
+ TpAccount *account,
+ const gchar *chat_id,
+ gboolean is_chatroom)
{
- EmpathyAccountChooser *account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->account_chooser_chats);
+ EmpathyAccountChooser *account_chooser;
- tp_clear_object (&self->selected_account);
- self->selected_account = g_object_ref (account);
+ account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->account_chooser);
- g_free (self->selected_chat_id);
- self->selected_chat_id = g_strdup (chat_id);
+ tp_clear_object (&self->selected_account);
+ self->selected_account = g_object_ref (account);
- self->selected_is_chatroom = is_chatroom;
+ g_free (self->selected_chat_id);
+ self->selected_chat_id = g_strdup (chat_id);
- 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);
-}
+ self->selected_is_chatroom = is_chatroom;
-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;
+ 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);
}
static void
-log_window_destroy_cb (GtkWidget *widget,
- EmpathyLogWindow *window)
+toolbutton_profile_clicked (GtkToolButton *toolbutton,
+ EmpathyLogWindow *window)
{
- g_free (window->last_find);
- g_object_unref (window->log_manager);
- tp_clear_object (&window->selected_account);
- g_free (window->selected_chat_id);
+ GtkTreeView *view;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GList *paths;
+ TpAccount *account;
+ TplEntity *target;
+ EmpathyContact *contact;
+ gint type;
- g_free (window);
+ g_return_if_fail (window != NULL);
+
+ view = GTK_TREE_VIEW (log_window->treeview_who);
+ selection = gtk_tree_view_get_selection (view);
+
+ paths = gtk_tree_selection_get_selected_rows (selection, &model);
+ g_return_if_fail (paths != NULL);
+
+ path = paths->data;
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_ACCOUNT, &account,
+ COL_WHO_TARGET, &target,
+ COL_WHO_TYPE, &type,
+ -1);
+
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+
+ g_return_if_fail (type == COL_TYPE_NORMAL);
+
+ contact = empathy_contact_from_tpl_contact (account, target);
+ empathy_contact_information_dialog_show (contact,
+ GTK_WINDOW (window->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;
+ GtkTreePath *path;
+ GList *paths;
+ TpAccount *account;
+ TplEntity *target;
+ EmpathyContact *contact;
+ gint type;
- str = gtk_entry_get_text (GTK_ENTRY (window->entry_find));
+ g_return_if_fail (window != NULL);
- is_sensitive &= !EMP_STR_EMPTY (str);
- is_sensitive &=
- !window->last_find ||
- (window->last_find && tp_strdiff (window->last_find, str));
+ view = GTK_TREE_VIEW (log_window->treeview_who);
+ selection = gtk_tree_view_get_selection (view);
- gtk_widget_set_sensitive (window->button_find, is_sensitive);
+ paths = gtk_tree_selection_get_selected_rows (selection, &model);
+ g_return_if_fail (paths != NULL);
+
+ path = paths->data;
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_ACCOUNT, &account,
+ COL_WHO_TARGET, &target,
+ COL_WHO_TYPE, &type,
+ -1);
+
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+
+ g_return_if_fail (type == COL_TYPE_NORMAL);
+
+ contact = empathy_contact_from_tpl_contact (account, target);
+ empathy_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)
-{
- 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);
+toolbutton_av_clicked (GtkToolButton *toolbutton,
+ EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GList *paths;
+ TpAccount *account;
+ gchar *contact;
+ gint type;
+ gboolean video;
+
+ g_return_if_fail (window != NULL);
+
+ view = GTK_TREE_VIEW (log_window->treeview_who);
+ selection = gtk_tree_view_get_selection (view);
+
+ paths = gtk_tree_selection_get_selected_rows (selection, &model);
+ g_return_if_fail (paths != NULL);
+
+ path = paths->data;
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_ACCOUNT, &account,
+ COL_WHO_NAME, &contact,
+ COL_WHO_TYPE, &type,
+ -1);
+
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+
+ g_return_if_fail (type == COL_TYPE_NORMAL);
+
+ 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 void
-log_window_find_changed_cb (GtkTreeSelection *selection,
- EmpathyLogWindow *window)
+GtkWidget *
+empathy_log_window_show (TpAccount *account,
+ const gchar *chat_id,
+ gboolean is_chatroom,
+ GtkWindow *parent)
{
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkTreeIter iter;
- TpAccount *account;
- TplEntity *target;
- gchar *date;
- GDate *gdate;
+ EmpathyAccountChooser *account_chooser;
+ GtkBuilder *gui;
+ gchar *filename;
+ EmpathyLogWindow *window;
+ 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,
+ "treeview_events", &window->treeview_events,
+ "notebook", &window->notebook,
+ "spinner", &window->spinner,
+ 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);
+
+ /* 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);
+ empathy_account_chooser_set_all (account_chooser);
+
+ g_signal_connect (window->account_chooser, "changed",
+ G_CALLBACK (log_window_chats_accounts_changed_cb),
+ window);
- /* Get selected information */
- view = GTK_TREE_VIEW (window->treeview_find);
- model = gtk_tree_view_get_model (view);
+ label = gtk_label_new (_("Show"));
- 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);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ window->account_chooser,
+ FALSE, FALSE, 0);
- empathy_chat_view_clear (window->chatview_find);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ label,
+ FALSE, FALSE, 0);
- return;
- }
+ gtk_widget_show_all (vbox);
+ gtk_container_add (GTK_CONTAINER (accounts), vbox);
- gtk_widget_set_sensitive (window->button_previous, TRUE);
- gtk_widget_set_sensitive (window->button_next, TRUE);
+ /* Search entry */
+ vbox = gtk_vbox_new (FALSE, 3);
- gtk_tree_model_get (model, &iter,
- COL_FIND_ACCOUNT, &account,
- COL_FIND_TARGET, &target,
- COL_FIND_DATE, &date,
- -1);
+ window->search_entry = gtk_entry_new ();
+ gtk_entry_set_icon_from_stock (GTK_ENTRY (window->search_entry),
+ GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
+ gtk_entry_set_icon_from_stock (GTK_ENTRY (window->search_entry),
+ GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
- /* Clear all current messages shown in the textview */
- empathy_chat_view_clear (window->chatview_find);
+ label = gtk_label_new (_("Search"));
- /* Turn off scrolling temporarily */
- empathy_chat_view_scroll (window->chatview_find, FALSE);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ window->search_entry,
+ FALSE, FALSE, 0);
- /* Get messages */
- gdate = gdate_from_str (date);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ label,
+ FALSE, FALSE, 0);
- 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);
+ gtk_widget_show_all (vbox);
+ gtk_container_add (GTK_CONTAINER (search), vbox);
- g_date_free (gdate);
- }
+ g_signal_connect (window->search_entry, "changed",
+ G_CALLBACK (log_window_search_entry_changed_cb),
+ window);
- g_object_unref (account);
- g_object_unref (target);
- g_free (date);
-}
+ g_signal_connect (window->search_entry, "activate",
+ G_CALLBACK (log_window_search_entry_activate_cb),
+ window);
+ g_signal_connect (window->search_entry, "icon-press",
+ G_CALLBACK (log_window_search_entry_icon_pressed_cb),
+ window);
-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);
- }
+ /* Contacts */
+ log_window_events_setup (window);
+ log_window_who_setup (window);
+ log_window_what_setup (window);
+ log_window_when_setup (window);
+
+ log_window_who_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_find_populate (EmpathyLogWindow *window,
- const gchar *search_criteria)
+log_window_destroy_cb (GtkWidget *widget,
+ EmpathyLogWindow *window)
{
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkListStore *store;
+ if (window->source != 0)
+ g_source_remove (window->source);
- view = GTK_TREE_VIEW (window->treeview_find);
- model = gtk_tree_view_get_model (view);
- store = GTK_LIST_STORE (model);
+ 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);
- empathy_chat_view_clear (window->chatview_find);
+ g_free (window);
+}
- gtk_list_store_clear (store);
+static gboolean
+account_equal (TpAccount *a,
+ TpAccount *b)
+{
+ return g_str_equal (tp_proxy_get_object_path (a),
+ tp_proxy_get_object_path (b));
+}
- if (EMP_STR_EMPTY (search_criteria)) {
- /* Just clear the search. */
- return;
- }
+static gboolean
+entity_equal (TplEntity *a,
+ TplEntity *b)
+{
+ return g_str_equal (tpl_entity_get_identifier (a),
+ tpl_entity_get_identifier (b));
+}
- tpl_log_manager_search_async (window->log_manager,
- search_criteria, TPL_EVENT_MASK_TEXT,
- log_manager_searched_new_cb, (gpointer) store);
+static gboolean
+is_same_confroom (TplEvent *e1,
+ TplEvent *e2)
+{
+ TplEntity *sender1 = tpl_event_get_sender (e1);
+ TplEntity *receiver1 = tpl_event_get_receiver (e1);
+ TplEntity *sender2 = tpl_event_get_sender (e2);
+ TplEntity *receiver2 = tpl_event_get_receiver (e2);
+ TplEntity *room1, *room2;
+
+ if (tpl_entity_get_entity_type (sender1) == TPL_ENTITY_ROOM)
+ room1 = sender1;
+ else if (tpl_entity_get_entity_type (receiver1) == TPL_ENTITY_ROOM)
+ room1 = receiver1;
+ else
+ return FALSE;
+
+ if (tpl_entity_get_entity_type (sender2) == TPL_ENTITY_ROOM)
+ room2 = sender2;
+ else if (tpl_entity_get_entity_type (receiver2) == TPL_ENTITY_ROOM)
+ room2 = receiver2;
+ else
+ return FALSE;
+
+ return g_str_equal (tpl_entity_get_identifier (room1),
+ tpl_entity_get_identifier (room2));
}
-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 TplEntity *
+event_get_target (TplEvent *event)
+{
+ TplEntity *sender = tpl_event_get_sender (event);
+ TplEntity *receiver = tpl_event_get_receiver (event);
+
+ if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
+ return receiver;
+
+ return sender;
}
-static void
-start_find_search (EmpathyLogWindow *window)
+static gboolean
+model_is_parent (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ TplEvent *event)
{
- const gchar *str;
+ TplEvent *stored_event;
+ TplEntity *target;
+ TpAccount *account;
+ gboolean found = FALSE;
+ GtkTreeIter parent;
+
+ if (gtk_tree_model_iter_parent (model, &parent, iter))
+ return FALSE;
+
+ gtk_tree_model_get (model, iter,
+ COL_EVENTS_ACCOUNT, &account,
+ COL_EVENTS_TARGET, &target,
+ COL_EVENTS_EVENT, &stored_event,
+ -1);
+
+ if (G_OBJECT_TYPE (event) == G_OBJECT_TYPE (stored_event) &&
+ account_equal (account, tpl_event_get_account (event)) &&
+ (entity_equal (target, event_get_target (event)) ||
+ is_same_confroom (event, stored_event)))
+ {
+ GtkTreeIter child;
+ gint64 timestamp;
+
+ gtk_tree_model_iter_nth_child (model, &child, iter,
+ gtk_tree_model_iter_n_children (model, iter) - 1);
+
+ gtk_tree_model_get (model, &child,
+ COL_EVENTS_TS, &timestamp,
+ -1);
+
+ if (ABS (tpl_event_get_timestamp (event) - timestamp) < MAX_GAP)
+ {
+ /* The gap is smaller than 30 min */
+ found = TRUE;
+ }
+ }
+
+ g_object_unref (stored_event);
+ g_object_unref (account);
+ g_object_unref (target);
- str = gtk_entry_get_text (GTK_ENTRY (window->entry_find));
+ return found;
+}
+
+static const gchar *
+get_contact_alias_for_message (EmpathyMessage *message)
+{
+ EmpathyContact *sender, *receiver;
- /* Don't find the same crap again */
- if (window->last_find && !tp_strdiff (window->last_find, str)) {
- return;
- }
+ sender = empathy_message_get_sender (message);
+ receiver = empathy_message_get_receiver (message);
- g_free (window->last_find);
- window->last_find = g_strdup (str);
+ if (empathy_contact_is_user (sender))
+ return empathy_contact_get_alias (receiver);
- log_window_find_populate (window, str);
+ return empathy_contact_get_alias (sender);
}
static void
-log_window_button_find_clicked_cb (GtkWidget *widget,
- EmpathyLogWindow *window)
+get_parent_iter_for_message (TplEvent *event,
+ EmpathyMessage *message,
+ GtkTreeIter *parent)
{
- start_find_search (window);
+ GtkTreeStore *store;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean parent_found = FALSE;
+ gboolean next;
+
+ store = log_window->store_events;
+ model = GTK_TREE_MODEL (store);
+
+ for (next = gtk_tree_model_get_iter_first (model, &iter);
+ next;
+ next = gtk_tree_model_iter_next (model, &iter))
+ {
+ if ((parent_found = model_is_parent (model, &iter, event)))
+ break;
+ }
+
+ if (parent_found)
+ {
+ *parent = iter;
+ }
+ else
+ {
+ GDateTime *date;
+ gchar *body, *pretty_date;
+
+ date = g_date_time_new_from_unix_utc (
+ tpl_event_get_timestamp (event));
+
+ pretty_date = g_date_time_format (date,
+ C_("A date with the time", "%A, %e %B %Y %X"));
+
+ body = g_markup_printf_escaped (_("Chat with %s"),
+ get_contact_alias_for_message (message));
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ COL_EVENTS_TS, tpl_event_get_timestamp (event),
+ COL_EVENTS_PRETTY_DATE, pretty_date,
+ COL_EVENTS_TEXT, body,
+ COL_EVENTS_ICON, "stock_text_justify",
+ COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
+ COL_EVENTS_TARGET, event_get_target (event),
+ COL_EVENTS_EVENT, event,
+ -1);
+
+ *parent = iter;
+
+ g_free (body);
+ g_free (pretty_date);
+ g_date_time_unref (date);
+ }
+}
+
+static const gchar *
+get_icon_for_event (TplEvent *event)
+{
+ const gchar *icon = NULL;
+
+#ifdef HAVE_CALL_LOGS
+ if (TPL_IS_CALL_EVENT (event))
+ {
+ TplCallEvent *call = TPL_CALL_EVENT (event);
+ 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)
+ icon = EMPATHY_IMAGE_CALL_MISSED;
+ else if (tpl_entity_get_entity_type (sender) == TPL_ENTITY_SELF)
+ icon = EMPATHY_IMAGE_CALL_OUTGOING;
+ else if (tpl_entity_get_entity_type (receiver) == TPL_ENTITY_SELF)
+ icon = EMPATHY_IMAGE_CALL_INCOMING;
+ }
+#endif
+
+ return icon;
}
static void
-log_window_entry_find_activate_cb (GtkWidget *entry,
- EmpathyLogWindow *self)
+log_window_append_chat_message (TplEvent *event,
+ EmpathyMessage *message)
{
- start_find_search (self);
+ GtkTreeStore *store = log_window->store_events;
+ GtkTreeIter iter, parent;
+ gchar *pretty_date, *body;
+ GDateTime *date;
+
+ date = g_date_time_new_from_unix_utc (
+ tpl_event_get_timestamp (event));
+
+ pretty_date = g_date_time_format (date, "%X");
+
+ get_parent_iter_for_message (event, message, &parent);
+
+ if (tpl_text_event_get_message_type (TPL_TEXT_EVENT (event))
+ == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION)
+ {
+ /* Translators: this is an emote: '* Danielle waves' */
+ body = g_markup_printf_escaped (_("<i>* %s %s</i>"),
+ tpl_entity_get_alias (tpl_event_get_sender (event)),
+ empathy_message_get_body (message));
+ }
+ else
+ {
+ /* Translators: this is a message: 'Danielle: hello'
+ * The string in bold is the sender's name */
+ body = g_markup_printf_escaped (_("<b>%s:</b> %s"),
+ tpl_entity_get_alias (tpl_event_get_sender (event)),
+ empathy_message_get_body (message));
+ }
+
+ gtk_tree_store_append (store, &iter, &parent);
+ gtk_tree_store_set (store, &iter,
+ COL_EVENTS_TS, tpl_event_get_timestamp (event),
+ COL_EVENTS_PRETTY_DATE, pretty_date,
+ COL_EVENTS_TEXT, body,
+ COL_EVENTS_ICON, get_icon_for_event (event),
+ COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
+ COL_EVENTS_TARGET, event_get_target (event),
+ COL_EVENTS_EVENT, event,
+ -1);
+
+ g_free (body);
+ g_free (pretty_date);
+ g_date_time_unref (date);
}
+#ifdef HAVE_CALL_LOGS
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);
- }
+log_window_append_call (TplEvent *event,
+ EmpathyMessage *message)
+{
+ TplCallEvent *call = TPL_CALL_EVENT (event);
+ GtkTreeStore *store = log_window->store_events;
+ GtkTreeIter iter, child;
+ gchar *pretty_date, *duration, *finished;
+ GDateTime *started_date, *finished_date;
+ GTimeSpan span;
+
+ started_date = g_date_time_new_from_unix_utc (
+ tpl_event_get_timestamp (event));
+
+ pretty_date = g_date_time_format (started_date,
+ C_("A date with the time", "%A, %e %B %Y %X"));
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ COL_EVENTS_TS, tpl_event_get_timestamp (event),
+ COL_EVENTS_PRETTY_DATE, pretty_date,
+ COL_EVENTS_TEXT, empathy_message_get_body (message),
+ COL_EVENTS_ICON, get_icon_for_event (event),
+ COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
+ COL_EVENTS_TARGET, event_get_target (event),
+ COL_EVENTS_EVENT, event,
+ -1);
+
+ if (tpl_call_event_get_end_reason (call) != TPL_CALL_END_REASON_NO_ANSWER)
+ {
+ gchar *body;
+
+ span = tpl_call_event_get_duration (TPL_CALL_EVENT (event));
+ if (span < 60)
+ duration = g_strdup_printf (_("%" G_GINT64_FORMAT " seconds"), span);
+ else
+ duration = g_strdup_printf (_("%" G_GINT64_FORMAT " minutes"),
+ span / 60);
+
+ finished_date = g_date_time_add (started_date, -span);
+ finished = g_date_time_format (finished_date, "%X");
+ g_date_time_unref (finished_date);
+
+ body = g_strdup_printf (_("Call took %s, ended at %s"),
+ duration, finished);
+
+ g_free (duration);
+ g_free (finished);
+
+ gtk_tree_store_append (store, &child, &iter);
+ gtk_tree_store_set (store, &child,
+ COL_EVENTS_TS, tpl_event_get_timestamp (event),
+ COL_EVENTS_TEXT, body,
+ COL_EVENTS_ACCOUNT, tpl_event_get_account (event),
+ COL_EVENTS_TARGET, event_get_target (event),
+ COL_EVENTS_EVENT, event,
+ -1);
+
+ g_free (body);
+ }
+
+ g_free (pretty_date);
+ g_date_time_unref (started_date);
}
+#endif
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);
- }
+log_window_append_message (TplEvent *event,
+ EmpathyMessage *message)
+{
+ if (TPL_IS_TEXT_EVENT (event))
+ log_window_append_chat_message (event, message);
+#ifdef HAVE_CALL_LOGS
+ else if (TPL_IS_CALL_EVENT (event))
+ log_window_append_call (event, message);
+#endif
+ else
+ DEBUG ("Message type not handled");
}
static void
-log_window_button_close_clicked_cb (GtkWidget *widget,
- EmpathyLogWindow *window)
+add_all_accounts_and_entities (GList **accounts,
+ GList **entities)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ view = GTK_TREE_VIEW (log_window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+
+ if (!gtk_tree_model_get_iter_first (model, &iter))
+ return;
+
+ do
+ {
+ TpAccount *account;
+ TplEntity *entity;
+ gint type;
+
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_ACCOUNT, &account,
+ COL_WHO_TARGET, &entity,
+ COL_WHO_TYPE, &type,
+ -1);
+
+ if (type != COL_TYPE_NORMAL)
+ continue;
+
+ if (accounts != NULL)
+ *accounts = g_list_append (*accounts, account);
+
+ if (entities != NULL)
+ *entities = g_list_append (*entities, entity);
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+}
+
+static gboolean
+log_window_get_selected (EmpathyLogWindow *window,
+ GList **accounts,
+ GList **entities,
+ GList **dates,
+ TplEventTypeMask *event_mask,
+ EventSubtype *subtype)
{
- gtk_widget_destroy (window->window);
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ TplEventTypeMask ev = 0;
+ EventSubtype st = 0;
+ GList *paths, *l;
+ gint type;
+
+ view = GTK_TREE_VIEW (window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+
+ paths = gtk_tree_selection_get_selected_rows (selection, NULL);
+ if (paths == NULL)
+ return FALSE;
+
+ if (accounts != NULL)
+ *accounts = NULL;
+ if (entities != NULL)
+ *entities = NULL;
+
+ for (l = paths; l != NULL; l = l->next)
+ {
+ GtkTreePath *path = l->data;
+ TpAccount *account;
+ TplEntity *entity;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_ACCOUNT, &account,
+ COL_WHO_TARGET, &entity,
+ COL_WHO_TYPE, &type,
+ -1);
+
+ if (type == COL_TYPE_ANY)
+ {
+ if (accounts != NULL || entities != NULL)
+ add_all_accounts_and_entities (accounts, entities);
+ break;
+ }
+
+ if (accounts != NULL)
+ *accounts = g_list_append (*accounts, g_object_ref (account));
+
+ if (entities != NULL)
+ *entities = g_list_append (*entities, g_object_ref (entity));
+
+ g_object_unref (account);
+ g_object_unref (entity);
+ }
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+
+ view = GTK_TREE_VIEW (window->treeview_what);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+
+ paths = gtk_tree_selection_get_selected_rows (selection, NULL);
+ for (l = paths; l != NULL; l = l->next)
+ {
+ GtkTreePath *path = l->data;
+ TplEventTypeMask mask;
+ EventSubtype submask;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHAT_TYPE, &mask,
+ COL_WHAT_SUBTYPE, &submask,
+ -1);
+
+ ev |= mask;
+ st |= submask;
+ }
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+
+ view = GTK_TREE_VIEW (window->treeview_when);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+
+ if (dates != NULL)
+ {
+ *dates = NULL;
+
+ paths = gtk_tree_selection_get_selected_rows (selection, NULL);
+ for (l = paths; l != NULL; l = l->next)
+ {
+ GtkTreePath *path = l->data;
+ GDate *date;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHEN_DATE, &date,
+ -1);
+
+ *dates = g_list_append (*dates, date);
+ }
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+ }
+
+ if (event_mask != NULL)
+ *event_mask = ev;
+
+ if (subtype != NULL)
+ *subtype = st;
+
+ return TRUE;
}
-/*
- * Chats Code
- */
+static gboolean
+model_has_entity (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ TplLogSearchHit *hit = data;
+ TplEntity *e;
+ TpAccount *a;
+ gboolean ret = FALSE;
+
+ 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))
+ {
+ ret = has_element = TRUE;
+ }
+
+ tp_clear_object (&e);
+ tp_clear_object (&a);
+
+ return ret;
+}
+
+static gboolean
+model_has_date (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ GDate *date = data;
+ GDate *d;
+
+ gtk_tree_model_get (model, iter,
+ COL_WHEN_DATE, &d,
+ -1);
+
+ if (!g_date_compare (date, d))
+ {
+ has_element = TRUE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
static void
-log_window_chats_changed_cb (GtkTreeSelection *selection,
- EmpathyLogWindow *window)
+get_events_for_date (TplActionChain *chain, gpointer user_data);
+
+static void
+populate_events_from_search_hits (GList *accounts,
+ GList *targets,
+ GList *dates)
{
- gboolean selected;
+ TplEventTypeMask event_mask;
+ EventSubtype subtype;
+ GDate *anytime;
+ GList *l;
+ gboolean is_anytime = FALSE;
- /* 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 (!log_window_get_selected (log_window,
+ NULL, NULL, NULL, &event_mask, &subtype))
+ return;
+
+ anytime = g_date_new_dmy (2, 1, -1);
+ if (g_list_find_custom (dates, anytime, (GCompareFunc) g_date_compare))
+ is_anytime = TRUE;
+
+ for (l = log_window->hits; l != NULL; l = l->next)
+ {
+ TplLogSearchHit *hit = l->data;
+ GList *acc, *targ;
+ gboolean found = FALSE;
+
+ /* Protect against invalid data (corrupt or old log files). */
+ if (hit->account == NULL || hit->target == NULL)
+ continue;
+
+ for (acc = accounts, targ = targets;
+ acc != NULL && targ != NULL && !found;
+ acc = acc->next, targ = targ->next)
+ {
+ TpAccount *account = acc->data;
+ TplEntity *target = targ->data;
+
+ if (account_equal (hit->account, account) &&
+ entity_equal (hit->target, target))
+ found = TRUE;
+ }
+
+ if (!found)
+ continue;
+
+ if (is_anytime ||
+ g_list_find_custom (dates, hit->date, (GCompareFunc) g_date_compare)
+ != NULL)
+ {
+ Ctx *ctx;
- /* Use last date by default */
- gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats));
+ ctx = ctx_new (log_window, hit->account, hit->target, hit->date,
+ event_mask, subtype, log_window->count);
+ _tpl_action_chain_append (log_window->chain,
+ get_events_for_date, ctx);
+ }
+ }
- log_window_chats_get_messages (window, NULL);
+ _tpl_action_chain_start (log_window->chain);
+
+ g_date_free (anytime);
+}
+
+static gchar *
+format_date_for_display (GDate *date)
+{
+ gchar *text;
+ GDate *now = NULL;
+ gint days_elapsed;
+
+ /* g_date_strftime sucks */
+
+ now = g_date_new ();
+ g_date_set_time_t (now, time (NULL));
+
+ days_elapsed = g_date_days_between (date, now);
+
+ if (days_elapsed < 0)
+ {
+ text = NULL;
+ }
+ else if (days_elapsed == 0)
+ {
+ text = g_strdup (_("Today"));
+ }
+ else if (days_elapsed == 1)
+ {
+ text = g_strdup (_("Yesterday"));
+ }
+ else
+ {
+ GDateTime *dt;
+
+ dt = g_date_time_new_utc (g_date_get_year (date),
+ g_date_get_month (date), g_date_get_day (date),
+ 0, 0, 0);
+
+ if (days_elapsed <= 7)
+ text = g_date_time_format (dt, "%A");
+ else
+ text = g_date_time_format (dt,
+ C_("A date such as '23 May 2010', "
+ "%e is the day, %B the month and %Y the year",
+ "%e %B %Y"));
+
+ g_date_time_unref (dt);
+ }
+
+ g_date_free (now);
+
+ return text;
}
static void
-log_manager_got_entities_cb (GObject *manager,
- GAsyncResult *result,
- gpointer user_data)
-{
- 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;
-
- 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 (log_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 (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;
-
- /* 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,
- log_window);
-
- /* 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);
+populate_dates_from_search_hits (GList *accounts,
+ GList *targets)
+{
+ GList *l;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
-out:
- g_object_unref (account);
+ 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);
+ selection = gtk_tree_view_get_selection (view);
+
+ for (l = log_window->hits; l != NULL; l = l->next)
+ {
+ TplLogSearchHit *hit = l->data;
+ GList *acc, *targ;
+ gboolean found = FALSE;
+
+ /* Protect against invalid data (corrupt or old log files). */
+ if (hit->account == NULL || hit->target == NULL)
+ continue;
+
+ for (acc = accounts, targ = targets;
+ acc != NULL && targ != NULL && !found;
+ acc = acc->next, targ = targ->next)
+ {
+ TpAccount *account = acc->data;
+ TplEntity *target = targ->data;
+
+ if (account_equal (hit->account, account) &&
+ entity_equal (hit->target, target))
+ found = TRUE;
+ }
+
+ if (!found)
+ 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 = format_date_for_display (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);
+
+ if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 2))
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
}
static void
-log_window_chats_populate (EmpathyLogWindow *window)
+populate_entities_from_search_hits (void)
{
- EmpathyAccountChooser *account_chooser;
- TpAccount *account;
+ EmpathyAccountChooser *account_chooser;
+ TpAccount *account;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkListStore *store;
+ GList *l;
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkTreeSelection *selection;
- GtkListStore *store;
+ 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 (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);
+ }
+
+ /* FIXME: select old entity if still available */
+}
- account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats);
- account = empathy_account_chooser_dup_account (account_chooser);
+static void
+log_manager_searched_new_cb (GObject *manager,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GList *hits;
+ GtkTreeView *view;
+ GtkTreeSelection *selection;
+ GError *error = NULL;
- 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 (log_window == NULL)
+ return;
- if (account == NULL) {
- gtk_list_store_clear (store);
- return;
- }
+ if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager),
+ result, &hits, &error))
+ {
+ DEBUG ("%s. Aborting", error->message);
+ g_error_free (error);
+ return;
+ }
- /* Block signals to stop the logs being retrieved prematurely */
- g_signal_handlers_block_by_func (selection,
- log_window_chats_changed_cb,
- window);
+ tp_clear_pointer (&log_window->hits, tpl_log_manager_search_free);
+ log_window->hits = hits;
- gtk_list_store_clear (store);
+ populate_entities_from_search_hits ();
- /* Pass the account reference to the callback */
- tpl_log_manager_get_entities_async (window->log_manager, account,
- log_manager_got_entities_cb, account);
+ 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_chats_setup (EmpathyLogWindow *window)
+log_window_find_populate (EmpathyLogWindow *window,
+ const gchar *search_criteria)
{
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkTreeSelection *selection;
- GtkTreeSortable *sortable;
- GtkTreeViewColumn *column;
- GtkListStore *store;
- GtkCellRenderer *cell;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkListStore *store;
- view = GTK_TREE_VIEW (window->treeview_chats);
- selection = gtk_tree_view_get_selection (view);
+ gtk_tree_store_clear (window->store_events);
- /* new store */
- store = gtk_list_store_new (COL_CHAT_COUNT,
- G_TYPE_STRING, /* icon */
- G_TYPE_STRING, /* name */
- TP_TYPE_ACCOUNT, /* account */
- TPL_TYPE_ENTITY); /* target */
+ view = GTK_TREE_VIEW (window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+ store = GTK_LIST_STORE (model);
- model = GTK_TREE_MODEL (store);
- sortable = GTK_TREE_SORTABLE (store);
+ gtk_list_store_clear (store);
- gtk_tree_view_set_model (view, model);
+ 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);
- /* new column */
- column = gtk_tree_view_column_new ();
+ 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);
+ if (EMP_STR_EMPTY (search_criteria))
+ {
+ tp_clear_pointer (&window->hits, tpl_log_manager_search_free);
+ log_window_who_populate (window);
+ return;
+ }
- 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);
+ 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);
+}
- gtk_tree_view_append_column (view, column);
+static gboolean
+start_find_search (EmpathyLogWindow *window)
+{
+ const gchar *str;
- /* 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);
+ str = gtk_entry_get_text (GTK_ENTRY (window->search_entry));
- /* set up signals */
- g_signal_connect (selection, "changed",
- G_CALLBACK (log_window_chats_changed_cb),
- window);
+ /* Don't find the same crap again */
+ if (window->last_find && !tp_strdiff (window->last_find, str))
+ return FALSE;
- g_object_unref (store);
+ g_free (window->last_find);
+ window->last_find = g_strdup (str);
+
+ log_window_find_populate (window, str);
+
+ return FALSE;
}
static void
-log_window_chats_accounts_changed_cb (GtkWidget *combobox,
- EmpathyLogWindow *window)
+log_window_search_entry_changed_cb (GtkWidget *entry,
+ EmpathyLogWindow *window)
+{
+ if (window->source != 0)
+ g_source_remove (window->source);
+ window->source = g_timeout_add (500, (GSourceFunc) start_find_search,
+ window);
+}
+
+static void
+log_window_search_entry_activate_cb (GtkWidget *entry,
+ EmpathyLogWindow *self)
{
- /* Clear all current messages shown in the textview */
- empathy_chat_view_clear (window->chatview_chats);
+ start_find_search (self);
+}
- log_window_chats_populate (window);
+static void
+log_window_search_entry_icon_pressed_cb (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ if (icon_pos != GTK_ENTRY_ICON_SECONDARY)
+ return;
- /* No chat is selected as we just changed the account */
- gtk_widget_set_sensitive (window->calendar_chats, FALSE);
+ gtk_entry_buffer_set_text (gtk_entry_get_buffer (entry),
+ "", -1);
}
static void
-log_window_chats_set_selected (EmpathyLogWindow *window)
+log_window_update_buttons_sensitivity (EmpathyLogWindow *window,
+ GtkTreeModel *model,
+ GtkTreeSelection *selection)
{
- 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);
+ EmpathyContact *contact;
+ EmpathyCapabilities capabilities;
+ TpAccount *account;
+ TplEntity *target;
+ GtkTreeIter iter;
+ GList *paths;
+ GtkTreePath *path;
+ gboolean profile, chat, call, video;
+
+ profile = chat = call = video = FALSE;
+
+ if (!gtk_tree_model_get_iter_first (model, &iter))
+ goto out;
+
+ if (gtk_tree_selection_count_selected_rows (selection) != 1)
+ goto out;
+
+ if (gtk_tree_selection_iter_is_selected (selection, &iter))
+ goto out;
+
+ paths = gtk_tree_selection_get_selected_rows (selection, &model);
+ g_return_if_fail (paths != NULL);
+
+ path = paths->data;
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_ACCOUNT, &account,
+ COL_WHO_TARGET, &target,
+ -1);
+
+ g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
+
+ contact = empathy_contact_from_tpl_contact (account, target);
+
+ g_object_unref (account);
+ g_object_unref (target);
+
+ capabilities = empathy_contact_get_capabilities (contact);
+
+ profile = chat = TRUE;
+ call = capabilities & EMPATHY_CAPABILITIES_AUDIO;
+ video = capabilities & EMPATHY_CAPABILITIES_VIDEO;
+
+ out:
+ gtk_widget_set_sensitive (window->button_profile, profile);
+ gtk_widget_set_sensitive (window->button_chat, chat);
+ gtk_widget_set_sensitive (window->button_call, call);
+ gtk_widget_set_sensitive (window->button_video, video);
}
-static gboolean
-log_window_chats_get_selected (EmpathyLogWindow *window,
- TpAccount **account,
- TplEntity **target)
+static void
+log_window_who_changed_cb (GtkTreeSelection *selection,
+ EmpathyLogWindow *window)
{
- GtkTreeView *view;
- GtkTreeModel *model;
- GtkTreeSelection *selection;
- GtkTreeIter iter;
- TplEntity *targ;
- TpAccount *acc = NULL;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
- view = GTK_TREE_VIEW (window->treeview_chats);
- model = gtk_tree_view_get_model (view);
- selection = gtk_tree_view_get_selection (view);
+ DEBUG ("log_window_who_changed_cb");
- if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
- return FALSE;
- }
+ view = gtk_tree_selection_get_tree_view (selection);
+ model = gtk_tree_view_get_model (view);
- gtk_tree_model_get (model, &iter,
- COL_CHAT_ACCOUNT, &acc,
- COL_CHAT_TARGET, &targ,
- -1);
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ /* If 'Anyone' is selected, everything else should be deselected */
+ if (gtk_tree_selection_iter_is_selected (selection, &iter))
+ {
+ g_signal_handlers_block_by_func (selection,
+ log_window_who_changed_cb,
+ window);
- if (account != NULL) {
- *account = g_object_ref (acc);
- }
+ gtk_tree_selection_unselect_all (selection);
+ gtk_tree_selection_select_iter (selection, &iter);
- if (target != NULL) {
- *target = g_object_ref (targ);
- }
+ g_signal_handlers_unblock_by_func (selection,
+ log_window_who_changed_cb,
+ window);
+ }
+ }
- g_object_unref (acc);
- g_object_unref (targ);
+ log_window_update_buttons_sensitivity (window, model, selection);
- return TRUE;
+ /* The contact changed, so the dates need to be updated */
+ log_window_chats_get_messages (window, TRUE);
}
static void
-log_window_got_messages_for_date_cb (GObject *manager,
+log_manager_got_entities_cb (GObject *manager,
GAsyncResult *result,
gpointer user_data)
{
- EmpathyLogWindow *window = user_data;
- GList *events;
- GList *l;
- GError *error = NULL;
+ Ctx *ctx = user_data;
+ GList *entities;
+ GList *l;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GError *error = NULL;
+ gboolean select_account = FALSE;
if (log_window == NULL)
- return;
+ goto out;
- 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");
+ if (log_window->count != ctx->count)
+ goto out;
+
+ if (!tpl_log_manager_get_entities_finish (TPL_LOG_MANAGER (manager),
+ result, &entities, &error))
+ {
+ DEBUG ("%s. Aborting", error->message);
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);
+ 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);
+ }
+ }
+
+ /* 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);
- /* Turn back on scrolling */
- empathy_chat_view_scroll (window->chatview_find, TRUE);
+out:
+ _tpl_action_chain_continue (log_window->chain);
+ ctx_free (ctx);
+}
+
+static void
+get_entities_for_account (TplActionChain *chain, gpointer user_data)
+{
+ Ctx *ctx = user_data;
- /* Give the search entry main focus */
- gtk_widget_grab_focus (window->entry_chats);
+ tpl_log_manager_get_entities_async (ctx->window->log_manager, ctx->account,
+ log_manager_got_entities_cb, ctx);
}
+static void
+select_first_entity (TplActionChain *chain, gpointer user_data)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ view = GTK_TREE_VIEW (log_window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ _tpl_action_chain_continue (log_window->chain);
+}
static void
-log_window_get_messages_for_date (EmpathyLogWindow *window,
- GDate *date)
+log_window_who_populate (EmpathyLogWindow *window)
{
+ EmpathyAccountChooser *account_chooser;
TpAccount *account;
- TplEntity *target;
+ gboolean all_accounts;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkListStore *store;
+ Ctx *ctx;
+
+ if (window->hits != NULL)
+ {
+ populate_entities_from_search_hits ();
+ return;
+ }
- if (!log_window_chats_get_selected (window, &account, &target)) {
+ 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);
+
+ 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);
+
+ /* 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);
+
+ /* Unblock signals */
+ g_signal_handlers_unblock_by_func (selection,
+ log_window_who_changed_cb,
+ window);
+
+ _tpl_action_chain_clear (window->chain);
+ window->count++;
+
+ if (!all_accounts && account == NULL)
+ {
return;
- }
+ }
+ else if (!all_accounts)
+ {
+ ctx = ctx_new (window, account, NULL, NULL, 0, 0, window->count);
+ _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, window->count);
+ _tpl_action_chain_append (window->chain,
+ get_entities_for_account, ctx);
+ }
+
+ g_list_free (accounts);
+ }
+ _tpl_action_chain_append (window->chain, select_first_entity, NULL);
+ _tpl_action_chain_start (window->chain);
+}
- /* Clear all current messages shown in the textview */
- empathy_chat_view_clear (window->chatview_chats);
+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;
+}
- /* Turn off scrolling temporarily */
- empathy_chat_view_scroll (window->chatview_find, FALSE);
+static gboolean
+who_row_is_separator (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gint type;
- /* Get events */
- tpl_log_manager_get_events_for_date_async (window->log_manager,
- account, target, TPL_EVENT_MASK_TEXT,
- date,
- log_window_got_messages_for_date_cb,
- (gpointer) window);
+ gtk_tree_model_get (model, iter,
+ COL_WHO_TYPE, &type,
+ -1);
- g_object_unref (account);
- g_object_unref (target);
+ return (type == COL_TYPE_SEPARATOR);
}
static void
-log_manager_got_dates_cb (GObject *manager,
- GAsyncResult *result,
- gpointer user_data)
+log_window_events_setup (EmpathyLogWindow *window)
{
- EmpathyLogWindow *window = user_data;
- GList *dates;
- GList *l;
- guint year_selected;
- guint month_selected;
- gboolean day_selected = FALSE;
- GDate *date = NULL;
- GError *error = NULL;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeSortable *sortable;
+ GtkTreeViewColumn *column;
+ GtkTreeStore *store;
+ GtkCellRenderer *cell;
+
+ view = GTK_TREE_VIEW (window->treeview_events);
+ selection = gtk_tree_view_get_selection (view);
+
+ /* new store */
+ window->store_events = store = gtk_tree_store_new (COL_EVENTS_COUNT,
+ G_TYPE_INT, /* type */
+ G_TYPE_INT64, /* timestamp */
+ G_TYPE_STRING, /* stringified date */
+ G_TYPE_STRING, /* icon */
+ G_TYPE_STRING, /* name */
+ TP_TYPE_ACCOUNT, /* account */
+ TPL_TYPE_ENTITY, /* target */
+ TPL_TYPE_EVENT); /* event */
+
+ 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_EVENTS_ICON);
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_add_attribute (column, cell,
+ "markup", COL_EVENTS_TEXT);
+
+ cell = gtk_cell_renderer_text_new ();
+ g_object_set (cell, "xalign", 1.0, NULL);
+ gtk_tree_view_column_pack_end (column, cell, FALSE);
+ gtk_tree_view_column_add_attribute (column, cell,
+ "text", COL_EVENTS_PRETTY_DATE);
+
+ gtk_tree_view_append_column (view, column);
+
+ /* set up treeview properties */
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
+ gtk_tree_view_set_headers_visible (view, FALSE);
+
+ gtk_tree_sortable_set_sort_column_id (sortable,
+ COL_EVENTS_TS,
+ GTK_SORT_ASCENDING);
+
+ gtk_tree_view_set_enable_search (view, FALSE);
+
+ g_object_unref (store);
+}
- if (log_window == NULL)
- return;
+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_MULTIPLE);
+ 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);
+
+ gtk_tree_view_set_search_column (view, COL_WHO_NAME);
+
+ /* set up signals */
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (log_window_who_changed_cb), window);
+
+ g_object_unref (store);
+}
- 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;
- }
+static void
+log_window_chats_accounts_changed_cb (GtkWidget *combobox,
+ EmpathyLogWindow *window)
+{
+ /* Clear all current messages shown in the textview */
+ gtk_tree_store_clear (window->store_events);
- for (l = dates; l; l = l->next) {
- GDate *d = l->data;
+ log_window_who_populate (window);
+}
- gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats),
- &year_selected,
- &month_selected,
- NULL);
+static void
+log_window_chats_set_selected (EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ gboolean next;
+
+ view = GTK_TREE_VIEW (window->treeview_who);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+
+ for (next = gtk_tree_model_get_iter_first (model, &iter);
+ next;
+ next = gtk_tree_model_iter_next (model, &iter))
+ {
+ TpAccount *this_account;
+ TplEntity *this_target;
+ const gchar *this_chat_id;
+ gboolean this_is_chatroom;
+ gint this_type;
+
+ gtk_tree_model_get (model, &iter,
+ COL_WHO_TYPE, &this_type,
+ COL_WHO_ACCOUNT, &this_account,
+ COL_WHO_TARGET, &this_target,
+ -1);
+
+ if (this_type != COL_TYPE_NORMAL)
+ continue;
+
+ 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);
+}
- month_selected++;
+static gint
+sort_by_date (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ GDate *date1, *date2;
- if (!l->next) {
- date = d;
- }
+ gtk_tree_model_get (model, a,
+ COL_WHEN_DATE, &date1,
+ -1);
- if (g_date_get_year (d) != year_selected ||
- g_date_get_month (d) != month_selected) {
- continue;
- }
+ gtk_tree_model_get (model, b,
+ COL_WHEN_DATE, &date2,
+ -1);
+
+ return g_date_compare (date1, date2);
+}
- DEBUG ("Marking date: %04u-%02u-%02u", g_date_get_year (d),
- g_date_get_month (d), g_date_get_day (d));
+static gboolean
+when_row_is_separator (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gchar *when;
+ gboolean ret;
- gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats),
- g_date_get_day (d));
+ gtk_tree_model_get (model, iter,
+ COL_WHEN_TEXT, &when,
+ -1);
- if (l->next) {
- continue;
- }
+ ret = g_str_equal (when, "separator");
+ g_free (when);
+ return ret;
+}
+
+static void
+log_window_when_changed_cb (GtkTreeSelection *selection,
+ EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ DEBUG ("log_window_when_changed_cb");
+
+ view = gtk_tree_selection_get_tree_view (selection);
+ model = gtk_tree_view_get_model (view);
+
+ /* If 'Anytime' is selected, everything else should be deselected */
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ if (gtk_tree_selection_iter_is_selected (selection, &iter))
+ {
+ g_signal_handlers_block_by_func (selection,
+ log_window_when_changed_cb,
+ window);
+
+ gtk_tree_selection_unselect_all (selection);
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ g_signal_handlers_unblock_by_func (selection,
+ log_window_when_changed_cb,
+ window);
+ }
+ }
+
+ log_window_chats_get_messages (window, FALSE);
+}
+
+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_MULTIPLE);
+ 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);
+
+ gtk_tree_view_set_search_column (view, COL_WHEN_TEXT);
+
+ /* 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;
- day_selected = TRUE;
+ gtk_tree_model_get (model, iter,
+ COL_WHAT_TYPE, &type,
+ -1);
+
+ return (type == WHAT_TYPE_SEPARATOR);
+}
- gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats),
- g_date_get_day (d));
- }
+static void
+log_window_what_changed_cb (GtkTreeSelection *selection,
+ EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ DEBUG ("log_window_what_changed_cb");
+
+ view = gtk_tree_selection_get_tree_view (selection);
+ model = gtk_tree_view_get_model (view);
+
+ /* If 'Anything' is selected, everything else should be deselected */
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ if (gtk_tree_selection_iter_is_selected (selection, &iter))
+ {
+ g_signal_handlers_block_by_func (selection,
+ log_window_what_changed_cb,
+ window);
+
+ gtk_tree_selection_unselect_all (selection);
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ g_signal_handlers_unblock_by_func (selection,
+ log_window_what_changed_cb,
+ window);
+ }
+ }
+
+ /* The dates need to be updated if we're not searching */
+ log_window_chats_get_messages (window, window->hits == NULL);
+}
- if (!day_selected) {
- /* Unselect the day in the calendar */
- gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), 0);
- }
+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;
+};
- g_signal_handlers_unblock_by_func (window->calendar_chats,
- log_window_calendar_chats_day_selected_cb,
+static void
+log_window_what_setup (EmpathyLogWindow *window)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeViewColumn *column;
+ GtkTreeIter iter;
+ GtkTreeStore *store;
+ GtkCellRenderer *cell;
+ guint i;
+ struct event events [] = {
+ { TPL_EVENT_MASK_ANY, 0, NULL, _("Anything") },
+ { WHAT_TYPE_SEPARATOR, 0, NULL, "separator" },
+ { TPL_EVENT_MASK_TEXT, 0, "stock_text_justify", _("Text chats") },
+#ifdef HAVE_CALL_LOGS
+ { TPL_EVENT_MASK_CALL, EVENT_CALL_ALL, "call-start", _("Calls") },
+#endif
+ };
+#ifdef HAVE_CALL_LOGS
+ 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") }
+ };
+ GtkTreeIter parent;
+#endif
+
+ 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 */
+
+ model = GTK_TREE_MODEL (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);
+ gtk_tree_view_set_search_column (view, COL_WHAT_TEXT);
+
+ /* set up treeview properties */
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+ 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);
+ }
+
+#ifdef HAVE_CALL_LOGS
+ 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);
+ }
+#endif
+
+ gtk_tree_view_expand_all (view);
+
+ /* select 'Anything' */
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ /* set up signals */
+ g_signal_connect (view, "test-collapse-row",
+ G_CALLBACK (log_window_what_collapse_row_cb),
+ NULL);
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (log_window_what_changed_cb),
window);
- if (date != NULL) {
- /* Show messages of the most recent date */
- log_window_get_messages_for_date (window, date);
- }
+ g_object_unref (store);
+}
- g_list_foreach (dates, (GFunc) g_free, NULL);
- g_list_free (dates);
+static void
+start_spinner (void)
+{
+ gtk_spinner_start (GTK_SPINNER (log_window->spinner));
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook),
+ PAGE_EMPTY);
}
+static gboolean
+show_spinner (gpointer data)
+{
+ gboolean active;
+
+ if (log_window == NULL)
+ return FALSE;
+
+ g_object_get (log_window->spinner, "active", &active, NULL);
+
+ if (active)
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook),
+ PAGE_SPINNER);
+
+ return FALSE;
+}
static void
-log_window_chats_get_messages (EmpathyLogWindow *window,
- GDate *date)
+show_events (TplActionChain *chain,
+ gpointer user_data)
{
- TpAccount *account;
- TplEntity *target;
- guint year_selected;
- guint month_selected;
- guint day;
+ gtk_spinner_stop (GTK_SPINNER (log_window->spinner));
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook),
+ PAGE_EVENTS);
+ _tpl_action_chain_continue (chain);
+}
- if (!log_window_chats_get_selected (window, &account, &target)) {
- return;
- }
+static void
+log_window_got_messages_for_date_cb (GObject *manager,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ Ctx *ctx = user_data;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GList *events;
+ GList *l;
+ GError *error = NULL;
+ gint n;
- g_signal_handlers_block_by_func (window->calendar_chats,
- log_window_calendar_chats_day_selected_cb,
- window);
+ if (log_window == NULL)
+ {
+ ctx_free (ctx);
+ return;
+ }
- /* 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);
+ if (log_window->count != ctx->count)
+ goto out;
- month_selected++;
+ 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);
+ g_error_free (error);
+ goto out;
+ }
+
+ for (l = events; l; l = l->next)
+ {
+ TplEvent *event = l->data;
+ gboolean append = TRUE;
+
+#ifdef HAVE_CALL_LOGS
+ if (TPL_IS_CALL_EVENT (l->data)
+ && ctx->event_mask & TPL_EVENT_MASK_CALL
+ && ctx->event_mask != TPL_EVENT_MASK_ANY)
+ {
+ 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;
+ }
+ }
+ }
+#endif
+
+ if (append)
+ {
+ EmpathyMessage *msg = empathy_message_from_tpl_log_event (event);
+ log_window_append_message (event, msg);
+ tp_clear_object (&msg);
+ }
+
+ g_object_unref (event);
+ }
+ g_list_free (events);
- if (g_date_get_year (date) != year_selected &&
- g_date_get_month (date) != month_selected) {
- day = 0;
- }
+ view = GTK_TREE_VIEW (log_window->treeview_events);
+ model = gtk_tree_view_get_model (view);
+ n = gtk_tree_model_iter_n_children (model, NULL) - 1;
- gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), day);
+ if (n >= 0 && gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
+ {
+ GtkTreePath *path;
- g_signal_handlers_unblock_by_func (window->calendar_chats,
- log_window_calendar_chats_day_selected_cb,
- window);
- }
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_view_scroll_to_cell (view, path, NULL, FALSE, 0, 0);
+ gtk_tree_path_free (path);
+ }
- if (date != NULL) {
- /* Show messages of the selected date */
- log_window_get_messages_for_date (window, date);
- }
+ out:
+ ctx_free (ctx);
- g_object_unref (account);
- g_object_unref (target);
+ _tpl_action_chain_continue (log_window->chain);
}
static void
-log_window_calendar_chats_day_selected_cb (GtkWidget *calendar,
- EmpathyLogWindow *window)
+get_events_for_date (TplActionChain *chain, gpointer user_data)
{
- guint year;
- guint month;
- guint day;
- GDate *date;
+ Ctx *ctx = user_data;
- gtk_calendar_get_date (GTK_CALENDAR (calendar), &year, &month, &day);
- if (day == 0)
- /* No date selected */
- return;
+ 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);
+}
- /* We need this hear because it appears that the months start from 0 */
- month++;
+static void
+log_window_get_messages_for_dates (EmpathyLogWindow *window,
+ GList *dates)
+{
+ GList *accounts, *targets, *acc, *targ, *l;
+ TplEventTypeMask event_mask;
+ EventSubtype subtype;
+ GDate *date, *anytime, *separator;
- date = g_date_new_dmy (day, month, year);
+ if (!log_window_get_selected (window,
+ &accounts, &targets, NULL, &event_mask, &subtype))
+ return;
- DEBUG ("Currently selected date is: %04u-%02u-%02u", year, month, day);
+ anytime = g_date_new_dmy (2, 1, -1);
+ separator = g_date_new_dmy (1, 1, -1);
+
+ _tpl_action_chain_clear (window->chain);
+ window->count++;
+
+ for (acc = accounts, targ = targets;
+ acc != NULL && targ != NULL;
+ acc = acc->next, targ = targ->next)
+ {
+ TpAccount *account = acc->data;
+ TplEntity *target = targ->data;
+
+ for (l = dates; l != NULL; l = l->next)
+ {
+ date = l->data;
+
+ /* Get events */
+ if (g_date_compare (date, anytime) != 0)
+ {
+ Ctx *ctx;
+
+ ctx = ctx_new (window, account, target, date, event_mask, subtype,
+ window->count);
+ _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)
+ {
+ ctx = ctx_new (window, account, target, d,
+ event_mask, subtype, window->count);
+ _tpl_action_chain_append (window->chain, get_events_for_date, ctx);
+ }
+ }
+ }
+ }
+ }
+
+ start_spinner ();
+ g_timeout_add (1000, show_spinner, NULL);
+ _tpl_action_chain_append (window->chain, show_events, NULL);
+ _tpl_action_chain_start (window->chain);
+
+ g_list_free_full (accounts, g_object_unref);
+ g_list_free_full (targets, g_object_unref);
+ g_date_free (separator);
+ g_date_free (anytime);
+}
- log_window_chats_get_messages (window, date);
+static void
+log_manager_got_dates_cb (GObject *manager,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ Ctx *ctx = user_data;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GList *dates;
+ GList *l;
+ GError *error = NULL;
- g_date_free (date);
+ if (log_window == NULL)
+ goto out;
+
+ if (log_window->count != ctx->count)
+ goto out;
+
+ if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
+ result, &dates, &error))
+ {
+ DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
+ error->message);
+ goto out;
+ }
+
+ view = GTK_TREE_VIEW (log_window->treeview_when);
+ model = gtk_tree_view_get_model (view);
+ store = GTK_LIST_STORE (model);
+
+ for (l = dates; l != NULL; l = l->next)
+ {
+ GDate *date = l->data;
+
+ /* Add the date if it's not already there */
+ has_element = FALSE;
+ gtk_tree_model_foreach (model, model_has_date, date);
+ if (!has_element)
+ {
+ gchar *text = format_date_for_display (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))
+ {
+ gchar *separator = NULL;
+
+ if (gtk_tree_model_iter_next (model, &iter))
+ {
+ gtk_tree_model_get (model, &iter,
+ COL_WHEN_TEXT, &separator,
+ -1);
+ }
+
+ if (g_strcmp0 (separator, "separator") != 0)
+ {
+ 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);
+ }
+ }
+
+ g_list_free_full (dates, g_free);
+ out:
+ ctx_free (ctx);
+ _tpl_action_chain_continue (log_window->chain);
}
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);
+select_first_date (TplActionChain *chain, gpointer user_data)
+{
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ view = GTK_TREE_VIEW (log_window->treeview_when);
+ model = gtk_tree_view_get_model (view);
+ selection = gtk_tree_view_get_selection (view);
+
+ /* Show messages of the most recent date */
+ if (gtk_tree_model_iter_nth_child (model, &iter, NULL, 2))
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ _tpl_action_chain_continue (log_window->chain);
}
static void
-log_window_calendar_chats_month_changed_cb (GtkWidget *calendar,
- EmpathyLogWindow *window)
+get_dates_for_entity (TplActionChain *chain, gpointer user_data)
{
- TpAccount *account;
- TplEntity *target;
+ Ctx *ctx = user_data;
- gtk_calendar_clear_marks (GTK_CALENDAR (calendar));
+ tpl_log_manager_get_dates_async (ctx->window->log_manager,
+ ctx->account, ctx->entity, ctx->event_mask,
+ log_manager_got_dates_cb, ctx);
+}
- if (!log_window_chats_get_selected (window, &account, &target)) {
- DEBUG ("No chat selected to get dates for...");
- return;
- }
+static void
+log_window_chats_get_messages (EmpathyLogWindow *window,
+ gboolean force_get_dates)
+{
+ GList *accounts, *targets, *dates;
+ TplEventTypeMask event_mask;
+ GtkTreeView *view;
+ GtkTreeModel *model;
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+
+ if (!log_window_get_selected (window, &accounts, &targets,
+ &dates, &event_mask, NULL))
+ 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);
+ 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);
- g_object_unref (account);
- g_object_unref (target);
+ /* Clear all current messages shown in the textview */
+ gtk_tree_store_clear (window->store_events);
+
+ _tpl_action_chain_clear (window->chain);
+ window->count++;
+
+ /* If there's a search use the returned hits */
+ if (window->hits != NULL)
+ {
+ if (force_get_dates)
+ {
+ 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);
+
+ populate_dates_from_search_hits (accounts, targets);
+ }
+ else
+ {
+ populate_events_from_search_hits (accounts, targets, dates);
+ }
+ }
+ /* Either use the supplied date or get the last */
+ else if (force_get_dates || dates == NULL)
+ {
+ GList *acc, *targ;
+
+ 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 */
+ for (targ = targets, acc = accounts;
+ targ != NULL && acc != NULL;
+ targ = targ->next, acc = acc->next)
+ {
+ TpAccount *account = acc->data;
+ TplEntity *target = targ->data;
+ Ctx *ctx = ctx_new (window, account, target, NULL, event_mask, 0,
+ window->count);
+
+ _tpl_action_chain_append (window->chain, get_dates_for_entity, ctx);
+ }
+ _tpl_action_chain_append (window->chain, select_first_date, NULL);
+ _tpl_action_chain_start (window->chain);
+ }
+ else
+ {
+ /* Show messages of the selected date */
+ log_window_get_messages_for_dates (window, dates);
+ }
+
+ g_list_free_full (accounts, g_object_unref);
+ g_list_free_full (targets, g_object_unref);
+ g_list_free_full (dates, (GFreeFunc) g_date_free);
}
+typedef struct {
+ EmpathyAccountChooserFilterResultCallback callback;
+ gpointer user_data;
+} FilterCallbackData;
+
static void
-log_window_entry_chats_changed_cb (GtkWidget *entry,
- EmpathyLogWindow *window)
+got_entities (GObject *manager,
+ GAsyncResult *result,
+ gpointer user_data)
{
- const gchar *str;
+ FilterCallbackData *data = user_data;
+ GList *entities;
+ GError *error = NULL;
- str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats));
- empathy_chat_view_highlight (window->chatview_chats, str, FALSE);
+ 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
+ {
+ data->callback (entities != NULL, data->user_data);
+
+ g_list_free_full (entities, g_object_unref);
+ }
- if (str != NULL) {
- empathy_chat_view_find_next (window->chatview_chats,
- str,
- TRUE,
- FALSE);
- }
+ g_slice_free (FilterCallbackData, data);
}
static void
-log_window_entry_chats_activate_cb (GtkWidget *entry,
- EmpathyLogWindow *window)
+empathy_account_chooser_filter_has_logs (TpAccount *account,
+ EmpathyAccountChooserFilterResultCallback callback,
+ gpointer callback_data,
+ gpointer user_data)
{
- const gchar *str;
+ TplLogManager *manager = tpl_log_manager_dup_singleton ();
+ FilterCallbackData *cb_data = g_slice_new0 (FilterCallbackData);
+
+ cb_data->callback = callback;
+ cb_data->user_data = callback_data;
- str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats));
+ tpl_log_manager_get_entities_async (manager, account, got_entities, cb_data);
- if (str != NULL) {
- empathy_chat_view_find_next (window->chatview_chats,
- str,
- FALSE,
- FALSE);
- }
+ g_object_unref (manager);
+}
+
+static void
+log_window_logger_clear_account_cb (TpProxy *proxy,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ EmpathyLogWindow *window = user_data;
+
+ if (error != NULL)
+ g_warning ("Error when clearing logs: %s", error->message);
+
+ /* Refresh the log viewer so the logs are cleared if the account
+ * has been deleted */
+ gtk_tree_store_clear (window->store_events);
+ log_window_who_populate (window);
+
+ /* Re-filter the account chooser so the accounts without logs get greyed out */
+ empathy_account_chooser_set_filter (
+ EMPATHY_ACCOUNT_CHOOSER (window->account_chooser),
+ empathy_account_chooser_filter_has_logs, NULL);
+}
+
+static void
+log_window_clear_logs_chooser_select_account (EmpathyAccountChooser *chooser,
+ EmpathyLogWindow *window)
+{
+ EmpathyAccountChooser *account_chooser;
+
+ account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser);
+
+ empathy_account_chooser_set_account (chooser,
+ empathy_account_chooser_get_account (account_chooser));
+}
+
+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;
+
+ 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);
+
+ /* 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);
+
+ 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?"));
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ _("Clear All"), GTK_RESPONSE_APPLY,
+ NULL);
+
+ content_area = gtk_message_dialog_get_message_area (
+ GTK_MESSAGE_DIALOG (dialog));
+
+ 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);
+
+ gtk_widget_show_all (hbox);
+
+ response_id = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (response_id != GTK_RESPONSE_APPLY)
+ goto out;
+
+ bus = tp_dbus_daemon_dup (&error);
+ if (error != NULL)
+ {
+ g_warning ("Could not delete logs: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ 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);
+
+ tp_proxy_add_interface_by_id (logger, EMP_IFACE_QUARK_LOGGER);
+
+ if (empathy_account_chooser_has_all_selected (account_chooser))
+ {
+ DEBUG ("Deleting logs for all the accounts");
+
+ emp_cli_logger_call_clear (logger, -1,
+ log_window_logger_clear_account_cb,
+ window, NULL, G_OBJECT (window->window));
+ }
+ else
+ {
+ TpAccount *account;
+
+ account = empathy_account_chooser_get_account (account_chooser);
+
+ DEBUG ("Deleting logs for %s", tp_proxy_get_object_path (account));
+
+ 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..79e4b6db2 100644
--- a/libempathy-gtk/empathy-log-window.ui
+++ b/libempathy-gtk/empathy-log-window.ui
@@ -1,383 +1,351 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <!-- interface-requires gtk+ 2.12 -->
- <!-- interface-naming-policy toplevel-contextual -->
+ <!-- interface-requires gtk+ 3.0 -->
<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="can_focus">False</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="can_focus">False</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>
+ <property name="can_focus">False</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="can_focus">False</property>
+ <property name="use_action_appearance">False</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="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="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>
+ <property name="can_focus">False</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="can_focus">False</property>
+ <property name="use_action_appearance">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="can_focus">False</property>
+ <property name="use_action_appearance">False</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>
+ <property name="can_focus">False</property>
<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="can_focus">False</property>
+ <property name="use_action_appearance">False</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="fill">True</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="can_focus">False</property>
+ <property name="toolbar_style">both</property>
+ <child>
+ <object class="GtkToolButton" id="toolbutton_profile">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</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="can_focus">False</property>
+ <property name="use_action_appearance">False</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="can_focus">False</property>
+ <property name="use_action_appearance">False</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="can_focus">False</property>
+ <property name="use_action_appearance">False</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="GtkVBox" id="vbox_chats">
+ <object class="GtkSeparatorToolItem" id="toolbutton_sep1">
<property name="visible">True</property>
- <property name="spacing">6</property>
+ <property name="can_focus">False</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_accounts">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</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="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="shadow_type">in</property>
+ <property name="can_focus">False</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">False</property>
+ <property name="use_action_appearance">False</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="fill">True</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>
<child>
- <object class="GtkVBox" id="vbox191">
+ <object class="GtkHBox" id="hbox1">
+ <property name="height_request">160</property>
<property name="visible">True</property>
- <property name="spacing">6</property>
+ <property name="can_focus">False</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>
<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>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1"/>
+ </child>
</object>
</child>
</object>
<packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
<property name="position">0</property>
</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>
+ <child>
+ <object class="GtkTreeView" id="treeview_what">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection2"/>
+ </child>
+ </object>
+ </child>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_when">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_when">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection3"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
</object>
<packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="x_options">GTK_FILL</property>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
</packing>
</child>
<child>
- <object class="GtkHBox" id="hbox143">
+ <object class="GtkNotebook" id="notebook">
<property name="visible">True</property>
- <property name="spacing">6</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
<child>
- <object class="GtkImage" id="image247">
+ <object class="GtkScrolledWindow" id="scrolledwindow_events">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_events">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection4"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label1">
<property name="visible">True</property>
- <property name="stock">gtk-find</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">page 2</property>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
+ <property name="tab_fill">False</property>
</packing>
</child>
<child>
- <object class="GtkEntry" id="entry_chats">
+ <object class="GtkSpinner" id="spinner">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="activates_default">True</property>
+ <property name="can_focus">False</property>
</object>
<packing>
<property name="position">1</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>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hbox2">
- <property name="visible">True</property>
- <property name="spacing">12</property>
- <child>
- <placeholder/>
+ <child type="tab">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">page 2</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
</child>
<child>
- <object class="GtkButton" id="button_close2">
- <property name="label">gtk-close</property>
+ <object class="GtkScrolledWindow" id="scrolledwindow_empty">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_empty">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection5"/>
+ </child>
+ </object>
+ </child>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="pack_type">end</property>
- <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="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="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
</packing>
</child>
</object>
</child>
</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>
- </object>
</interface>
diff --git a/libempathy/Makefile.am b/libempathy/Makefile.am
index 48846311f..56ea9e780 100644
--- a/libempathy/Makefile.am
+++ b/libempathy/Makefile.am
@@ -28,6 +28,7 @@ BUILT_SOURCES = \
noinst_LTLIBRARIES = libempathy.la
libempathy_headers = \
+ action-chain-internal.h \
empathy-account-settings.h \
empathy-auth-factory.h \
empathy-channel-factory.h \
@@ -69,6 +70,7 @@ libempathy_headers = \
libempathy_la_SOURCES = \
$(libempathy_headers) \
+ action-chain.c \
empathy-account-settings.c \
empathy-auth-factory.c \
empathy-channel-factory.c \
diff --git a/libempathy/action-chain-internal.h b/libempathy/action-chain-internal.h
new file mode 100644
index 000000000..14750c938
--- /dev/null
+++ b/libempathy/action-chain-internal.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk>
+ */
+
+#ifndef __TPL_ACTION_CHAIN_H__
+#define __TPL_ACTION_CHAIN_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+typedef struct {
+ GQueue *chain;
+ GSimpleAsyncResult *simple;
+ gboolean running;
+} TplActionChain;
+
+TplActionChain *_tpl_action_chain_new_async (GObject *obj,
+ GAsyncReadyCallback cb,
+ gpointer user_data);
+void _tpl_action_chain_free (TplActionChain *self);
+typedef void (*TplPendingAction) (TplActionChain *ctx, gpointer user_data);
+void _tpl_action_chain_append (TplActionChain *self, TplPendingAction func,
+ gpointer user_data);
+void _tpl_action_chain_prepend (TplActionChain *self, TplPendingAction func,
+ gpointer user_data);
+void _tpl_action_chain_start (TplActionChain *self);
+void _tpl_action_chain_continue (TplActionChain *self);
+void _tpl_action_chain_terminate (TplActionChain *self, const GError *error);
+void _tpl_action_chain_clear (TplActionChain *self);
+
+gpointer _tpl_action_chain_get_object (TplActionChain *self);
+gboolean _tpl_action_chain_new_finish (GObject *source,
+ GAsyncResult *result, GError **error);
+
+#endif // __TPL_ACTION_CHAIN_H__
diff --git a/libempathy/action-chain.c b/libempathy/action-chain.c
new file mode 100644
index 000000000..b6bf25ab9
--- /dev/null
+++ b/libempathy/action-chain.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2009 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors: Cosimo Alfarano <cosimo.alfarano@collabora.co.uk>
+ */
+
+#include "config.h"
+#include "action-chain-internal.h"
+
+typedef struct {
+ TplPendingAction action;
+ gpointer user_data;
+} TplActionLink;
+
+
+TplActionChain *
+_tpl_action_chain_new_async (GObject *obj,
+ GAsyncReadyCallback cb,
+ gpointer user_data)
+{
+ TplActionChain *ret = g_slice_new0 (TplActionChain);
+
+ ret->chain = g_queue_new ();
+ ret->simple = g_simple_async_result_new (obj, cb, user_data,
+ _tpl_action_chain_new_async);
+
+ g_object_set_data (G_OBJECT (ret->simple), "chain", ret);
+
+ return ret;
+}
+
+
+static void
+link_free (TplActionLink *l)
+{
+ g_slice_free (TplActionLink, l);
+}
+
+
+void
+_tpl_action_chain_free (TplActionChain *self)
+{
+ g_queue_foreach (self->chain, (GFunc) link_free, NULL);
+ g_queue_free (self->chain);
+ g_object_unref (self->simple);
+ g_slice_free (TplActionChain, self);
+}
+
+
+gpointer // FIXME GObject *
+_tpl_action_chain_get_object (TplActionChain *self)
+{
+ GObject *obj;
+
+ g_return_val_if_fail (self != NULL && self->simple != NULL, NULL);
+
+ obj = g_async_result_get_source_object (G_ASYNC_RESULT (self->simple));
+ g_object_unref (obj); /* don't want the extra ref */
+
+ return obj;
+}
+
+
+void
+_tpl_action_chain_prepend (TplActionChain *self,
+ TplPendingAction func,
+ gpointer user_data)
+{
+ TplActionLink *l;
+
+ l = g_slice_new0 (TplActionLink);
+ l->action = func;
+ l->user_data = user_data;
+
+ g_queue_push_head (self->chain, l);
+}
+
+
+void
+_tpl_action_chain_append (TplActionChain *self,
+ TplPendingAction func,
+ gpointer user_data)
+{
+ TplActionLink *l;
+
+ l = g_slice_new0 (TplActionLink);
+ l->action = func;
+ l->user_data = user_data;
+
+ g_queue_push_tail (self->chain, l);
+}
+
+void
+_tpl_action_chain_start (TplActionChain *self)
+{
+ g_return_if_fail (!g_queue_is_empty (self->chain));
+
+ if (self->running)
+ return;
+
+ _tpl_action_chain_continue (self);
+}
+
+void
+_tpl_action_chain_continue (TplActionChain *self)
+{
+ if (g_queue_is_empty (self->chain))
+ {
+ self->running = FALSE;
+ g_simple_async_result_complete (self->simple);
+ }
+ else
+ {
+ TplActionLink *l = g_queue_pop_head (self->chain);
+
+ self->running = TRUE;
+ l->action (self, l->user_data);
+ link_free (l);
+ if (g_queue_is_empty (self->chain))
+ self->running = FALSE;
+ }
+}
+
+
+void
+_tpl_action_chain_clear (TplActionChain *self)
+{
+ g_queue_foreach (self->chain, (GFunc) link_free, NULL);
+ g_queue_clear (self->chain);
+}
+
+void
+_tpl_action_chain_terminate (TplActionChain *self,
+ const GError *error)
+{
+ GSimpleAsyncResult *simple = self->simple;
+
+ g_assert (error != NULL);
+
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
+}
+
+
+/**
+ * _tpl_action_chain_new_finish:
+ * @source: the #GObject pass to _tpl_action_chain_new_async()
+ * @result: the #GAsyncResult pass in callback
+ * @error: a pointer to a #GError that will be set on error, or NULL to ignore
+ *
+ * Get the result from running the action chain (%TRUE if the chain completed
+ * successfully, %FALSE with @error set if it was terminated).
+ *
+ * This function also frees the chain.
+ *
+ * Returns: %TRUE on success, %FALSE with @error set on error.
+ */
+gboolean
+_tpl_action_chain_new_finish (GObject *source,
+ GAsyncResult *result,
+ GError **error)
+{
+ TplActionChain *chain;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, source,
+ _tpl_action_chain_new_async), FALSE);
+
+ chain = g_object_get_data (G_OBJECT (result), "chain");
+
+ g_return_val_if_fail (chain != NULL, FALSE);
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
+ error))
+ return FALSE;
+
+ _tpl_action_chain_free (chain);
+ return TRUE;
+}
diff --git a/libempathy/empathy-contact.c b/libempathy/empathy-contact.c
index 55bc40bdd..8cca70944 100644
--- a/libempathy/empathy-contact.c
+++ b/libempathy/empathy-contact.c
@@ -190,14 +190,12 @@ contact_dispose (GObject *object)
{
EmpathyContactPriv *priv = GET_PRIV (object);
- if (priv->tp_contact)
+ if (priv->tp_contact != NULL)
{
- g_hash_table_remove (contacts_table, priv->tp_contact);
g_signal_handlers_disconnect_by_func (priv->tp_contact,
tp_contact_notify_cb, object);
- g_object_unref (priv->tp_contact);
}
- priv->tp_contact = NULL;
+ tp_clear_object (&priv->tp_contact);
if (priv->account)
g_object_unref (priv->account);
@@ -631,14 +629,48 @@ contact_set_property (GObject *object,
};
}
+static void
+remove_tp_contact (gpointer data,
+ GObject *object)
+{
+ g_hash_table_remove (contacts_table, data);
+}
+
static EmpathyContact *
empathy_contact_new (TpContact *tp_contact)
{
+ EmpathyContact *retval;
+
g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL);
- return g_object_new (EMPATHY_TYPE_CONTACT,
+ retval = g_object_new (EMPATHY_TYPE_CONTACT,
"tp-contact", tp_contact,
NULL);
+
+ g_object_weak_ref (G_OBJECT (retval), remove_tp_contact, tp_contact);
+
+ return retval;
+}
+
+typedef struct
+{
+ TplEntity *entity;
+ TpAccount *account;
+} FindContactData;
+
+static gboolean
+contact_is_tpl_entity (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ EmpathyContact *contact = value;
+ FindContactData *data = user_data;
+
+ return !tp_strdiff (empathy_contact_get_id (contact),
+ tpl_entity_get_identifier (data->entity)) &&
+ !tp_strdiff (tp_proxy_get_object_path (data->account),
+ tp_proxy_get_object_path (
+ empathy_contact_get_account (contact)));
}
EmpathyContact *
@@ -647,17 +679,49 @@ empathy_contact_from_tpl_contact (TpAccount *account,
{
EmpathyContact *retval;
gboolean is_user;
+ EmpathyContact *existing_contact = NULL;
g_return_val_if_fail (TPL_IS_ENTITY (tpl_entity), NULL);
- is_user = (TPL_ENTITY_SELF == tpl_entity_get_entity_type (tpl_entity));
+ if (contacts_table != NULL)
+ {
+ FindContactData data;
- retval = g_object_new (EMPATHY_TYPE_CONTACT,
- "id", tpl_entity_get_identifier (tpl_entity),
- "alias", tpl_entity_get_alias (tpl_entity),
- "account", account,
- "is-user", is_user,
- NULL);
+ data.entity = tpl_entity;
+ data.account = account;
+
+ existing_contact = g_hash_table_find (contacts_table,
+ contact_is_tpl_entity, &data);
+ }
+
+ if (existing_contact != NULL)
+ {
+ EmpathyContactPriv *priv;
+
+ retval = g_object_new (EMPATHY_TYPE_CONTACT,
+ "tp-contact", empathy_contact_get_tp_contact (existing_contact),
+ NULL);
+
+ priv = GET_PRIV (retval);
+
+ /* contact_set_property() calls empathy_contact_set_alias(), which
+ * tries to set the alias on the FolksPersona, but we don't want to
+ * do that when creating an EmpathyContact from a TplEntity. So just
+ * set priv->alias instead of passing it to g_object_new() instead. */
+ g_free (priv->alias);
+ priv->alias = g_strdup (tpl_entity_get_alias (tpl_entity));
+ }
+ else
+ {
+ is_user = (TPL_ENTITY_SELF == tpl_entity_get_entity_type (tpl_entity));
+
+ retval = g_object_new (EMPATHY_TYPE_CONTACT,
+ "id", tpl_entity_get_identifier (tpl_entity),
+ "alias", tpl_entity_get_alias (tpl_entity),
+ "account", account,
+ "is-user", is_user,
+ NULL);
+ }
if (!EMP_STR_EMPTY (tpl_entity_get_avatar_token (tpl_entity)))
contact_load_avatar_cache (retval,
@@ -697,16 +761,16 @@ const gchar *
empathy_contact_get_alias (EmpathyContact *contact)
{
EmpathyContactPriv *priv;
- const gchar *alias;
+ const gchar *alias = NULL;
g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
priv = GET_PRIV (contact);
- if (priv->tp_contact != NULL)
- alias = tp_contact_get_alias (priv->tp_contact);
- else
+ if (!EMP_STR_EMPTY (priv->alias))
alias = priv->alias;
+ else if (priv->tp_contact != NULL)
+ alias = tp_contact_get_alias (priv->tp_contact);
if (!EMP_STR_EMPTY (alias))
return alias;
diff --git a/libempathy/empathy-message.c b/libempathy/empathy-message.c
index 25ec498ce..d30ce3645 100644
--- a/libempathy/empathy-message.c
+++ b/libempathy/empathy-message.c
@@ -26,6 +26,8 @@
#include <string.h>
+#include <glib/gi18n-lib.h>
+
#include <telepathy-glib/util.h>
#include <telepathy-glib/account.h>
#include <telepathy-glib/account-manager.h>
@@ -33,6 +35,9 @@
#include <telepathy-logger/entity.h>
#include <telepathy-logger/event.h>
#include <telepathy-logger/text-event.h>
+#ifdef HAVE_CALL_LOGS
+# include <telepathy-logger/call-event.h>
+#endif
#include "empathy-message.h"
#include "empathy-utils.h"
@@ -304,6 +309,7 @@ empathy_message_from_tpl_log_event (TplEvent *logevent)
TplEntity *sender = NULL;
gchar *body= NULL;
EmpathyContact *contact;
+ TpChannelTextMessageType type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
g_return_val_if_fail (TPL_IS_EVENT (logevent), NULL);
@@ -323,24 +329,36 @@ empathy_message_from_tpl_log_event (TplEvent *logevent)
tpl_event_get_account_path (logevent));
g_object_unref (acc_man);
- /* TODO Currently only TplTextEvent exists as subclass of TplEvent, in
- * future more TplEvent will exist and EmpathyMessage should probably
- * be enhanced to support other types of log entries (ie TplCallEvent).
- *
- * For now we just check (simply) that we are dealing with the only supported type,
- * then there will be a if/then/else or switch handling all the supported
- * cases.
- */
- if (!TPL_IS_TEXT_EVENT (logevent))
+ if (TPL_IS_TEXT_EVENT (logevent)) {
+ body = g_strdup (tpl_text_event_get_message (
+ TPL_TEXT_EVENT (logevent)));
+
+ type = tpl_text_event_get_message_type (TPL_TEXT_EVENT (logevent));
+ }
+#ifdef HAVE_CALL_LOGS
+ else if (TPL_IS_CALL_EVENT (logevent)) {
+ TplCallEvent *call = TPL_CALL_EVENT (logevent);
+ if (tpl_call_event_get_end_reason (call) == TPL_CALL_END_REASON_NO_ANSWER)
+ body = g_strdup_printf (_("Missed call from %s"),
+ tpl_entity_get_alias (tpl_event_get_sender (logevent)));
+ else if (tpl_entity_get_entity_type (tpl_event_get_sender (logevent)) == TPL_ENTITY_SELF)
+ body = g_strdup_printf (_("Called %s"),
+ tpl_entity_get_alias (tpl_event_get_receiver (logevent)));
+ else
+ body = g_strdup_printf (_("Call from %s"),
+ tpl_entity_get_alias (tpl_event_get_sender (logevent)));
+ }
+#endif
+ else {
+ /* Unknown event type */
return NULL;
+ }
- body = g_strdup (tpl_text_event_get_message (
- TPL_TEXT_EVENT (logevent)));
receiver = tpl_event_get_receiver (logevent);
sender = tpl_event_get_sender (logevent);
retval = g_object_new (EMPATHY_TYPE_MESSAGE,
- "type", tpl_text_event_get_message_type (TPL_TEXT_EVENT (logevent)),
+ "type", type,
"body", body,
"is-backlog", TRUE,
"timestamp", tpl_event_get_timestamp (logevent),
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a8821f2f3..8ae5858a8 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -6,6 +6,7 @@ data/org.gnome.Empathy.gschema.xml.in
data/empathy-accounts.desktop.in.in
libempathy/empathy-ft-handler.c
+libempathy/empathy-message.c
libempathy/empathy-tp-contact-list.c
libempathy/empathy-tp-file.c
libempathy/empathy-utils.c