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