comparison finch/gntlog.c @ 23456:00eaff9396ec

propagate from branch 'im.pidgin.pidgin' (head e39a6e9be3df05b59a878001feb9276b9ceb66a9) to branch 'im.pidgin.pidgin.khc.msnp15' (head c5b8a906c26f5cb0fd8c7256852e3c1e346d7863)
author Ka-Hing Cheung <khc@hxbc.us>
date Wed, 06 Feb 2008 03:35:04 +0000
parents 88796aff14d6
children 561729870929 c96b34b58b85
comparison
equal deleted inserted replaced
23455:f182cf94145c 23456:00eaff9396ec
1 /**
2 * @file gntlog.c GNT Log viewer
3 * @ingroup finch
4 */
5
6 /* finch
7 *
8 * Finch is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 */
26 #include "internal.h"
27
28 #include <gnt.h>
29 #include <gntbox.h>
30 #include <gntbutton.h>
31 #include <gntentry.h>
32 #include <gntlabel.h>
33 #include <gnttextview.h>
34 #include <gnttree.h>
35 #include <gntwindow.h>
36
37 #include "account.h"
38 #include "debug.h"
39 #include "log.h"
40 #include "notify.h"
41 #include "request.h"
42 #include "util.h"
43
44 #include "gntlog.h"
45
46 static GHashTable *log_viewers = NULL;
47 static void populate_log_tree(FinchLogViewer *lv);
48 static FinchLogViewer *syslog_viewer = NULL;
49
50 struct log_viewer_hash_t {
51 PurpleLogType type;
52 char *screenname;
53 PurpleAccount *account;
54 PurpleContact *contact;
55 };
56
57 static guint log_viewer_hash(gconstpointer data)
58 {
59 const struct log_viewer_hash_t *viewer = data;
60
61 if (viewer->contact != NULL)
62 return g_direct_hash(viewer->contact);
63
64 return g_str_hash(viewer->screenname) +
65 g_str_hash(purple_account_get_username(viewer->account));
66 }
67
68 static gboolean log_viewer_equal(gconstpointer y, gconstpointer z)
69 {
70 const struct log_viewer_hash_t *a, *b;
71 int ret;
72 char *normal;
73
74 a = y;
75 b = z;
76
77 if (a->contact != NULL) {
78 if (b->contact != NULL)
79 return (a->contact == b->contact);
80 else
81 return FALSE;
82 } else {
83 if (b->contact != NULL)
84 return FALSE;
85 }
86
87 normal = g_strdup(purple_normalize(a->account, a->screenname));
88 ret = (a->account == b->account) &&
89 !strcmp(normal, purple_normalize(b->account, b->screenname));
90 g_free(normal);
91
92 return ret;
93 }
94
95 static const char *log_get_date(PurpleLog *log)
96 {
97 if (log->tm)
98 return purple_date_format_full(log->tm);
99 else
100 return purple_date_format_full(localtime(&log->time));
101 }
102
103 static void search_cb(GntWidget *button, FinchLogViewer *lv)
104 {
105 const char *search_term = gnt_entry_get_text(GNT_ENTRY(lv->entry));
106 GList *logs;
107
108 if (!(*search_term)) {
109 /* reset the tree */
110 gnt_tree_remove_all(GNT_TREE(lv->tree));
111 g_free(lv->search);
112 lv->search = NULL;
113 populate_log_tree(lv);
114 return;
115 }
116
117 if (lv->search != NULL && !strcmp(lv->search, search_term)) {
118 return;
119 }
120
121 g_free(lv->search);
122 lv->search = g_strdup(search_term);
123
124 gnt_tree_remove_all(GNT_TREE(lv->tree));
125 gnt_text_view_clear(GNT_TEXT_VIEW(lv->text));
126
127 for (logs = lv->logs; logs != NULL; logs = logs->next) {
128 char *read = purple_log_read((PurpleLog*)logs->data, NULL);
129 if (read && *read && purple_strcasestr(read, search_term)) {
130 PurpleLog *log = logs->data;
131
132 gnt_tree_add_row_last(GNT_TREE(lv->tree),
133 log,
134 gnt_tree_create_row(GNT_TREE(lv->tree), log_get_date(log)),
135 NULL);
136 }
137 g_free(read);
138 }
139
140 }
141
142 static void destroy_cb(GntWidget *w, struct log_viewer_hash_t *ht) {
143 FinchLogViewer *lv = syslog_viewer;
144
145 if (ht != NULL) {
146 lv = g_hash_table_lookup(log_viewers, ht);
147 g_hash_table_remove(log_viewers, ht);
148
149 g_free(ht->screenname);
150 g_free(ht);
151 } else
152 syslog_viewer = NULL;
153
154 purple_request_close_with_handle(lv);
155
156 g_list_foreach(lv->logs, (GFunc)purple_log_free, NULL);
157 g_list_free(lv->logs);
158
159 g_free(lv->search);
160 g_free(lv);
161
162 gnt_widget_destroy(w);
163 }
164
165 static void log_select_cb(GntWidget *w, gpointer old, gpointer new, FinchLogViewer *viewer) {
166 GntTree *tree = GNT_TREE(w);
167 PurpleLog *log = NULL;
168 PurpleLogReadFlags flags;
169 char *read = NULL, *strip, *newline;
170 int h;
171
172 if (!viewer->search && !gnt_tree_get_parent_key(tree, new))
173 return;
174
175 log = (PurpleLog *)new;
176
177 if (log == NULL)
178 return;
179
180 if (log->type != PURPLE_LOG_SYSTEM) {
181 char *title;
182 if (log->type == PURPLE_LOG_CHAT)
183 title = g_strdup_printf(_("Conversation in %s on %s"),
184 log->name, log_get_date(log));
185 else
186 title = g_strdup_printf(_("Conversation with %s on %s"),
187 log->name, log_get_date(log));
188
189 gnt_label_set_text(GNT_LABEL(viewer->label), title);
190 g_free(title);
191 }
192
193 read = purple_log_read(log, &flags);
194 if (flags != PURPLE_LOG_READ_NO_NEWLINE) {
195 newline = purple_strdup_withhtml(read);
196 strip = purple_markup_strip_html(newline);
197 g_free(newline);
198 } else {
199 strip = purple_markup_strip_html(read);
200 }
201 viewer->flags = flags;
202
203 purple_signal_emit(finch_log_get_handle(), "log-displaying", viewer, log);
204
205 gnt_text_view_clear(GNT_TEXT_VIEW(viewer->text));
206 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(viewer->text), strip, GNT_TEXT_FLAG_NORMAL);
207 gnt_widget_get_size(viewer->text, NULL, &h);
208 gnt_text_view_scroll(GNT_TEXT_VIEW(viewer->text), h - 2);
209 g_free(read);
210 g_free(strip);
211 }
212
213 /* I want to make this smarter, but haven't come up with a cool algorithm to do so, yet.
214 * I want the tree to be divided into groups like "Today," "Yesterday," "Last week,"
215 * "August," "2002," etc. based on how many conversation took place in each subdivision.
216 *
217 * For now, I'll just make it a flat list.
218 */
219 static void populate_log_tree(FinchLogViewer *lv)
220 /* Logs are made from trees in real life.
221 This is a tree made from logs */
222 {
223 const char *pmonth;
224 char *month = NULL;
225 char prev_top_month[30] = "";
226 GList *logs = lv->logs;
227
228 while (logs != NULL) {
229 PurpleLog *log = logs->data;
230
231 pmonth = purple_utf8_strftime(_("%B %Y"),
232 log->tm ? log->tm : localtime(&log->time));
233
234 if (strcmp(pmonth, prev_top_month) != 0) {
235 month = g_strdup(pmonth);
236 /* top level */
237 gnt_tree_add_row_last(GNT_TREE(lv->tree),
238 month,
239 gnt_tree_create_row(GNT_TREE(lv->tree), month),
240 NULL);
241 gnt_tree_set_expanded(GNT_TREE(lv->tree), month, FALSE);
242
243 strncpy(prev_top_month, month, sizeof(prev_top_month));
244 }
245
246 /* sub */
247 gnt_tree_add_row_last(GNT_TREE(lv->tree),
248 log,
249 gnt_tree_create_row(GNT_TREE(lv->tree), log_get_date(log)),
250 month);
251
252 logs = logs->next;
253 }
254 }
255
256 static FinchLogViewer *display_log_viewer(struct log_viewer_hash_t *ht, GList *logs,
257 const char *title, int log_size)
258 {
259 FinchLogViewer *lv;
260 char *text;
261 GntWidget *vbox, *hbox;
262 GntWidget *size_label;
263
264 if (logs == NULL)
265 {
266 /* No logs were found. */
267 const char *log_preferences = NULL;
268
269 if (ht == NULL) {
270 if (!purple_prefs_get_bool("/purple/logging/log_system"))
271 log_preferences = _("System events will only be logged if the \"Log all status changes to system log\" preference is enabled.");
272 } else {
273 if (ht->type == PURPLE_LOG_IM) {
274 if (!purple_prefs_get_bool("/purple/logging/log_ims"))
275 log_preferences = _("Instant messages will only be logged if the \"Log all instant messages\" preference is enabled.");
276 } else if (ht->type == PURPLE_LOG_CHAT) {
277 if (!purple_prefs_get_bool("/purple/logging/log_chats"))
278 log_preferences = _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
279 }
280 g_free(ht->screenname);
281 g_free(ht);
282 }
283
284 purple_notify_info(NULL, title, _("No logs were found"), log_preferences);
285 return NULL;
286 }
287
288 lv = g_new0(FinchLogViewer, 1);
289 lv->logs = logs;
290
291 if (ht != NULL)
292 g_hash_table_insert(log_viewers, ht, lv);
293
294 /* Window ***********/
295 lv->window = gnt_vwindow_new(FALSE);
296 gnt_box_set_title(GNT_BOX(lv->window), title);
297 gnt_box_set_toplevel(GNT_BOX(lv->window), TRUE);
298 gnt_box_set_pad(GNT_BOX(lv->window), 0);
299 g_signal_connect(G_OBJECT(lv->window), "destroy", G_CALLBACK(destroy_cb), ht);
300
301 vbox = gnt_vbox_new(FALSE);
302 gnt_box_add_widget(GNT_BOX(lv->window), vbox);
303
304 /* Label ************/
305 text = g_strdup_printf("%s", title);
306 lv->label = gnt_label_new(text);
307 g_free(text);
308 gnt_box_add_widget(GNT_BOX(vbox), lv->label);
309
310 hbox = gnt_hbox_new(FALSE);
311 gnt_box_add_widget(GNT_BOX(vbox), hbox);
312 /* List *************/
313 lv->tree = gnt_tree_new();
314 gnt_widget_set_size(lv->tree, 30, 0);
315 populate_log_tree(lv);
316 g_signal_connect (G_OBJECT(lv->tree), "selection-changed",
317 G_CALLBACK (log_select_cb),
318 lv);
319 gnt_box_add_widget(GNT_BOX(hbox), lv->tree);
320
321 /* Viewer ************/
322 lv->text = gnt_text_view_new();
323 gnt_box_add_widget(GNT_BOX(hbox), lv->text);
324
325 hbox = gnt_hbox_new(FALSE);
326 gnt_box_add_widget(GNT_BOX(vbox), hbox);
327 /* Log size ************/
328 if (log_size) {
329 char *sz_txt = purple_str_size_to_units(log_size);
330 text = g_strdup_printf("%s %s", _("Total log size:"), sz_txt);
331 size_label = gnt_label_new(text);
332 gnt_box_add_widget(GNT_BOX(hbox), size_label);
333 g_free(sz_txt);
334 g_free(text);
335 }
336
337 /* Search box **********/
338 gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Scroll/Search: ")));
339 lv->entry = gnt_entry_new("");
340 gnt_box_add_widget(GNT_BOX(hbox), lv->entry);
341 g_signal_connect(GNT_ENTRY(lv->entry), "activate", G_CALLBACK(search_cb), lv);
342
343 gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(lv->text), lv->entry);
344 gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(lv->text), lv->entry);
345
346 gnt_widget_show(lv->window);
347
348 return lv;
349 }
350
351 void finch_log_show(PurpleLogType type, const char *screenname, PurpleAccount *account) {
352 struct log_viewer_hash_t *ht;
353 FinchLogViewer *lv = NULL;
354 const char *name = screenname;
355 char *title;
356
357 g_return_if_fail(account != NULL);
358 g_return_if_fail(screenname != NULL);
359
360 ht = g_new0(struct log_viewer_hash_t, 1);
361
362 ht->type = type;
363 ht->screenname = g_strdup(screenname);
364 ht->account = account;
365
366 if (log_viewers == NULL) {
367 log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
368 } else if ((lv = g_hash_table_lookup(log_viewers, ht))) {
369 gnt_window_present(lv->window);
370 g_free(ht->screenname);
371 g_free(ht);
372 return;
373 }
374
375 if (type == PURPLE_LOG_CHAT) {
376 PurpleChat *chat;
377
378 chat = purple_blist_find_chat(account, screenname);
379 if (chat != NULL)
380 name = purple_chat_get_name(chat);
381
382 title = g_strdup_printf(_("Conversations in %s"), name);
383 } else {
384 PurpleBuddy *buddy;
385
386 buddy = purple_find_buddy(account, screenname);
387 if (buddy != NULL)
388 name = purple_buddy_get_contact_alias(buddy);
389
390 title = g_strdup_printf(_("Conversations with %s"), name);
391 }
392
393 display_log_viewer(ht, purple_log_get_logs(type, screenname, account),
394 title, purple_log_get_total_size(type, screenname, account));
395
396 g_free(title);
397 }
398
399 void finch_log_show_contact(PurpleContact *contact) {
400 struct log_viewer_hash_t *ht;
401 PurpleBlistNode *child;
402 FinchLogViewer *lv = NULL;
403 GList *logs = NULL;
404 const char *name = NULL;
405 char *title;
406 int total_log_size = 0;
407
408 g_return_if_fail(contact != NULL);
409
410 ht = g_new0(struct log_viewer_hash_t, 1);
411 ht->type = PURPLE_LOG_IM;
412 ht->contact = contact;
413
414 if (log_viewers == NULL) {
415 log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
416 } else if ((lv = g_hash_table_lookup(log_viewers, ht))) {
417 gnt_window_present(lv->window);
418 g_free(ht);
419 return;
420 }
421
422 for (child = contact->node.child ; child ; child = child->next) {
423 if (!PURPLE_BLIST_NODE_IS_BUDDY(child))
424 continue;
425
426 logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, ((PurpleBuddy *)child)->name,
427 ((PurpleBuddy *)child)->account), logs);
428 total_log_size += purple_log_get_total_size(PURPLE_LOG_IM, ((PurpleBuddy *)child)->name, ((PurpleBuddy *)child)->account);
429 }
430 logs = g_list_sort(logs, purple_log_compare);
431
432 if (contact->alias != NULL)
433 name = contact->alias;
434 else if (contact->priority != NULL)
435 name = purple_buddy_get_contact_alias(contact->priority);
436
437 /* This will happen if the contact doesn't have an alias,
438 * and none of the contact's buddies are online.
439 * There is probably a better way to deal with this. */
440 if (name == NULL) {
441 if (contact->node.child != NULL && PURPLE_BLIST_NODE_IS_BUDDY(contact->node.child))
442 name = purple_buddy_get_contact_alias((PurpleBuddy *) contact->node.child);
443 if (name == NULL)
444 name = "";
445 }
446
447 title = g_strdup_printf(_("Conversations with %s"), name);
448 display_log_viewer(ht, logs, title, total_log_size);
449 g_free(title);
450 }
451
452 void finch_syslog_show()
453 {
454 GList *accounts = NULL;
455 GList *logs = NULL;
456
457 if (syslog_viewer != NULL) {
458 gnt_window_present(syslog_viewer->window);
459 return;
460 }
461
462 for(accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) {
463
464 PurpleAccount *account = (PurpleAccount *)accounts->data;
465 if(purple_find_prpl(purple_account_get_protocol_id(account)) == NULL)
466 continue;
467
468 logs = g_list_concat(purple_log_get_system_logs(account), logs);
469 }
470 logs = g_list_sort(logs, purple_log_compare);
471
472 syslog_viewer = display_log_viewer(NULL, logs, _("System Log"), 0);
473 }
474
475 /****************************************************************************
476 * GNT LOG SUBSYSTEM *******************************************************
477 ****************************************************************************/
478
479 void *
480 finch_log_get_handle(void)
481 {
482 static int handle;
483
484 return &handle;
485 }
486
487 void finch_log_init(void)
488 {
489 void *handle = finch_log_get_handle();
490
491 purple_signal_register(handle, "log-displaying",
492 purple_marshal_VOID__POINTER_POINTER,
493 NULL, 2,
494 purple_value_new(PURPLE_TYPE_BOXED,
495 "FinchLogViewer *"),
496 purple_value_new(PURPLE_TYPE_SUBTYPE,
497 PURPLE_SUBTYPE_LOG));
498 }
499
500 void
501 finch_log_uninit(void)
502 {
503 purple_signals_unregister_by_instance(finch_log_get_handle());
504 }