New window for viewing logs.
authorXavier Claessens <xclaesse@gmail.com>
Wed, 13 Jun 2007 21:58:16 +0000 (21:58 +0000)
committerXavier Claessens <xclaesse@src.gnome.org>
Wed, 13 Jun 2007 21:58:16 +0000 (21:58 +0000)
2007-06-13 Xavier Claessens  <xclaesse@gmail.com>

* libempathy-gtk/Makefile.am:
* libempathy-gtk/gossip-log-window.glade:
* libempathy-gtk/gossip-log-window.h:
* libempathy-gtk/gossip-log-window.c:
* libempathy/empathy-log-manager.c:
* libempathy/empathy-log-manager.h:
* libempathy-gtk/gossip-chat.c:
* libempathy-gtk/empathy-main-window.c: New window for viewing logs.

* libempathy-gtk/gossip-chat-view.c: Do not use smooth scroll when
resizing the view.

* libempathy-gtk/gossip-contact-list-store.c: Do not set active
contacts when creating the store, and when contact groups changed.

* src/empathy-main.c: Fix warning when using command-line options.

* libempathy/empathy-tp-contact-list.c: Check if we have an aliasing
iface before setting the alias of a contact.

* TODO: Updated.

* data/jabber.profile: Ignore ssl errors by default. This is a security
vulnerability but we don't really have the choice.

* libempathy/gossip-contact.h:
* libempathy/gossip-contact.c: Add a "is-user" property to know if
it's our self contact.

* libempathy/gossip-message.h:
* libempathy/gossip-message.c: Add a "receiver" property like that we
have our self contact for nick highlight.

svn path=/trunk/; revision=148

21 files changed:
ChangeLog
TODO
data/jabber.profile
libempathy-gtk/Makefile.am
libempathy-gtk/empathy-contact-widget.c
libempathy-gtk/empathy-main-window.c
libempathy-gtk/gossip-chat-view.c
libempathy-gtk/gossip-chat.c
libempathy-gtk/gossip-contact-list-store.c
libempathy-gtk/gossip-log-window.c [new file with mode: 0644]
libempathy-gtk/gossip-log-window.glade [new file with mode: 0644]
libempathy-gtk/gossip-log-window.h [new file with mode: 0644]
libempathy/empathy-log-manager.c
libempathy/empathy-log-manager.h
libempathy/empathy-tp-chat.c
libempathy/empathy-tp-contact-list.c
libempathy/gossip-contact.c
libempathy/gossip-contact.h
libempathy/gossip-message.c
libempathy/gossip-message.h
src/empathy-main.c

index 22557f2..98ca5b0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,38 @@
+2007-06-13 Xavier Claessens  <xclaesse@gmail.com>
+
+       * libempathy-gtk/Makefile.am:
+       * libempathy-gtk/gossip-log-window.glade:
+       * libempathy-gtk/gossip-log-window.h:
+       * libempathy-gtk/gossip-log-window.c:
+       * libempathy/empathy-log-manager.c:
+       * libempathy/empathy-log-manager.h:
+       * libempathy-gtk/gossip-chat.c:
+       * libempathy-gtk/empathy-main-window.c: New window for viewing logs.
+
+       * libempathy-gtk/gossip-chat-view.c: Do not use smooth scroll when
+       resizing the view.
+
+       * libempathy-gtk/gossip-contact-list-store.c: Do not set active
+       contacts when creating the store, and when contact groups changed.
+
+       * src/empathy-main.c: Fix warning when using command-line options.
+
+       * libempathy/empathy-tp-contact-list.c: Check if we have an aliasing
+       iface before setting the alias of a contact.
+
+       * TODO: Updated.
+
+       * data/jabber.profile: Ignore ssl errors by default. This is a security
+       vulnerability but we don't really have the choice.
+
+       * libempathy/gossip-contact.h:
+       * libempathy/gossip-contact.c: Add a "is-user" property to know if
+       it's our self contact.
+
+       * libempathy/gossip-message.h:
+       * libempathy/gossip-message.c: Add a "receiver" property like that we
+       have our self contact for nick highlight.
+
 2007-06-10 Xavier Claessens  <xclaesse@gmail.com>
 
        * libempathy-gtk/gossip-spell-dialog.glade:
diff --git a/TODO b/TODO
index 5be759c..a111701 100644 (file)
--- a/TODO
+++ b/TODO
@@ -2,14 +2,18 @@ Things you can do if you want to help:
 
  - Rename all files and functions name to use the empathy namespace. Bug #444490.
  - Porting gossip-account-widget-*.{c,h} from gossip project (Guillaume is already working on IRC widget).
- - Porting various UI widgets from gossip to libempathy-gtk for contact info, adding contact, personal info, etc.
- - GtkWidget-ify gossip widgets imported in libempathy-gtk. Actually most window/dialog do not inherit from GtkWindow/GtkDialog. Need to create a glade catalog.
+ - UI for inviting a contact in a chatroom.
+ - UI for accept/refuse invitation to join a chatroom.
+ - UI to send a message directly to a contact.
+ - GtkWidget-ify libempathy-gtk. Actually most window/dialog do not inherit from GtkWindow/GtkDialog. Need to create a glade catalog.
  - Filter channels before dispatching them. For example we need a GtkStatusIcon that blink when an event arrives (text/voip/ft channel) and tells the MC to dispatch the channel only when the user clicked the icon. Like in gossip. For that we need a filter DBus API in MC, not yet written, a draft spec is proposed on the telepathy ML.
  - Make use of NetworkManager to set the presence
  - Remove Quit option everywhere, empathy is a session service and shouldn't be leaved.
  - Add sound events
  - Add register capability in GossipAccountsDialog if the profile says it's supported.
+ - Write a manual based on gossip's.
  - Testing and Bugfixing.
+ - Update translations.
 
 SoC projects:
  - Adding VoIP support based on the patch proposed for gossip.
index 55a1e69..c91691d 100644 (file)
@@ -9,4 +9,5 @@ DefaultAccountDomain = jabber.org
 SupportsInvisible = 0
 VCardField = X-Jabber
 VCardDefault = true
+Default-ignore-ssl-errors = 1
 
index 6a6c720..113b37c 100644 (file)
@@ -39,6 +39,7 @@ libempathy_gtk_la_SOURCES =                                                   \
        gossip-account-chooser.c                gossip-account-chooser.h        \
        gossip-new-chatroom-dialog.c            gossip-new-chatroom-dialog.h    \
        gossip-chatrooms-window.c               gossip-chatrooms-window.h       \
+       gossip-log-window.c                     gossip-log-window.h             \
        gossip-ui-utils.c                       gossip-ui-utils.h
 
 libempathy_gtk_la_LIBADD =                             \
@@ -61,6 +62,7 @@ glade_DATA =                                  \
        gossip-group-chat.glade                 \
        gossip-chatrooms-window.glade           \
        gossip-spell-dialog.glade               \
+       gossip-log-window.glade                 \
        gossip-chat.glade
 
 dtddir = $(datadir)/empathy
index e93117b..ac43700 100644 (file)
@@ -148,10 +148,7 @@ empathy_contact_widget_new (GossipContact *contact,
        information = g_slice_new0 (EmpathyContactWidget);
        information->editable = editable;
        if (contact) {
-               GossipContact *user_contact;
-       
-               user_contact = gossip_contact_get_user (contact);
-               information->is_user = gossip_contact_equal (contact, user_contact);
+               information->is_user = gossip_contact_is_user (contact);
                information->can_change_contact = FALSE;
        } else {
                information->is_user = FALSE;
index 7f86932..0e7b1fd 100644 (file)
@@ -50,6 +50,7 @@
 #include "gossip-about-dialog.h"
 #include "gossip-new-chatroom-dialog.h"
 #include "gossip-chatrooms-window.h"
+#include "gossip-log-window.h"
 
 #define DEBUG_DOMAIN "MainWindow"
 
@@ -533,7 +534,7 @@ static void
 main_window_chat_history_cb (GtkWidget         *widget,
                             EmpathyMainWindow *window)
 {
-       //gossip_log_window_show (NULL, NULL);
+       gossip_log_window_show (NULL, NULL, FALSE, GTK_WINDOW (window->window));
 }
 
 static void
index 9fea2fb..e8db262 100644 (file)
@@ -79,7 +79,7 @@ struct _GossipChatViewPriv {
        BlockType      last_block_type;
 
        gboolean       allow_scrolling;
-       guint          scroll_src;
+       guint          scroll_timeout;
        GTimer        *scroll_time;
        gboolean       is_group_chat;
 
@@ -350,8 +350,8 @@ chat_view_finalize (GObject *object)
        if (priv->scroll_time) {
                g_timer_destroy (priv->scroll_time);
        }
-       if (priv->scroll_src) {
-               g_source_remove (priv->scroll_src);
+       if (priv->scroll_timeout) {
+               g_source_remove (priv->scroll_timeout);
        }
 
        G_OBJECT_CLASS (gossip_chat_view_parent_class)->finalize (object);
@@ -382,7 +382,10 @@ chat_view_size_allocate (GtkWidget     *widget,
        GTK_WIDGET_CLASS (gossip_chat_view_parent_class)->size_allocate (widget, alloc);
 
        if (down) {
-               gossip_chat_view_scroll_down (GOSSIP_CHAT_VIEW (widget));
+               GtkAdjustment *adj;
+
+               adj = GTK_TEXT_VIEW (widget)->vadjustment;
+               gtk_adjustment_set_value (adj, adj->upper - adj->page_size);
        }
 }
 
@@ -1059,7 +1062,6 @@ chat_view_maybe_append_fancy_header (GossipChatView *view,
 {
        GossipChatViewPriv *priv;
        GossipContact      *sender;
-       GossipContact      *my_contact;
        const gchar        *name;
        gboolean            header;
        GtkTextIter         iter;
@@ -1075,9 +1077,8 @@ chat_view_maybe_append_fancy_header (GossipChatView *view,
        priv = GET_PRIV (view);
 
        sender = gossip_message_get_sender (msg);
-       my_contact = gossip_contact_get_user (sender);
        name = gossip_contact_get_name (sender);
-       from_self = gossip_contact_equal (sender, my_contact);
+       from_self = gossip_contact_is_user (sender);
 
        gossip_debug (DEBUG_DOMAIN, "Maybe add fancy header");
 
@@ -1186,7 +1187,6 @@ chat_view_append_irc_action (GossipChatView *view,
                             GossipMessage  *msg)
 {
        GossipChatViewPriv *priv;
-       GossipContact      *my_contact;
        GossipContact      *sender;
        const gchar        *name;
        GtkTextIter         iter;
@@ -1199,11 +1199,9 @@ chat_view_append_irc_action (GossipChatView *view,
        gossip_debug (DEBUG_DOMAIN, "Add IRC action");
 
        sender = gossip_message_get_sender (msg);
-       my_contact = gossip_contact_get_user (sender);
        name = gossip_contact_get_name (sender);
 
-       /* Skip the "/me ". */
-       if (gossip_contact_equal (sender, my_contact)) {
+       if (gossip_contact_is_user (sender)) {
                tag = "irc-action-self";
        } else {
                tag = "irc-action-other";
@@ -1236,7 +1234,6 @@ chat_view_append_fancy_action (GossipChatView *view,
 {
        GossipChatViewPriv *priv;
        GossipContact      *sender;
-       GossipContact      *my_contact;
        const gchar        *name;
        const gchar        *body;
        GtkTextIter         iter;
@@ -1249,10 +1246,9 @@ chat_view_append_fancy_action (GossipChatView *view,
        gossip_debug (DEBUG_DOMAIN, "Add fancy action");
 
        sender = gossip_message_get_sender (msg);
-       my_contact = gossip_contact_get_user (sender);
        name = gossip_contact_get_name (sender);
 
-       if (gossip_contact_equal (sender, my_contact)) {
+       if (gossip_contact_is_user (sender)) {
                tag = "fancy-action-self";
                line_tag = "fancy-line-self";
        } else {
@@ -1280,7 +1276,6 @@ chat_view_append_irc_message (GossipChatView *view,
 {
        GossipChatViewPriv *priv;
        GossipContact      *sender;
-       GossipContact      *my_contact;
        const gchar        *name;
        const gchar        *body;
        const gchar        *nick_tag;
@@ -1294,10 +1289,9 @@ chat_view_append_irc_message (GossipChatView *view,
 
        body = gossip_message_get_body (msg);
        sender = gossip_message_get_sender (msg);
-       my_contact = gossip_contact_get_user (sender);
        name = gossip_contact_get_name (sender);
 
-       if (gossip_contact_equal (sender, my_contact)) {
+       if (gossip_contact_is_user (sender)) {
                nick_tag = "irc-nick-self";
                body_tag = "irc-body-self";
        } else {
@@ -1338,16 +1332,14 @@ chat_view_append_fancy_message (GossipChatView *view,
 {
        GossipChatViewPriv *priv;
        GossipContact      *sender;
-       GossipContact      *my_contact;
        const gchar        *body;
        const gchar        *tag;
 
        priv = GET_PRIV (view);
 
        sender = gossip_message_get_sender (msg);
-       my_contact = gossip_contact_get_user (sender);
 
-       if (gossip_contact_equal (sender, my_contact)) {
+       if (gossip_contact_is_user (sender)) {
                tag = "fancy-body-self";
        } else {
                tag = "fancy-body-other";
@@ -1441,7 +1433,6 @@ gossip_chat_view_append_message (GossipChatView *view,
 {
        GossipChatViewPriv *priv;
        GossipContact      *sender;
-       GossipContact      *my_contact;
        const gchar        *body;
        gboolean            scroll_down;
 
@@ -1480,14 +1471,12 @@ gossip_chat_view_append_message (GossipChatView *view,
                }
        }
 
-       my_contact = gossip_contact_get_user (sender);
-
        /* Reset the last inserted contact. */
        if (priv->last_contact) {
                g_object_unref (priv->last_contact);
        }
 
-       if (gossip_contact_equal (my_contact, sender)) {
+       if (gossip_contact_is_user (sender)) {
                priv->last_block_type = BLOCK_TYPE_SELF;
                priv->last_contact = NULL;
        } else {
@@ -1658,7 +1647,7 @@ chat_view_scroll_cb (GossipChatView *view)
                gtk_adjustment_set_value (adj, max_val);
                g_timer_destroy (priv->scroll_time);
                priv->scroll_time = NULL;
-               priv->scroll_src = 0;
+               priv->scroll_timeout = 0;
                return FALSE;
        }
 
@@ -1683,16 +1672,15 @@ gossip_chat_view_scroll_down (GossipChatView *view)
        gossip_debug (DEBUG_DOMAIN, "Scrolling down");
 
        if (priv->scroll_time) {
-               g_timer_destroy (priv->scroll_time);
+               g_timer_reset (priv->scroll_time);
+       } else {
+               priv->scroll_time = g_timer_new();
        }
-       if (priv->scroll_src) {
-               g_source_remove (priv->scroll_src);
+       if (!priv->scroll_timeout) {
+               priv->scroll_timeout = g_timeout_add (SCROLL_DELAY,
+                                                     (GSourceFunc) chat_view_scroll_cb,
+                                                     view);
        }
-
-       priv->scroll_time = g_timer_new();
-       priv->scroll_src = g_timeout_add (SCROLL_DELAY,
-                                         (GSourceFunc) chat_view_scroll_cb,
-                                         view);
 }
 
 gboolean
index 0f63356..657535d 100644 (file)
@@ -341,9 +341,8 @@ static void
 chat_send (GossipChat  *chat,
           const gchar *msg)
 {
-       GossipChatPriv    *priv;
-       GossipMessage     *message;
-       GossipContact     *own_contact;
+       GossipChatPriv *priv;
+       GossipMessage  *message;
 
        priv = GET_PRIV (chat);
 
@@ -361,9 +360,7 @@ chat_send (GossipChat  *chat,
        /* FIXME: add here something to let group/privrate chat handle
         *        some special messages */
 
-       own_contact = empathy_contact_manager_get_user (priv->manager, chat->account);
        message = gossip_message_new (msg);
-       gossip_message_set_sender (message, own_contact);
 
        empathy_tp_chat_send (priv->tp_chat, message);
 
@@ -415,6 +412,7 @@ chat_message_received_cb (EmpathyTpChat *tp_chat,
        if (timestamp >= priv->time_joined) {
                empathy_log_manager_add_message (priv->log_manager,
                                                 gossip_chat_get_id (chat),
+                                                gossip_chat_is_group_chat (chat),
                                                 message);
        }
 
@@ -1035,14 +1033,12 @@ chat_state_changed_cb (EmpathyTpChat             *tp_chat,
                       GossipChat                *chat)
 {
        GossipChatPriv *priv;
-       GossipContact  *own_contact;
        GList          *l;
        gboolean        was_composing;
 
        priv = GET_PRIV (chat);
 
-       own_contact = gossip_contact_get_user (contact);
-       if (gossip_contact_equal (own_contact, contact)) {
+       if (gossip_contact_is_user (contact)) {
                /* We don't care about our own chat state */
                return;
        }
@@ -1112,7 +1108,8 @@ chat_add_logs (GossipChat *chat)
        /* Add messages from last conversation */
        messages = empathy_log_manager_get_last_messages (priv->log_manager,
                                                          chat->account,
-                                                         gossip_chat_get_id (chat));
+                                                         gossip_chat_get_id (chat),
+                                                         gossip_chat_is_group_chat (chat));
        num_messages  = g_list_length (messages);
 
        for (l = messages, i = 0; l; l = l->next, i++) {
@@ -1498,7 +1495,7 @@ gossip_chat_should_play_sound (GossipChat *chat)
 gboolean
 gossip_chat_should_highlight_nick (GossipMessage *message)
 {
-       GossipContact *my_contact;
+       GossipContact *contact;
        const gchar   *msg, *to;
        gchar         *cf_msg, *cf_to;
        gchar         *ch;
@@ -1515,8 +1512,12 @@ gossip_chat_should_highlight_nick (GossipMessage *message)
                return FALSE;
        }
 
-       my_contact = gossip_contact_get_user (gossip_message_get_sender (message));
-       to = gossip_contact_get_name (my_contact);
+       contact = gossip_message_get_receiver (message);
+       if (!contact || !gossip_contact_is_user (contact)) {
+               return FALSE;
+       }
+
+       to = gossip_contact_get_name (contact);
        if (!to) {
                return FALSE;
        }
index 4a5b550..887ce13 100644 (file)
@@ -57,6 +57,7 @@ struct _GossipContactListStorePriv {
        gboolean                    is_compact;
        gboolean                    show_active;
        GossipContactListStoreSort  sort_criterium;
+       guint                       inhibit_active;
 
        GossipContactGroupsFunc     get_contact_groups;
        gpointer                    get_contact_groups_data;
@@ -92,6 +93,7 @@ static void             contact_list_store_set_property              (GObject
                                                                      const GValue                *value,
                                                                      GParamSpec                  *pspec);
 static void             contact_list_store_setup                     (GossipContactListStore      *store);
+static gboolean         contact_list_store_inibit_active_cb          (GossipContactListStore      *store);
 static void             contact_list_store_contact_added_cb          (EmpathyContactList          *list_iface,
                                                                      GossipContact               *contact,
                                                                      GossipContactListStore      *store);
@@ -231,9 +233,9 @@ gossip_contact_list_store_init (GossipContactListStore *store)
 
        priv = GET_PRIV (store);
 
-       priv->is_compact = FALSE;
-       priv->show_active = TRUE;
-       priv->show_avatars = TRUE;
+       priv->inhibit_active = g_timeout_add (1000,
+                                             (GSourceFunc) contact_list_store_inibit_active_cb,
+                                             store);
 }
 
 static void
@@ -249,6 +251,10 @@ contact_list_store_finalize (GObject *object)
                g_object_unref (priv->list);
        }
 
+       if (priv->inhibit_active) {
+               g_source_remove (priv->inhibit_active);
+       }
+
        G_OBJECT_CLASS (gossip_contact_list_store_parent_class)->finalize (object);
 }
 
@@ -320,7 +326,6 @@ gossip_contact_list_store_new (EmpathyContactList *list_iface)
        GossipContactListStore     *store;
        GossipContactListStorePriv *priv;
        GList                      *contacts, *l;
-       gboolean                    show_active;
 
        g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list_iface), NULL);
 
@@ -340,9 +345,7 @@ gossip_contact_list_store_new (EmpathyContactList *list_iface)
                          G_CALLBACK (contact_list_store_contact_removed_cb),
                          store);
 
-       /* Add contacts already created. Do not highlight them. */
-       show_active = priv->show_active;
-       priv->show_active = FALSE;
+       /* Add contacts already created. */
        contacts = empathy_contact_list_get_members (priv->list);
        for (l = contacts; l; l = l->next) {
                GossipContact *contact;
@@ -354,7 +357,6 @@ gossip_contact_list_store_new (EmpathyContactList *list_iface)
                g_object_unref (contact);
        }
        g_list_free (contacts);
-       priv->show_active = show_active;
 
        return store;
 }
@@ -681,6 +683,14 @@ void
 gossip_contact_list_store_update_contact_groups (GossipContactListStore *store,
                                                 GossipContact          *contact)
 {
+       GossipContactListStorePriv *priv;
+       gboolean                    show_active;
+
+       g_return_if_fail (GOSSIP_IS_CONTACT_LIST_STORE (store));
+       g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+       priv = GET_PRIV (store);
+
        gossip_debug (DEBUG_DOMAIN, "Contact:'%s' updating groups",
                      gossip_contact_get_name (contact));
 
@@ -688,8 +698,11 @@ gossip_contact_list_store_update_contact_groups (GossipContactListStore *store,
         * would have to check the groups already set up for each
         * contact and then see what has been updated.
         */
+       show_active = priv->show_active;
+       priv->show_active = FALSE;
        contact_list_store_remove_contact (store, contact);
        contact_list_store_add_contact (store, contact);
+       priv->show_active = show_active;
 }
 
 static void
@@ -726,6 +739,19 @@ contact_list_store_setup (GossipContactListStore *store)
        gossip_contact_list_store_set_sort_criterium (store, priv->sort_criterium);
 }
 
+static gboolean
+contact_list_store_inibit_active_cb (GossipContactListStore *store)
+{
+       GossipContactListStorePriv *priv;
+
+       priv = GET_PRIV (store);
+
+       priv->show_active = TRUE;
+       priv->inhibit_active = 0;
+
+       return FALSE;
+}
+
 static void
 contact_list_store_contact_added_cb (EmpathyContactList     *list_iface,
                                     GossipContact          *contact,
diff --git a/libempathy-gtk/gossip-log-window.c b/libempathy-gtk/gossip-log-window.c
new file mode 100644 (file)
index 0000000..3ec7cdb
--- /dev/null
@@ -0,0 +1,1153 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006-2007 Imendio AB
+ * Copyright (C) 2007 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ * Authors: Martyn Russell <martyn@imendio.com>
+ *          Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+
+#include <libempathy/empathy-log-manager.h>
+#include <libempathy/gossip-chatroom-manager.h>
+#include <libempathy/gossip-chatroom.h>
+#include <libempathy/gossip-message.h>
+#include <libempathy/gossip-debug.h>
+#include <libempathy/gossip-utils.h>
+#include <libempathy/gossip-time.h>
+
+#include "gossip-log-window.h"
+#include "gossip-account-chooser.h"
+#include "gossip-chat-view.h"
+#include "gossip-ui-utils.h"
+
+#define DEBUG_DOMAIN "LogWindow"
+
+typedef struct {
+       GtkWidget         *window;
+
+       GtkWidget         *notebook;
+
+       GtkWidget         *entry_find;
+       GtkWidget         *button_find;
+       GtkWidget         *treeview_find;
+       GtkWidget         *scrolledwindow_find;
+       GossipChatView    *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;
+       GossipChatView    *chatview_chats;
+
+       gchar             *last_find;
+
+       EmpathyLogManager *log_manager;
+} GossipLogWindow;
+
+static void
+log_window_destroy_cb (GtkWidget       *widget,
+                      GossipLogWindow *window);
+static void
+log_window_entry_find_changed_cb (GtkWidget       *entry,
+                                 GossipLogWindow *window);
+static void
+log_window_find_changed_cb (GtkTreeSelection *selection,
+                           GossipLogWindow  *window);
+static void
+log_window_find_populate (GossipLogWindow *window,
+                         const gchar     *search_criteria);
+static void
+log_window_find_setup (GossipLogWindow *window);
+static void
+log_window_button_find_clicked_cb (GtkWidget       *widget,
+                                  GossipLogWindow *window);
+static void
+log_window_button_next_clicked_cb (GtkWidget       *widget,
+                                  GossipLogWindow *window);
+static void
+log_window_button_previous_clicked_cb (GtkWidget       *widget,
+                                      GossipLogWindow *window);
+static void
+log_window_chats_changed_cb (GtkTreeSelection *selection,
+                            GossipLogWindow  *window);
+static void
+log_window_chats_populate (GossipLogWindow *window);
+static void
+log_window_chats_setup (GossipLogWindow *window);
+static void
+log_window_chats_accounts_changed_cb (GtkWidget       *combobox,
+                                     GossipLogWindow *window);
+static void
+log_window_chats_new_message_cb (GossipContact   *own_contact,
+                                GossipMessage   *message,
+                                GossipLogWindow *window);
+//static gboolean
+//log_window_chats_is_today_selected (GossipLogWindow *window);
+static void
+log_window_chats_set_selected  (GossipLogWindow *window,
+                               McAccount       *account,
+                               const gchar     *chat_id,
+                               gboolean         is_chatroom);
+static gboolean
+log_window_chats_get_selected (GossipLogWindow  *window,
+                              McAccount       **account,
+                              gchar           **chat_id,
+                              gboolean         *is_chatroom);
+static void
+log_window_chats_get_messages (GossipLogWindow *window,
+                              const gchar     *date_to_show);
+static void
+log_window_calendar_chats_day_selected_cb (GtkWidget       *calendar,
+                                          GossipLogWindow *window);
+static void
+log_window_calendar_chats_month_changed_cb (GtkWidget       *calendar,
+                                           GossipLogWindow *window);
+static void
+log_window_entry_chats_changed_cb (GtkWidget       *entry,
+                                  GossipLogWindow *window);
+static void
+log_window_entry_chats_activate_cb (GtkWidget       *entry,
+                                   GossipLogWindow *window);
+
+enum {
+       COL_FIND_ACCOUNT_ICON,
+       COL_FIND_ACCOUNT_NAME,
+       COL_FIND_ACCOUNT,
+       COL_FIND_CHAT_NAME,
+       COL_FIND_CHAT_ID,
+       COL_FIND_IS_CHATROOM,
+       COL_FIND_DATE,
+       COL_FIND_DATE_READABLE,
+       COL_FIND_COUNT
+};
+
+enum {
+       COL_CHAT_ICON,
+       COL_CHAT_NAME,
+       COL_CHAT_ACCOUNT,
+       COL_CHAT_ID,
+       COL_CHAT_IS_CHATROOM,
+       COL_CHAT_COUNT
+};
+
+void
+gossip_log_window_show (McAccount   *account,
+                       const gchar *chat_id,
+                       gboolean     is_chatroom,
+                       GtkWindow   *parent)
+{
+       static GossipLogWindow *window = NULL;
+       GossipAccountChooser   *account_chooser;
+       GList                  *accounts;
+       gint                    account_num;
+       GladeXML               *glade;
+
+       if (window) {
+               gtk_window_present (GTK_WINDOW (window->window));
+
+               if (account && chat_id) {
+                       gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), 1);
+                       log_window_chats_set_selected (window, account,
+                                                      chat_id, is_chatroom);
+               }
+
+               return;
+       }
+
+       window = g_new0 (GossipLogWindow, 1);
+       window->log_manager = empathy_log_manager_new ();
+
+       glade = gossip_glade_get_file ("gossip-log-window.glade",
+                                      "log_window",
+                                      NULL,
+                                      "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);
+       gossip_glade_connect (glade,
+                             window,
+                             "log_window", "destroy", log_window_destroy_cb,
+                             "entry_find", "changed", log_window_entry_find_changed_cb,
+                             "button_previous", "clicked", log_window_button_previous_clicked_cb,
+                             "button_next", "clicked", log_window_button_next_clicked_cb,
+                             "button_find", "clicked", log_window_button_find_clicked_cb,
+                             "entry_chats", "changed", log_window_entry_chats_changed_cb,
+                             "entry_chats", "activate", log_window_entry_chats_activate_cb,
+                             NULL);
+
+       g_object_unref (glade);
+
+       g_object_add_weak_pointer (G_OBJECT (window->window),
+                                  (gpointer) &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 GossipChatView */
+       window->chatview_find = gossip_chat_view_new ();
+       gtk_container_add (GTK_CONTAINER (window->scrolledwindow_find),
+                          GTK_WIDGET (window->chatview_find));
+       gtk_widget_show (GTK_WIDGET (window->chatview_find));
+
+       /* Configure Contacts GossipChatView */
+       window->chatview_chats = gossip_chat_view_new ();
+       gtk_container_add (GTK_CONTAINER (window->scrolledwindow_chats),
+                          GTK_WIDGET (window->chatview_chats));
+       gtk_widget_show (GTK_WIDGET (window->chatview_chats));
+
+       /* Account chooser for chats */
+       window->account_chooser_chats = gossip_account_chooser_new ();
+       account_chooser = GOSSIP_ACCOUNT_CHOOSER (window->account_chooser_chats);
+       gossip_account_chooser_set_can_select_all (account_chooser, TRUE);
+
+       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 */
+       accounts = mc_accounts_list ();
+       account_num = g_list_length (accounts);
+       mc_accounts_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);
+       }
+
+       /* Search List */
+       log_window_find_setup (window);
+
+       /* Contacts */
+       log_window_chats_setup (window);
+       log_window_chats_populate (window);
+
+       /* Select chat */
+       if (account && chat_id) {
+               gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), 1);
+               log_window_chats_set_selected (window, account,
+                                              chat_id, is_chatroom);
+       }
+
+       if (parent) {
+               gtk_window_set_transient_for (GTK_WINDOW (window->window),
+                                             GTK_WINDOW (parent));
+       }
+
+       gtk_widget_show (window->window);
+}
+
+static void
+log_window_destroy_cb (GtkWidget       *widget,
+                      GossipLogWindow *window)
+{
+       g_signal_handlers_disconnect_by_func (window->log_manager,
+                                             log_window_chats_new_message_cb,
+                                             window);
+
+       g_free (window->last_find);
+       g_object_unref (window->log_manager);
+
+       g_free (window);
+}
+
+/*
+ * Search code.
+ */
+static void
+log_window_entry_find_changed_cb (GtkWidget       *entry,
+                                 GossipLogWindow *window)
+{
+       const gchar *str;
+       gboolean     is_sensitive = TRUE;
+
+       str = gtk_entry_get_text (GTK_ENTRY (window->entry_find));
+
+       is_sensitive &= !G_STR_EMPTY (str);
+       is_sensitive &= 
+               !window->last_find || 
+               (window->last_find && strcmp (window->last_find, str) != 0);
+
+       gtk_widget_set_sensitive (window->button_find, is_sensitive);
+}
+
+static void
+log_window_find_changed_cb (GtkTreeSelection *selection,
+                           GossipLogWindow  *window)
+{
+       GtkTreeView   *view;
+       GtkTreeModel  *model;
+       GtkTreeIter    iter;
+       McAccount     *account;
+       gchar         *chat_id;
+       gboolean       is_chatroom;
+       gchar         *date;
+       GossipMessage *message;
+       GList         *messages;
+       GList         *l;
+       gboolean       can_do_previous;
+       gboolean       can_do_next;
+
+       /* Get selected information */
+       view = GTK_TREE_VIEW (window->treeview_find);
+       model = gtk_tree_view_get_model (view);
+
+       if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+               gtk_widget_set_sensitive (window->button_previous, FALSE);
+               gtk_widget_set_sensitive (window->button_next, FALSE);
+
+               gossip_chat_view_clear (window->chatview_find);
+       
+               return;
+       }
+
+       gtk_widget_set_sensitive (window->button_previous, TRUE);
+       gtk_widget_set_sensitive (window->button_next, TRUE);
+
+       gtk_tree_model_get (model, &iter,
+                           COL_FIND_ACCOUNT, &account,
+                           COL_FIND_CHAT_ID, &chat_id,
+                           COL_FIND_IS_CHATROOM, &is_chatroom,
+                           COL_FIND_DATE, &date,
+                           -1);
+
+       /* Clear all current messages shown in the textview */
+       gossip_chat_view_clear (window->chatview_find);
+
+       /* Turn off scrolling temporarily */
+       gossip_chat_view_scroll (window->chatview_find, FALSE);
+
+       /* Get messages */
+       messages = empathy_log_manager_get_messages_for_date (window->log_manager,
+                                                             account,
+                                                             chat_id,
+                                                             is_chatroom,
+                                                             date);
+       g_object_unref (account);
+       g_free (date);
+       g_free (chat_id);
+
+       for (l = messages; l; l = l->next) {
+               message = l->data;
+               gossip_chat_view_append_message (window->chatview_find, message);
+               g_object_unref (message);
+       }
+       g_list_free (messages);
+
+       /* Scroll to the most recent messages */
+       gossip_chat_view_scroll (window->chatview_find, TRUE);
+
+       /* Highlight and find messages */
+       gossip_chat_view_highlight (window->chatview_find,
+                                   window->last_find);
+       gossip_chat_view_find_next (window->chatview_find,
+                                   window->last_find,
+                                   TRUE);
+       gossip_chat_view_find_abilities (window->chatview_find,
+                                        window->last_find,
+                                        &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 void
+log_window_find_populate (GossipLogWindow *window,
+                         const gchar     *search_criteria)
+{
+       GList              *hits, *l;
+
+       GtkTreeView        *view;
+       GtkTreeModel       *model;
+       GtkTreeSelection   *selection;
+       GtkListStore       *store;
+       GtkTreeIter         iter;
+
+       view = GTK_TREE_VIEW (window->treeview_find);
+       model = gtk_tree_view_get_model (view);
+       selection = gtk_tree_view_get_selection (view);
+       store = GTK_LIST_STORE (model);
+
+       gossip_chat_view_clear (window->chatview_find);
+
+       gtk_list_store_clear (store);
+
+       if (G_STR_EMPTY (search_criteria)) {
+               /* Just clear the search. */
+               return;
+       }
+
+       hits = empathy_log_manager_search_new (window->log_manager, search_criteria);
+
+       for (l = hits; l; l = l->next) {
+               EmpathyLogSearchHit *hit;
+               const gchar         *account_name;
+               const gchar         *account_icon;
+               gchar               *date_readable;
+
+               hit = l->data;
+
+               /* Protect against invalid data (corrupt or old log files. */
+               if (!hit->account || !hit->chat_id) {
+                       continue;
+               }
+
+               date_readable = empathy_log_manager_get_date_readable (hit->date);
+               account_name = mc_account_get_display_name (hit->account);
+               account_icon = gossip_icon_name_from_account (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, hit->chat_id, /* FIXME */
+                                   COL_FIND_CHAT_ID, hit->chat_id,
+                                   COL_FIND_IS_CHATROOM, hit->is_chatroom,
+                                   COL_FIND_DATE, hit->date,
+                                   COL_FIND_DATE_READABLE, date_readable,
+                                   -1);
+
+               g_free (date_readable);
+
+               /* FIXME: Update COL_FIND_CHAT_NAME */
+               if (hit->is_chatroom) {
+               } else {
+               }
+       }
+
+       if (hits) {
+               empathy_log_manager_search_free (hits);
+       }
+}
+
+static void
+log_window_find_setup (GossipLogWindow *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 */
+                                   MC_TYPE_ACCOUNT,        /* account */
+                                   G_TYPE_STRING,          /* chat name */
+                                   G_TYPE_STRING,          /* chat id */
+                                   G_TYPE_BOOLEAN,         /* is chatroom */
+                                   G_TYPE_STRING,          /* date */
+                                   G_TYPE_STRING);         /* date_readable */
+
+       model = GTK_TREE_MODEL (store);
+       sortable = GTK_TREE_SORTABLE (store);
+
+       gtk_tree_view_set_model (view, model);
+
+       /* New column */
+       column = gtk_tree_view_column_new ();
+
+       cell = gtk_cell_renderer_pixbuf_new ();
+       gtk_tree_view_column_pack_start (column, cell, FALSE);
+       gtk_tree_view_column_add_attribute (column, cell,
+                                           "icon-name",
+                                           COL_FIND_ACCOUNT_ICON);
+
+       cell = gtk_cell_renderer_text_new ();
+       gtk_tree_view_column_pack_start (column, cell, TRUE);
+       gtk_tree_view_column_add_attribute (column, cell,
+                                           "text",
+                                           COL_FIND_ACCOUNT_NAME);
+
+       gtk_tree_view_column_set_title (column, _("Account"));
+       gtk_tree_view_append_column (view, column);
+
+       gtk_tree_view_column_set_resizable (column, TRUE);
+       gtk_tree_view_column_set_clickable (column, TRUE);
+
+       cell = gtk_cell_renderer_text_new ();
+       offset = gtk_tree_view_insert_column_with_attributes (view, -1, _("Conversation"),
+                                                             cell, "text", COL_FIND_CHAT_NAME,
+                                                             NULL);
+
+       column = gtk_tree_view_get_column (view, offset - 1);
+       gtk_tree_view_column_set_sort_column_id (column, COL_FIND_CHAT_NAME);
+       gtk_tree_view_column_set_resizable (column, TRUE);
+       gtk_tree_view_column_set_clickable (column, TRUE);
+
+       cell = gtk_cell_renderer_text_new ();
+       offset = gtk_tree_view_insert_column_with_attributes (view, -1, _("Date"),
+                                                             cell, "text", COL_FIND_DATE_READABLE,
+                                                             NULL);
+
+       column = gtk_tree_view_get_column (view, offset - 1);
+       gtk_tree_view_column_set_sort_column_id (column, COL_FIND_DATE);
+       gtk_tree_view_column_set_resizable (column, TRUE);
+       gtk_tree_view_column_set_clickable (column, TRUE);
+
+       /* Set up treeview properties */
+       gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+       gtk_tree_sortable_set_sort_column_id (sortable,
+                                             COL_FIND_DATE,
+                                             GTK_SORT_ASCENDING);
+
+       /* Set up signals */
+       g_signal_connect (selection, "changed",
+                         G_CALLBACK (log_window_find_changed_cb),
+                         window);
+
+       g_object_unref (store);
+}
+
+static void
+log_window_button_find_clicked_cb (GtkWidget       *widget,
+                                  GossipLogWindow *window)
+{
+       const gchar *str;
+
+       str = gtk_entry_get_text (GTK_ENTRY (window->entry_find));
+
+       /* Don't find the same crap again */
+       if (window->last_find && strcmp (window->last_find, str) == 0) {
+               return;
+       }
+
+       g_free (window->last_find);
+       window->last_find = g_strdup (str);
+
+       log_window_find_populate (window, str);
+}
+
+static void
+log_window_button_next_clicked_cb (GtkWidget       *widget,
+                                  GossipLogWindow *window)
+{
+       if (window->last_find) {
+               gboolean can_do_previous;
+               gboolean can_do_next;
+
+               gossip_chat_view_find_next (window->chatview_find,
+                                           window->last_find,
+                                           FALSE);
+               gossip_chat_view_find_abilities (window->chatview_find,
+                                                window->last_find,
+                                                &can_do_previous,
+                                                &can_do_next);
+               gtk_widget_set_sensitive (window->button_previous, can_do_previous);
+               gtk_widget_set_sensitive (window->button_next, can_do_next);
+       }
+}
+
+static void
+log_window_button_previous_clicked_cb (GtkWidget       *widget,
+                                      GossipLogWindow *window)
+{
+       if (window->last_find) {
+               gboolean can_do_previous;
+               gboolean can_do_next;
+
+               gossip_chat_view_find_previous (window->chatview_find,
+                                               window->last_find,
+                                               FALSE);
+               gossip_chat_view_find_abilities (window->chatview_find,
+                                                window->last_find,
+                                                &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);
+       }
+}
+
+/*
+ * Chats Code
+ */
+
+static void
+log_window_chats_changed_cb (GtkTreeSelection *selection,
+                            GossipLogWindow  *window)
+{
+       /* Use last date by default */
+       gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats));
+
+       log_window_chats_get_messages (window, NULL);
+}
+
+static void
+log_window_chats_populate (GossipLogWindow *window)
+{
+       GossipAccountChooser *account_chooser;
+       McAccount            *account;
+       GList                *chats, *l;
+
+       GtkTreeView          *view;
+       GtkTreeModel         *model;
+       GtkTreeSelection     *selection;
+       GtkListStore         *store;
+       GtkTreeIter           iter;
+
+       account_chooser = GOSSIP_ACCOUNT_CHOOSER (window->account_chooser_chats);
+       account = gossip_account_chooser_get_account (account_chooser);
+
+       view = GTK_TREE_VIEW (window->treeview_chats);
+       model = gtk_tree_view_get_model (view);
+       selection = gtk_tree_view_get_selection (view);
+       store = GTK_LIST_STORE (model);
+
+       /* Block signals to stop the logs being retrieved prematurely */
+       g_signal_handlers_block_by_func (selection,
+                                        log_window_chats_changed_cb,
+                                        window);
+
+       gtk_list_store_clear (store);
+
+       chats = empathy_log_manager_get_chats (window->log_manager, account);
+       for (l = chats; l; l = l->next) {
+               EmpathyLogSearchHit *hit;
+
+               hit = l->data;
+
+               gtk_list_store_append (store, &iter);
+               gtk_list_store_set (store, &iter,
+                                   COL_CHAT_ICON, "empathy-available",
+                                   COL_CHAT_NAME, hit->chat_id,                                
+                                   COL_CHAT_ACCOUNT, account,
+                                   COL_CHAT_ID, hit->chat_id,
+                                   COL_CHAT_IS_CHATROOM, hit->is_chatroom,
+                                   -1);
+
+               /* FIXME: Update COL_CHAT_ICON/NAME */
+               if (hit->is_chatroom) {
+               } else {
+               }
+       }
+       empathy_log_manager_search_free (chats);
+
+       /* Unblock signals */
+       g_signal_handlers_unblock_by_func (selection,
+                                          log_window_chats_changed_cb,
+                                          window);
+
+
+       g_object_unref (account);
+}
+
+static void
+log_window_chats_setup (GossipLogWindow *window)
+{
+       GtkTreeView       *view;
+       GtkTreeModel      *model;
+       GtkTreeSelection  *selection;
+       GtkTreeSortable   *sortable;
+       GtkTreeViewColumn *column;
+       GtkListStore      *store;
+       GtkCellRenderer   *cell;
+
+       view = GTK_TREE_VIEW (window->treeview_chats);
+       selection = gtk_tree_view_get_selection (view);
+
+       /* new store */
+       store = gtk_list_store_new (COL_CHAT_COUNT,
+                                   G_TYPE_STRING,    /* icon */
+                                   G_TYPE_STRING,    /* name */
+                                   MC_TYPE_ACCOUNT,  /* account */
+                                   G_TYPE_STRING,    /* id */
+                                   G_TYPE_BOOLEAN);  /* is chatroom */
+
+       model = GTK_TREE_MODEL (store);
+       sortable = GTK_TREE_SORTABLE (store);
+
+       gtk_tree_view_set_model (view, model);
+
+       /* new column */
+       column = gtk_tree_view_column_new ();
+
+       cell = gtk_cell_renderer_pixbuf_new ();
+       gtk_tree_view_column_pack_start (column, cell, FALSE);
+       gtk_tree_view_column_add_attribute (column, cell,
+                                           "icon-name",
+                                           COL_CHAT_ICON);
+
+       cell = gtk_cell_renderer_text_new ();
+       g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+       gtk_tree_view_column_pack_start (column, cell, TRUE);
+       gtk_tree_view_column_add_attribute (column, cell,
+                                           "text",
+                                           COL_CHAT_NAME);
+
+       gtk_tree_view_append_column (view, column);
+
+       /* set up treeview properties */
+       gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+       gtk_tree_sortable_set_sort_column_id (sortable,
+                                             COL_CHAT_NAME,
+                                             GTK_SORT_ASCENDING);
+
+       /* set up signals */
+       g_signal_connect (selection, "changed",
+                         G_CALLBACK (log_window_chats_changed_cb),
+                         window);
+
+       g_object_unref (store);
+}
+
+static void
+log_window_chats_accounts_changed_cb (GtkWidget       *combobox,
+                                     GossipLogWindow *window)
+{
+       /* Clear all current messages shown in the textview */
+       gossip_chat_view_clear (window->chatview_chats);
+
+       log_window_chats_populate (window);
+}
+
+static void
+log_window_chats_new_message_cb (GossipContact   *own_contact,
+                                GossipMessage   *message,
+                                GossipLogWindow *window)
+{
+       gossip_chat_view_append_message (window->chatview_chats, message);
+
+       /* Scroll to the most recent messages */
+       gossip_chat_view_scroll_down (window->chatview_chats);
+}
+
+#if 0
+static gboolean
+log_window_chats_is_today_selected (GossipLogWindow *window)
+{
+       GossipTime  t;
+       gchar      *timestamp;
+       guint       year_selected;
+       guint       year;
+       guint       month;
+       guint       month_selected;
+       guint       day;
+       guint       day_selected;
+       gboolean    selected;
+
+       t = gossip_time_get_current ();
+       timestamp = gossip_time_to_string_local (t, "%Y%m%d");
+
+       sscanf (timestamp, "%4d%2d%2d", &year, &month, &day);
+
+       gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats),
+                              &year_selected,
+                              &month_selected,
+                              &day_selected);
+
+       /* Hack since this starts at 0 */
+       month_selected++;
+
+       selected = (day_selected == day &&
+                   month_selected == month &&
+                   year_selected == year);
+
+       g_free (timestamp);
+
+       return selected;
+}
+#endif
+
+static void
+log_window_chats_set_selected  (GossipLogWindow *window,
+                               McAccount       *account,
+                               const gchar     *chat_id,
+                               gboolean         is_chatroom)
+{
+       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)) {
+               McAccount *this_account;
+               gchar     *this_chat_id;
+               gboolean   this_is_chatroom;
+
+               gtk_tree_model_get (model, &iter,
+                                   COL_CHAT_ACCOUNT, &this_account,
+                                   COL_CHAT_ID, &this_chat_id,
+                                   COL_CHAT_IS_CHATROOM, &this_is_chatroom,
+                                   -1);
+
+               if (gossip_account_equal (this_account, account) &&
+                   strcmp (this_chat_id, chat_id) == 0 &&
+                   this_is_chatroom == 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_free (this_chat_id);
+                       break;
+               }
+
+               g_object_unref (this_account);
+               g_free (this_chat_id);
+       }
+}
+
+static gboolean
+log_window_chats_get_selected (GossipLogWindow  *window,
+                              McAccount       **account,
+                              gchar           **chat_id,
+                              gboolean         *is_chatroom)
+{
+       GtkTreeView      *view;
+       GtkTreeModel     *model;
+       GtkTreeSelection *selection;
+       GtkTreeIter       iter;
+       gchar            *id = NULL;
+       McAccount        *acc = NULL;
+       gboolean          room = FALSE;
+
+       view = GTK_TREE_VIEW (window->treeview_chats);
+       model = gtk_tree_view_get_model (view);
+       selection = gtk_tree_view_get_selection (view);
+
+       if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+               return FALSE;
+       }
+
+       gtk_tree_model_get (model, &iter,
+                           COL_CHAT_ACCOUNT, &acc,
+                           COL_CHAT_ID, &id,
+                           COL_CHAT_IS_CHATROOM, &room,
+                           -1);
+
+       if (chat_id) {
+               *chat_id = id;
+       } else {
+               g_free (id);
+       }
+       if (account) {
+               *account = acc;
+       } else {
+               g_object_unref (acc);
+       }
+       if (is_chatroom) {
+               *is_chatroom = room;
+       }
+
+       return TRUE;
+}
+
+static void
+log_window_chats_get_messages (GossipLogWindow *window,
+                              const gchar     *date_to_show)
+{
+       McAccount     *account;
+       gchar         *chat_id;
+       gboolean       is_chatroom;
+       GossipMessage *message;
+       GList         *messages;
+       GList         *dates = NULL;
+       GList         *l;
+       const gchar   *date;
+       guint          year_selected;
+       guint          year;
+       guint          month;
+       guint          month_selected;
+       guint          day;
+
+       if (!log_window_chats_get_selected (window, &account,
+                                           &chat_id, &is_chatroom)) {
+               return;
+       }
+
+       g_signal_handlers_block_by_func (window->calendar_chats,
+                                        log_window_calendar_chats_day_selected_cb,
+                                        window);
+
+       /* Either use the supplied date or get the last */
+       date = date_to_show;
+       if (!date) {
+               gboolean day_selected = FALSE;
+
+               /* Get a list of dates and show them on the calendar */
+               dates = empathy_log_manager_get_dates (window->log_manager,
+                                                      account, chat_id,
+                                                      is_chatroom);
+
+               for (l = dates; l; l = l->next) {
+                       const gchar *str;
+
+                       str = l->data;
+                       if (!str) {
+                               continue;
+                       }
+
+                       sscanf (str, "%4d%2d%2d", &year, &month, &day);
+                       gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats),
+                                              &year_selected,
+                                              &month_selected,
+                                              NULL);
+
+                       month_selected++;
+
+                       if (!l->next) {
+                               date = str;
+                       }
+
+                       if (year != year_selected || month != month_selected) {
+                               continue;
+                       }
+
+
+                       gossip_debug (DEBUG_DOMAIN, "Marking date:'%s'", str);
+                       gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats), day);
+
+                       if (l->next) {
+                               continue;
+                       }
+
+                       day_selected = TRUE;
+
+                       gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), day);
+               }
+
+               if (!day_selected) {
+                       /* Unselect the day in the calendar */
+                       gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), 0);
+               }
+       } else {
+               sscanf (date, "%4d%2d%2d", &year, &month, &day);
+               gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats),
+                                      &year_selected,
+                                      &month_selected,
+                                      NULL);
+
+               month_selected++;
+
+               if (year != year_selected && month != month_selected) {
+                       day = 0;
+               }
+
+               gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), day);
+       }
+       g_signal_handlers_unblock_by_func (window->calendar_chats,
+                                          log_window_calendar_chats_day_selected_cb,
+                                          window);
+
+       /* Clear all current messages shown in the textview */
+       gossip_chat_view_clear (window->chatview_chats);
+
+       /* Turn off scrolling temporarily */
+       gossip_chat_view_scroll (window->chatview_find, FALSE);
+
+       /* Get messages */
+       messages = empathy_log_manager_get_messages_for_date (window->log_manager,
+                                                             account, chat_id,
+                                                             is_chatroom,
+                                                             date);
+
+       for (l = messages; l; l = l->next) {
+               message = l->data;
+
+               gossip_chat_view_append_message (window->chatview_chats,
+                                                message);
+               g_object_unref (message);
+       }
+       g_list_free (messages);
+
+       g_list_foreach (dates, (GFunc) g_free, NULL);
+       g_list_free (dates);
+
+       g_object_unref (account);
+       g_free (chat_id);
+
+       /* Turn back on scrolling */
+       gossip_chat_view_scroll (window->chatview_find, TRUE);
+
+       /* Scroll to the most recent messages */
+       gossip_chat_view_scroll_down (window->chatview_chats);
+
+       /* Give the search entry main focus */
+       gtk_widget_grab_focus (window->entry_chats);
+}
+
+static void
+log_window_calendar_chats_day_selected_cb (GtkWidget       *calendar,
+                                          GossipLogWindow *window)
+{
+       guint  year;
+       guint  month;
+       guint  day;
+
+       gchar *date;
+
+       gtk_calendar_get_date (GTK_CALENDAR (calendar), &year, &month, &day);
+
+       /* We need this hear because it appears that the months start from 0 */
+       month++;
+
+       date = g_strdup_printf ("%4.4d%2.2d%2.2d", year, month, day);
+
+       gossip_debug (DEBUG_DOMAIN, "Currently selected date is:'%s'", date);
+
+       log_window_chats_get_messages (window, date);
+
+       g_free (date);
+}
+
+static void
+log_window_calendar_chats_month_changed_cb (GtkWidget       *calendar,
+                                           GossipLogWindow *window)
+{
+       McAccount     *account;
+       gchar         *chat_id;
+       gboolean       is_chatroom;
+       guint          year_selected;
+       guint          month_selected;
+
+       GList         *dates;
+       GList         *l;
+
+       gtk_calendar_clear_marks (GTK_CALENDAR (calendar));
+
+       if (!log_window_chats_get_selected (window, &account,
+                                           &chat_id, &is_chatroom)) {
+               gossip_debug (DEBUG_DOMAIN, "No chat selected to get dates for...");
+               return;
+       }
+
+       g_object_get (calendar,
+                     "month", &month_selected,
+                     "year", &year_selected,
+                     NULL);
+
+       /* We need this hear because it appears that the months start from 0 */
+       month_selected++;
+
+       /* Get the log object for this contact */
+       dates = empathy_log_manager_get_dates (window->log_manager, account,
+                                              chat_id, is_chatroom);
+       g_object_unref (account);
+       g_free (chat_id);
+
+       for (l = dates; l; l = l->next) {
+               const gchar *str;
+               guint        year;
+               guint        month;
+               guint        day;
+
+               str = l->data;
+               if (!str) {
+                       continue;
+               }
+
+               sscanf (str, "%4d%2d%2d", &year, &month, &day);
+
+               if (year == year_selected && month == month_selected) {
+                       gossip_debug (DEBUG_DOMAIN, "Marking date:'%s'", str);
+                       gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats), day);
+               }
+       }
+
+       g_list_foreach (dates, (GFunc) g_free, NULL);
+       g_list_free (dates);
+
+       gossip_debug (DEBUG_DOMAIN,
+                     "Currently showing month %d and year %d",
+                     month_selected, year_selected);
+}
+
+static void
+log_window_entry_chats_changed_cb (GtkWidget       *entry,
+                                  GossipLogWindow *window)
+{
+       const gchar *str;
+
+       str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats));
+       gossip_chat_view_highlight (window->chatview_chats, str);
+
+       if (str) {
+               gossip_chat_view_find_next (window->chatview_chats,
+                                           str,
+                                           TRUE);
+       }
+}
+
+static void
+log_window_entry_chats_activate_cb (GtkWidget       *entry,
+                                   GossipLogWindow *window)
+{
+       const gchar *str;
+
+       str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats));
+
+       if (str) {
+               gossip_chat_view_find_next (window->chatview_chats,
+                                           str,
+                                           FALSE);
+       }
+}
+
diff --git a/libempathy-gtk/gossip-log-window.glade b/libempathy-gtk/gossip-log-window.glade
new file mode 100644 (file)
index 0000000..4309a35
--- /dev/null
@@ -0,0 +1,470 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="log_window">
+  <property name="title" translatable="yes">View Previous Conversations</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="default_width">640</property>
+  <property name="default_height">450</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="icon_name">gtk-justify-left</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+
+  <child>
+    <widget class="GtkNotebook" id="notebook">
+      <property name="border_width">2</property>
+      <property name="visible">True</property>
+      <property name="can_focus">True</property>
+      <property name="show_tabs">True</property>
+      <property name="show_border">True</property>
+      <property name="tab_pos">GTK_POS_TOP</property>
+      <property name="scrollable">False</property>
+      <property name="enable_popup">False</property>
+
+      <child>
+       <widget class="GtkVBox" id="vbox192">
+         <property name="border_width">12</property>
+         <property name="visible">True</property>
+         <property name="homogeneous">False</property>
+         <property name="spacing">6</property>
+
+         <child>
+           <widget class="GtkHBox" id="hbox144">
+             <property name="visible">True</property>
+             <property name="homogeneous">False</property>
+             <property name="spacing">12</property>
+
+             <child>
+               <widget class="GtkLabel" id="label628">
+                 <property name="visible">True</property>
+                 <property name="label" translatable="yes">_For:</property>
+                 <property name="use_underline">True</property>
+                 <property name="use_markup">False</property>
+                 <property name="justify">GTK_JUSTIFY_LEFT</property>
+                 <property name="wrap">False</property>
+                 <property name="selectable">False</property>
+                 <property name="xalign">0.5</property>
+                 <property name="yalign">0.5</property>
+                 <property name="xpad">0</property>
+                 <property name="ypad">0</property>
+                 <property name="mnemonic_widget">entry_find</property>
+                 <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+                 <property name="width_chars">-1</property>
+                 <property name="single_line_mode">False</property>
+                 <property name="angle">0</property>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">False</property>
+                 <property name="fill">False</property>
+               </packing>
+             </child>
+
+             <child>
+               <widget class="GtkEntry" id="entry_find">
+                 <property name="visible">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="has_focus">True</property>
+                 <property name="editable">True</property>
+                 <property name="visibility">True</property>
+                 <property name="max_length">0</property>
+                 <property name="text" translatable="yes"></property>
+                 <property name="has_frame">True</property>
+                 <property name="invisible_char">*</property>
+                 <property name="activates_default">True</property>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">True</property>
+                 <property name="fill">True</property>
+               </packing>
+             </child>
+
+             <child>
+               <widget class="GtkButton" id="button_find">
+                 <property name="visible">True</property>
+                 <property name="sensitive">False</property>
+                 <property name="can_default">True</property>
+                 <property name="has_default">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="label">gtk-find</property>
+                 <property name="use_stock">True</property>
+                 <property name="relief">GTK_RELIEF_NORMAL</property>
+                 <property name="focus_on_click">False</property>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">False</property>
+                 <property name="fill">False</property>
+               </packing>
+             </child>
+           </widget>
+           <packing>
+             <property name="padding">0</property>
+             <property name="expand">False</property>
+             <property name="fill">False</property>
+           </packing>
+         </child>
+
+         <child>
+           <widget class="GtkVPaned" id="vpaned1">
+             <property name="visible">True</property>
+             <property name="can_focus">True</property>
+             <property name="position">120</property>
+
+             <child>
+               <widget class="GtkScrolledWindow" id="scrolledwindow14">
+                 <property name="visible">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+                 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                 <property name="shadow_type">GTK_SHADOW_IN</property>
+                 <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+                 <child>
+                   <widget class="GtkTreeView" id="treeview_find">
+                     <property name="visible">True</property>
+                     <property name="can_focus">True</property>
+                     <property name="headers_visible">True</property>
+                     <property name="rules_hint">False</property>
+                     <property name="reorderable">False</property>
+                     <property name="enable_search">False</property>
+                     <property name="fixed_height_mode">False</property>
+                     <property name="hover_selection">False</property>
+                     <property name="hover_expand">False</property>
+                   </widget>
+                 </child>
+               </widget>
+               <packing>
+                 <property name="shrink">True</property>
+                 <property name="resize">False</property>
+               </packing>
+             </child>
+
+             <child>
+               <widget class="GtkVBox" id="vbox215">
+                 <property name="visible">True</property>
+                 <property name="homogeneous">False</property>
+                 <property name="spacing">6</property>
+
+                 <child>
+                   <widget class="GtkScrolledWindow" id="scrolledwindow_find">
+                     <property name="visible">True</property>
+                     <property name="can_focus">True</property>
+                     <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+                     <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+                     <property name="shadow_type">GTK_SHADOW_IN</property>
+                     <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+                     <child>
+                       <placeholder/>
+                     </child>
+                   </widget>
+                   <packing>
+                     <property name="padding">0</property>
+                     <property name="expand">True</property>
+                     <property name="fill">True</property>
+                   </packing>
+                 </child>
+
+                 <child>
+                   <widget class="GtkHBox" id="hbox171">
+                     <property name="visible">True</property>
+                     <property name="homogeneous">False</property>
+                     <property name="spacing">12</property>
+
+                     <child>
+                       <placeholder/>
+                     </child>
+
+                     <child>
+                       <widget class="GtkButton" id="button_next">
+                         <property name="visible">True</property>
+                         <property name="sensitive">False</property>
+                         <property name="can_focus">True</property>
+                         <property name="label">gtk-media-next</property>
+                         <property name="use_stock">True</property>
+                         <property name="relief">GTK_RELIEF_NORMAL</property>
+                         <property name="focus_on_click">False</property>
+                       </widget>
+                       <packing>
+                         <property name="padding">0</property>
+                         <property name="expand">False</property>
+                         <property name="fill">False</property>
+                         <property name="pack_type">GTK_PACK_END</property>
+                       </packing>
+                     </child>
+
+                     <child>
+                       <widget class="GtkButton" id="button_previous">
+                         <property name="visible">True</property>
+                         <property name="sensitive">False</property>
+                         <property name="can_focus">True</property>
+                         <property name="label">gtk-media-previous</property>
+                         <property name="use_stock">True</property>
+                         <property name="relief">GTK_RELIEF_NORMAL</property>
+                         <property name="focus_on_click">False</property>
+                       </widget>
+                       <packing>
+                         <property name="padding">0</property>
+                         <property name="expand">False</property>
+                         <property name="fill">False</property>
+                         <property name="pack_type">GTK_PACK_END</property>
+                       </packing>
+                     </child>
+                   </widget>
+                   <packing>
+                     <property name="padding">0</property>
+                     <property name="expand">False</property>
+                     <property name="fill">False</property>
+                   </packing>
+                 </child>
+               </widget>
+               <packing>
+                 <property name="shrink">True</property>
+                 <property name="resize">True</property>
+               </packing>
+             </child>
+           </widget>
+           <packing>
+             <property name="padding">0</property>
+             <property name="expand">True</property>
+             <property name="fill">True</property>
+           </packing>
+         </child>
+       </widget>
+       <packing>
+         <property name="tab_expand">False</property>
+         <property name="tab_fill">True</property>
+       </packing>
+      </child>
+
+      <child>
+       <widget class="GtkLabel" id="label595">
+         <property name="visible">True</property>
+         <property name="label" translatable="yes">Search</property>
+         <property name="use_underline">False</property>
+         <property name="use_markup">False</property>
+         <property name="justify">GTK_JUSTIFY_LEFT</property>
+         <property name="wrap">False</property>
+         <property name="selectable">False</property>
+         <property name="xalign">0.5</property>
+         <property name="yalign">0.5</property>
+         <property name="xpad">0</property>
+         <property name="ypad">0</property>
+         <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+         <property name="width_chars">-1</property>
+         <property name="single_line_mode">False</property>
+         <property name="angle">0</property>
+       </widget>
+       <packing>
+         <property name="type">tab</property>
+       </packing>
+      </child>
+
+      <child>
+       <widget class="GtkTable" id="table7">
+         <property name="border_width">12</property>
+         <property name="visible">True</property>
+         <property name="n_rows">2</property>
+         <property name="n_columns">2</property>
+         <property name="homogeneous">False</property>
+         <property name="row_spacing">6</property>
+         <property name="column_spacing">6</property>
+
+         <child>
+           <widget class="GtkHBox" id="hbox143">
+             <property name="visible">True</property>
+             <property name="homogeneous">False</property>
+             <property name="spacing">6</property>
+
+             <child>
+               <widget class="GtkImage" id="image247">
+                 <property name="visible">True</property>
+                 <property name="stock">gtk-find</property>
+                 <property name="icon_size">4</property>
+                 <property name="xalign">0.5</property>
+                 <property name="yalign">0.5</property>
+                 <property name="xpad">0</property>
+                 <property name="ypad">0</property>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">False</property>
+                 <property name="fill">False</property>
+               </packing>
+             </child>
+
+             <child>
+               <widget class="GtkEntry" id="entry_chats">
+                 <property name="visible">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="editable">True</property>
+                 <property name="visibility">True</property>
+                 <property name="max_length">0</property>
+                 <property name="text" translatable="yes"></property>
+                 <property name="has_frame">True</property>
+                 <property name="invisible_char">*</property>
+                 <property name="activates_default">True</property>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">True</property>
+                 <property name="fill">True</property>
+               </packing>
+             </child>
+           </widget>
+           <packing>
+             <property name="left_attach">1</property>
+             <property name="right_attach">2</property>
+             <property name="top_attach">0</property>
+             <property name="bottom_attach">1</property>
+             <property name="x_options">fill</property>
+             <property name="y_options">fill</property>
+           </packing>
+         </child>
+
+         <child>
+           <widget class="GtkVBox" id="vbox191">
+             <property name="visible">True</property>
+             <property name="homogeneous">False</property>
+             <property name="spacing">6</property>
+
+             <child>
+               <widget class="GtkScrolledWindow" id="scrolledwindow13">
+                 <property name="width_request">150</property>
+                 <property name="visible">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+                 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                 <property name="shadow_type">GTK_SHADOW_IN</property>
+                 <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+                 <child>
+                   <widget class="GtkTreeView" id="treeview_chats">
+                     <property name="visible">True</property>
+                     <property name="can_focus">True</property>
+                     <property name="headers_visible">False</property>
+                     <property name="rules_hint">False</property>
+                     <property name="reorderable">False</property>
+                     <property name="enable_search">True</property>
+                     <property name="fixed_height_mode">False</property>
+                     <property name="hover_selection">False</property>
+                     <property name="hover_expand">False</property>
+                   </widget>
+                 </child>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">True</property>
+                 <property name="fill">True</property>
+               </packing>
+             </child>
+
+             <child>
+               <widget class="GtkCalendar" id="calendar_chats">
+                 <property name="visible">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="display_options">GTK_CALENDAR_SHOW_HEADING|GTK_CALENDAR_SHOW_DAY_NAMES</property>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">False</property>
+                 <property name="fill">False</property>
+               </packing>
+             </child>
+           </widget>
+           <packing>
+             <property name="left_attach">0</property>
+             <property name="right_attach">1</property>
+             <property name="top_attach">1</property>
+             <property name="bottom_attach">2</property>
+             <property name="x_options">fill</property>
+           </packing>
+         </child>
+
+         <child>
+           <widget class="GtkScrolledWindow" id="scrolledwindow_chats">
+             <property name="visible">True</property>
+             <property name="can_focus">True</property>
+             <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+             <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+             <property name="shadow_type">GTK_SHADOW_IN</property>
+             <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+             <child>
+               <placeholder/>
+             </child>
+           </widget>
+           <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>
+           </packing>
+         </child>
+
+         <child>
+           <widget class="GtkVBox" id="vbox_chats">
+             <property name="visible">True</property>
+             <property name="homogeneous">False</property>
+             <property name="spacing">6</property>
+
+             <child>
+               <placeholder/>
+             </child>
+           </widget>
+           <packing>
+             <property name="left_attach">0</property>
+             <property name="right_attach">1</property>
+             <property name="top_attach">0</property>
+             <property name="bottom_attach">1</property>
+             <property name="x_options">fill</property>
+             <property name="y_options">fill</property>
+           </packing>
+         </child>
+       </widget>
+       <packing>
+         <property name="tab_expand">False</property>
+         <property name="tab_fill">True</property>
+       </packing>
+      </child>
+
+      <child>
+       <widget class="GtkLabel" id="label596">
+         <property name="visible">True</property>
+         <property name="label" translatable="yes">Conversations</property>
+         <property name="use_underline">False</property>
+         <property name="use_markup">False</property>
+         <property name="justify">GTK_JUSTIFY_LEFT</property>
+         <property name="wrap">False</property>
+         <property name="selectable">False</property>
+         <property name="xalign">0.5</property>
+         <property name="yalign">0.5</property>
+         <property name="xpad">0</property>
+         <property name="ypad">0</property>
+         <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+         <property name="width_chars">-1</property>
+         <property name="single_line_mode">False</property>
+         <property name="angle">0</property>
+       </widget>
+       <packing>
+         <property name="type">tab</property>
+       </packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
diff --git a/libempathy-gtk/gossip-log-window.h b/libempathy-gtk/gossip-log-window.h
new file mode 100644 (file)
index 0000000..7801072
--- /dev/null
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006-2007 Imendio AB
+ * Copyright (C) 2007 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ * Authors: Martyn Russell <martyn@imendio.com>
+ *          Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#ifndef __GOSSIP_LOG_WINDOW_H__
+#define __GOSSIP_LOG_WINDOW_H__
+
+#include <libmissioncontrol/mc-account.h>
+
+G_BEGIN_DECLS
+
+void gossip_log_window_show (McAccount   *account,
+                            const gchar *chat_id,
+                            gboolean     chatroom,
+                            GtkWindow   *parent);
+
+G_END_DECLS
+
+#endif /* __GOSSIP_LOG_WINDOW_H__ */
index ca5297e..49c67ec 100644 (file)
@@ -39,6 +39,7 @@
 
 #define LOG_DIR_CREATE_MODE       (S_IRUSR | S_IWUSR | S_IXUSR)
 #define LOG_FILE_CREATE_MODE      (S_IRUSR | S_IWUSR)
+#define LOG_DIR_CHATROOMS         "chatrooms"
 #define LOG_FILENAME_SUFFIX       ".log"
 #define LOG_TIME_FORMAT_FULL      "%Y%m%dT%H:%M:%S"
 #define LOG_TIME_FORMAT           "%Y%m%d"
     "</log>\n"
 
 struct _EmpathyLogManagerPriv {
-       gboolean dummy;
+       gchar *basedir;
 };
 
-static void    empathy_log_manager_class_init         (EmpathyLogManagerClass *klass);
-static void    empathy_log_manager_init               (EmpathyLogManager      *manager);
-static void    log_manager_finalize                   (GObject                *object);
-static gchar * log_manager_get_dir                    (McAccount              *account,
-                                                      const gchar            *chat_id);
-static gchar * log_manager_get_filename               (McAccount              *account,
-                                                      const gchar            *chat_id);
-static gchar * log_manager_get_filename_for_date      (McAccount              *account,
-                                                      const gchar            *chat_id,
-                                                      const gchar            *date);
-static gchar * log_manager_get_timestamp_filename     (void);
-static gchar * log_manager_get_timestamp_from_message (GossipMessage          *message);
+static void                 empathy_log_manager_class_init         (EmpathyLogManagerClass *klass);
+static void                 empathy_log_manager_init               (EmpathyLogManager      *manager);
+static void                 log_manager_finalize                   (GObject                *object);
+static const gchar *        log_manager_get_basedir                (EmpathyLogManager      *manager);
+static GList *              log_manager_get_all_files              (EmpathyLogManager      *manager,
+                                                                   const gchar            *dir);
+static GList *              log_manager_get_chats                  (EmpathyLogManager      *manager,
+                                                                   const gchar            *dir,
+                                                                   gboolean                is_chatroom);
+static gchar *              log_manager_get_dir                    (EmpathyLogManager      *manager,
+                                                                   McAccount              *account,
+                                                                   const gchar            *chat_id,
+                                                                   gboolean                chatroom);
+static gchar *              log_manager_get_filename               (EmpathyLogManager      *manager,
+                                                                   McAccount              *account,
+                                                                   const gchar            *chat_id,
+                                                                   gboolean                chatroom);
+static gchar *              log_manager_get_filename_for_date      (EmpathyLogManager      *manager,
+                                                                   McAccount              *account,
+                                                                   const gchar            *chat_id,
+                                                                   gboolean                chatroom,
+                                                                   const gchar            *date);
+static gchar *              log_manager_get_timestamp_filename     (void);
+static gchar *              log_manager_get_timestamp_from_message (GossipMessage          *message);
+static EmpathyLogSearchHit *log_manager_search_hit_new             (EmpathyLogManager      *manager,
+                                                                   const gchar            *filename);
 
 G_DEFINE_TYPE (EmpathyLogManager, empathy_log_manager, G_TYPE_OBJECT);
 
@@ -87,6 +102,11 @@ empathy_log_manager_init (EmpathyLogManager *manager)
 static void
 log_manager_finalize (GObject *object)
 {
+       EmpathyLogManagerPriv *priv;
+
+       priv = GET_PRIV (object);
+
+       g_free (priv->basedir);
 }
 
 EmpathyLogManager *
@@ -107,6 +127,7 @@ empathy_log_manager_new (void)
 void
 empathy_log_manager_add_message (EmpathyLogManager *manager,
                                 const gchar       *chat_id,
+                                gboolean           chatroom,
                                 GossipMessage     *message)
 {
        FILE          *file;
@@ -132,7 +153,7 @@ empathy_log_manager_add_message (EmpathyLogManager *manager,
                return;
        }
 
-       filename = log_manager_get_filename (account, chat_id);
+       filename = log_manager_get_filename (manager, account, chat_id, chatroom);
 
        gossip_debug (DEBUG_DOMAIN, "Adding message: '%s' to file: '%s'",
                      body_str, filename);
@@ -154,24 +175,17 @@ empathy_log_manager_add_message (EmpathyLogManager *manager,
        timestamp = log_manager_get_timestamp_from_message (message);
 
        str = gossip_contact_get_name (sender);
-       if (!str) {
-               contact_name = g_strdup ("");
-       } else {
-               contact_name = g_markup_escape_text (str, -1);
-       }
+       contact_name = g_markup_escape_text (str, -1);
 
        str = gossip_contact_get_id (sender);
-       if (!str) {
-               contact_id = g_strdup ("");
-       } else {
-               contact_id = g_markup_escape_text (str, -1);
-       }
+       contact_id = g_markup_escape_text (str, -1);
 
        g_fprintf (file,
-                  "<message time='%s' id='%s' name='%s'>%s</message>\n" LOG_FOOTER,
+                  "<message time='%s' id='%s' name='%s' isuser='%s'>%s</message>\n" LOG_FOOTER,
                   timestamp,
                   contact_id,
                   contact_name,
+                  gossip_contact_is_user (sender) ? "true" : "false",
                   body);
 
        fclose (file);
@@ -185,7 +199,8 @@ empathy_log_manager_add_message (EmpathyLogManager *manager,
 GList *
 empathy_log_manager_get_dates (EmpathyLogManager *manager,
                               McAccount         *account,
-                              const gchar       *chat_id)
+                              const gchar       *chat_id,
+                              gboolean           chatroom)
 {
        GList       *dates = NULL;
        gchar       *date;
@@ -198,7 +213,7 @@ empathy_log_manager_get_dates (EmpathyLogManager *manager,
        g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
        g_return_val_if_fail (chat_id != NULL, NULL);
 
-       directory = log_manager_get_dir (account, chat_id);
+       directory = log_manager_get_dir (manager, account, chat_id, chatroom);
        if (!directory) {
                return NULL;
        }
@@ -238,6 +253,7 @@ GList *
 empathy_log_manager_get_messages_for_date (EmpathyLogManager *manager,
                                           McAccount         *account,
                                           const gchar       *chat_id,
+                                          gboolean           chatroom,
                                           const gchar       *date)
 {
        gchar            *filename;
@@ -251,7 +267,7 @@ empathy_log_manager_get_messages_for_date (EmpathyLogManager *manager,
        g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
        g_return_val_if_fail (chat_id != NULL, NULL);
 
-       filename = log_manager_get_filename_for_date (account, chat_id, date);
+       filename = log_manager_get_filename_for_date (manager, account, chat_id, chatroom, date);
 
        gossip_debug (DEBUG_DOMAIN, "Attempting to parse filename:'%s'...", filename);
 
@@ -291,6 +307,8 @@ empathy_log_manager_get_messages_for_date (EmpathyLogManager *manager,
                gchar         *sender_id;
                gchar         *sender_name;
                gchar         *body;
+               gchar         *is_user_str;
+               gboolean       is_user = FALSE;
 
                if (strcmp (node->name, "message") != 0) {
                        continue;
@@ -300,10 +318,16 @@ empathy_log_manager_get_messages_for_date (EmpathyLogManager *manager,
                time = xmlGetProp (node, "time");
                sender_id = xmlGetProp (node, "id");
                sender_name = xmlGetProp (node, "name");
+               is_user_str = xmlGetProp (node, "isuser");
+
+               if (is_user_str) {
+                       is_user = strcmp (is_user_str, "true") == 0;
+               }
 
                t = gossip_time_parse (time);
 
                sender = gossip_contact_new_full (account, sender_id, sender_name);
+               gossip_contact_set_is_user (sender, is_user);
                message = gossip_message_new (body);
                gossip_message_set_sender (message, sender);
                gossip_message_set_timestamp (message, t);
@@ -329,7 +353,8 @@ empathy_log_manager_get_messages_for_date (EmpathyLogManager *manager,
 GList *
 empathy_log_manager_get_last_messages (EmpathyLogManager *manager,
                                       McAccount         *account,
-                                      const gchar       *chat_id)
+                                      const gchar       *chat_id,
+                                      gboolean           chatroom)
 {
        GList *messages = NULL;
        GList *dates;
@@ -339,13 +364,14 @@ empathy_log_manager_get_last_messages (EmpathyLogManager *manager,
        g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
        g_return_val_if_fail (chat_id != NULL, NULL);
 
-       dates = empathy_log_manager_get_dates (manager, account, chat_id);
+       dates = empathy_log_manager_get_dates (manager, account, chat_id, chatroom);
 
        l = g_list_last (dates);
        if (l) {
                messages = empathy_log_manager_get_messages_for_date (manager,
                                                                      account,
                                                                      chat_id,
+                                                                     chatroom,
                                                                      l->data);
        }
 
@@ -355,22 +381,238 @@ empathy_log_manager_get_last_messages (EmpathyLogManager *manager,
        return messages;
 }
 
+GList *
+empathy_log_manager_get_chats (EmpathyLogManager *manager,
+                              McAccount         *account)
+{
+       const gchar *basedir;
+       gchar       *dir;
+
+       basedir = log_manager_get_basedir (manager);
+       dir = g_build_filename (basedir,
+                               mc_account_get_unique_name (account),
+                               NULL);
+
+       return log_manager_get_chats (manager, dir, FALSE);
+}
+
+GList *
+empathy_log_manager_search_new (EmpathyLogManager *manager,
+                               const gchar       *text)
+{
+       GList *files, *l;
+       GList *hits = NULL;
+       gchar *text_casefold;
+
+       g_return_val_if_fail (EMPATHY_IS_LOG_MANAGER (manager), NULL);
+       g_return_val_if_fail (!G_STR_EMPTY (text), NULL);
+
+       text_casefold = g_utf8_casefold (text, -1);
+
+       files = log_manager_get_all_files (manager, NULL);
+       gossip_debug (DEBUG_DOMAIN, "Found %d log files in total",
+                     g_list_length (files));
+
+       for (l = files; l; l = l->next) {
+               gchar       *filename;
+               GMappedFile *file;
+               gsize        length;
+               gchar       *contents;
+               gchar       *contents_casefold;
+
+               filename = l->data;
+
+               file = g_mapped_file_new (filename, FALSE, NULL);
+               if (!file) {
+                       continue;
+               }
+
+               length = g_mapped_file_get_length (file);
+               contents = g_mapped_file_get_contents (file);
+               contents_casefold = g_utf8_casefold (contents, length);
+
+               g_mapped_file_free (file);
+
+               if (strstr (contents_casefold, text_casefold)) {
+                       EmpathyLogSearchHit *hit;
+
+                       hit = log_manager_search_hit_new (manager, filename);
+
+                       if (hit) {
+                               hits = g_list_prepend (hits, hit);
+                               gossip_debug (DEBUG_DOMAIN, 
+                                             "Found text:'%s' in file:'%s' on date:'%s'...",
+                                             text, hit->filename, hit->date);
+                       }
+               }
+
+               g_free (contents_casefold);
+               g_free (filename);
+       }
+       g_list_free (files);
+
+       g_free (text_casefold);
+
+       return hits;
+}
+
+void
+empathy_log_manager_search_free (GList *hits)
+{
+       GList               *l;
+       EmpathyLogSearchHit *hit;
+
+       for (l = hits; l; l = l->next) {
+               hit = l->data;
+
+               if (hit->account) {
+                       g_object_unref (hit->account);
+               }
+
+               g_free (hit->date);
+               g_free (hit->filename);
+               g_free (hit->chat_id);
+
+               g_slice_free (EmpathyLogSearchHit, hit);
+       }
+       
+       g_list_free (hits);
+}
+
+/* Format is just date, 20061201. */
+gchar *
+empathy_log_manager_get_date_readable (const gchar *date)
+{
+       GossipTime t;
+
+       t = gossip_time_parse (date);
+
+       return gossip_time_to_string_local (t, "%a %d %b %Y");
+}
+
+static const gchar *
+log_manager_get_basedir (EmpathyLogManager *manager)
+{
+       EmpathyLogManagerPriv *priv;    
+
+       priv = GET_PRIV (manager);
+
+       if (priv->basedir) {
+               return priv->basedir;
+       }
+
+       priv->basedir = g_build_path (G_DIR_SEPARATOR_S,
+                                     g_get_home_dir (),
+                                     ".gnome2",
+                                     PACKAGE_NAME,
+                                     "logs",
+                                     NULL);
+
+       return priv->basedir;
+}
+
+static GList *
+log_manager_get_all_files (EmpathyLogManager *manager,
+                          const gchar       *dir)
+{
+       GDir        *gdir;
+       GList       *files = NULL;
+       const gchar *name;
+       
+       if (!dir) {
+               dir = log_manager_get_basedir (manager);
+       }
+
+       gdir = g_dir_open (dir, 0, NULL);
+       if (!gdir) {
+               return NULL;
+       }
+
+       while ((name = g_dir_read_name (gdir)) != NULL) {
+               gchar *filename;
+
+               filename = g_build_filename (dir, name, NULL);
+               if (g_str_has_suffix (filename, LOG_FILENAME_SUFFIX)) {
+                       files = g_list_prepend (files, filename);
+                       continue;
+               }
+
+               if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
+                       /* Recursively get all log files */
+                       files = g_list_concat (files, log_manager_get_all_files (manager, filename));
+               }
+               g_free (filename);
+       }
+
+       g_dir_close (gdir);
+
+       return files;
+}
+
+static GList *
+log_manager_get_chats (EmpathyLogManager *manager,
+                      const gchar       *dir,
+                      gboolean           is_chatroom)
+{
+       GDir        *gdir;
+       GList       *hits = NULL;
+       const gchar *name;
+
+       gdir = g_dir_open (dir, 0, NULL);
+       if (!gdir) {
+               return NULL;
+       }
+
+       while ((name = g_dir_read_name (gdir)) != NULL) {
+               EmpathyLogSearchHit *hit;
+               gchar *filename;
+
+               filename = g_build_filename (dir, name, NULL);
+               if (strcmp (name, LOG_DIR_CHATROOMS) == 0) {
+                       hits = g_list_concat (hits, log_manager_get_chats (manager, filename, TRUE));
+                       g_free (filename);
+                       continue;
+               }
+
+               hit = g_slice_new0 (EmpathyLogSearchHit);
+               hit->chat_id = g_strdup (name);
+               hit->is_chatroom = is_chatroom;
+
+               hits = g_list_prepend (hits, hit);
+       }
+
+       g_dir_close (gdir);
+
+       return hits;
+}
+
 static gchar *
-log_manager_get_dir (McAccount   *account,
-                    const gchar *chat_id)
+log_manager_get_dir (EmpathyLogManager *manager,
+                    McAccount         *account,
+                    const gchar       *chat_id,
+                    gboolean           chatroom)
 {
        const gchar *account_id;
        gchar       *basedir;
+       gchar       *str;
 
        account_id = mc_account_get_unique_name (account);
-       basedir = g_build_path (G_DIR_SEPARATOR_S,
-                               g_get_home_dir (),
-                               ".gnome2",
-                               PACKAGE_NAME,
-                               "logs",
-                               account_id,
-                               chat_id,
-                               NULL);
+       basedir = 
+       str = g_build_path (G_DIR_SEPARATOR_S,
+                           log_manager_get_basedir (manager),
+                           account_id,
+                           chat_id,
+                           NULL);
+
+       if (chatroom) {
+               basedir = g_build_path (G_DIR_SEPARATOR_S,
+                                       str,
+                                       LOG_DIR_CHATROOMS,
+                                       NULL);
+               g_free (str);
+       } else {
+               basedir = str;
+       }
 
        if (!g_file_test (basedir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
                gossip_debug (DEBUG_DOMAIN, "Creating directory:'%s'", basedir);
@@ -382,14 +624,16 @@ log_manager_get_dir (McAccount   *account,
 }
 
 static gchar *
-log_manager_get_filename (McAccount   *account,
-                         const gchar *chat_id)
+log_manager_get_filename (EmpathyLogManager *manager,
+                         McAccount         *account,
+                         const gchar       *chat_id,
+                         gboolean           chatroom)
 {
        gchar *basedir;
        gchar *timestamp;
        gchar *filename;
 
-       basedir = log_manager_get_dir (account, chat_id);
+       basedir = log_manager_get_dir (manager, account, chat_id, chatroom);
        timestamp = log_manager_get_timestamp_filename ();
        filename = g_build_filename (basedir, timestamp, NULL);
 
@@ -400,15 +644,17 @@ log_manager_get_filename (McAccount   *account,
 }
 
 static gchar *
-log_manager_get_filename_for_date (McAccount   *account,
-                                  const gchar *chat_id,
-                                  const gchar *date)
+log_manager_get_filename_for_date (EmpathyLogManager *manager,
+                                  McAccount         *account,
+                                  const gchar       *chat_id,
+                                  gboolean           chatroom,
+                                  const gchar       *date)
 {
        gchar *basedir;
        gchar *timestamp;
        gchar *filename;
 
-       basedir = log_manager_get_dir (account, chat_id);
+       basedir = log_manager_get_dir (manager, account, chat_id, chatroom);
        timestamp = g_strconcat (date, LOG_FILENAME_SUFFIX, NULL);
        filename = g_build_filename (basedir, timestamp, NULL);
 
@@ -445,3 +691,39 @@ log_manager_get_timestamp_from_message (GossipMessage *message)
        return gossip_time_to_string_utc (t, LOG_TIME_FORMAT_FULL);
 }
 
+static EmpathyLogSearchHit *
+log_manager_search_hit_new (EmpathyLogManager *manager,
+                           const gchar       *filename)
+{
+       EmpathyLogSearchHit  *hit;
+       const gchar          *account_name;
+       const gchar          *end;
+       gchar               **strv;
+       guint                 len;
+
+       if (!g_str_has_suffix (filename, LOG_FILENAME_SUFFIX)) {
+               return NULL;
+       }
+
+       strv = g_strsplit (filename, G_DIR_SEPARATOR_S, -1);
+       len = g_strv_length (strv);
+
+       hit = g_slice_new0 (EmpathyLogSearchHit);
+
+       end = strstr (strv[len-1], LOG_FILENAME_SUFFIX);
+       hit->date = g_strndup (strv[len-1], end - strv[len-1]);
+       hit->chat_id = g_strdup (strv[len-2]);
+       hit->is_chatroom = (strcmp (strv[len-3], LOG_DIR_CHATROOMS) == 0);
+       if (hit->is_chatroom) {
+               account_name = strv[len-4];
+       } else {
+               account_name = strv[len-3];
+       }
+       hit->account = mc_account_lookup (account_name);
+       hit->filename = g_strdup (filename);
+
+       g_strfreev (strv);
+
+       return hit;
+}
+
index 8df68d5..9a163fd 100644 (file)
@@ -41,6 +41,7 @@ G_BEGIN_DECLS
 typedef struct _EmpathyLogManager      EmpathyLogManager;
 typedef struct _EmpathyLogManagerClass EmpathyLogManagerClass;
 typedef struct _EmpathyLogManagerPriv  EmpathyLogManagerPriv;
+typedef struct _EmpathyLogSearchHit    EmpathyLogSearchHit;
 
 struct _EmpathyLogManager {
        GObject parent;
@@ -50,21 +51,39 @@ struct _EmpathyLogManagerClass {
        GObjectClass parent_class;
 };
 
+struct _EmpathyLogSearchHit {
+       McAccount *account;
+       gchar     *chat_id;
+       gboolean   is_chatroom;
+       gchar     *filename;
+       gchar     *date;
+};
+
 GType              empathy_log_manager_get_type              (void) G_GNUC_CONST;
 EmpathyLogManager *empathy_log_manager_new                   (void);
 void               empathy_log_manager_add_message           (EmpathyLogManager *manager,
                                                              const gchar       *chat_id,
+                                                             gboolean           chatroom,
                                                              GossipMessage     *message);
 GList *            empathy_log_manager_get_dates             (EmpathyLogManager *manager,
                                                              McAccount         *account,
-                                                             const gchar       *chat_id);
+                                                             const gchar       *chat_id,
+                                                             gboolean           chatroom);
 GList *            empathy_log_manager_get_messages_for_date (EmpathyLogManager *manager,
                                                              McAccount         *account,
                                                              const gchar       *chat_id,
+                                                             gboolean           chatroom,
                                                              const gchar       *date);
 GList *            empathy_log_manager_get_last_messages     (EmpathyLogManager *manager,
                                                              McAccount         *account,
-                                                             const gchar       *chat_id);
+                                                             const gchar       *chat_id,
+                                                             gboolean           chatroom);
+GList *            empathy_log_manager_get_chats             (EmpathyLogManager *manager,
+                                                             McAccount         *account);
+GList *            empathy_log_manager_search_new            (EmpathyLogManager *manager,
+                                                             const gchar       *text);
+void               empathy_log_manager_search_free           (GList             *hits);
+gchar *            empathy_log_manager_get_date_readable     (const gchar       *date);
 
 G_END_DECLS
 
index d63e825..a2852e1 100644 (file)
@@ -790,12 +790,13 @@ tp_chat_emit_message (EmpathyTpChat *chat,
        EmpathyTpChatPriv *priv;
        GossipMessage     *message;
        GossipContact     *sender;
+       GossipContact     *receiver;
 
        priv = GET_PRIV (chat);
 
+       receiver = empathy_tp_contact_list_get_user (priv->list);
        if (from_handle == 0) {
-               sender = empathy_tp_contact_list_get_user (priv->list);
-               g_object_ref (sender);
+               sender = g_object_ref (receiver);
        } else {
                sender = empathy_tp_contact_list_get_from_handle (priv->list,
                                                                  from_handle);
@@ -804,6 +805,7 @@ tp_chat_emit_message (EmpathyTpChat *chat,
        message = gossip_message_new (message_body);
        gossip_message_set_type (message, type);
        gossip_message_set_sender (message, sender);
+       gossip_message_set_receiver (message, receiver);
        gossip_message_set_timestamp (message, (GossipTime) timestamp);
 
        g_signal_emit (chat, signals[MESSAGE_RECEIVED], 0, message);
index c812790..b696e99 100644 (file)
@@ -380,8 +380,8 @@ empathy_tp_contact_list_new (McAccount *account)
                              error ? error->message : "No error given");
                g_clear_error (&error);
        } else {
-               /* FIXME: this adds the handle to the roster */
                priv->user_contact = empathy_tp_contact_list_get_from_handle (list, handle);
+               gossip_contact_set_is_user (priv->user_contact, TRUE);
        }
 
        return list;
@@ -1276,6 +1276,10 @@ tp_contact_list_name_updated_cb (GossipContact        *contact,
 
        priv = GET_PRIV (list);
        
+       if (!priv->aliasing_iface) {
+               return;
+       }
+
        handle = gossip_contact_get_handle (contact);
        new_name = gossip_contact_get_name (contact);
 
index 82147f6..53fe7e1 100644 (file)
@@ -30,7 +30,6 @@
 #include "gossip-contact.h"
 #include "gossip-utils.h"
 #include "gossip-debug.h"
-#include "empathy-contact-manager.h"
 
 #define DEBUG_DOMAIN "Contact"
 
@@ -47,6 +46,7 @@ struct _GossipContactPriv {
        GList              *groups;
        GossipSubscription  subscription;
        guint               handle;
+       gboolean            is_user;
 };
 
 static void contact_class_init    (GossipContactClass *class);
@@ -70,7 +70,8 @@ enum {
        PROP_PRESENCE,
        PROP_GROUPS,
        PROP_SUBSCRIPTION,
-       PROP_HANDLE
+       PROP_HANDLE,
+       PROP_IS_USER
 };
 
 static gpointer parent_class = NULL;
@@ -180,6 +181,13 @@ contact_class_init (GossipContactClass *class)
                                                            G_MAXUINT,
                                                            0,
                                                            G_PARAM_READWRITE));
+       g_object_class_install_property (object_class,
+                                        PROP_IS_USER,
+                                        g_param_spec_boolean ("is-user",
+                                                              "Contact is-user",
+                                                              "Is contact the user",
+                                                              FALSE,
+                                                              G_PARAM_READWRITE));
 
        g_type_class_add_private (object_class, sizeof (GossipContactPriv));
 }
@@ -187,17 +195,6 @@ contact_class_init (GossipContactClass *class)
 static void
 contact_init (GossipContact *contact)
 {
-       GossipContactPriv *priv;
-
-       priv = GET_PRIV (contact);
-
-       priv->id       = NULL;
-       priv->name     = NULL;
-       priv->avatar   = NULL;
-       priv->account  = NULL;
-       priv->presence = NULL;
-       priv->groups   = NULL;
-       priv->handle   = 0;
 }
 
 static void
@@ -269,6 +266,9 @@ contact_get_property (GObject    *object,
        case PROP_HANDLE:
                g_value_set_uint (value, priv->handle);
                break;
+       case PROP_IS_USER:
+               g_value_set_boolean (value, priv->is_user);
+               break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
                break;
@@ -318,6 +318,10 @@ contact_set_property (GObject      *object,
                gossip_contact_set_handle (GOSSIP_CONTACT (object),
                                           g_value_get_uint (value));
                break;
+       case PROP_IS_USER:
+               gossip_contact_set_is_user (GOSSIP_CONTACT (object),
+                                           g_value_get_boolean (value));
+               break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
                break;
@@ -449,6 +453,18 @@ gossip_contact_get_handle (GossipContact *contact)
        return priv->handle;
 }
 
+gboolean
+gossip_contact_is_user (GossipContact *contact)
+{
+       GossipContactPriv *priv;
+
+       g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), FALSE);
+
+       priv = GET_PRIV (contact);
+
+       return priv->is_user;
+}
+
 void
 gossip_contact_set_id (GossipContact *contact,
                       const gchar   *id)
@@ -629,6 +645,25 @@ gossip_contact_set_handle (GossipContact *contact,
        g_object_notify (G_OBJECT (contact), "handle");
 }
 
+void
+gossip_contact_set_is_user (GossipContact *contact,
+                           gboolean       is_user)
+{
+       GossipContactPriv *priv;
+
+       g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+       priv = GET_PRIV (contact);
+
+       if (priv->is_user == is_user) {
+               return;
+       }
+
+       priv->is_user = is_user;
+
+       g_object_notify (G_OBJECT (contact), "is-user");
+}
+
 void
 gossip_contact_add_group (GossipContact *contact,
                          const gchar   *group)
@@ -722,24 +757,6 @@ gossip_contact_get_status (GossipContact *contact)
        return gossip_presence_state_get_default_status (MC_PRESENCE_OFFLINE);
 }
 
-GossipContact *
-gossip_contact_get_user (GossipContact *contact)
-{
-       GossipContactPriv     *priv;
-       EmpathyContactManager *manager;
-       GossipContact         *user_contact;
-
-       g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
-
-       priv = GET_PRIV (contact);
-
-       manager = empathy_contact_manager_new ();
-       user_contact = empathy_contact_manager_get_user (manager, priv->account);
-       g_object_unref (manager);
-
-       return user_contact;
-}
-
 gboolean
 gossip_contact_equal (gconstpointer v1,
                      gconstpointer v2)
index 23da375..14b32fd 100644 (file)
@@ -69,6 +69,7 @@ GossipPresence *   gossip_contact_get_presence              (GossipContact
 GList *            gossip_contact_get_groups                (GossipContact      *contact);
 GossipSubscription gossip_contact_get_subscription          (GossipContact      *contact);
 guint              gossip_contact_get_handle                (GossipContact      *contact);
+gboolean           gossip_contact_is_user                   (GossipContact      *contact);
 void               gossip_contact_set_id                    (GossipContact      *contact,
                                                             const gchar        *id);
 void               gossip_contact_set_name                  (GossipContact      *contact,
@@ -85,6 +86,8 @@ void               gossip_contact_set_subscription          (GossipContact
                                                             GossipSubscription  subscription);
 void               gossip_contact_set_handle                (GossipContact      *contact,
                                                             guint               handle);
+void               gossip_contact_set_is_user               (GossipContact      *contact,
+                                                            gboolean            is_user);
 void               gossip_contact_add_group                 (GossipContact      *contact,
                                                             const gchar        *group);
 void               gossip_contact_remove_group              (GossipContact      *contact,
@@ -93,7 +96,6 @@ gboolean           gossip_contact_is_online                 (GossipContact
 gboolean           gossip_contact_is_in_group               (GossipContact      *contact,
                                                             const gchar        *group);
 const gchar *      gossip_contact_get_status                (GossipContact      *contact);
-GossipContact *    gossip_contact_get_user                  (GossipContact      *contact);
 gboolean           gossip_contact_equal                     (gconstpointer       v1,
                                                             gconstpointer       v2);
 guint              gossip_contact_hash                      (gconstpointer       key);
index c4844e6..a46a2a5 100644 (file)
@@ -33,6 +33,7 @@ typedef struct _GossipMessagePriv GossipMessagePriv;
 struct _GossipMessagePriv {
        GossipMessageType     type;
        GossipContact        *sender;
+       GossipContact        *receiver;
        gchar                *body;
        GossipTime            timestamp;
 
@@ -54,6 +55,7 @@ enum {
        PROP_0,
        PROP_TYPE,
        PROP_SENDER,
+       PROP_RECEIVER,
        PROP_BODY,
        PROP_TIMESTAMP,
 };
@@ -114,7 +116,13 @@ gossip_message_class_init (GossipMessageClass *class)
                                                              "The sender of the message",
                                                              GOSSIP_TYPE_CONTACT,
                                                              G_PARAM_READWRITE));
-
+       g_object_class_install_property (object_class,
+                                        PROP_RECEIVER,
+                                        g_param_spec_object ("receiver",
+                                                             "Message Receiver",
+                                                             "The receiver of the message",
+                                                             GOSSIP_TYPE_CONTACT,
+                                                             G_PARAM_READWRITE));
        g_object_class_install_property (object_class,
                                         PROP_BODY,
                                         g_param_spec_string ("body",
@@ -144,9 +152,6 @@ gossip_message_init (GossipMessage *message)
 
        priv = GET_PRIV (message);
 
-       priv->type = GOSSIP_MESSAGE_TYPE_NORMAL;
-       priv->sender = NULL;
-       priv->body = NULL;
        priv->timestamp = gossip_time_get_current ();
 }
 
@@ -160,6 +165,9 @@ gossip_message_finalize (GObject *object)
        if (priv->sender) {
                g_object_unref (priv->sender);
        }
+       if (priv->receiver) {
+               g_object_unref (priv->receiver);
+       }
 
        g_free (priv->body);
 
@@ -183,6 +191,9 @@ message_get_property (GObject    *object,
        case PROP_SENDER:
                g_value_set_object (value, priv->sender);
                break;
+       case PROP_RECEIVER:
+               g_value_set_object (value, priv->receiver);
+               break;
        case PROP_BODY:
                g_value_set_string (value, priv->body);
                break;
@@ -211,6 +222,10 @@ message_set_property (GObject      *object,
                gossip_message_set_sender (GOSSIP_MESSAGE (object),
                                           GOSSIP_CONTACT (g_value_get_object (value)));
                break;
+       case PROP_RECEIVER:
+               gossip_message_set_receiver (GOSSIP_MESSAGE (object),
+                                            GOSSIP_CONTACT (g_value_get_object (value)));
+               break;
        case PROP_BODY:
                gossip_message_set_body (GOSSIP_MESSAGE (object),
                                         g_value_get_string (value));
@@ -290,6 +305,39 @@ gossip_message_set_sender (GossipMessage *message, GossipContact *contact)
        g_object_notify (G_OBJECT (message), "sender");
 }
 
+GossipContact *
+gossip_message_get_receiver (GossipMessage *message)
+{
+       GossipMessagePriv *priv;
+
+       g_return_val_if_fail (GOSSIP_IS_MESSAGE (message), NULL);
+
+       priv = GET_PRIV (message);
+
+       return priv->receiver;
+}
+
+void
+gossip_message_set_receiver (GossipMessage *message, GossipContact *contact)
+{
+       GossipMessagePriv *priv;
+       GossipContact     *old_receiver;
+
+       g_return_if_fail (GOSSIP_IS_MESSAGE (message));
+       g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+       priv = GET_PRIV (message);
+
+       old_receiver = priv->receiver;
+       priv->receiver = g_object_ref (contact);
+
+       if (old_receiver) {
+               g_object_unref (old_receiver);
+       }
+
+       g_object_notify (G_OBJECT (message), "receiver");
+}
+
 const gchar *
 gossip_message_get_body (GossipMessage *message)
 {
index 770ecfe..aa49480 100644 (file)
@@ -66,6 +66,9 @@ void              gossip_message_set_type                (GossipMessage        *
 GossipContact *   gossip_message_get_sender              (GossipMessage        *message);
 void              gossip_message_set_sender              (GossipMessage        *message,
                                                          GossipContact        *contact);
+GossipContact *   gossip_message_get_receiver            (GossipMessage        *message);
+void              gossip_message_set_receiver            (GossipMessage        *message,
+                                                         GossipContact        *contact);
 const gchar *     gossip_message_get_body                (GossipMessage        *message);
 void              gossip_message_set_body                (GossipMessage        *message,
                                                          const gchar          *body);
index f64b2d6..92a66b2 100644 (file)
@@ -127,6 +127,7 @@ main (int argc, char *argv[])
                  0, G_OPTION_ARG_NONE, &no_connect,
                  N_("Don't connect on startup"),
                  NULL },
+               { NULL }
        };
 
        bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);