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