Generate closures for src/
[empathy-mirror.git] / libempathy-gtk / empathy-log-window.c
blob650c3b4422bb2f09b9fe0d05793e14374db339c8
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.
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.
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.
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
21 * Authors: Martyn Russell <martyn@imendio.com>
22 * Xavier Claessens <xclaesse@gmail.com>
25 #include "config.h"
27 #include <string.h>
28 #include <stdlib.h>
30 #include <glib/gi18n-lib.h>
31 #include <gtk/gtk.h>
33 #include <telepathy-glib/account-manager.h>
34 #include <telepathy-logger/log-manager.h>
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>
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"
48 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
49 #include <libempathy/empathy-debug.h>
51 typedef struct {
52 GtkWidget *window;
54 GtkWidget *notebook;
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;
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;
72 gchar *last_find;
74 TplLogManager *log_manager;
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;
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 gchar **chat_id,
111 gboolean *is_chatroom);
112 static void log_window_chats_get_messages (EmpathyLogWindow *window,
113 GDate *date_to_show);
114 static void log_window_calendar_chats_day_selected_cb (GtkWidget *calendar,
115 EmpathyLogWindow *window);
116 static void log_window_calendar_chats_month_changed_cb (GtkWidget *calendar,
117 EmpathyLogWindow *window);
118 static void log_window_entry_chats_changed_cb (GtkWidget *entry,
119 EmpathyLogWindow *window);
120 static void log_window_entry_chats_activate_cb (GtkWidget *entry,
121 EmpathyLogWindow *window);
123 enum {
124 COL_FIND_ACCOUNT_ICON,
125 COL_FIND_ACCOUNT_NAME,
126 COL_FIND_ACCOUNT,
127 COL_FIND_CHAT_NAME,
128 COL_FIND_CHAT_ID,
129 COL_FIND_IS_CHATROOM,
130 COL_FIND_DATE,
131 COL_FIND_DATE_READABLE,
132 COL_FIND_COUNT
135 enum {
136 COL_CHAT_ICON,
137 COL_CHAT_NAME,
138 COL_CHAT_ACCOUNT,
139 COL_CHAT_ID,
140 COL_CHAT_IS_CHATROOM,
141 COL_CHAT_COUNT
144 static EmpathyLogWindow *log_window = NULL;
146 static void
147 account_manager_prepared_cb (GObject *source_object,
148 GAsyncResult *result,
149 gpointer user_data)
151 TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
152 EmpathyLogWindow *window = user_data;
153 guint account_num;
154 GList *accounts;
155 GError *error = NULL;
157 if (log_window == NULL)
158 return;
160 if (!tp_account_manager_prepare_finish (account_manager, result, &error)) {
161 DEBUG ("Failed to prepare account manager: %s", error->message);
162 g_error_free (error);
163 return;
166 accounts = tp_account_manager_get_valid_accounts (account_manager);
167 account_num = g_list_length (accounts);
168 g_list_free (accounts);
170 if (account_num > 1) {
171 gtk_widget_show (window->vbox_chats);
172 gtk_widget_show (window->account_chooser_chats);
173 } else {
174 gtk_widget_hide (window->vbox_chats);
175 gtk_widget_hide (window->account_chooser_chats);
179 static void
180 account_chooser_ready_cb (EmpathyAccountChooser *chooser,
181 EmpathyLogWindow *window)
183 gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), 1);
185 /* We'll display the account once the model has been populate with the chats
186 * of this account. */
187 empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER (
188 window->account_chooser_chats), window->selected_account);
191 static void
192 select_account_once_ready (EmpathyLogWindow *self,
193 TpAccount *account,
194 const gchar *chat_id,
195 gboolean is_chatroom)
197 EmpathyAccountChooser *account_chooser = EMPATHY_ACCOUNT_CHOOSER (self->account_chooser_chats);
199 tp_clear_object (&self->selected_account);
200 self->selected_account = g_object_ref (account);
202 g_free (self->selected_chat_id);
203 self->selected_chat_id = g_strdup (chat_id);
205 self->selected_is_chatroom = is_chatroom;
207 if (empathy_account_chooser_is_ready (account_chooser))
208 account_chooser_ready_cb (account_chooser, self);
209 else
210 /* Chat will be selected once the account chooser is ready */
211 g_signal_connect (account_chooser, "ready",
212 G_CALLBACK (account_chooser_ready_cb), self);
215 GtkWidget *
216 empathy_log_window_show (TpAccount *account,
217 const gchar *chat_id,
218 gboolean is_chatroom,
219 GtkWindow *parent)
221 EmpathyAccountChooser *account_chooser;
222 TpAccountManager *account_manager;
223 GtkBuilder *gui;
224 gchar *filename;
225 EmpathyLogWindow *window;
227 if (log_window != NULL) {
228 gtk_window_present (GTK_WINDOW (log_window->window));
230 if (account != NULL && chat_id != NULL) {
231 gtk_notebook_set_current_page (GTK_NOTEBOOK (log_window->notebook), 1);
232 select_account_once_ready (log_window, account, chat_id, is_chatroom);
235 return log_window->window;
238 log_window = g_new0 (EmpathyLogWindow, 1);
239 log_window->log_manager = tpl_log_manager_dup_singleton ();
241 window = log_window;
243 filename = empathy_file_lookup ("empathy-log-window.ui",
244 "libempathy-gtk");
245 gui = empathy_builder_get_file (filename,
246 "log_window", &window->window,
247 "notebook", &window->notebook,
248 "entry_find", &window->entry_find,
249 "button_find", &window->button_find,
250 "treeview_find", &window->treeview_find,
251 "scrolledwindow_find", &window->scrolledwindow_find,
252 "button_previous", &window->button_previous,
253 "button_next", &window->button_next,
254 "entry_chats", &window->entry_chats,
255 "calendar_chats", &window->calendar_chats,
256 "vbox_chats", &window->vbox_chats,
257 "treeview_chats", &window->treeview_chats,
258 "scrolledwindow_chats", &window->scrolledwindow_chats,
259 NULL);
260 g_free (filename);
262 empathy_builder_connect (gui, window,
263 "log_window", "destroy", log_window_destroy_cb,
264 "entry_find", "changed", log_window_entry_find_changed_cb,
265 "entry_find", "activate", log_window_entry_find_activate_cb,
266 "button_previous", "clicked", log_window_button_previous_clicked_cb,
267 "button_next", "clicked", log_window_button_next_clicked_cb,
268 "button_close", "clicked", log_window_button_close_clicked_cb,
269 "button_close2", "clicked", log_window_button_close_clicked_cb,
270 "button_find", "clicked", log_window_button_find_clicked_cb,
271 "entry_chats", "changed", log_window_entry_chats_changed_cb,
272 "entry_chats", "activate", log_window_entry_chats_activate_cb,
273 NULL);
275 g_object_unref (gui);
277 g_object_add_weak_pointer (G_OBJECT (window->window),
278 (gpointer) &log_window);
280 /* We set this up here so we can block it when needed. */
281 g_signal_connect (window->calendar_chats, "day-selected",
282 G_CALLBACK (log_window_calendar_chats_day_selected_cb),
283 window);
284 g_signal_connect (window->calendar_chats, "month-changed",
285 G_CALLBACK (log_window_calendar_chats_month_changed_cb),
286 window);
288 /* Configure Search EmpathyChatView */
289 window->chatview_find = empathy_theme_manager_create_view (empathy_theme_manager_get ());
290 gtk_container_add (GTK_CONTAINER (window->scrolledwindow_find),
291 GTK_WIDGET (window->chatview_find));
292 gtk_widget_show (GTK_WIDGET (window->chatview_find));
294 /* Configure Contacts EmpathyChatView */
295 window->chatview_chats = empathy_theme_manager_create_view (empathy_theme_manager_get ());
296 gtk_container_add (GTK_CONTAINER (window->scrolledwindow_chats),
297 GTK_WIDGET (window->chatview_chats));
298 gtk_widget_show (GTK_WIDGET (window->chatview_chats));
300 /* Account chooser for chats */
301 window->account_chooser_chats = empathy_account_chooser_new ();
302 account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats);
304 gtk_box_pack_start (GTK_BOX (window->vbox_chats),
305 window->account_chooser_chats,
306 FALSE, TRUE, 0);
308 g_signal_connect (window->account_chooser_chats, "changed",
309 G_CALLBACK (log_window_chats_accounts_changed_cb),
310 window);
312 /* Populate */
313 account_manager = tp_account_manager_dup ();
314 tp_account_manager_prepare_async (account_manager, NULL,
315 account_manager_prepared_cb, window);
316 g_object_unref (account_manager);
318 /* Search List */
319 log_window_find_setup (window);
321 /* Contacts */
322 log_window_chats_setup (window);
323 log_window_chats_populate (window);
325 if (account != NULL && chat_id != NULL)
326 select_account_once_ready (window, account, chat_id, is_chatroom);
328 if (parent != NULL) {
329 gtk_window_set_transient_for (GTK_WINDOW (window->window),
330 GTK_WINDOW (parent));
333 gtk_widget_show (window->window);
335 return window->window;
338 static void
339 log_window_destroy_cb (GtkWidget *widget,
340 EmpathyLogWindow *window)
342 g_free (window->last_find);
343 g_object_unref (window->log_manager);
344 tp_clear_object (&window->selected_account);
345 g_free (window->selected_chat_id);
347 g_free (window);
351 * Search code.
353 static void
354 log_window_entry_find_changed_cb (GtkWidget *entry,
355 EmpathyLogWindow *window)
357 const gchar *str;
358 gboolean is_sensitive = TRUE;
360 str = gtk_entry_get_text (GTK_ENTRY (window->entry_find));
362 is_sensitive &= !EMP_STR_EMPTY (str);
363 is_sensitive &=
364 !window->last_find ||
365 (window->last_find && tp_strdiff (window->last_find, str));
367 gtk_widget_set_sensitive (window->button_find, is_sensitive);
370 static void
371 got_messages_for_date_cb (GObject *manager,
372 GAsyncResult *result,
373 gpointer user_data)
375 EmpathyLogWindow *window = user_data;
376 GList *messages;
377 GList *l;
378 gboolean can_do_previous;
379 gboolean can_do_next;
380 GError *error = NULL;
382 if (log_window == NULL)
383 return;
385 if (!tpl_log_manager_get_messages_for_date_finish (TPL_LOG_MANAGER (manager),
386 result, &messages, &error)) {
387 DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
388 error->message);
389 empathy_chat_view_append_event (window->chatview_find,
390 "Unable to retrieve messages for the selected date");
391 g_error_free (error);
392 return;
395 for (l = messages; l; l = l->next) {
396 EmpathyMessage *message;
398 g_assert (TPL_IS_ENTRY (l->data));
400 message = empathy_message_from_tpl_log_entry (l->data);
401 g_object_unref (l->data);
402 empathy_chat_view_append_message (window->chatview_find, message);
403 g_object_unref (message);
405 g_list_free (messages);
407 /* Scroll to the most recent messages */
408 empathy_chat_view_scroll (window->chatview_find, TRUE);
410 /* Highlight and find messages */
411 empathy_chat_view_highlight (window->chatview_find,
412 window->last_find,
413 FALSE);
414 empathy_chat_view_find_next (window->chatview_find,
415 window->last_find,
416 TRUE,
417 FALSE);
418 empathy_chat_view_find_abilities (window->chatview_find,
419 window->last_find,
420 FALSE,
421 &can_do_previous,
422 &can_do_next);
423 gtk_widget_set_sensitive (window->button_previous, can_do_previous);
424 gtk_widget_set_sensitive (window->button_next, can_do_next);
425 gtk_widget_set_sensitive (window->button_find, FALSE);
428 static GDate *
429 gdate_from_str (const gchar *str)
431 guint u;
432 guint day, month, year;
434 if (sscanf (str, "%u", &u) != 1)
435 return NULL;
437 day = (u % 100);
438 month = ((u / 100) % 100);
439 year = (u / 10000);
441 if (!g_date_valid_dmy (day, month, year))
442 return NULL;
444 return g_date_new_dmy (day, month, year);
447 static void
448 log_window_find_changed_cb (GtkTreeSelection *selection,
449 EmpathyLogWindow *window)
451 GtkTreeView *view;
452 GtkTreeModel *model;
453 GtkTreeIter iter;
454 TpAccount *account;
455 gchar *chat_id;
456 gboolean is_chatroom;
457 gchar *date;
458 GDate *gdate;
460 /* Get selected information */
461 view = GTK_TREE_VIEW (window->treeview_find);
462 model = gtk_tree_view_get_model (view);
464 if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
465 gtk_widget_set_sensitive (window->button_previous, FALSE);
466 gtk_widget_set_sensitive (window->button_next, FALSE);
468 empathy_chat_view_clear (window->chatview_find);
470 return;
473 gtk_widget_set_sensitive (window->button_previous, TRUE);
474 gtk_widget_set_sensitive (window->button_next, TRUE);
476 gtk_tree_model_get (model, &iter,
477 COL_FIND_ACCOUNT, &account,
478 COL_FIND_CHAT_ID, &chat_id,
479 COL_FIND_IS_CHATROOM, &is_chatroom,
480 COL_FIND_DATE, &date,
481 -1);
483 /* Clear all current messages shown in the textview */
484 empathy_chat_view_clear (window->chatview_find);
486 /* Turn off scrolling temporarily */
487 empathy_chat_view_scroll (window->chatview_find, FALSE);
489 /* Get messages */
490 gdate = gdate_from_str (date);
492 if (gdate != NULL) {
493 tpl_log_manager_get_messages_for_date_async (window->log_manager,
494 account,
495 chat_id,
496 is_chatroom,
497 gdate,
498 got_messages_for_date_cb,
499 window);
501 g_date_free (gdate);
504 g_object_unref (account);
505 g_free (date);
506 g_free (chat_id);
510 static void
511 log_manager_searched_new_cb (GObject *manager,
512 GAsyncResult *result,
513 gpointer user_data)
515 GList *hits;
516 GList *l;
517 GtkTreeIter iter;
518 GtkListStore *store = user_data;
519 GError *error = NULL;
521 if (log_window == NULL)
522 return;
524 if (!tpl_log_manager_search_finish (TPL_LOG_MANAGER (manager), result,
525 &hits, &error)) {
526 DEBUG ("%s. Aborting", error->message);
527 g_error_free (error);
528 return;
531 for (l = hits; l; l = l->next) {
532 TplLogSearchHit *hit;
533 const gchar *account_name;
534 const gchar *account_icon;
535 gchar date_readable[255];
536 gchar tmp[255];
538 hit = l->data;
540 /* Protect against invalid data (corrupt or old log files. */
541 if (hit->account == NULL || hit->chat_id == NULL) {
542 continue;
545 g_date_strftime (date_readable, sizeof (date_readable),
546 EMPATHY_DATE_FORMAT_DISPLAY_SHORT, hit->date);
548 g_date_strftime (tmp, sizeof (tmp),
549 "%Y%m%d", hit->date);
551 account_name = tp_account_get_display_name (hit->account);
552 account_icon = tp_account_get_icon_name (hit->account);
554 gtk_list_store_append (store, &iter);
555 gtk_list_store_set (store, &iter,
556 COL_FIND_ACCOUNT_ICON, account_icon,
557 COL_FIND_ACCOUNT_NAME, account_name,
558 COL_FIND_ACCOUNT, hit->account,
559 COL_FIND_CHAT_NAME, hit->chat_id, /* FIXME */
560 COL_FIND_CHAT_ID, hit->chat_id,
561 COL_FIND_IS_CHATROOM, hit->is_chatroom,
562 COL_FIND_DATE, tmp,
563 COL_FIND_DATE_READABLE, date_readable,
564 -1);
566 /* FIXME: Update COL_FIND_CHAT_NAME */
567 if (hit->is_chatroom) {
568 } else {
572 if (hits != NULL) {
573 tpl_log_manager_search_free (hits);
577 static void
578 log_window_find_populate (EmpathyLogWindow *window,
579 const gchar *search_criteria)
581 GtkTreeView *view;
582 GtkTreeModel *model;
583 GtkTreeSelection *selection;
584 GtkListStore *store;
586 view = GTK_TREE_VIEW (window->treeview_find);
587 model = gtk_tree_view_get_model (view);
588 selection = gtk_tree_view_get_selection (view);
589 store = GTK_LIST_STORE (model);
591 empathy_chat_view_clear (window->chatview_find);
593 gtk_list_store_clear (store);
595 if (EMP_STR_EMPTY (search_criteria)) {
596 /* Just clear the search. */
597 return;
600 tpl_log_manager_search_async (window->log_manager, search_criteria,
601 log_manager_searched_new_cb, (gpointer) store);
604 static void
605 log_window_find_setup (EmpathyLogWindow *window)
607 GtkTreeView *view;
608 GtkTreeModel *model;
609 GtkTreeSelection *selection;
610 GtkTreeSortable *sortable;
611 GtkTreeViewColumn *column;
612 GtkListStore *store;
613 GtkCellRenderer *cell;
614 gint offset;
616 view = GTK_TREE_VIEW (window->treeview_find);
617 selection = gtk_tree_view_get_selection (view);
619 /* New store */
620 store = gtk_list_store_new (COL_FIND_COUNT,
621 G_TYPE_STRING, /* account icon name */
622 G_TYPE_STRING, /* account name */
623 TP_TYPE_ACCOUNT, /* account */
624 G_TYPE_STRING, /* chat name */
625 G_TYPE_STRING, /* chat id */
626 G_TYPE_BOOLEAN, /* is chatroom */
627 G_TYPE_STRING, /* date */
628 G_TYPE_STRING); /* date_readable */
630 model = GTK_TREE_MODEL (store);
631 sortable = GTK_TREE_SORTABLE (store);
633 gtk_tree_view_set_model (view, model);
635 /* New column */
636 column = gtk_tree_view_column_new ();
638 cell = gtk_cell_renderer_pixbuf_new ();
639 gtk_tree_view_column_pack_start (column, cell, FALSE);
640 gtk_tree_view_column_add_attribute (column, cell,
641 "icon-name",
642 COL_FIND_ACCOUNT_ICON);
644 cell = gtk_cell_renderer_text_new ();
645 gtk_tree_view_column_pack_start (column, cell, TRUE);
646 gtk_tree_view_column_add_attribute (column, cell,
647 "text",
648 COL_FIND_ACCOUNT_NAME);
650 gtk_tree_view_column_set_title (column, _("Account"));
651 gtk_tree_view_append_column (view, column);
653 gtk_tree_view_column_set_resizable (column, TRUE);
654 gtk_tree_view_column_set_clickable (column, TRUE);
656 cell = gtk_cell_renderer_text_new ();
657 offset = gtk_tree_view_insert_column_with_attributes (view, -1, _("Conversation"),
658 cell, "text", COL_FIND_CHAT_NAME,
659 NULL);
661 column = gtk_tree_view_get_column (view, offset - 1);
662 gtk_tree_view_column_set_sort_column_id (column, COL_FIND_CHAT_NAME);
663 gtk_tree_view_column_set_resizable (column, TRUE);
664 gtk_tree_view_column_set_clickable (column, TRUE);
666 cell = gtk_cell_renderer_text_new ();
667 offset = gtk_tree_view_insert_column_with_attributes (view, -1, _("Date"),
668 cell, "text", COL_FIND_DATE_READABLE,
669 NULL);
671 column = gtk_tree_view_get_column (view, offset - 1);
672 gtk_tree_view_column_set_sort_column_id (column, COL_FIND_DATE);
673 gtk_tree_view_column_set_resizable (column, TRUE);
674 gtk_tree_view_column_set_clickable (column, TRUE);
676 /* Set up treeview properties */
677 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
678 gtk_tree_sortable_set_sort_column_id (sortable,
679 COL_FIND_DATE,
680 GTK_SORT_ASCENDING);
682 /* Set up signals */
683 g_signal_connect (selection, "changed",
684 G_CALLBACK (log_window_find_changed_cb),
685 window);
687 g_object_unref (store);
690 static void
691 start_find_search (EmpathyLogWindow *window)
693 const gchar *str;
695 str = gtk_entry_get_text (GTK_ENTRY (window->entry_find));
697 /* Don't find the same crap again */
698 if (window->last_find && !tp_strdiff (window->last_find, str)) {
699 return;
702 g_free (window->last_find);
703 window->last_find = g_strdup (str);
705 log_window_find_populate (window, str);
708 static void
709 log_window_button_find_clicked_cb (GtkWidget *widget,
710 EmpathyLogWindow *window)
712 start_find_search (window);
715 static void
716 log_window_entry_find_activate_cb (GtkWidget *entry,
717 EmpathyLogWindow *self)
719 start_find_search (self);
722 static void
723 log_window_button_next_clicked_cb (GtkWidget *widget,
724 EmpathyLogWindow *window)
726 if (window->last_find) {
727 gboolean can_do_previous;
728 gboolean can_do_next;
730 empathy_chat_view_find_next (window->chatview_find,
731 window->last_find,
732 FALSE,
733 FALSE);
734 empathy_chat_view_find_abilities (window->chatview_find,
735 window->last_find,
736 FALSE,
737 &can_do_previous,
738 &can_do_next);
739 gtk_widget_set_sensitive (window->button_previous, can_do_previous);
740 gtk_widget_set_sensitive (window->button_next, can_do_next);
744 static void
745 log_window_button_previous_clicked_cb (GtkWidget *widget,
746 EmpathyLogWindow *window)
748 if (window->last_find) {
749 gboolean can_do_previous;
750 gboolean can_do_next;
752 empathy_chat_view_find_previous (window->chatview_find,
753 window->last_find,
754 FALSE,
755 FALSE);
756 empathy_chat_view_find_abilities (window->chatview_find,
757 window->last_find,
758 FALSE,
759 &can_do_previous,
760 &can_do_next);
761 gtk_widget_set_sensitive (window->button_previous, can_do_previous);
762 gtk_widget_set_sensitive (window->button_next, can_do_next);
766 static void
767 log_window_button_close_clicked_cb (GtkWidget *widget,
768 EmpathyLogWindow *window)
770 gtk_widget_destroy (window->window);
774 * Chats Code
777 static void
778 log_window_chats_changed_cb (GtkTreeSelection *selection,
779 EmpathyLogWindow *window)
781 /* Use last date by default */
782 gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats));
784 log_window_chats_get_messages (window, NULL);
787 static void
788 log_manager_got_chats_cb (GObject *manager,
789 GAsyncResult *result,
790 gpointer user_data)
792 EmpathyLogWindow *window = user_data;
793 GList *chats;
794 GList *l;
795 GtkTreeView *view;
796 GtkTreeModel *model;
797 GtkTreeSelection *selection;
798 GtkListStore *store;
799 GtkTreeIter iter;
800 GError *error = NULL;
801 gboolean select_account = FALSE;
803 if (log_window == NULL)
804 return;
806 if (!tpl_log_manager_get_chats_finish (TPL_LOG_MANAGER (manager),
807 result, &chats, &error)) {
808 DEBUG ("%s. Aborting", error->message);
809 g_error_free (error);
810 return;
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);
818 for (l = chats; l; l = l->next) {
819 TplLogSearchHit *hit;
821 hit = l->data;
823 if (hit->account == NULL)
824 continue;
826 gtk_list_store_append (store, &iter);
827 gtk_list_store_set (store, &iter,
828 COL_CHAT_ICON, "empathy-available", /* FIXME */
829 COL_CHAT_NAME, hit->chat_id,
830 COL_CHAT_ACCOUNT, hit->account,
831 COL_CHAT_ID, hit->chat_id,
832 COL_CHAT_IS_CHATROOM, hit->is_chatroom,
833 -1);
835 if (window->selected_account != NULL &&
836 !tp_strdiff (tp_proxy_get_object_path (hit->account),
837 tp_proxy_get_object_path (window->selected_account)))
838 select_account = TRUE;
840 /* FIXME: Update COL_CHAT_ICON/NAME */
841 if (hit->is_chatroom) {
842 } else {
845 tpl_log_manager_search_free (chats);
847 /* Unblock signals */
848 g_signal_handlers_unblock_by_func (selection,
849 log_window_chats_changed_cb,
850 window);
852 /* We display the selected account if we populate the model with chats from
853 * this account. */
854 if (select_account)
855 log_window_chats_set_selected (window);
858 static void
859 log_window_chats_populate (EmpathyLogWindow *window)
861 EmpathyAccountChooser *account_chooser;
862 TpAccount *account;
864 GtkTreeView *view;
865 GtkTreeModel *model;
866 GtkTreeSelection *selection;
867 GtkListStore *store;
869 account_chooser = EMPATHY_ACCOUNT_CHOOSER (window->account_chooser_chats);
870 account = empathy_account_chooser_dup_account (account_chooser);
872 view = GTK_TREE_VIEW (window->treeview_chats);
873 model = gtk_tree_view_get_model (view);
874 selection = gtk_tree_view_get_selection (view);
875 store = GTK_LIST_STORE (model);
877 if (account == NULL) {
878 gtk_list_store_clear (store);
879 return;
882 /* Block signals to stop the logs being retrieved prematurely */
883 g_signal_handlers_block_by_func (selection,
884 log_window_chats_changed_cb,
885 window);
887 gtk_list_store_clear (store);
889 tpl_log_manager_get_chats_async (window->log_manager, account,
890 log_manager_got_chats_cb, (gpointer) window);
893 static void
894 log_window_chats_setup (EmpathyLogWindow *window)
896 GtkTreeView *view;
897 GtkTreeModel *model;
898 GtkTreeSelection *selection;
899 GtkTreeSortable *sortable;
900 GtkTreeViewColumn *column;
901 GtkListStore *store;
902 GtkCellRenderer *cell;
904 view = GTK_TREE_VIEW (window->treeview_chats);
905 selection = gtk_tree_view_get_selection (view);
907 /* new store */
908 store = gtk_list_store_new (COL_CHAT_COUNT,
909 G_TYPE_STRING, /* icon */
910 G_TYPE_STRING, /* name */
911 TP_TYPE_ACCOUNT, /* account */
912 G_TYPE_STRING, /* id */
913 G_TYPE_BOOLEAN); /* is chatroom */
915 model = GTK_TREE_MODEL (store);
916 sortable = GTK_TREE_SORTABLE (store);
918 gtk_tree_view_set_model (view, model);
920 /* new column */
921 column = gtk_tree_view_column_new ();
923 cell = gtk_cell_renderer_pixbuf_new ();
924 gtk_tree_view_column_pack_start (column, cell, FALSE);
925 gtk_tree_view_column_add_attribute (column, cell,
926 "icon-name",
927 COL_CHAT_ICON);
929 cell = gtk_cell_renderer_text_new ();
930 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
931 gtk_tree_view_column_pack_start (column, cell, TRUE);
932 gtk_tree_view_column_add_attribute (column, cell,
933 "text",
934 COL_CHAT_NAME);
936 gtk_tree_view_append_column (view, column);
938 /* set up treeview properties */
939 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
940 gtk_tree_sortable_set_sort_column_id (sortable,
941 COL_CHAT_NAME,
942 GTK_SORT_ASCENDING);
944 /* set up signals */
945 g_signal_connect (selection, "changed",
946 G_CALLBACK (log_window_chats_changed_cb),
947 window);
949 g_object_unref (store);
952 static void
953 log_window_chats_accounts_changed_cb (GtkWidget *combobox,
954 EmpathyLogWindow *window)
956 /* Clear all current messages shown in the textview */
957 empathy_chat_view_clear (window->chatview_chats);
959 log_window_chats_populate (window);
962 static void
963 log_window_chats_set_selected (EmpathyLogWindow *window)
965 GtkTreeView *view;
966 GtkTreeModel *model;
967 GtkTreeSelection *selection;
968 GtkTreeIter iter;
969 GtkTreePath *path;
970 gboolean ok;
972 view = GTK_TREE_VIEW (window->treeview_chats);
973 model = gtk_tree_view_get_model (view);
974 selection = gtk_tree_view_get_selection (view);
976 if (!gtk_tree_model_get_iter_first (model, &iter)) {
977 return;
980 for (ok = TRUE; ok; ok = gtk_tree_model_iter_next (model, &iter)) {
981 TpAccount *this_account;
982 gchar *this_chat_id;
983 gboolean this_is_chatroom;
985 gtk_tree_model_get (model, &iter,
986 COL_CHAT_ACCOUNT, &this_account,
987 COL_CHAT_ID, &this_chat_id,
988 COL_CHAT_IS_CHATROOM, &this_is_chatroom,
989 -1);
991 if (this_account == window->selected_account &&
992 !tp_strdiff (this_chat_id, window->selected_chat_id) &&
993 this_is_chatroom == window->selected_is_chatroom) {
994 gtk_tree_selection_select_iter (selection, &iter);
995 path = gtk_tree_model_get_path (model, &iter);
996 gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 0.5, 0.0);
997 gtk_tree_path_free (path);
998 g_object_unref (this_account);
999 g_free (this_chat_id);
1000 break;
1003 g_object_unref (this_account);
1004 g_free (this_chat_id);
1007 tp_clear_object (&window->selected_account);
1008 tp_clear_pointer (&window->selected_chat_id, g_free);
1011 static gboolean
1012 log_window_chats_get_selected (EmpathyLogWindow *window,
1013 TpAccount **account,
1014 gchar **chat_id,
1015 gboolean *is_chatroom)
1017 GtkTreeView *view;
1018 GtkTreeModel *model;
1019 GtkTreeSelection *selection;
1020 GtkTreeIter iter;
1021 gchar *id = NULL;
1022 TpAccount *acc = NULL;
1023 gboolean room = FALSE;
1025 view = GTK_TREE_VIEW (window->treeview_chats);
1026 model = gtk_tree_view_get_model (view);
1027 selection = gtk_tree_view_get_selection (view);
1029 if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
1030 return FALSE;
1033 gtk_tree_model_get (model, &iter,
1034 COL_CHAT_ACCOUNT, &acc,
1035 COL_CHAT_ID, &id,
1036 COL_CHAT_IS_CHATROOM, &room,
1037 -1);
1039 if (chat_id != NULL) {
1040 *chat_id = id;
1041 } else {
1042 g_free (id);
1044 if (account != NULL) {
1045 *account = acc;
1046 } else {
1047 g_object_unref (acc);
1049 if (is_chatroom) {
1050 *is_chatroom = room;
1053 return TRUE;
1056 static void
1057 log_window_got_messages_for_date_cb (GObject *manager,
1058 GAsyncResult *result,
1059 gpointer user_data)
1061 EmpathyLogWindow *window = user_data;
1062 GList *messages;
1063 GList *l;
1064 GError *error = NULL;
1066 if (log_window == NULL)
1067 return;
1069 if (!tpl_log_manager_get_messages_for_date_finish (TPL_LOG_MANAGER (manager),
1070 result, &messages, &error)) {
1071 DEBUG ("Unable to retrieve messages for the selected date: %s. Aborting",
1072 error->message);
1073 empathy_chat_view_append_event (window->chatview_find,
1074 "Unable to retrieve messages for the selected date");
1075 g_error_free (error);
1076 return;
1079 for (l = messages; l; l = l->next) {
1080 EmpathyMessage *message = empathy_message_from_tpl_log_entry (l->data);
1081 g_object_unref (l->data);
1082 empathy_chat_view_append_message (window->chatview_chats,
1083 message);
1084 g_object_unref (message);
1086 g_list_free (messages);
1088 /* Turn back on scrolling */
1089 empathy_chat_view_scroll (window->chatview_find, TRUE);
1091 /* Give the search entry main focus */
1092 gtk_widget_grab_focus (window->entry_chats);
1096 static void
1097 log_window_get_messages_for_date (EmpathyLogWindow *window,
1098 GDate *date)
1100 TpAccount *account;
1101 gchar *chat_id;
1102 gboolean is_chatroom;
1104 if (!log_window_chats_get_selected (window, &account,
1105 &chat_id, &is_chatroom)) {
1106 return;
1109 /* Clear all current messages shown in the textview */
1110 empathy_chat_view_clear (window->chatview_chats);
1112 /* Turn off scrolling temporarily */
1113 empathy_chat_view_scroll (window->chatview_find, FALSE);
1115 /* Get messages */
1116 tpl_log_manager_get_messages_for_date_async (window->log_manager,
1117 account, chat_id,
1118 is_chatroom,
1119 date,
1120 log_window_got_messages_for_date_cb,
1121 (gpointer) window);
1123 g_free (chat_id);
1124 g_object_unref (account);
1127 static void
1128 log_manager_got_dates_cb (GObject *manager,
1129 GAsyncResult *result,
1130 gpointer user_data)
1132 EmpathyLogWindow *window = user_data;
1133 GList *dates;
1134 GList *l;
1135 guint year_selected;
1136 guint month_selected;
1137 gboolean day_selected = FALSE;
1138 GDate *date = NULL;
1139 GError *error = NULL;
1141 if (log_window == NULL)
1142 return;
1144 if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
1145 result, &dates, &error)) {
1146 DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
1147 error->message);
1148 empathy_chat_view_append_event (window->chatview_find,
1149 "Unable to retrieve messages' dates");
1150 return;
1153 for (l = dates; l; l = l->next) {
1154 GDate *d = l->data;
1156 gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats),
1157 &year_selected,
1158 &month_selected,
1159 NULL);
1161 month_selected++;
1163 if (!l->next) {
1164 date = d;
1167 if (g_date_get_year (d) != year_selected ||
1168 g_date_get_month (d) != month_selected) {
1169 continue;
1172 DEBUG ("Marking date: %04u-%02u-%02u", g_date_get_year (d),
1173 g_date_get_month (d), g_date_get_day (d));
1175 gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats),
1176 g_date_get_day (d));
1178 if (l->next) {
1179 continue;
1182 day_selected = TRUE;
1184 gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats),
1185 g_date_get_day (d));
1188 if (!day_selected) {
1189 /* Unselect the day in the calendar */
1190 gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), 0);
1193 g_signal_handlers_unblock_by_func (window->calendar_chats,
1194 log_window_calendar_chats_day_selected_cb,
1195 window);
1197 if (date != NULL) {
1198 /* Show messages of the most recent date */
1199 log_window_get_messages_for_date (window, date);
1202 g_list_foreach (dates, (GFunc) g_free, NULL);
1203 g_list_free (dates);
1207 static void
1208 log_window_chats_get_messages (EmpathyLogWindow *window,
1209 GDate *date)
1211 TpAccount *account;
1212 gchar *chat_id;
1213 gboolean is_chatroom;
1214 guint year_selected;
1215 guint month_selected;
1216 guint day;
1219 if (!log_window_chats_get_selected (window, &account,
1220 &chat_id, &is_chatroom)) {
1221 return;
1224 g_signal_handlers_block_by_func (window->calendar_chats,
1225 log_window_calendar_chats_day_selected_cb,
1226 window);
1228 /* Either use the supplied date or get the last */
1229 if (date == NULL) {
1230 /* Get a list of dates and show them on the calendar */
1231 tpl_log_manager_get_dates_async (window->log_manager,
1232 account, chat_id,
1233 is_chatroom,
1234 log_manager_got_dates_cb, (gpointer) window);
1235 /* signal unblocked at the end of the CB flow */
1236 } else {
1237 day = g_date_get_day (date);
1238 gtk_calendar_get_date (GTK_CALENDAR (window->calendar_chats),
1239 &year_selected,
1240 &month_selected,
1241 NULL);
1243 month_selected++;
1245 if (g_date_get_year (date) != year_selected &&
1246 g_date_get_month (date) != month_selected) {
1247 day = 0;
1250 gtk_calendar_select_day (GTK_CALENDAR (window->calendar_chats), day);
1252 g_signal_handlers_unblock_by_func (window->calendar_chats,
1253 log_window_calendar_chats_day_selected_cb,
1254 window);
1257 if (date != NULL) {
1258 /* Show messages of the selected date */
1259 log_window_get_messages_for_date (window, date);
1262 g_object_unref (account);
1263 g_free (chat_id);
1266 static void
1267 log_window_calendar_chats_day_selected_cb (GtkWidget *calendar,
1268 EmpathyLogWindow *window)
1270 guint year;
1271 guint month;
1272 guint day;
1273 GDate *date;
1275 gtk_calendar_get_date (GTK_CALENDAR (calendar), &year, &month, &day);
1276 if (day == 0)
1277 /* No date selected */
1278 return;
1280 /* We need this hear because it appears that the months start from 0 */
1281 month++;
1283 date = g_date_new_dmy (day, month, year);
1285 DEBUG ("Currently selected date is: %04u-%02u-%02u", year, month, day);
1287 log_window_chats_get_messages (window, date);
1289 g_date_free (date);
1292 static void
1293 log_window_updating_calendar_month_cb (GObject *manager,
1294 GAsyncResult *result, gpointer user_data)
1296 EmpathyLogWindow *window = user_data;
1297 GList *dates;
1298 GList *l;
1299 guint year_selected;
1300 guint month_selected;
1301 GError *error = NULL;
1303 if (log_window == NULL)
1304 return;
1306 if (!tpl_log_manager_get_dates_finish (TPL_LOG_MANAGER (manager),
1307 result, &dates, &error)) {
1308 DEBUG ("Unable to retrieve messages' dates: %s. Aborting",
1309 error->message);
1310 empathy_chat_view_append_event (window->chatview_find,
1311 "Unable to retrieve messages' dates");
1312 g_error_free (error);
1313 return;
1316 gtk_calendar_clear_marks (GTK_CALENDAR (window->calendar_chats));
1317 g_object_get (window->calendar_chats,
1318 "month", &month_selected,
1319 "year", &year_selected,
1320 NULL);
1322 /* We need this here because it appears that the months start from 0 */
1323 month_selected++;
1325 for (l = dates; l; l = l->next) {
1326 GDate *date = l->data;
1328 if (g_date_get_year (date) == year_selected &&
1329 g_date_get_month (date) == month_selected) {
1330 DEBUG ("Marking date: %04u-%02u-%02u", g_date_get_year (date),
1331 g_date_get_month (date), g_date_get_day (date));
1332 gtk_calendar_mark_day (GTK_CALENDAR (window->calendar_chats), g_date_get_day (date));
1336 g_list_foreach (dates, (GFunc) g_free, NULL);
1337 g_list_free (dates);
1339 DEBUG ("Currently showing month %d and year %d", month_selected,
1340 year_selected);
1343 static void
1344 log_window_calendar_chats_month_changed_cb (GtkWidget *calendar,
1345 EmpathyLogWindow *window)
1347 TpAccount *account;
1348 gchar *chat_id;
1349 gboolean is_chatroom;
1351 gtk_calendar_clear_marks (GTK_CALENDAR (calendar));
1353 if (!log_window_chats_get_selected (window, &account,
1354 &chat_id, &is_chatroom)) {
1355 DEBUG ("No chat selected to get dates for...");
1356 return;
1359 /* Get the log object for this contact */
1360 tpl_log_manager_get_dates_async (window->log_manager, account,
1361 chat_id, is_chatroom,
1362 log_window_updating_calendar_month_cb,
1363 (gpointer) window);
1365 g_object_unref (account);
1366 g_free (chat_id);
1369 static void
1370 log_window_entry_chats_changed_cb (GtkWidget *entry,
1371 EmpathyLogWindow *window)
1373 const gchar *str;
1375 str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats));
1376 empathy_chat_view_highlight (window->chatview_chats, str, FALSE);
1378 if (str != NULL) {
1379 empathy_chat_view_find_next (window->chatview_chats,
1380 str,
1381 TRUE,
1382 FALSE);
1386 static void
1387 log_window_entry_chats_activate_cb (GtkWidget *entry,
1388 EmpathyLogWindow *window)
1390 const gchar *str;
1392 str = gtk_entry_get_text (GTK_ENTRY (window->entry_chats));
1394 if (str != NULL) {
1395 empathy_chat_view_find_next (window->chatview_chats,
1396 str,
1397 FALSE,
1398 FALSE);