Mercurial > pidgin
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 } |