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