3 * Finch is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 #include <gntbutton.h>
29 #include <gnttextview.h>
31 #include <gntwindow.h>
42 static GHashTable
*log_viewers
= NULL
;
43 static void populate_log_tree(FinchLogViewer
*lv
);
44 static FinchLogViewer
*syslog_viewer
= NULL
;
46 struct log_viewer_hash_t
{
49 PurpleAccount
*account
;
50 PurpleContact
*contact
;
53 static guint
log_viewer_hash(gconstpointer data
)
55 const struct log_viewer_hash_t
*viewer
= data
;
57 if (viewer
->contact
!= NULL
)
58 return g_direct_hash(viewer
->contact
);
60 if (viewer
->account
) {
61 return g_str_hash(viewer
->username
) +
62 g_str_hash(purple_account_get_username(viewer
->account
));
65 return g_direct_hash(viewer
);
68 static gboolean
log_viewer_equal(gconstpointer y
, gconstpointer z
)
70 const struct log_viewer_hash_t
*a
, *b
;
77 if (a
->contact
!= NULL
) {
78 if (b
->contact
!= NULL
)
79 return (a
->contact
== b
->contact
);
83 if (b
->contact
!= NULL
)
87 if (a
->username
&& b
->username
) {
88 normal
= g_strdup(purple_normalize(a
->account
, a
->username
));
89 ret
= (a
->account
== b
->account
) &&
90 purple_strequal(normal
, purple_normalize(b
->account
, b
->username
));
99 static gchar
*log_get_date(PurpleLog
*log
)
103 dt
= g_date_time_to_local(log
->time
);
104 ret
= g_date_time_format(dt
, "%c");
105 g_date_time_unref(dt
);
109 static void search_cb(GntWidget
*button
, FinchLogViewer
*lv
)
111 const char *search_term
= gnt_entry_get_text(GNT_ENTRY(lv
->entry
));
114 if (!(*search_term
)) {
116 gnt_tree_remove_all(GNT_TREE(lv
->tree
));
119 populate_log_tree(lv
);
123 if (lv
->search
!= NULL
&& purple_strequal(lv
->search
, search_term
)) {
128 lv
->search
= g_strdup(search_term
);
130 gnt_tree_remove_all(GNT_TREE(lv
->tree
));
131 gnt_text_view_clear(GNT_TEXT_VIEW(lv
->text
));
133 for (logs
= lv
->logs
; logs
!= NULL
; logs
= logs
->next
) {
134 char *read
= purple_log_read((PurpleLog
*)logs
->data
, NULL
);
135 if (read
&& *read
&& purple_strcasestr(read
, search_term
)) {
136 PurpleLog
*log
= logs
->data
;
137 gchar
*log_date
= log_get_date(log
);
139 gnt_tree_add_row_last(GNT_TREE(lv
->tree
),
141 gnt_tree_create_row(GNT_TREE(lv
->tree
), log_date
),
150 static void destroy_cb(GntWidget
*w
, struct log_viewer_hash_t
*ht
)
152 FinchLogViewer
*lv
= syslog_viewer
;
155 lv
= g_hash_table_lookup(log_viewers
, ht
);
156 g_hash_table_remove(log_viewers
, ht
);
158 g_free(ht
->username
);
161 syslog_viewer
= NULL
;
163 purple_request_close_with_handle(lv
);
165 g_list_foreach(lv
->logs
, (GFunc
)purple_log_free
, NULL
);
166 g_list_free(lv
->logs
);
171 gnt_widget_destroy(w
);
174 static void log_select_cb(GntWidget
*w
, gpointer old
, gpointer
new, FinchLogViewer
*viewer
)
176 GntTree
*tree
= GNT_TREE(w
);
177 PurpleLog
*log
= NULL
;
178 PurpleLogReadFlags flags
;
179 char *read
= NULL
, *strip
, *newline
;
181 if (!viewer
->search
&& !gnt_tree_get_parent_key(tree
, new))
184 log
= (PurpleLog
*)new;
189 if (log
->type
!= PURPLE_LOG_SYSTEM
) {
190 gchar
*log_date
= log_get_date(log
);
192 if (log
->type
== PURPLE_LOG_CHAT
)
193 title
= g_strdup_printf(_("Conversation in %s on %s"),
194 log
->name
, log_date
);
196 title
= g_strdup_printf(_("Conversation with %s on %s"),
197 log
->name
, log_date
);
199 gnt_label_set_text(GNT_LABEL(viewer
->label
), title
);
204 read
= purple_log_read(log
, &flags
);
205 if (flags
!= PURPLE_LOG_READ_NO_NEWLINE
) {
206 newline
= purple_strdup_withhtml(read
);
207 strip
= purple_markup_strip_html(newline
);
210 strip
= purple_markup_strip_html(read
);
212 viewer
->flags
= flags
;
214 purple_signal_emit(finch_log_get_handle(), "log-displaying", viewer
, log
);
216 gnt_text_view_clear(GNT_TEXT_VIEW(viewer
->text
));
217 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(viewer
->text
), strip
, GNT_TEXT_FLAG_NORMAL
);
222 /* I want to make this smarter, but haven't come up with a cool algorithm to do so, yet.
223 * I want the tree to be divided into groups like "Today," "Yesterday," "Last week,"
224 * "August," "2002," etc. based on how many conversation took place in each subdivision.
226 * For now, I'll just make it a flat list.
228 static void populate_log_tree(FinchLogViewer
*lv
)
229 /* Logs are made from trees in real life.
230 This is a tree made from logs */
234 char prev_top_month
[30] = "";
235 GList
*logs
= lv
->logs
;
237 while (logs
!= NULL
) {
238 PurpleLog
*log
= logs
->data
;
242 dt
= g_date_time_to_local(log
->time
);
243 pmonth
= g_date_time_format(dt
, _("%B %Y"));
245 if (!purple_strequal(pmonth
, prev_top_month
)) {
246 month
= g_strdup(pmonth
);
248 gnt_tree_add_row_last(GNT_TREE(lv
->tree
),
250 gnt_tree_create_row(GNT_TREE(lv
->tree
), month
),
252 gnt_tree_set_expanded(GNT_TREE(lv
->tree
), month
, FALSE
);
254 g_strlcpy(prev_top_month
, month
, sizeof(prev_top_month
));
258 log_date
= g_date_time_format(dt
, "%c");
259 gnt_tree_add_row_last(GNT_TREE(lv
->tree
),
261 gnt_tree_create_row(GNT_TREE(lv
->tree
), log_date
),
266 g_date_time_unref(dt
);
271 static FinchLogViewer
*display_log_viewer(struct log_viewer_hash_t
*ht
, GList
*logs
,
272 const char *title
, int log_size
)
276 GntWidget
*vbox
, *hbox
;
277 GntWidget
*size_label
;
281 /* No logs were found. */
282 const char *log_preferences
= NULL
;
285 if (!purple_prefs_get_bool("/purple/logging/log_system"))
286 log_preferences
= _("System events will only be logged if the \"Log all status changes to system log\" preference is enabled.");
288 if (ht
->type
== PURPLE_LOG_IM
) {
289 if (!purple_prefs_get_bool("/purple/logging/log_ims"))
290 log_preferences
= _("Instant messages will only be logged if the \"Log all instant messages\" preference is enabled.");
291 } else if (ht
->type
== PURPLE_LOG_CHAT
) {
292 if (!purple_prefs_get_bool("/purple/logging/log_chats"))
293 log_preferences
= _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
295 g_free(ht
->username
);
299 purple_notify_info(NULL
, title
, _("No logs were found"), log_preferences
, NULL
);
303 lv
= g_new0(FinchLogViewer
, 1);
307 g_hash_table_insert(log_viewers
, ht
, lv
);
309 /* Window ***********/
310 lv
->window
= gnt_vwindow_new(FALSE
);
311 gnt_box_set_title(GNT_BOX(lv
->window
), title
);
312 gnt_box_set_toplevel(GNT_BOX(lv
->window
), TRUE
);
313 gnt_box_set_pad(GNT_BOX(lv
->window
), 0);
314 g_signal_connect(G_OBJECT(lv
->window
), "destroy", G_CALLBACK(destroy_cb
), ht
);
316 vbox
= gnt_vbox_new(FALSE
);
317 gnt_box_add_widget(GNT_BOX(lv
->window
), vbox
);
319 /* Label ************/
320 text
= g_strdup_printf("%s", title
);
321 lv
->label
= gnt_label_new_with_format(text
, GNT_TEXT_FLAG_BOLD
);
323 gnt_box_add_widget(GNT_BOX(vbox
), lv
->label
);
325 hbox
= gnt_hbox_new(FALSE
);
326 gnt_box_add_widget(GNT_BOX(vbox
), hbox
);
327 /* List *************/
328 lv
->tree
= gnt_tree_new();
329 gnt_widget_set_size(lv
->tree
, 30, 0);
330 populate_log_tree(lv
);
331 g_signal_connect (G_OBJECT(lv
->tree
), "selection-changed",
332 G_CALLBACK (log_select_cb
),
334 gnt_box_add_widget(GNT_BOX(hbox
), lv
->tree
);
336 /* Viewer ************/
337 lv
->text
= gnt_text_view_new();
338 gnt_box_add_widget(GNT_BOX(hbox
), lv
->text
);
339 gnt_text_view_set_flag(GNT_TEXT_VIEW(lv
->text
), GNT_TEXT_VIEW_TOP_ALIGN
);
341 hbox
= gnt_hbox_new(FALSE
);
342 gnt_box_add_widget(GNT_BOX(vbox
), hbox
);
343 /* Log size ************/
345 char *sz_txt
= purple_str_size_to_units(log_size
);
346 text
= g_strdup_printf("%s %s", _("Total log size:"), sz_txt
);
347 size_label
= gnt_label_new(text
);
348 gnt_box_add_widget(GNT_BOX(hbox
), size_label
);
353 /* Search box **********/
354 gnt_box_add_widget(GNT_BOX(hbox
), gnt_label_new(_("Scroll/Search: ")));
355 lv
->entry
= gnt_entry_new("");
356 gnt_box_add_widget(GNT_BOX(hbox
), lv
->entry
);
357 g_signal_connect(GNT_ENTRY(lv
->entry
), "activate", G_CALLBACK(search_cb
), lv
);
359 gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(lv
->text
), lv
->entry
);
360 gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(lv
->text
), lv
->entry
);
362 gnt_widget_show(lv
->window
);
368 our_logging_blows(PurpleLogSet
*set
, PurpleLogSet
*setagain
, GList
**list
)
370 /* The iteration happens on the first list. So we use the shorter list in front */
371 if (set
->type
!= PURPLE_LOG_IM
)
373 *list
= g_list_concat(purple_log_get_logs(PURPLE_LOG_IM
, set
->name
, set
->account
), *list
);
376 void finch_log_show(PurpleLogType type
, const char *username
, PurpleAccount
*account
)
378 struct log_viewer_hash_t
*ht
;
379 FinchLogViewer
*lv
= NULL
;
380 const char *name
= username
;
385 if (type
!= PURPLE_LOG_IM
) {
386 g_return_if_fail(account
!= NULL
);
387 g_return_if_fail(username
!= NULL
);
390 ht
= g_new0(struct log_viewer_hash_t
, 1);
393 ht
->username
= g_strdup(username
);
394 ht
->account
= account
;
396 if (log_viewers
== NULL
) {
397 log_viewers
= g_hash_table_new(log_viewer_hash
, log_viewer_equal
);
398 } else if ((lv
= g_hash_table_lookup(log_viewers
, ht
))) {
399 gnt_window_present(lv
->window
);
400 g_free(ht
->username
);
405 if (type
== PURPLE_LOG_CHAT
) {
408 chat
= purple_blist_find_chat(account
, username
);
410 name
= purple_chat_get_name(chat
);
412 title
= g_strdup_printf(_("Conversations in %s"), name
);
417 buddy
= purple_blist_find_buddy(account
, username
);
419 name
= purple_buddy_get_contact_alias(buddy
);
420 title
= g_strdup_printf(_("Conversations with %s"), name
);
422 title
= g_strdup(_("All Conversations"));
427 logs
= purple_log_get_logs(type
, username
, account
);
428 size
= purple_log_get_total_size(type
, username
, account
);
430 /* This will happen only for IMs */
431 GHashTable
*table
= purple_log_get_log_sets();
432 g_hash_table_foreach(table
, (GHFunc
)our_logging_blows
, &logs
);
433 g_hash_table_destroy(table
);
434 logs
= g_list_sort(logs
, purple_log_compare
);
438 display_log_viewer(ht
, logs
, title
, size
);
443 void finch_log_show_contact(PurpleContact
*contact
)
445 struct log_viewer_hash_t
*ht
;
446 PurpleBlistNode
*child
;
447 FinchLogViewer
*lv
= NULL
;
449 const char *name
= NULL
;
451 int total_log_size
= 0;
453 g_return_if_fail(contact
!= NULL
);
455 ht
= g_new0(struct log_viewer_hash_t
, 1);
456 ht
->type
= PURPLE_LOG_IM
;
457 ht
->contact
= contact
;
459 if (log_viewers
== NULL
) {
460 log_viewers
= g_hash_table_new(log_viewer_hash
, log_viewer_equal
);
461 } else if ((lv
= g_hash_table_lookup(log_viewers
, ht
))) {
462 gnt_window_present(lv
->window
);
467 for (child
= purple_blist_node_get_first_child((PurpleBlistNode
*)contact
); child
;
468 child
= purple_blist_node_get_sibling_next(child
)) {
470 PurpleAccount
*account
;
471 if (!PURPLE_IS_BUDDY(child
))
474 name
= purple_buddy_get_name((PurpleBuddy
*)child
);
475 account
= purple_buddy_get_account((PurpleBuddy
*)child
);
476 logs
= g_list_concat(purple_log_get_logs(PURPLE_LOG_IM
, name
,
478 total_log_size
+= purple_log_get_total_size(PURPLE_LOG_IM
, name
, account
);
480 logs
= g_list_sort(logs
, purple_log_compare
);
482 name
= purple_contact_get_alias(contact
);
484 name
= purple_buddy_get_contact_alias(purple_contact_get_priority_buddy(contact
));
486 /* This will happen if the contact doesn't have an alias,
487 * and none of the contact's buddies are online.
488 * There is probably a better way to deal with this. */
490 child
= purple_blist_node_get_first_child((PurpleBlistNode
*)contact
);
491 if (child
!= NULL
&& PURPLE_IS_BUDDY(child
))
492 name
= purple_buddy_get_contact_alias((PurpleBuddy
*)child
);
497 title
= g_strdup_printf(_("Conversations with %s"), name
);
498 display_log_viewer(ht
, logs
, title
, total_log_size
);
502 void finch_syslog_show()
504 GList
*accounts
= NULL
;
507 if (syslog_viewer
!= NULL
) {
508 gnt_window_present(syslog_viewer
->window
);
512 for(accounts
= purple_accounts_get_all(); accounts
!= NULL
; accounts
= accounts
->next
) {
514 PurpleAccount
*account
= (PurpleAccount
*)accounts
->data
;
515 if(purple_protocols_find(purple_account_get_protocol_id(account
)) == NULL
)
518 logs
= g_list_concat(purple_log_get_system_logs(account
), logs
);
520 logs
= g_list_sort(logs
, purple_log_compare
);
522 syslog_viewer
= display_log_viewer(NULL
, logs
, _("System Log"), 0);
525 /****************************************************************************
526 * GNT LOG SUBSYSTEM *******************************************************
527 ****************************************************************************/
530 finch_log_get_handle(void)
537 void finch_log_init(void)
539 void *handle
= finch_log_get_handle();
541 purple_signal_register(handle
, "log-displaying",
542 purple_marshal_VOID__POINTER_POINTER
,
544 G_TYPE_POINTER
, /* (FinchLogViewer *) */
549 finch_log_uninit(void)
551 purple_signals_unregister_by_instance(finch_log_get_handle());