finch/gntlog.c

changeset 41104
06289a48546f
parent 41103
db1b68e96dbf
child 41105
02c3569bc32b
equal deleted inserted replaced
41103:db1b68e96dbf 41104:06289a48546f
1 /*
2 * finch
3 *
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.
7 *
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.
12 *
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.
17 *
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
21 */
22
23 #include <glib/gi18n-lib.h>
24
25 #include <purple.h>
26
27 #include <gnt.h>
28
29 #include "gntlog.h"
30
31 static GHashTable *log_viewers = NULL;
32 static void populate_log_tree(FinchLogViewer *lv);
33 static FinchLogViewer *syslog_viewer = NULL;
34
35 struct log_viewer_hash {
36 PurpleLogType type;
37 char *username;
38 PurpleAccount *account;
39 PurpleContact *contact;
40 };
41
42 static guint log_viewer_hash(gconstpointer data)
43 {
44 const struct log_viewer_hash *viewer = data;
45
46 if (viewer->contact != NULL)
47 return g_direct_hash(viewer->contact);
48
49 if (viewer->account) {
50 return g_str_hash(viewer->username) +
51 g_str_hash(purple_account_get_username(viewer->account));
52 }
53
54 return g_direct_hash(viewer);
55 }
56
57 static gboolean log_viewer_equal(gconstpointer y, gconstpointer z)
58 {
59 const struct log_viewer_hash *a, *b;
60 int ret;
61 char *normal;
62
63 a = y;
64 b = z;
65
66 if (a->contact != NULL) {
67 if (b->contact != NULL)
68 return (a->contact == b->contact);
69 else
70 return FALSE;
71 } else {
72 if (b->contact != NULL)
73 return FALSE;
74 }
75
76 if (a->username && b->username) {
77 normal = g_strdup(purple_normalize(a->account, a->username));
78 ret = (a->account == b->account) &&
79 purple_strequal(normal, purple_normalize(b->account, b->username));
80 g_free(normal);
81 } else {
82 ret = (a == b);
83 }
84
85 return ret;
86 }
87
88 static gchar *log_get_date(PurpleLog *log)
89 {
90 GDateTime *dt;
91 gchar *ret;
92 dt = g_date_time_to_local(log->time);
93 ret = g_date_time_format(dt, "%c");
94 g_date_time_unref(dt);
95 return ret;
96 }
97
98 static void search_cb(GntWidget *button, FinchLogViewer *lv)
99 {
100 const char *search_term = gnt_entry_get_text(GNT_ENTRY(lv->entry));
101 GList *logs;
102
103 if (!(*search_term)) {
104 /* reset the tree */
105 gnt_tree_remove_all(GNT_TREE(lv->tree));
106 g_free(lv->search);
107 lv->search = NULL;
108 populate_log_tree(lv);
109 return;
110 }
111
112 if (lv->search != NULL && purple_strequal(lv->search, search_term)) {
113 return;
114 }
115
116 g_free(lv->search);
117 lv->search = g_strdup(search_term);
118
119 gnt_tree_remove_all(GNT_TREE(lv->tree));
120 gnt_text_view_clear(GNT_TEXT_VIEW(lv->text));
121
122 for (logs = lv->logs; logs != NULL; logs = logs->next) {
123 char *read = purple_log_read((PurpleLog*)logs->data, NULL);
124 if (read && *read && purple_strcasestr(read, search_term)) {
125 PurpleLog *log = logs->data;
126 gchar *log_date = log_get_date(log);
127
128 gnt_tree_add_row_last(GNT_TREE(lv->tree),
129 log,
130 gnt_tree_create_row(GNT_TREE(lv->tree), log_date),
131 NULL);
132 g_free(log_date);
133 }
134 g_free(read);
135 }
136
137 }
138
139 static void
140 destroy_cb(GntWidget *w, struct log_viewer_hash *ht)
141 {
142 FinchLogViewer *lv = syslog_viewer;
143
144 if (ht != NULL) {
145 lv = g_hash_table_lookup(log_viewers, ht);
146 g_hash_table_remove(log_viewers, ht);
147
148 g_free(ht->username);
149 g_free(ht);
150 } else
151 syslog_viewer = NULL;
152
153 purple_request_close_with_handle(lv);
154
155 g_list_free_full(lv->logs, (GDestroyNotify)purple_log_free);
156
157 g_free(lv->search);
158 g_free(lv);
159
160 gnt_widget_destroy(w);
161 }
162
163 static void log_select_cb(GntWidget *w, gpointer old, gpointer new, FinchLogViewer *viewer)
164 {
165 GntTree *tree = GNT_TREE(w);
166 PurpleLog *log = NULL;
167 PurpleLogReadFlags flags;
168 char *read = NULL, *strip, *newline;
169
170 if (!viewer->search && !gnt_tree_get_parent_key(tree, new))
171 return;
172
173 log = (PurpleLog *)new;
174
175 if (log == NULL)
176 return;
177
178 if (log->type != PURPLE_LOG_SYSTEM) {
179 gchar *log_date = log_get_date(log);
180 char *title;
181 if (log->type == PURPLE_LOG_CHAT)
182 title = g_strdup_printf(_("Conversation in %s on %s"),
183 log->name, log_date);
184 else
185 title = g_strdup_printf(_("Conversation with %s on %s"),
186 log->name, log_date);
187
188 gnt_label_set_text(GNT_LABEL(viewer->label), title);
189 g_free(log_date);
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 g_free(read);
208 g_free(strip);
209 }
210
211 /* I want to make this smarter, but haven't come up with a cool algorithm to do so, yet.
212 * I want the tree to be divided into groups like "Today," "Yesterday," "Last week,"
213 * "August," "2002," etc. based on how many conversation took place in each subdivision.
214 *
215 * For now, I'll just make it a flat list.
216 */
217 static void populate_log_tree(FinchLogViewer *lv)
218 /* Logs are made from trees in real life.
219 This is a tree made from logs */
220 {
221 gchar *pmonth;
222 gchar *month = NULL;
223 char prev_top_month[30] = "";
224 GList *logs = lv->logs;
225
226 while (logs != NULL) {
227 PurpleLog *log = logs->data;
228 GDateTime *dt;
229 gchar *log_date;
230
231 dt = g_date_time_to_local(log->time);
232 pmonth = g_date_time_format(dt, _("%B %Y"));
233
234 if (!purple_strequal(pmonth, prev_top_month)) {
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 g_strlcpy(prev_top_month, month, sizeof(prev_top_month));
244 }
245
246 /* sub */
247 log_date = g_date_time_format(dt, "%c");
248 gnt_tree_add_row_last(GNT_TREE(lv->tree),
249 log,
250 gnt_tree_create_row(GNT_TREE(lv->tree), log_date),
251 month);
252
253 g_free(log_date);
254 g_free(pmonth);
255 g_date_time_unref(dt);
256 logs = logs->next;
257 }
258 }
259
260 static FinchLogViewer *
261 display_log_viewer(struct log_viewer_hash *ht, GList *logs, const char *title,
262 int log_size)
263 {
264 FinchLogViewer *lv;
265 char *text;
266 GntWidget *vbox, *hbox;
267 GntWidget *size_label;
268
269 if (logs == NULL)
270 {
271 /* No logs were found. */
272 const char *log_preferences = NULL;
273
274 if (ht == NULL) {
275 if (!purple_prefs_get_bool("/purple/logging/log_system"))
276 log_preferences = _("System events will only be logged if the \"Log all status changes to system log\" preference is enabled.");
277 } else {
278 if (ht->type == PURPLE_LOG_IM) {
279 if (!purple_prefs_get_bool("/purple/logging/log_ims"))
280 log_preferences = _("Instant messages will only be logged if the \"Log all instant messages\" preference is enabled.");
281 } else if (ht->type == PURPLE_LOG_CHAT) {
282 if (!purple_prefs_get_bool("/purple/logging/log_chats"))
283 log_preferences = _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
284 }
285 g_free(ht->username);
286 g_free(ht);
287 }
288
289 purple_notify_info(NULL, title, _("No logs were found"), log_preferences, NULL);
290 return NULL;
291 }
292
293 lv = g_new0(FinchLogViewer, 1);
294 lv->logs = logs;
295
296 if (ht != NULL)
297 g_hash_table_insert(log_viewers, ht, lv);
298
299 /* Window ***********/
300 lv->window = gnt_vwindow_new(FALSE);
301 gnt_box_set_title(GNT_BOX(lv->window), title);
302 gnt_box_set_toplevel(GNT_BOX(lv->window), TRUE);
303 gnt_box_set_pad(GNT_BOX(lv->window), 0);
304 g_signal_connect(G_OBJECT(lv->window), "destroy", G_CALLBACK(destroy_cb), ht);
305
306 vbox = gnt_vbox_new(FALSE);
307 gnt_box_add_widget(GNT_BOX(lv->window), vbox);
308
309 /* Label ************/
310 text = g_strdup(title);
311 lv->label = gnt_label_new_with_format(text, GNT_TEXT_FLAG_BOLD);
312 g_free(text);
313 gnt_box_add_widget(GNT_BOX(vbox), lv->label);
314
315 hbox = gnt_hbox_new(FALSE);
316 gnt_box_add_widget(GNT_BOX(vbox), hbox);
317 /* List *************/
318 lv->tree = gnt_tree_new();
319 gnt_widget_set_size(lv->tree, 30, 0);
320 populate_log_tree(lv);
321 g_signal_connect (G_OBJECT(lv->tree), "selection-changed",
322 G_CALLBACK (log_select_cb),
323 lv);
324 gnt_box_add_widget(GNT_BOX(hbox), lv->tree);
325
326 /* Viewer ************/
327 lv->text = gnt_text_view_new();
328 gnt_box_add_widget(GNT_BOX(hbox), lv->text);
329 gnt_text_view_set_flag(GNT_TEXT_VIEW(lv->text), GNT_TEXT_VIEW_TOP_ALIGN);
330
331 hbox = gnt_hbox_new(FALSE);
332 gnt_box_add_widget(GNT_BOX(vbox), hbox);
333 /* Log size ************/
334 if (log_size) {
335 char *sz_txt = g_format_size(log_size);
336 text = g_strdup_printf("%s %s", _("Total log size:"), sz_txt);
337 size_label = gnt_label_new(text);
338 gnt_box_add_widget(GNT_BOX(hbox), size_label);
339 g_free(sz_txt);
340 g_free(text);
341 }
342
343 /* Search box **********/
344 gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Scroll/Search: ")));
345 lv->entry = gnt_entry_new("");
346 gnt_box_add_widget(GNT_BOX(hbox), lv->entry);
347 g_signal_connect(GNT_ENTRY(lv->entry), "activate", G_CALLBACK(search_cb), lv);
348
349 gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(lv->text), lv->entry);
350 gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(lv->text), lv->entry);
351
352 gnt_widget_show(lv->window);
353
354 return lv;
355 }
356
357 static void
358 our_logging_blows(PurpleLogSet *set, PurpleLogSet *setagain, GList **list)
359 {
360 /* The iteration happens on the first list. So we use the shorter list in front */
361 if (set->type != PURPLE_LOG_IM)
362 return;
363 *list = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, set->name, set->account), *list);
364 }
365
366 void finch_log_show(PurpleLogType type, const char *username, PurpleAccount *account)
367 {
368 struct log_viewer_hash *ht;
369 FinchLogViewer *lv = NULL;
370 const char *name = username;
371 char *title;
372 GList *logs = NULL;
373 int size = 0;
374
375 if (type != PURPLE_LOG_IM) {
376 g_return_if_fail(account != NULL);
377 g_return_if_fail(username != NULL);
378 }
379
380 ht = g_new0(struct log_viewer_hash, 1);
381
382 ht->type = type;
383 ht->username = g_strdup(username);
384 ht->account = account;
385
386 if (log_viewers == NULL) {
387 log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
388 } else if ((lv = g_hash_table_lookup(log_viewers, ht))) {
389 gnt_window_present(lv->window);
390 g_free(ht->username);
391 g_free(ht);
392 return;
393 }
394
395 if (type == PURPLE_LOG_CHAT) {
396 PurpleChat *chat;
397
398 chat = purple_blist_find_chat(account, username);
399 if (chat != NULL)
400 name = purple_chat_get_name(chat);
401
402 title = g_strdup_printf(_("Conversations in %s"), name);
403 } else {
404 PurpleBuddy *buddy;
405
406 if (username) {
407 buddy = purple_blist_find_buddy(account, username);
408 if (buddy != NULL)
409 name = purple_buddy_get_contact_alias(buddy);
410 title = g_strdup_printf(_("Conversations with %s"), name);
411 } else {
412 title = g_strdup(_("All Conversations"));
413 }
414 }
415
416 if (username) {
417 logs = purple_log_get_logs(type, username, account);
418 size = purple_log_get_total_size(type, username, account);
419 } else {
420 /* This will happen only for IMs */
421 GHashTable *table = purple_log_get_log_sets();
422 g_hash_table_foreach(table, (GHFunc)our_logging_blows, &logs);
423 g_hash_table_destroy(table);
424 logs = g_list_sort(logs, purple_log_compare);
425 size = 0;
426 }
427
428 display_log_viewer(ht, logs, title, size);
429
430 g_free(title);
431 }
432
433 void finch_log_show_contact(PurpleContact *contact)
434 {
435 struct log_viewer_hash *ht;
436 PurpleBlistNode *child;
437 FinchLogViewer *lv = NULL;
438 GList *logs = NULL;
439 const char *name = NULL;
440 char *title;
441 int total_log_size = 0;
442
443 g_return_if_fail(contact != NULL);
444
445 ht = g_new0(struct log_viewer_hash, 1);
446 ht->type = PURPLE_LOG_IM;
447 ht->contact = contact;
448
449 if (log_viewers == NULL) {
450 log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
451 } else if ((lv = g_hash_table_lookup(log_viewers, ht))) {
452 gnt_window_present(lv->window);
453 g_free(ht);
454 return;
455 }
456
457 for (child = purple_blist_node_get_first_child((PurpleBlistNode*)contact); child;
458 child = purple_blist_node_get_sibling_next(child)) {
459 const char *name;
460 PurpleAccount *account;
461 if (!PURPLE_IS_BUDDY(child))
462 continue;
463
464 name = purple_buddy_get_name((PurpleBuddy *)child);
465 account = purple_buddy_get_account((PurpleBuddy *)child);
466 logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, name,
467 account), logs);
468 total_log_size += purple_log_get_total_size(PURPLE_LOG_IM, name, account);
469 }
470 logs = g_list_sort(logs, purple_log_compare);
471
472 name = purple_contact_get_alias(contact);
473 if (!name)
474 name = purple_buddy_get_contact_alias(purple_contact_get_priority_buddy(contact));
475
476 /* This will happen if the contact doesn't have an alias,
477 * and none of the contact's buddies are online.
478 * There is probably a better way to deal with this. */
479 if (name == NULL) {
480 child = purple_blist_node_get_first_child((PurpleBlistNode*)contact);
481 if (PURPLE_IS_BUDDY(child)) {
482 name = purple_buddy_get_contact_alias((PurpleBuddy *)child);
483 }
484 if (name == NULL)
485 name = "";
486 }
487
488 title = g_strdup_printf(_("Conversations with %s"), name);
489 display_log_viewer(ht, logs, title, total_log_size);
490 g_free(title);
491 }
492
493 void finch_syslog_show()
494 {
495 GList *accounts = NULL;
496 GList *logs = NULL;
497
498 if (syslog_viewer != NULL) {
499 gnt_window_present(syslog_viewer->window);
500 return;
501 }
502
503 for(accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) {
504
505 PurpleAccount *account = (PurpleAccount *)accounts->data;
506 if(purple_account_get_protocol(account) == NULL)
507 continue;
508
509 logs = g_list_concat(purple_log_get_system_logs(account), logs);
510 }
511 logs = g_list_sort(logs, purple_log_compare);
512
513 syslog_viewer = display_log_viewer(NULL, logs, _("System Log"), 0);
514 }
515
516 /****************************************************************************
517 * GNT LOG SUBSYSTEM *******************************************************
518 ****************************************************************************/
519
520 void *
521 finch_log_get_handle(void)
522 {
523 static int handle;
524
525 return &handle;
526 }
527
528 void finch_log_init(void)
529 {
530 void *handle = finch_log_get_handle();
531
532 purple_signal_register(handle, "log-displaying",
533 purple_marshal_VOID__POINTER_POINTER,
534 G_TYPE_NONE, 2,
535 G_TYPE_POINTER, /* (FinchLogViewer *) */
536 PURPLE_TYPE_LOG);
537 }
538
539 void
540 finch_log_uninit(void)
541 {
542 purple_signals_unregister_by_instance(finch_log_get_handle());
543 }

mercurial