register PurpleAccountOption as a boxed type
[pidgin-git.git] / finch / gntlog.c
blobab23da5755ba68be8525501a6e5242b4a120007b
1 /*
2 * finch
4 * Finch is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include <internal.h>
24 #include "finch.h"
26 #include <gnt.h>
27 #include <gntbox.h>
28 #include <gntbutton.h>
29 #include <gntentry.h>
30 #include <gntlabel.h>
31 #include <gnttextview.h>
32 #include <gnttree.h>
33 #include <gntwindow.h>
35 #include "account.h"
36 #include "debug.h"
37 #include "log.h"
38 #include "notify.h"
39 #include "request.h"
40 #include "util.h"
42 #include "gntlog.h"
44 static GHashTable *log_viewers = NULL;
45 static void populate_log_tree(FinchLogViewer *lv);
46 static FinchLogViewer *syslog_viewer = NULL;
48 struct log_viewer_hash {
49 PurpleLogType type;
50 char *username;
51 PurpleAccount *account;
52 PurpleContact *contact;
55 static guint log_viewer_hash(gconstpointer data)
57 const struct log_viewer_hash *viewer = data;
59 if (viewer->contact != NULL)
60 return g_direct_hash(viewer->contact);
62 if (viewer->account) {
63 return g_str_hash(viewer->username) +
64 g_str_hash(purple_account_get_username(viewer->account));
67 return g_direct_hash(viewer);
70 static gboolean log_viewer_equal(gconstpointer y, gconstpointer z)
72 const struct log_viewer_hash *a, *b;
73 int ret;
74 char *normal;
76 a = y;
77 b = z;
79 if (a->contact != NULL) {
80 if (b->contact != NULL)
81 return (a->contact == b->contact);
82 else
83 return FALSE;
84 } else {
85 if (b->contact != NULL)
86 return FALSE;
89 if (a->username && b->username) {
90 normal = g_strdup(purple_normalize(a->account, a->username));
91 ret = (a->account == b->account) &&
92 purple_strequal(normal, purple_normalize(b->account, b->username));
93 g_free(normal);
94 } else {
95 ret = (a == b);
98 return ret;
101 static gchar *log_get_date(PurpleLog *log)
103 GDateTime *dt;
104 gchar *ret;
105 dt = g_date_time_to_local(log->time);
106 ret = g_date_time_format(dt, "%c");
107 g_date_time_unref(dt);
108 return ret;
111 static void search_cb(GntWidget *button, FinchLogViewer *lv)
113 const char *search_term = gnt_entry_get_text(GNT_ENTRY(lv->entry));
114 GList *logs;
116 if (!(*search_term)) {
117 /* reset the tree */
118 gnt_tree_remove_all(GNT_TREE(lv->tree));
119 g_free(lv->search);
120 lv->search = NULL;
121 populate_log_tree(lv);
122 return;
125 if (lv->search != NULL && purple_strequal(lv->search, search_term)) {
126 return;
129 g_free(lv->search);
130 lv->search = g_strdup(search_term);
132 gnt_tree_remove_all(GNT_TREE(lv->tree));
133 gnt_text_view_clear(GNT_TEXT_VIEW(lv->text));
135 for (logs = lv->logs; logs != NULL; logs = logs->next) {
136 char *read = purple_log_read((PurpleLog*)logs->data, NULL);
137 if (read && *read && purple_strcasestr(read, search_term)) {
138 PurpleLog *log = logs->data;
139 gchar *log_date = log_get_date(log);
141 gnt_tree_add_row_last(GNT_TREE(lv->tree),
142 log,
143 gnt_tree_create_row(GNT_TREE(lv->tree), log_date),
144 NULL);
145 g_free(log_date);
147 g_free(read);
152 static void
153 destroy_cb(GntWidget *w, struct log_viewer_hash *ht)
155 FinchLogViewer *lv = syslog_viewer;
157 if (ht != NULL) {
158 lv = g_hash_table_lookup(log_viewers, ht);
159 g_hash_table_remove(log_viewers, ht);
161 g_free(ht->username);
162 g_free(ht);
163 } else
164 syslog_viewer = NULL;
166 purple_request_close_with_handle(lv);
168 g_list_free_full(lv->logs, (GDestroyNotify)purple_log_free);
170 g_free(lv->search);
171 g_free(lv);
173 gnt_widget_destroy(w);
176 static void log_select_cb(GntWidget *w, gpointer old, gpointer new, FinchLogViewer *viewer)
178 GntTree *tree = GNT_TREE(w);
179 PurpleLog *log = NULL;
180 PurpleLogReadFlags flags;
181 char *read = NULL, *strip, *newline;
183 if (!viewer->search && !gnt_tree_get_parent_key(tree, new))
184 return;
186 log = (PurpleLog *)new;
188 if (log == NULL)
189 return;
191 if (log->type != PURPLE_LOG_SYSTEM) {
192 gchar *log_date = log_get_date(log);
193 char *title;
194 if (log->type == PURPLE_LOG_CHAT)
195 title = g_strdup_printf(_("Conversation in %s on %s"),
196 log->name, log_date);
197 else
198 title = g_strdup_printf(_("Conversation with %s on %s"),
199 log->name, log_date);
201 gnt_label_set_text(GNT_LABEL(viewer->label), title);
202 g_free(log_date);
203 g_free(title);
206 read = purple_log_read(log, &flags);
207 if (flags != PURPLE_LOG_READ_NO_NEWLINE) {
208 newline = purple_strdup_withhtml(read);
209 strip = purple_markup_strip_html(newline);
210 g_free(newline);
211 } else {
212 strip = purple_markup_strip_html(read);
214 viewer->flags = flags;
216 purple_signal_emit(finch_log_get_handle(), "log-displaying", viewer, log);
218 gnt_text_view_clear(GNT_TEXT_VIEW(viewer->text));
219 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(viewer->text), strip, GNT_TEXT_FLAG_NORMAL);
220 g_free(read);
221 g_free(strip);
224 /* I want to make this smarter, but haven't come up with a cool algorithm to do so, yet.
225 * I want the tree to be divided into groups like "Today," "Yesterday," "Last week,"
226 * "August," "2002," etc. based on how many conversation took place in each subdivision.
228 * For now, I'll just make it a flat list.
230 static void populate_log_tree(FinchLogViewer *lv)
231 /* Logs are made from trees in real life.
232 This is a tree made from logs */
234 gchar *pmonth;
235 gchar *month = NULL;
236 char prev_top_month[30] = "";
237 GList *logs = lv->logs;
239 while (logs != NULL) {
240 PurpleLog *log = logs->data;
241 GDateTime *dt;
242 gchar *log_date;
244 dt = g_date_time_to_local(log->time);
245 pmonth = g_date_time_format(dt, _("%B %Y"));
247 if (!purple_strequal(pmonth, prev_top_month)) {
248 month = g_strdup(pmonth);
249 /* top level */
250 gnt_tree_add_row_last(GNT_TREE(lv->tree),
251 month,
252 gnt_tree_create_row(GNT_TREE(lv->tree), month),
253 NULL);
254 gnt_tree_set_expanded(GNT_TREE(lv->tree), month, FALSE);
256 g_strlcpy(prev_top_month, month, sizeof(prev_top_month));
259 /* sub */
260 log_date = g_date_time_format(dt, "%c");
261 gnt_tree_add_row_last(GNT_TREE(lv->tree),
262 log,
263 gnt_tree_create_row(GNT_TREE(lv->tree), log_date),
264 month);
266 g_free(log_date);
267 g_free(pmonth);
268 g_date_time_unref(dt);
269 logs = logs->next;
273 static FinchLogViewer *
274 display_log_viewer(struct log_viewer_hash *ht, GList *logs, const char *title,
275 int log_size)
277 FinchLogViewer *lv;
278 char *text;
279 GntWidget *vbox, *hbox;
280 GntWidget *size_label;
282 if (logs == NULL)
284 /* No logs were found. */
285 const char *log_preferences = NULL;
287 if (ht == NULL) {
288 if (!purple_prefs_get_bool("/purple/logging/log_system"))
289 log_preferences = _("System events will only be logged if the \"Log all status changes to system log\" preference is enabled.");
290 } else {
291 if (ht->type == PURPLE_LOG_IM) {
292 if (!purple_prefs_get_bool("/purple/logging/log_ims"))
293 log_preferences = _("Instant messages will only be logged if the \"Log all instant messages\" preference is enabled.");
294 } else if (ht->type == PURPLE_LOG_CHAT) {
295 if (!purple_prefs_get_bool("/purple/logging/log_chats"))
296 log_preferences = _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
298 g_free(ht->username);
299 g_free(ht);
302 purple_notify_info(NULL, title, _("No logs were found"), log_preferences, NULL);
303 return NULL;
306 lv = g_new0(FinchLogViewer, 1);
307 lv->logs = logs;
309 if (ht != NULL)
310 g_hash_table_insert(log_viewers, ht, lv);
312 /* Window ***********/
313 lv->window = gnt_vwindow_new(FALSE);
314 gnt_box_set_title(GNT_BOX(lv->window), title);
315 gnt_box_set_toplevel(GNT_BOX(lv->window), TRUE);
316 gnt_box_set_pad(GNT_BOX(lv->window), 0);
317 g_signal_connect(G_OBJECT(lv->window), "destroy", G_CALLBACK(destroy_cb), ht);
319 vbox = gnt_vbox_new(FALSE);
320 gnt_box_add_widget(GNT_BOX(lv->window), vbox);
322 /* Label ************/
323 text = g_strdup_printf("%s", title);
324 lv->label = gnt_label_new_with_format(text, GNT_TEXT_FLAG_BOLD);
325 g_free(text);
326 gnt_box_add_widget(GNT_BOX(vbox), lv->label);
328 hbox = gnt_hbox_new(FALSE);
329 gnt_box_add_widget(GNT_BOX(vbox), hbox);
330 /* List *************/
331 lv->tree = gnt_tree_new();
332 gnt_widget_set_size(lv->tree, 30, 0);
333 populate_log_tree(lv);
334 g_signal_connect (G_OBJECT(lv->tree), "selection-changed",
335 G_CALLBACK (log_select_cb),
336 lv);
337 gnt_box_add_widget(GNT_BOX(hbox), lv->tree);
339 /* Viewer ************/
340 lv->text = gnt_text_view_new();
341 gnt_box_add_widget(GNT_BOX(hbox), lv->text);
342 gnt_text_view_set_flag(GNT_TEXT_VIEW(lv->text), GNT_TEXT_VIEW_TOP_ALIGN);
344 hbox = gnt_hbox_new(FALSE);
345 gnt_box_add_widget(GNT_BOX(vbox), hbox);
346 /* Log size ************/
347 if (log_size) {
348 char *sz_txt = g_format_size(log_size);
349 text = g_strdup_printf("%s %s", _("Total log size:"), sz_txt);
350 size_label = gnt_label_new(text);
351 gnt_box_add_widget(GNT_BOX(hbox), size_label);
352 g_free(sz_txt);
353 g_free(text);
356 /* Search box **********/
357 gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Scroll/Search: ")));
358 lv->entry = gnt_entry_new("");
359 gnt_box_add_widget(GNT_BOX(hbox), lv->entry);
360 g_signal_connect(GNT_ENTRY(lv->entry), "activate", G_CALLBACK(search_cb), lv);
362 gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(lv->text), lv->entry);
363 gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(lv->text), lv->entry);
365 gnt_widget_show(lv->window);
367 return lv;
370 static void
371 our_logging_blows(PurpleLogSet *set, PurpleLogSet *setagain, GList **list)
373 /* The iteration happens on the first list. So we use the shorter list in front */
374 if (set->type != PURPLE_LOG_IM)
375 return;
376 *list = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, set->name, set->account), *list);
379 void finch_log_show(PurpleLogType type, const char *username, PurpleAccount *account)
381 struct log_viewer_hash *ht;
382 FinchLogViewer *lv = NULL;
383 const char *name = username;
384 char *title;
385 GList *logs = NULL;
386 int size = 0;
388 if (type != PURPLE_LOG_IM) {
389 g_return_if_fail(account != NULL);
390 g_return_if_fail(username != NULL);
393 ht = g_new0(struct log_viewer_hash, 1);
395 ht->type = type;
396 ht->username = g_strdup(username);
397 ht->account = account;
399 if (log_viewers == NULL) {
400 log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
401 } else if ((lv = g_hash_table_lookup(log_viewers, ht))) {
402 gnt_window_present(lv->window);
403 g_free(ht->username);
404 g_free(ht);
405 return;
408 if (type == PURPLE_LOG_CHAT) {
409 PurpleChat *chat;
411 chat = purple_blist_find_chat(account, username);
412 if (chat != NULL)
413 name = purple_chat_get_name(chat);
415 title = g_strdup_printf(_("Conversations in %s"), name);
416 } else {
417 PurpleBuddy *buddy;
419 if (username) {
420 buddy = purple_blist_find_buddy(account, username);
421 if (buddy != NULL)
422 name = purple_buddy_get_contact_alias(buddy);
423 title = g_strdup_printf(_("Conversations with %s"), name);
424 } else {
425 title = g_strdup(_("All Conversations"));
429 if (username) {
430 logs = purple_log_get_logs(type, username, account);
431 size = purple_log_get_total_size(type, username, account);
432 } else {
433 /* This will happen only for IMs */
434 GHashTable *table = purple_log_get_log_sets();
435 g_hash_table_foreach(table, (GHFunc)our_logging_blows, &logs);
436 g_hash_table_destroy(table);
437 logs = g_list_sort(logs, purple_log_compare);
438 size = 0;
441 display_log_viewer(ht, logs, title, size);
443 g_free(title);
446 void finch_log_show_contact(PurpleContact *contact)
448 struct log_viewer_hash *ht;
449 PurpleBlistNode *child;
450 FinchLogViewer *lv = NULL;
451 GList *logs = NULL;
452 const char *name = NULL;
453 char *title;
454 int total_log_size = 0;
456 g_return_if_fail(contact != NULL);
458 ht = g_new0(struct log_viewer_hash, 1);
459 ht->type = PURPLE_LOG_IM;
460 ht->contact = contact;
462 if (log_viewers == NULL) {
463 log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
464 } else if ((lv = g_hash_table_lookup(log_viewers, ht))) {
465 gnt_window_present(lv->window);
466 g_free(ht);
467 return;
470 for (child = purple_blist_node_get_first_child((PurpleBlistNode*)contact); child;
471 child = purple_blist_node_get_sibling_next(child)) {
472 const char *name;
473 PurpleAccount *account;
474 if (!PURPLE_IS_BUDDY(child))
475 continue;
477 name = purple_buddy_get_name((PurpleBuddy *)child);
478 account = purple_buddy_get_account((PurpleBuddy *)child);
479 logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, name,
480 account), logs);
481 total_log_size += purple_log_get_total_size(PURPLE_LOG_IM, name, account);
483 logs = g_list_sort(logs, purple_log_compare);
485 name = purple_contact_get_alias(contact);
486 if (!name)
487 name = purple_buddy_get_contact_alias(purple_contact_get_priority_buddy(contact));
489 /* This will happen if the contact doesn't have an alias,
490 * and none of the contact's buddies are online.
491 * There is probably a better way to deal with this. */
492 if (name == NULL) {
493 child = purple_blist_node_get_first_child((PurpleBlistNode*)contact);
494 if (PURPLE_IS_BUDDY(child)) {
495 name = purple_buddy_get_contact_alias((PurpleBuddy *)child);
497 if (name == NULL)
498 name = "";
501 title = g_strdup_printf(_("Conversations with %s"), name);
502 display_log_viewer(ht, logs, title, total_log_size);
503 g_free(title);
506 void finch_syslog_show()
508 GList *accounts = NULL;
509 GList *logs = NULL;
511 if (syslog_viewer != NULL) {
512 gnt_window_present(syslog_viewer->window);
513 return;
516 for(accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) {
518 PurpleAccount *account = (PurpleAccount *)accounts->data;
519 if(purple_protocols_find(purple_account_get_protocol_id(account)) == NULL)
520 continue;
522 logs = g_list_concat(purple_log_get_system_logs(account), logs);
524 logs = g_list_sort(logs, purple_log_compare);
526 syslog_viewer = display_log_viewer(NULL, logs, _("System Log"), 0);
529 /****************************************************************************
530 * GNT LOG SUBSYSTEM *******************************************************
531 ****************************************************************************/
533 void *
534 finch_log_get_handle(void)
536 static int handle;
538 return &handle;
541 void finch_log_init(void)
543 void *handle = finch_log_get_handle();
545 purple_signal_register(handle, "log-displaying",
546 purple_marshal_VOID__POINTER_POINTER,
547 G_TYPE_NONE, 2,
548 G_TYPE_POINTER, /* (FinchLogViewer *) */
549 PURPLE_TYPE_LOG);
552 void
553 finch_log_uninit(void)
555 purple_signals_unregister_by_instance(finch_log_get_handle());