]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-log-window.c
Merge branch 'folks-async-and-prepare'
[empathy.git] / libempathy-gtk / empathy-log-window.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2006-2007 Imendio AB
4  * Copyright (C) 2007-2008 Collabora Ltd.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA  02110-1301  USA
20  *
21  * Authors: Martyn Russell <martyn@imendio.com>
22  *          Xavier Claessens <xclaesse@gmail.com>
23  */
24
25 #include "config.h"
26
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include <glib/gi18n-lib.h>
31 #include <gtk/gtk.h>
32
33 #include <telepathy-glib/account-manager.h>
34 #include <telepathy-logger/log-manager.h>
35
36 #include <libempathy/empathy-chatroom-manager.h>
37 #include <libempathy/empathy-chatroom.h>
38 #include <libempathy/empathy-message.h>
39 #include <libempathy/empathy-utils.h>
40 #include <libempathy/empathy-time.h>
41
42 #include "empathy-log-window.h"
43 #include "empathy-account-chooser.h"
44 #include "empathy-chat-view.h"
45 #include "empathy-theme-manager.h"
46 #include "empathy-ui-utils.h"
47
48 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
49 #include <libempathy/empathy-debug.h>
50
51 typedef struct {
52         GtkWidget         *window;
53
54         GtkWidget         *notebook;
55
56         GtkWidget         *entry_find;
57         GtkWidget         *button_find;
58         GtkWidget         *treeview_find;
59         GtkWidget         *scrolledwindow_find;
60         EmpathyChatView    *chatview_find;
61         GtkWidget         *button_previous;
62         GtkWidget         *button_next;
63
64         GtkWidget         *vbox_chats;
65         GtkWidget         *account_chooser_chats;
66         GtkWidget         *entry_chats;
67         GtkWidget         *calendar_chats;
68         GtkWidget         *treeview_chats;
69         GtkWidget         *scrolledwindow_chats;
70         EmpathyChatView    *chatview_chats;
71
72         gchar             *last_find;
73
74         TplLogManager     *log_manager;
75
76         /* Those are only used while waiting for the account chooser to be ready */
77         TpAccount         *selected_account;
78         gchar             *selected_chat_id;
79         gboolean          selected_is_chatroom;
80 } EmpathyLogWindow;
81
82 static void     log_window_destroy_cb                      (GtkWidget        *widget,
83                                                             EmpathyLogWindow *window);
84 static void     log_window_entry_find_changed_cb           (GtkWidget        *entry,
85                                                             EmpathyLogWindow *window);
86 static void     log_window_find_changed_cb                 (GtkTreeSelection *selection,
87                                                             EmpathyLogWindow *window);
88 static void     log_window_find_populate                   (EmpathyLogWindow *window,
89                                                             const gchar      *search_criteria);
90 static void     log_window_find_setup                      (EmpathyLogWindow *window);
91 static void     log_window_button_find_clicked_cb          (GtkWidget        *widget,
92                                                             EmpathyLogWindow *window);
93 static void     log_window_entry_find_activate_cb          (GtkWidget        *widget,
94                                                             EmpathyLogWindow *window);
95 static void     log_window_button_next_clicked_cb          (GtkWidget        *widget,
96                                                             EmpathyLogWindow *window);
97 static void     log_window_button_previous_clicked_cb      (GtkWidget        *widget,
98                                                             EmpathyLogWindow *window);
99 static void     log_window_chats_changed_cb                (GtkTreeSelection *selection,
100                                                             EmpathyLogWindow *window);
101 static void     log_window_chats_populate                  (EmpathyLogWindow *window);
102 static void     log_window_chats_setup                     (EmpathyLogWindow *window);
103 static void     log_window_chats_accounts_changed_cb       (GtkWidget        *combobox,
104                                                             EmpathyLogWindow *window);
105 static void     log_window_chats_set_selected              (EmpathyLogWindow *window,
106                                                             TpAccount        *account,
107                                                             const gchar      *chat_id,
108                                                             gboolean          is_chatroom);
109 static gboolean log_window_chats_get_selected              (EmpathyLogWindow *window,
110                                                             TpAccount       **account,
111                                                             gchar           **chat_id,
112                                                             gboolean         *is_chatroom);
113 static void     log_window_chats_get_messages              (EmpathyLogWindow *window,
114                                                             GDate      *date_to_show);
115 static void     log_window_calendar_chats_day_selected_cb  (GtkWidget        *calendar,
116                                                             EmpathyLogWindow *window);
117 static void     log_window_calendar_chats_month_changed_cb (GtkWidget        *calendar,
118                                                             EmpathyLogWindow *window);
119 static void     log_window_entry_chats_changed_cb          (GtkWidget        *entry,
120                                                             EmpathyLogWindow *window);
121 static void     log_window_entry_chats_activate_cb         (GtkWidget        *entry,
122                                                             EmpathyLogWindow *window);
123
124 enum {
125         COL_FIND_ACCOUNT_ICON,
126         COL_FIND_ACCOUNT_NAME,
127         COL_FIND_ACCOUNT,
128         COL_FIND_CHAT_NAME,
129         COL_FIND_CHAT_ID,
130         COL_FIND_IS_CHATROOM,
131         COL_FIND_DATE,
132         COL_FIND_DATE_READABLE,
133         COL_FIND_COUNT
134 };
135
136 enum {
137         COL_CHAT_ICON,
138         COL_CHAT_NAME,
139         COL_CHAT_ACCOUNT,
140         COL_CHAT_ID,
141         COL_CHAT_IS_CHATROOM,
142         COL_CHAT_COUNT
143 };
144
145 static EmpathyLogWindow *log_window = NULL;
146
147 static void
148 account_manager_prepared_cb (GObject *source_object,
149                              GAsyncResult *result,
150                              gpointer user_data)
151 {
152         TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
153         EmpathyLogWindow *window = user_data;
154         guint account_num;
155         GList *accounts;
156         GError *error = NULL;
157
158         if (log_window == NULL)
159                 return;
160
161         if (!tp_account_manager_prepare_finish (account_manager, result, &error)) {
162                 DEBUG ("Failed to prepare account manager: %s", error->message);
163                 g_error_free (error);
164                 return;
165         }
166
167         accounts = tp_account_manager_get_valid_accounts (account_manager);
168         account_num = g_list_length (accounts);
169         g_list_free (accounts);
170
171         if (account_num > 1) {
172                 gtk_widget_show (window->vbox_chats);
173                 gtk_widget_show (window->account_chooser_chats);
174         } else {
175                 gtk_widget_hide (window->vbox_chats);
176                 gtk_widget_hide (window->account_chooser_chats);
177         }
178 }
179
180 static void
181 account_chooser_ready_cb (EmpathyAccountChooser *chooser,
182                         EmpathyLogWindow *window)
183 {
184         gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), 1);
185         log_window_chats_set_selected (window, window->selected_account,
186                                        window->selected_chat_id, window->selected_is_chatroom);
187 }
188
189 GtkWidget *
190 empathy_log_window_show (TpAccount  *account,
191                         const gchar *chat_id,
192                         gboolean     is_chatroom,
193                         GtkWindow   *parent)
194 {
195         EmpathyAccountChooser   *account_chooser;
196         TpAccountManager        *account_manager;
197         GtkBuilder             *gui;
198         gchar                  *filename;
199         EmpathyLogWindow       *window;
200
201         if (log_window) {
202                 gtk_window_present (GTK_WINDOW (log_window->window));
203
204                 if (account && chat_id) {
205                         gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook), 1);
206                         log_window_chats_set_selected (log_window, account,
207                                                        chat_id, is_chatroom);
208                 }
209
210                 return log_window->window;
211         }
212
213         log_window = g_new0 (EmpathyLogWindow, 1);
214         log_window->log_manager = tpl_log_manager_dup_singleton ();
215
216         window = log_window;
217
218         filename = empathy_file_lookup ("empathy-log-window.ui",
219                                         "libempathy-gtk");
220         gui = empathy_builder_get_file (filename,
221                                        "log_window", &window->window,
222                                        "notebook", &window->notebook,
223                                        "entry_find", &window->entry_find,
224                                        "button_find", &window->button_find,
225                                        "treeview_find", &window->treeview_find,
226                                        "scrolledwindow_find", &window->scrolledwindow_find,
227                                        "button_previous", &window->button_previous,
228                                        "button_next", &window->button_next,
229                                        "entry_chats", &window->entry_chats,
230                                        "calendar_chats", &window->calendar_chats,
231                                        "vbox_chats", &window->vbox_chats,
232                                        "treeview_chats", &window->treeview_chats,
233                                        "scrolledwindow_chats", &window->scrolledwindow_chats,
234                                        NULL);
235         g_free (filename);
236
237         empathy_builder_connect (gui, window,
238                               "log_window", "destroy", log_window_destroy_cb,
239                               "entry_find", "changed", log_window_entry_find_changed_cb,
240                               "entry_find", "activate", log_window_entry_find_activate_cb,
241                               "button_previous", "clicked", log_window_button_previous_clicked_cb,
242                               "button_next", "clicked", log_window_button_next_clicked_cb,
243                               "button_find", "clicked", log_window_button_find_clicked_cb,
244                               "entry_chats", "changed", log_window_entry_chats_changed_cb,
245                               "entry_chats", "activate", log_window_entry_chats_activate_cb,
246                               NULL);
247
248         g_object_unref (gui);
249
250         g_object_add_weak_pointer (G_OBJECT (window->window),
251                                    (gpointer) &log_window);
252
253         /* We set this up here so we can block it when needed. */
254         g_signal_connect (window->calendar_chats, "day-selected",
255                           G_CALLBACK (log_window_calendar_chats_day_selected_cb),
256                           window);
257         g_signal_connect (window->calendar_chats, "month-changed",
258                           G_CALLBACK (log_window_calendar_chats_month_changed_cb),
259                           window);
260
261         /* Configure Search EmpathyChatView */
262         window->chatview_find = empathy_theme_manager_create_view (empathy_theme_manager_get ());
263         gtk_container_add (GTK_CONTAINER (window->scrolledwindow_find),
264                            GTK_WIDGET (window->chatview_find));
265         gtk_widget_show (GTK_WIDGET (window->chatview_find));
266
267         /* Configure Contacts EmpathyChatView */
268         window->chatview_chats = empathy_theme_manager_create_view (empathy_theme_manager_get ());
269         gtk_container_add (GTK_CONTAINER (window->scrolledwindow_chats),
270                            GTK_WIDGET (window->chatview_chats));
271         gtk_widget_show (GTK_WIDGET (window->chatview_chats));
272
273         /* Account chooser for chats */
274         window->account_chooser_chats = empathy_account_chooser_new ();
275         account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats);
276
277         gtk_box_pack_start (GTK_BOX (window->vbox_chats),
278                             window->account_chooser_chats,
279                             FALSE, TRUE, 0);
280
281         g_signal_connect (window->account_chooser_chats, "changed",
282                           G_CALLBACK (log_window_chats_accounts_changed_cb),
283                           window);
284
285         /* Populate */
286         account_manager = tp_account_manager_dup ();
287         tp_account_manager_prepare_async (account_manager, NULL,
288                                           account_manager_prepared_cb, window);
289         g_object_unref (account_manager);
290
291         /* Search List */
292         log_window_find_setup (window);
293
294         /* Contacts */
295         log_window_chats_setup (window);
296         log_window_chats_populate (window);
297
298         if (account && chat_id) {
299                 window->selected_account = account;
300                 window->selected_chat_id = g_strdup (chat_id);
301                 window->selected_is_chatroom = is_chatroom;
302
303                 if (empathy_account_chooser_is_ready (account_chooser))
304                         account_chooser_ready_cb (account_chooser, window);
305                 else
306                         /* Chat will be selected once the account chooser is ready */
307                         g_signal_connect (account_chooser, "ready",
308                                           G_CALLBACK (account_chooser_ready_cb), window);
309         }
310
311         if (parent) {
312                 gtk_window_set_transient_for (GTK_WINDOW (window->window),
313                                               GTK_WINDOW (parent));
314         }
315
316         gtk_widget_show (window->window);
317
318         return window->window;
319 }
320
321 static void
322 log_window_destroy_cb (GtkWidget       *widget,
323                        EmpathyLogWindow *window)
324 {
325         g_free (window->last_find);
326         g_object_unref (window->log_manager);
327         g_free (window->selected_chat_id);
328
329         g_free (window);
330 }
331
332 /*
333  * Search code.
334  */
335 static void
336 log_window_entry_find_changed_cb (GtkWidget       *entry,
337                                   EmpathyLogWindow *window)
338 {
339         const gchar *str;
340         gboolean     is_sensitive = TRUE;
341
342         str = gtk_entry_get_text (GTK_ENTRY (window->entry_find));
343
344         is_sensitive &= !EMP_STR_EMPTY (str);
345         is_sensitive &=
346                 !window->last_find ||
347                 (window->last_find && strcmp (window->last_find, str) != 0);
348
349         gtk_widget_set_sensitive (window->button_find, is_sensitive);
350 }
351
352 static void
353 got_messages_for_date_cb (GObject *manager,
354                        GAsyncResult *result,
355                        gpointer user_data)
356 {
357         EmpathyLogWindow *window = user_data;
358         GList         *messages;
359         GList         *l;
360         gboolean       can_do_previous;
361         gboolean       can_do_next;
362         GError        *error = NULL;
363
364         if (log_window == NULL)
365                 return;
366
367         if (!tpl_log_manager_get_messages_for_date_finish (TPL_LOG_MANAGER (manager),
368                 result, &messages, &error)) {
369                         DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
370                                         error->message);
371                         empathy_chat_view_append_event (window->chatview_find,
372                                         "Unable to retrieve messages for the selected date");
373                         g_error_free (error);
374                         return;
375         }
376
377         for (l = messages; l; l = l->next) {
378                         EmpathyMessage *message;
379
380                         g_assert (TPL_IS_ENTRY (l->data));
381
382                         message = empathy_message_from_tpl_log_entry (l->data);
383                         g_object_unref (l->data);
384                         empathy_chat_view_append_message (window->chatview_find, message);
385                         g_object_unref (message);
386         }
387         g_list_free (messages);
388
389         /* Scroll to the most recent messages */
390         empathy_chat_view_scroll (window->chatview_find, TRUE);
391
392         /* Highlight and find messages */
393         empathy_chat_view_highlight (window->chatview_find,
394                         window->last_find,
395                         FALSE);
396         empathy_chat_view_find_next (window->chatview_find,
397                         window->last_find,
398                         TRUE,
399                         FALSE);
400         empathy_chat_view_find_abilities (window->chatview_find,
401                         window->last_find,
402                         FALSE,
403                         &can_do_previous,
404                         &can_do_next);
405         gtk_widget_set_sensitive (window->button_previous, can_do_previous);
406         gtk_widget_set_sensitive (window->button_next, can_do_next);
407         gtk_widget_set_sensitive (window->button_find, FALSE);
408 }
409
410 static GDate *
411 gdate_from_str (const gchar *str)
412 {
413         guint u;
414         guint day, month, year;
415
416         if (sscanf (str, "%u", &u) != 1)
417                 return NULL;
418
419         day = (u % 100);
420         month = ((u / 100) % 100);
421         year = (u / 10000);
422
423         if (!g_date_valid_dmy (day, month, year))
424                 return NULL;
425
426         return g_date_new_dmy (day, month, year);
427 }
428
429 static void
430 log_window_find_changed_cb (GtkTreeSelection *selection,
431                             EmpathyLogWindow  *window)
432 {
433         GtkTreeView   *view;
434         GtkTreeModel  *model;
435         GtkTreeIter    iter;
436         TpAccount     *account;
437         gchar         *chat_id;
438         gboolean       is_chatroom;
439         gchar         *date;
440         GDate         *gdate;
441
442         /* Get selected information */
443         view = GTK_TREE_VIEW (window->treeview_find);
444         model = gtk_tree_view_get_model (view);
445
446         if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
447                 gtk_widget_set_sensitive (window->button_previous, FALSE);
448                 gtk_widget_set_sensitive (window->button_next, FALSE);
449
450                 empathy_chat_view_clear (window->chatview_find);
451
452                 return;
453         }
454
455         gtk_widget_set_sensitive (window->button_previous, TRUE);
456         gtk_widget_set_sensitive (window->button_next, TRUE);
457
458         gtk_tree_model_get (model, &iter,
459                             COL_FIND_ACCOUNT, &account,
460                             COL_FIND_CHAT_ID, &chat_id,
461                             COL_FIND_IS_CHATROOM, &is_chatroom,
462                             COL_FIND_DATE, &date,
463                             -1);
464
465         /* Clear all current messages shown in the textview */
466         empathy_chat_view_clear (window->chatview_find);
467
468         /* Turn off scrolling temporarily */
469         empathy_chat_view_scroll (window->chatview_find, FALSE);
470
471         /* Get messages */
472         gdate = gdate_from_str (date);
473
474         if (gdate != NULL) {
475                 tpl_log_manager_get_messages_for_date_async (window->log_manager,
476                                                                       account,
477                                                                       chat_id,
478                                                                       is_chatroom,
479                                                                       gdate,
480                                                                       got_messages_for_date_cb,
481                                                                       window);
482
483                 g_date_free (gdate);
484         }
485
486         g_object_unref (account);
487         g_free (date);
488         g_free (chat_id);
489 }
490
491
492 static void
493 log_manager_searched_new_cb (GObject *manager,
494                              GAsyncResult *result,
495                              gpointer user_data)
496 {
497         GList               *hits;
498         GList               *l;
499         GtkTreeIter          iter;
500         GtkListStore        *store = user_data;
501         GError              *error = NULL;
502
503         if (log_window == NULL)
504                 return;
505
506         if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager), result,
507                 &hits, &error)) {
508                         DEBUG ("%s. Aborting", error->message);
509                         g_error_free (error);
510                         return;
511         }
512
513         for (l = hits; l; l = l->next) {
514                         TplLogSearchHit *hit;
515                         const gchar         *account_name;
516                         const gchar         *account_icon;
517                         gchar               date_readable[255];
518                         gchar               tmp[255];
519
520                         hit = l->data;
521
522                         /* Protect against invalid data (corrupt or old log files. */
523                         if (!hit->account || !hit->chat_id) {
524                                         continue;
525                         }
526
527                         g_date_strftime (date_readable, sizeof (date_readable),
528                                 EMPATHY_TIME_FORMAT_DISPLAY_LONG, hit->date);
529
530                         g_date_strftime (tmp, sizeof (tmp),
531                                 "%Y%m%d", hit->date);
532
533                         account_name = tp_account_get_display_name (hit->account);
534                         account_icon = tp_account_get_icon_name (hit->account);
535
536                         gtk_list_store_append (store, &iter);
537                         gtk_list_store_set (store, &iter,
538                                         COL_FIND_ACCOUNT_ICON, account_icon,
539                                         COL_FIND_ACCOUNT_NAME, account_name,
540                                         COL_FIND_ACCOUNT, hit->account,
541                                         COL_FIND_CHAT_NAME, hit->chat_id, /* FIXME */
542                                         COL_FIND_CHAT_ID, hit->chat_id,
543                                         COL_FIND_IS_CHATROOM, hit->is_chatroom,
544                                         COL_FIND_DATE, tmp,
545                                         COL_FIND_DATE_READABLE, date_readable,
546                                         -1);
547
548                         /* FIXME: Update COL_FIND_CHAT_NAME */
549                         if (hit->is_chatroom) {
550                         } else {
551                         }
552         }
553
554         if (hits) {
555                         tpl_log_manager_search_free (hits);
556         }
557 }
558
559 static void
560 log_window_find_populate (EmpathyLogWindow *window,
561                           const gchar     *search_criteria)
562 {
563         GtkTreeView        *view;
564         GtkTreeModel       *model;
565         GtkTreeSelection   *selection;
566         GtkListStore       *store;
567
568         view = GTK_TREE_VIEW (window->treeview_find);
569         model = gtk_tree_view_get_model (view);
570         selection = gtk_tree_view_get_selection (view);
571         store = GTK_LIST_STORE (model);
572
573         empathy_chat_view_clear (window->chatview_find);
574
575         gtk_list_store_clear (store);
576
577         if (EMP_STR_EMPTY (search_criteria)) {
578                 /* Just clear the search. */
579                 return;
580         }
581
582         tpl_log_manager_search_async (window->log_manager, search_criteria,
583                         log_manager_searched_new_cb, (gpointer) store);
584 }
585
586 static void
587 log_window_find_setup (EmpathyLogWindow *window)
588 {
589         GtkTreeView       *view;
590         GtkTreeModel      *model;
591         GtkTreeSelection  *selection;
592         GtkTreeSortable   *sortable;
593         GtkTreeViewColumn *column;
594         GtkListStore      *store;
595         GtkCellRenderer   *cell;
596         gint               offset;
597
598         view = GTK_TREE_VIEW (window->treeview_find);
599         selection = gtk_tree_view_get_selection (view);
600
601         /* New store */
602         store = gtk_list_store_new (COL_FIND_COUNT,
603                                     G_TYPE_STRING,          /* account icon name */
604                                     G_TYPE_STRING,          /* account name */
605                                     TP_TYPE_ACCOUNT,        /* account */
606                                     G_TYPE_STRING,          /* chat name */
607                                     G_TYPE_STRING,          /* chat id */
608                                     G_TYPE_BOOLEAN,         /* is chatroom */
609                                     G_TYPE_STRING,          /* date */
610                                     G_TYPE_STRING);         /* date_readable */
611
612         model = GTK_TREE_MODEL (store);
613         sortable = GTK_TREE_SORTABLE (store);
614
615         gtk_tree_view_set_model (view, model);
616
617         /* New column */
618         column = gtk_tree_view_column_new ();
619
620         cell = gtk_cell_renderer_pixbuf_new ();
621         gtk_tree_view_column_pack_start (column, cell, FALSE);
622         gtk_tree_view_column_add_attribute (column, cell,
623                                             "icon-name",
624                                             COL_FIND_ACCOUNT_ICON);
625
626         cell = gtk_cell_renderer_text_new ();
627         gtk_tree_view_column_pack_start (column, cell, TRUE);
628         gtk_tree_view_column_add_attribute (column, cell,
629                                             "text",
630                                             COL_FIND_ACCOUNT_NAME);
631
632         gtk_tree_view_column_set_title (column, _("Account"));
633         gtk_tree_view_append_column (view, column);
634
635         gtk_tree_view_column_set_resizable (column, TRUE);
636         gtk_tree_view_column_set_clickable (column, TRUE);
637
638         cell = gtk_cell_renderer_text_new ();
639         offset = gtk_tree_view_insert_column_with_attributes (view, -1, _("Conversation"),
640                                                               cell, "text", COL_FIND_CHAT_NAME,
641                                                               NULL);
642
643         column = gtk_tree_view_get_column (view, offset - 1);
644         gtk_tree_view_column_set_sort_column_id (column, COL_FIND_CHAT_NAME);
645         gtk_tree_view_column_set_resizable (column, TRUE);
646         gtk_tree_view_column_set_clickable (column, TRUE);
647
648         cell = gtk_cell_renderer_text_new ();
649         offset = gtk_tree_view_insert_column_with_attributes (view, -1, _("Date"),
650                                                               cell, "text", COL_FIND_DATE_READABLE,
651                                                               NULL);
652
653         column = gtk_tree_view_get_column (view, offset - 1);
654         gtk_tree_view_column_set_sort_column_id (column, COL_FIND_DATE);
655         gtk_tree_view_column_set_resizable (column, TRUE);
656         gtk_tree_view_column_set_clickable (column, TRUE);
657
658         /* Set up treeview properties */
659         gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
660         gtk_tree_sortable_set_sort_column_id (sortable,
661                                               COL_FIND_DATE,
662                                               GTK_SORT_ASCENDING);
663
664         /* Set up signals */
665         g_signal_connect (selection, "changed",
666                           G_CALLBACK (log_window_find_changed_cb),
667                           window);
668
669         g_object_unref (store);
670 }
671
672 static void
673 start_find_search (EmpathyLogWindow *window)
674 {
675         const gchar *str;
676
677         str = gtk_entry_get_text (GTK_ENTRY (window->entry_find));
678
679         /* Don't find the same crap again */
680         if (window->last_find && strcmp (window->last_find, str) == 0) {
681                 return;
682         }
683
684         g_free (window->last_find);
685         window->last_find = g_strdup (str);
686
687         log_window_find_populate (window, str);
688 }
689
690 static void
691 log_window_button_find_clicked_cb (GtkWidget       *widget,
692                                    EmpathyLogWindow *window)
693 {
694         start_find_search (window);
695 }
696
697 static void
698 log_window_entry_find_activate_cb (GtkWidget *entry,
699                                    EmpathyLogWindow *self)
700 {
701         start_find_search (self);
702 }
703
704 static void
705 log_window_button_next_clicked_cb (GtkWidget       *widget,
706                                    EmpathyLogWindow *window)
707 {
708         if (window->last_find) {
709                 gboolean can_do_previous;
710                 gboolean can_do_next;
711
712                 empathy_chat_view_find_next (window->chatview_find,
713                                             window->last_find,
714                                             FALSE,
715                                             FALSE);
716                 empathy_chat_view_find_abilities (window->chatview_find,
717                                                  window->last_find,
718                                                  FALSE,
719                                                  &can_do_previous,
720                                                  &can_do_next);
721                 gtk_widget_set_sensitive (window->button_previous, can_do_previous);
722                 gtk_widget_set_sensitive (window->button_next, can_do_next);
723         }
724 }
725
726 static void
727 log_window_button_previous_clicked_cb (GtkWidget       *widget,
728                                        EmpathyLogWindow *window)
729 {
730         if (window->last_find) {
731                 gboolean can_do_previous;
732                 gboolean can_do_next;
733
734                 empathy_chat_view_find_previous (window->chatview_find,
735                                                 window->last_find,
736                                                 FALSE,
737                                                 FALSE);
738                 empathy_chat_view_find_abilities (window->chatview_find,
739                                                  window->last_find,
740                                                  FALSE,
741                                                  &can_do_previous,
742                                                  &can_do_next);
743                 gtk_widget_set_sensitive (window->button_previous, can_do_previous);
744                 gtk_widget_set_sensitive (window->button_next, can_do_next);
745         }
746 }
747
748 /*
749  * Chats Code
750  */
751
752 static void
753 log_window_chats_changed_cb (GtkTreeSelection *selection,
754                              EmpathyLogWindow  *window)
755 {
756         /* Use last date by default */
757         gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats));
758
759         log_window_chats_get_messages (window, NULL);
760 }
761
762 static void
763 log_manager_got_chats_cb (GObject *manager,
764                        GAsyncResult *result,
765                        gpointer user_data)
766 {
767         EmpathyLogWindow      *window = user_data;
768         GList                 *chats;
769         GList                 *l;
770         EmpathyAccountChooser *account_chooser;
771         TpAccount             *account;
772         GtkTreeView           *view;
773         GtkTreeModel          *model;
774         GtkTreeSelection      *selection;
775         GtkListStore          *store;
776         GtkTreeIter            iter;
777         GError                *error = NULL;
778
779         if (log_window == NULL)
780                 return;
781
782         if (!tpl_log_manager_get_chats_finish (TPL_LOG_MANAGER (manager),
783                 result, &chats, &error)) {
784                         DEBUG ("%s. Aborting", error->message);
785                         g_error_free (error);
786                         return;
787         }
788
789         account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats);
790         account = empathy_account_chooser_dup_account (account_chooser);
791
792         view = GTK_TREE_VIEW (window->treeview_chats);
793         model = gtk_tree_view_get_model (view);
794         selection = gtk_tree_view_get_selection (view);
795         store = GTK_LIST_STORE (model);
796
797         for (l = chats; l; l = l->next) {
798                         TplLogSearchHit *hit;
799
800                         hit = l->data;
801
802                         gtk_list_store_append (store, &iter);
803                         gtk_list_store_set (store, &iter,
804                                         COL_CHAT_ICON, "empathy-available", /* FIXME */
805                                         COL_CHAT_NAME, hit->chat_id,
806                                         COL_CHAT_ACCOUNT, account,
807                                         COL_CHAT_ID, hit->chat_id,
808                                         COL_CHAT_IS_CHATROOM, hit->is_chatroom,
809                                         -1);
810
811                         /* FIXME: Update COL_CHAT_ICON/NAME */
812                         if (hit->is_chatroom) {
813                         } else {
814                         }
815         }
816         tpl_log_manager_search_free (chats);
817
818         /* Unblock signals */
819         g_signal_handlers_unblock_by_func (selection,
820                         log_window_chats_changed_cb,
821                         window);
822
823         g_object_unref (account);
824 }
825
826 static void
827 log_window_chats_populate (EmpathyLogWindow *window)
828 {
829         EmpathyAccountChooser *account_chooser;
830         TpAccount             *account;
831
832         GtkTreeView          *view;
833         GtkTreeModel         *model;
834         GtkTreeSelection     *selection;
835         GtkListStore         *store;
836
837         account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats);
838         account = empathy_account_chooser_dup_account (account_chooser);
839
840         view = GTK_TREE_VIEW (window->treeview_chats);
841         model = gtk_tree_view_get_model (view);
842         selection = gtk_tree_view_get_selection (view);
843         store = GTK_LIST_STORE (model);
844
845         if (account == NULL) {
846                 gtk_list_store_clear (store);
847                 return;
848         }
849
850         /* Block signals to stop the logs being retrieved prematurely */
851         g_signal_handlers_block_by_func (selection,
852                                          log_window_chats_changed_cb,
853                                          window);
854
855         gtk_list_store_clear (store);
856
857         tpl_log_manager_get_chats_async (window->log_manager, account,
858                         log_manager_got_chats_cb, (gpointer) window);
859 }
860
861 static void
862 log_window_chats_setup (EmpathyLogWindow *window)
863 {
864         GtkTreeView       *view;
865         GtkTreeModel      *model;
866         GtkTreeSelection  *selection;
867         GtkTreeSortable   *sortable;
868         GtkTreeViewColumn *column;
869         GtkListStore      *store;
870         GtkCellRenderer   *cell;
871
872         view = GTK_TREE_VIEW (window->treeview_chats);
873         selection = gtk_tree_view_get_selection (view);
874
875         /* new store */
876         store = gtk_list_store_new (COL_CHAT_COUNT,
877                                     G_TYPE_STRING,        /* icon */
878                                     G_TYPE_STRING,        /* name */
879                                     TP_TYPE_ACCOUNT,      /* account */
880                                     G_TYPE_STRING,        /* id */
881                                     G_TYPE_BOOLEAN);      /* is chatroom */
882
883         model = GTK_TREE_MODEL (store);
884         sortable = GTK_TREE_SORTABLE (store);
885
886         gtk_tree_view_set_model (view, model);
887
888         /* new column */
889         column = gtk_tree_view_column_new ();
890
891         cell = gtk_cell_renderer_pixbuf_new ();
892         gtk_tree_view_column_pack_start (column, cell, FALSE);
893         gtk_tree_view_column_add_attribute (column, cell,
894                                             "icon-name",
895                                             COL_CHAT_ICON);
896
897         cell = gtk_cell_renderer_text_new ();
898         g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
899         gtk_tree_view_column_pack_start (column, cell, TRUE);
900         gtk_tree_view_column_add_attribute (column, cell,
901                                             "text",
902                                             COL_CHAT_NAME);
903
904         gtk_tree_view_append_column (view, column);
905
906         /* set up treeview properties */
907         gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
908         gtk_tree_sortable_set_sort_column_id (sortable,
909                                               COL_CHAT_NAME,
910                                               GTK_SORT_ASCENDING);
911
912         /* set up signals */
913         g_signal_connect (selection, "changed",
914                           G_CALLBACK (log_window_chats_changed_cb),
915                           window);
916
917         g_object_unref (store);
918 }
919
920 static void
921 log_window_chats_accounts_changed_cb (GtkWidget       *combobox,
922                                       EmpathyLogWindow *window)
923 {
924         /* Clear all current messages shown in the textview */
925         empathy_chat_view_clear (window->chatview_chats);
926
927         log_window_chats_populate (window);
928 }
929
930 static void
931 log_window_chats_set_selected  (EmpathyLogWindow *window,
932                                 TpAccount        *account,
933                                 const gchar     *chat_id,
934                                 gboolean         is_chatroom)
935 {
936         EmpathyAccountChooser *account_chooser;
937         GtkTreeView          *view;
938         GtkTreeModel         *model;
939         GtkTreeSelection     *selection;
940         GtkTreeIter           iter;
941         GtkTreePath          *path;
942         gboolean              ok;
943
944         account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats);
945         empathy_account_chooser_set_account (account_chooser, account);
946
947         view = GTK_TREE_VIEW (window->treeview_chats);
948         model = gtk_tree_view_get_model (view);
949         selection = gtk_tree_view_get_selection (view);
950
951         if (!gtk_tree_model_get_iter_first (model, &iter)) {
952                 return;
953         }
954
955         for (ok = TRUE; ok; ok = gtk_tree_model_iter_next (model, &iter)) {
956                 TpAccount *this_account;
957                 gchar     *this_chat_id;
958                 gboolean   this_is_chatroom;
959
960                 gtk_tree_model_get (model, &iter,
961                                     COL_CHAT_ACCOUNT, &this_account,
962                                     COL_CHAT_ID, &this_chat_id,
963                                     COL_CHAT_IS_CHATROOM, &this_is_chatroom,
964                                     -1);
965
966                 if (this_account == account &&
967                     strcmp (this_chat_id, chat_id) == 0 &&
968                     this_is_chatroom == is_chatroom) {
969                         gtk_tree_selection_select_iter (selection, &iter);
970                         path = gtk_tree_model_get_path (model, &iter);
971                         gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 0.5, 0.0);
972                         gtk_tree_path_free (path);
973                         g_object_unref (this_account);
974                         g_free (this_chat_id);
975                         break;
976                 }
977
978                 g_object_unref (this_account);
979                 g_free (this_chat_id);
980         }
981 }
982
983 static gboolean
984 log_window_chats_get_selected (EmpathyLogWindow  *window,
985                                TpAccount       **account,
986                                gchar           **chat_id,
987                                gboolean         *is_chatroom)
988 {
989         GtkTreeView      *view;
990         GtkTreeModel     *model;
991         GtkTreeSelection *selection;
992         GtkTreeIter       iter;
993         gchar            *id = NULL;
994         TpAccount        *acc = NULL;
995         gboolean          room = FALSE;
996
997         view = GTK_TREE_VIEW (window->treeview_chats);
998         model = gtk_tree_view_get_model (view);
999         selection = gtk_tree_view_get_selection (view);
1000
1001         if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
1002                 return FALSE;
1003         }
1004
1005         gtk_tree_model_get (model, &iter,
1006                             COL_CHAT_ACCOUNT, &acc,
1007                             COL_CHAT_ID, &id,
1008                             COL_CHAT_IS_CHATROOM, &room,
1009                             -1);
1010
1011         if (chat_id) {
1012                 *chat_id = id;
1013         } else {
1014                 g_free (id);
1015         }
1016         if (account) {
1017                 *account = acc;
1018         } else {
1019                 g_object_unref (acc);
1020         }
1021         if (is_chatroom) {
1022                 *is_chatroom = room;
1023         }
1024
1025         return TRUE;
1026 }
1027
1028 static void
1029 log_window_got_messages_for_date_cb (GObject *manager,
1030     GAsyncResult *result,
1031     gpointer user_data)
1032 {
1033   EmpathyLogWindow *window = user_data;
1034   GList *messages;
1035   GList *l;
1036   GError *error = NULL;
1037
1038   if (log_window == NULL)
1039     return;
1040
1041   if (!tpl_log_manager_get_messages_for_date_finish (TPL_LOG_MANAGER (manager),
1042         result, &messages, &error)) {
1043       DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
1044           error->message);
1045       empathy_chat_view_append_event (window->chatview_find,
1046           "Unable to retrieve messages for the selected date");
1047       g_error_free (error);
1048       return;
1049   }
1050
1051   for (l = messages; l; l = l->next) {
1052       EmpathyMessage *message = empathy_message_from_tpl_log_entry (l->data);
1053       g_object_unref (l->data);
1054       empathy_chat_view_append_message (window->chatview_chats,
1055           message);
1056       g_object_unref (message);
1057   }
1058   g_list_free (messages);
1059
1060   /* Turn back on scrolling */
1061   empathy_chat_view_scroll (window->chatview_find, TRUE);
1062
1063   /* Give the search entry main focus */
1064   gtk_widget_grab_focus (window->entry_chats);
1065 }
1066
1067
1068 static void
1069 log_window_get_messages_for_date (EmpathyLogWindow *window,
1070                                   GDate *date)
1071 {
1072   TpAccount *account;
1073   gchar *chat_id;
1074   gboolean is_chatroom;
1075
1076   if (!log_window_chats_get_selected (window, &account,
1077         &chat_id, &is_chatroom)) {
1078       return;
1079   }
1080
1081   /* Clear all current messages shown in the textview */
1082   empathy_chat_view_clear (window->chatview_chats);
1083
1084   /* Turn off scrolling temporarily */
1085   empathy_chat_view_scroll (window->chatview_find, FALSE);
1086
1087   /* Get messages */
1088   tpl_log_manager_get_messages_for_date_async (window->log_manager,
1089       account, chat_id,
1090       is_chatroom,
1091       date,
1092       log_window_got_messages_for_date_cb,
1093       (gpointer) window);
1094 }
1095
1096 static void
1097 log_manager_got_dates_cb (GObject *manager,
1098                           GAsyncResult *result,
1099                           gpointer user_data)
1100 {
1101   EmpathyLogWindow *window = user_data;
1102   GList         *dates;
1103   GList         *l;
1104   guint          year_selected;
1105   guint          month_selected;
1106   gboolean       day_selected = FALSE;
1107   GDate         *date = NULL;
1108   GError        *error = NULL;
1109
1110   if (log_window == NULL)
1111     return;
1112
1113   if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
1114         result, &dates, &error)) {
1115     DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
1116         error->message);
1117     empathy_chat_view_append_event (window->chatview_find,
1118         "Unable to retrieve messages' dates");
1119       return;
1120   }
1121
1122   for (l = dates; l; l = l->next) {
1123       GDate *d = l->data;
1124
1125       gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats),
1126           &year_selected,
1127           &month_selected,
1128           NULL);
1129
1130       month_selected++;
1131
1132       if (!l->next) {
1133           date = d;
1134       }
1135
1136       if (g_date_get_year (d) != year_selected ||
1137           g_date_get_month (d) != month_selected) {
1138           continue;
1139       }
1140
1141       DEBUG ("Marking date: %04u-%02u-%02u", g_date_get_year (d),
1142           g_date_get_month (d), g_date_get_day (d));
1143
1144       gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats),
1145           g_date_get_day (d));
1146
1147       if (l->next) {
1148           continue;
1149       }
1150
1151       day_selected = TRUE;
1152
1153       gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats),
1154           g_date_get_day (d));
1155   }
1156
1157   if (!day_selected) {
1158       /* Unselect the day in the calendar */
1159       gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), 0);
1160   }
1161
1162   g_signal_handlers_unblock_by_func (window->calendar_chats,
1163       log_window_calendar_chats_day_selected_cb,
1164       window);
1165
1166   if (date) {
1167       /* Show messages of the most recent date */
1168       log_window_get_messages_for_date (window, date);
1169   }
1170
1171   g_list_foreach (dates, (GFunc) g_free, NULL);
1172   g_list_free (dates);
1173 }
1174
1175
1176 static void
1177 log_window_chats_get_messages (EmpathyLogWindow *window,
1178                                GDate     *date)
1179 {
1180         TpAccount     *account;
1181         gchar         *chat_id;
1182         gboolean       is_chatroom;
1183         guint          year_selected;
1184         guint          month_selected;
1185         guint          day;
1186
1187
1188         if (!log_window_chats_get_selected (window, &account,
1189                                             &chat_id, &is_chatroom)) {
1190                 return;
1191         }
1192
1193         g_signal_handlers_block_by_func (window->calendar_chats,
1194                                          log_window_calendar_chats_day_selected_cb,
1195                                          window);
1196
1197         /* Either use the supplied date or get the last */
1198         if (date == NULL) {
1199                 /* Get a list of dates and show them on the calendar */
1200                 tpl_log_manager_get_dates_async (window->log_manager,
1201                                                        account, chat_id,
1202                                                        is_chatroom,
1203                                                        log_manager_got_dates_cb, (gpointer) window);
1204     /* signal unblocked at the end of the CB flow */
1205         } else {
1206                 day = g_date_get_day (date);
1207                 gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats),
1208                                 &year_selected,
1209                                 &month_selected,
1210                                 NULL);
1211
1212                 month_selected++;
1213
1214                 if (g_date_get_year (date) != year_selected &&
1215                         g_date_get_month (date) != month_selected) {
1216                         day = 0;
1217                 }
1218
1219                 gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), day);
1220
1221     g_signal_handlers_unblock_by_func (window->calendar_chats,
1222         log_window_calendar_chats_day_selected_cb,
1223         window);
1224         }
1225
1226         if (date != NULL) {
1227                 /* Show messages of the selected date */
1228                 log_window_get_messages_for_date (window, date);
1229         }
1230
1231         g_object_unref (account);
1232         g_free (chat_id);
1233 }
1234
1235 static void
1236 log_window_calendar_chats_day_selected_cb (GtkWidget       *calendar,
1237                                            EmpathyLogWindow *window)
1238 {
1239         guint  year;
1240         guint  month;
1241         guint  day;
1242         GDate *date;
1243
1244         gtk_calendar_get_date (GTK_CALENDAR (calendar), &year, &month, &day);
1245         if (day == 0)
1246                 /* No date selected */
1247                 return;
1248
1249         /* We need this hear because it appears that the months start from 0 */
1250         month++;
1251
1252         date = g_date_new_dmy (day, month, year);
1253
1254         DEBUG ("Currently selected date is: %04u-%02u-%02u", year, month, day);
1255
1256         log_window_chats_get_messages (window, date);
1257
1258         g_date_free (date);
1259 }
1260
1261 static void
1262 log_window_updating_calendar_month_cb (GObject *manager,
1263                 GAsyncResult *result, gpointer user_data)
1264 {
1265         EmpathyLogWindow *window = user_data;
1266         GList                                   *dates;
1267         GList                                   *l;
1268         guint                                    year_selected;
1269         guint                                    month_selected;
1270         GError                          *error = NULL;
1271
1272         if (log_window == NULL)
1273                 return;
1274
1275         if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
1276                 result, &dates, &error)) {
1277                         DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
1278                                         error->message);
1279                         empathy_chat_view_append_event (window->chatview_find,
1280                                         "Unable to retrieve messages' dates");
1281                         g_error_free (error);
1282                         return;
1283         }
1284
1285         gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats));
1286         g_object_get (window->calendar_chats,
1287                         "month", &month_selected,
1288                         "year", &year_selected,
1289                         NULL);
1290
1291         /* We need this here because it appears that the months start from 0 */
1292         month_selected++;
1293
1294         for (l = dates; l; l = l->next) {
1295                         GDate *date = l->data;
1296
1297                         if (g_date_get_year (date) == year_selected &&
1298                             g_date_get_month (date) == month_selected) {
1299                                         DEBUG ("Marking date: %04u-%02u-%02u", g_date_get_year (date),
1300                                                 g_date_get_month (date), g_date_get_day (date));
1301                                         gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats), g_date_get_day (date));
1302                         }
1303         }
1304
1305         g_list_foreach (dates, (GFunc) g_free, NULL);
1306         g_list_free (dates);
1307
1308         DEBUG ("Currently showing month %d and year %d", month_selected,
1309                         year_selected);
1310 }
1311
1312 static void
1313 log_window_calendar_chats_month_changed_cb (GtkWidget       *calendar,
1314                                             EmpathyLogWindow *window)
1315 {
1316         TpAccount     *account;
1317         gchar         *chat_id;
1318         gboolean       is_chatroom;
1319
1320         gtk_calendar_clear_marks (GTK_CALENDAR (calendar));
1321
1322         if (!log_window_chats_get_selected (window, &account,
1323                                             &chat_id, &is_chatroom)) {
1324                 DEBUG ("No chat selected to get dates for...");
1325                 return;
1326         }
1327
1328         /* Get the log object for this contact */
1329         tpl_log_manager_get_dates_async (window->log_manager, account,
1330                                                chat_id, is_chatroom,
1331                                                log_window_updating_calendar_month_cb,
1332                                                (gpointer) window);
1333
1334         g_object_unref (account);
1335         g_free (chat_id);
1336 }
1337
1338 static void
1339 log_window_entry_chats_changed_cb (GtkWidget       *entry,
1340                                    EmpathyLogWindow *window)
1341 {
1342         const gchar *str;
1343
1344         str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats));
1345         empathy_chat_view_highlight (window->chatview_chats, str, FALSE);
1346
1347         if (str) {
1348                 empathy_chat_view_find_next (window->chatview_chats,
1349                                             str,
1350                                             TRUE,
1351                                             FALSE);
1352         }
1353 }
1354
1355 static void
1356 log_window_entry_chats_activate_cb (GtkWidget       *entry,
1357                                     EmpathyLogWindow *window)
1358 {
1359         const gchar *str;
1360
1361         str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats));
1362
1363         if (str) {
1364                 empathy_chat_view_find_next (window->chatview_chats,
1365                                             str,
1366                                             FALSE,
1367                                             FALSE);
1368         }
1369 }