3 * Pidgin 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
31 #include "pidginstock.h"
35 #include "gtkwebview.h"
37 #include "gtk3compat.h"
39 static GHashTable
*log_viewers
= NULL
;
40 static void populate_log_tree(PidginLogViewer
*lv
);
41 static PidginLogViewer
*syslog_viewer
= NULL
;
43 struct log_viewer_hash_t
{
46 PurpleAccount
*account
;
47 PurpleContact
*contact
;
50 static guint
log_viewer_hash(gconstpointer data
)
52 const struct log_viewer_hash_t
*viewer
= data
;
54 if (viewer
->contact
!= NULL
)
55 return g_direct_hash(viewer
->contact
);
57 return g_str_hash(viewer
->buddyname
) +
58 g_str_hash(purple_account_get_username(viewer
->account
));
61 static gboolean
log_viewer_equal(gconstpointer y
, gconstpointer z
)
63 const struct log_viewer_hash_t
*a
, *b
;
70 if (a
->contact
!= NULL
) {
71 if (b
->contact
!= NULL
)
72 return (a
->contact
== b
->contact
);
76 if (b
->contact
!= NULL
)
80 normal
= g_strdup(purple_normalize(a
->account
, a
->buddyname
));
81 ret
= (a
->account
== b
->account
) &&
82 !strcmp(normal
, purple_normalize(b
->account
, b
->buddyname
));
88 static void select_first_log(PidginLogViewer
*lv
)
94 model
= GTK_TREE_MODEL(lv
->treestore
);
96 if (!gtk_tree_model_get_iter_first(model
, &iter
))
99 path
= gtk_tree_model_get_path(model
, &iter
);
100 if (gtk_tree_model_iter_children(model
, &it
, &iter
))
102 gtk_tree_view_expand_row(GTK_TREE_VIEW(lv
->treeview
), path
, TRUE
);
103 path
= gtk_tree_model_get_path(model
, &it
);
106 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(lv
->treeview
)), path
);
108 gtk_tree_path_free(path
);
111 static const char *log_get_date(PurpleLog
*log
)
114 return purple_date_format_full(log
->tm
);
116 return purple_date_format_full(localtime(&log
->time
));
119 static void search_cb(GtkWidget
*button
, PidginLogViewer
*lv
)
121 const char *search_term
= gtk_entry_get_text(GTK_ENTRY(lv
->entry
));
124 if (!(*search_term
)) {
126 gtk_tree_store_clear(lv
->treestore
);
127 populate_log_tree(lv
);
130 webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(lv
->web_view
));
131 select_first_log(lv
);
135 if (lv
->search
!= NULL
&& !strcmp(lv
->search
, search_term
))
137 /* Searching for the same term acts as "Find Next" */
138 webkit_web_view_search_text(WEBKIT_WEB_VIEW(lv
->web_view
), lv
->search
, FALSE
, TRUE
, TRUE
);
142 pidgin_set_cursor(lv
->window
, GDK_WATCH
);
145 lv
->search
= g_strdup(search_term
);
147 gtk_tree_store_clear(lv
->treestore
);
148 webkit_web_view_open(WEBKIT_WEB_VIEW(lv
->web_view
), "about:blank"); /* clear the view */
150 for (logs
= lv
->logs
; logs
!= NULL
; logs
= logs
->next
) {
151 char *read
= purple_log_read((PurpleLog
*)logs
->data
, NULL
);
152 if (read
&& *read
&& purple_strcasestr(read
, search_term
)) {
154 PurpleLog
*log
= logs
->data
;
156 gtk_tree_store_append (lv
->treestore
, &iter
, NULL
);
157 gtk_tree_store_set(lv
->treestore
, &iter
,
158 0, log_get_date(log
),
164 select_first_log(lv
);
165 pidgin_clear_cursor(lv
->window
);
168 static void destroy_cb(GtkWidget
*w
, gint resp
, struct log_viewer_hash_t
*ht
) {
169 PidginLogViewer
*lv
= syslog_viewer
;
172 if (resp
== GTK_RESPONSE_HELP
) {
173 GtkTreeSelection
*sel
;
176 PurpleLog
*log
= NULL
;
180 lv
= g_hash_table_lookup(log_viewers
, ht
);
181 model
= GTK_TREE_MODEL(lv
->treestore
);
183 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(lv
->treeview
));
184 if (gtk_tree_selection_get_selected(sel
, &model
, &iter
)) {
188 gtk_tree_model_get_value (model
, &iter
, 1, &val
);
189 log
= g_value_get_pointer(&val
);
195 logdir
= g_build_filename(purple_data_dir(), "logs", NULL
);
197 logdir
= purple_log_get_log_dir(log
->type
, log
->name
, log
->account
);
199 winpidgin_shell_execute(logdir
, "explore", NULL
);
206 lv
= g_hash_table_lookup(log_viewers
, ht
);
207 g_hash_table_remove(log_viewers
, ht
);
209 g_free(ht
->buddyname
);
212 syslog_viewer
= NULL
;
214 purple_request_close_with_handle(lv
);
216 g_list_foreach(lv
->logs
, (GFunc
)purple_log_free
, NULL
);
217 g_list_free(lv
->logs
);
222 gtk_widget_destroy(w
);
225 static void log_row_activated_cb(GtkTreeView
*tv
, GtkTreePath
*path
, GtkTreeViewColumn
*col
, PidginLogViewer
*viewer
) {
226 if (gtk_tree_view_row_expanded(tv
, path
))
227 gtk_tree_view_collapse_row(tv
, path
);
229 gtk_tree_view_expand_row(tv
, path
, FALSE
);
232 static void delete_log_cleanup_cb(gpointer
*data
)
234 g_free(data
[1]); /* iter */
238 static void delete_log_cb(gpointer
*data
)
240 if (!purple_log_delete((PurpleLog
*)data
[2]))
242 purple_notify_error(NULL
, NULL
, _("Log Deletion Failed"),
243 _("Check permissions and try again."), NULL
);
247 GtkTreeStore
*treestore
= data
[0];
248 GtkTreeIter
*iter
= (GtkTreeIter
*)data
[1];
249 GtkTreePath
*path
= gtk_tree_model_get_path(GTK_TREE_MODEL(treestore
), iter
);
250 gboolean first
= !gtk_tree_path_prev(path
);
252 if (!gtk_tree_store_remove(treestore
, iter
) && first
)
254 /* iter was the last child at its level */
256 if (gtk_tree_path_up(path
))
258 gtk_tree_model_get_iter(GTK_TREE_MODEL(treestore
), iter
, path
);
259 gtk_tree_store_remove(treestore
, iter
);
263 gtk_tree_path_free(path
);
266 delete_log_cleanup_cb(data
);
269 static void log_delete_log_cb(GtkWidget
*menuitem
, gpointer
*data
)
271 PidginLogViewer
*lv
= data
[0];
272 PurpleLog
*log
= data
[1];
273 const char *time
= log_get_date(log
);
278 if (log
->type
== PURPLE_LOG_IM
)
280 PurpleBuddy
*buddy
= purple_blist_find_buddy(log
->account
, log
->name
);
282 name
= purple_buddy_get_contact_alias(buddy
);
286 tmp
= g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
287 "conversation with %s which started at %s?"), name
, time
);
289 else if (log
->type
== PURPLE_LOG_CHAT
)
291 PurpleChat
*chat
= purple_blist_find_chat(log
->account
, log
->name
);
293 name
= purple_chat_get_name(chat
);
297 tmp
= g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
298 "conversation in %s which started at %s?"), name
, time
);
300 else if (log
->type
== PURPLE_LOG_SYSTEM
)
302 tmp
= g_strdup_printf(_("Are you sure you want to permanently delete the system log "
303 "which started at %s?"), time
);
306 g_return_if_reached();
308 /* The only way to free data in all cases is to tie it to the menuitem with
309 * g_object_set_data_full(). But, since we need to get some data down to
310 * delete_log_cb() to delete the log from the log viewer after the file is
311 * deleted, we have to allocate a new data array and make sure it gets freed
313 data2
= g_new(gpointer
, 3);
314 data2
[0] = lv
->treestore
;
315 data2
[1] = data
[3]; /* iter */
317 purple_request_action(lv
, NULL
, _("Delete Log?"), tmp
, 0,
320 _("Delete"), delete_log_cb
,
321 _("Cancel"), delete_log_cleanup_cb
);
325 static void log_show_popup_menu(GtkWidget
*treeview
, GdkEventButton
*event
, gpointer
*data
)
327 GtkWidget
*menu
= gtk_menu_new();
328 GtkWidget
*menuitem
= gtk_menu_item_new_with_label(_("Delete Log..."));
330 if (!purple_log_is_deletable((PurpleLog
*)data
[1]))
331 gtk_widget_set_sensitive(menuitem
, FALSE
);
333 g_signal_connect(menuitem
, "activate", G_CALLBACK(log_delete_log_cb
), data
);
334 g_object_set_data_full(G_OBJECT(menuitem
), "log-viewer-data", data
, g_free
);
335 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
336 gtk_widget_show_all(menu
);
338 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, (GtkMenuPositionFunc
)data
[2], NULL
,
339 (event
!= NULL
) ? event
->button
: 0,
340 gdk_event_get_time((GdkEvent
*)event
));
343 static gboolean
log_button_press_cb(GtkWidget
*treeview
, GdkEventButton
*event
, PidginLogViewer
*lv
)
345 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3)
353 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview
), event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
))
355 iter
= g_new(GtkTreeIter
, 1);
356 gtk_tree_model_get_iter(GTK_TREE_MODEL(lv
->treestore
), iter
, path
);
358 gtk_tree_model_get_value(GTK_TREE_MODEL(lv
->treestore
), iter
, 1, &val
);
359 gtk_tree_path_free(path
);
361 log
= g_value_get_pointer(&val
);
369 data
= g_new(gpointer
, 4);
375 log_show_popup_menu(treeview
, event
, data
);
382 static gboolean
log_popup_menu_cb(GtkWidget
*treeview
, PidginLogViewer
*lv
)
384 GtkTreeSelection
*sel
;
390 iter
= g_new(GtkTreeIter
, 1);
391 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(lv
->treeview
));
392 if (!gtk_tree_selection_get_selected(sel
, NULL
, iter
))
398 gtk_tree_model_get_value(GTK_TREE_MODEL(lv
->treestore
),
399 iter
, NODE_COLUMN
, &val
);
401 log
= g_value_get_pointer(&val
);
406 data
= g_new(gpointer
, 4);
409 data
[2] = pidgin_treeview_popup_menu_position_func
;
412 log_show_popup_menu(treeview
, NULL
, data
);
416 static gboolean
search_find_cb(gpointer data
)
418 PidginLogViewer
*viewer
= data
;
419 webkit_web_view_mark_text_matches(WEBKIT_WEB_VIEW(viewer
->web_view
), viewer
->search
, FALSE
, 0);
420 webkit_web_view_set_highlight_text_matches(WEBKIT_WEB_VIEW(viewer
->web_view
), TRUE
);
421 webkit_web_view_search_text(WEBKIT_WEB_VIEW(viewer
->web_view
), viewer
->search
, FALSE
, TRUE
, TRUE
);
425 static void log_select_cb(GtkTreeSelection
*sel
, PidginLogViewer
*viewer
) {
428 GtkTreeModel
*model
= GTK_TREE_MODEL(viewer
->treestore
);
429 PurpleLog
*log
= NULL
;
430 PurpleLogReadFlags flags
;
433 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
))
437 gtk_tree_model_get_value (model
, &iter
, 1, &val
);
438 log
= g_value_get_pointer(&val
);
444 pidgin_set_cursor(viewer
->window
, GDK_WATCH
);
446 if (log
->type
!= PURPLE_LOG_SYSTEM
) {
448 if (log
->type
== PURPLE_LOG_CHAT
)
449 title
= g_strdup_printf(_("<span size='larger' weight='bold'>Conversation in %s on %s</span>"),
450 log
->name
, log_get_date(log
));
452 title
= g_strdup_printf(_("<span size='larger' weight='bold'>Conversation with %s on %s</span>"),
453 log
->name
, log_get_date(log
));
455 gtk_label_set_markup(viewer
->label
, title
);
459 read
= purple_log_read(log
, &flags
);
460 viewer
->flags
= flags
;
462 webkit_web_view_open(WEBKIT_WEB_VIEW(viewer
->web_view
), "about:blank");
464 purple_signal_emit(pidgin_log_get_handle(), "log-displaying", viewer
, log
);
466 /* plaintext log (html one starts with <html> tag) */
469 char *newRead
= purple_strreplace(read
, "\n", "<br>");
474 webkit_web_view_load_html_string(WEBKIT_WEB_VIEW(viewer
->web_view
), read
, "");
477 if (viewer
->search
!= NULL
) {
478 webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(viewer
->web_view
));
479 g_idle_add(search_find_cb
, viewer
);
482 pidgin_clear_cursor(viewer
->window
);
485 /* I want to make this smarter, but haven't come up with a cool algorithm to do so, yet.
486 * I want the tree to be divided into groups like "Today," "Yesterday," "Last week,"
487 * "August," "2002," etc. based on how many conversation took place in each subdivision.
489 * For now, I'll just make it a flat list.
491 static void populate_log_tree(PidginLogViewer
*lv
)
492 /* Logs are made from trees in real life.
493 This is a tree made from logs */
496 char prev_top_month
[30] = "";
497 GtkTreeIter toplevel
, child
;
498 GList
*logs
= lv
->logs
;
500 while (logs
!= NULL
) {
501 PurpleLog
*log
= logs
->data
;
503 month
= purple_utf8_strftime(_("%B %Y"),
504 log
->tm
? log
->tm
: localtime(&log
->time
));
506 if (strcmp(month
, prev_top_month
) != 0)
509 gtk_tree_store_append(lv
->treestore
, &toplevel
, NULL
);
510 gtk_tree_store_set(lv
->treestore
, &toplevel
, 0, month
, 1, NULL
, -1);
512 g_strlcpy(prev_top_month
, month
, sizeof(prev_top_month
));
516 gtk_tree_store_append(lv
->treestore
, &child
, &toplevel
);
517 gtk_tree_store_set(lv
->treestore
, &child
,
518 0, log_get_date(log
),
526 static PidginLogViewer
*display_log_viewer(struct log_viewer_hash_t
*ht
, GList
*logs
,
527 const char *title
, GtkWidget
*icon
, int log_size
)
530 GtkWidget
*title_box
;
533 GtkCellRenderer
*rend
;
534 GtkTreeViewColumn
*col
;
535 GtkTreeSelection
*sel
;
539 GtkWidget
*find_button
;
540 GtkWidget
*size_label
;
544 /* No logs were found. */
545 const char *log_preferences
= NULL
;
548 if (!purple_prefs_get_bool("/purple/logging/log_system"))
549 log_preferences
= _("System events will only be logged if the \"Log all status changes to system log\" preference is enabled.");
551 if (ht
->type
== PURPLE_LOG_IM
) {
552 if (!purple_prefs_get_bool("/purple/logging/log_ims"))
553 log_preferences
= _("Instant messages will only be logged if the \"Log all instant messages\" preference is enabled.");
554 } else if (ht
->type
== PURPLE_LOG_CHAT
) {
555 if (!purple_prefs_get_bool("/purple/logging/log_chats"))
556 log_preferences
= _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
558 g_free(ht
->buddyname
);
563 gtk_widget_destroy(icon
);
565 purple_notify_info(NULL
, title
, _("No logs were found"), log_preferences
, NULL
);
569 lv
= g_new0(PidginLogViewer
, 1);
573 g_hash_table_insert(log_viewers
, ht
, lv
);
575 /* Window ***********/
576 lv
->window
= gtk_dialog_new_with_buttons(title
, NULL
, 0,
577 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
, NULL
);
579 /* Steal the "HELP" response and use it to trigger browsing to the logs folder */
580 gtk_dialog_add_button(GTK_DIALOG(lv
->window
), _("_Browse logs folder"), GTK_RESPONSE_HELP
);
582 gtk_container_set_border_width (GTK_CONTAINER(lv
->window
), PIDGIN_HIG_BOX_SPACE
);
583 gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(lv
->window
))), 0);
584 g_signal_connect(G_OBJECT(lv
->window
), "response",
585 G_CALLBACK(destroy_cb
), ht
);
586 gtk_window_set_role(GTK_WINDOW(lv
->window
), "log_viewer");
588 /* Icon *************/
590 title_box
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, PIDGIN_HIG_BOX_SPACE
);
591 gtk_container_set_border_width(GTK_CONTAINER(title_box
), PIDGIN_HIG_BOX_SPACE
);
592 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(lv
->window
))),
593 title_box
, FALSE
, FALSE
, 0);
595 gtk_box_pack_start(GTK_BOX(title_box
), icon
, FALSE
, FALSE
, 0);
597 title_box
= gtk_dialog_get_content_area(GTK_DIALOG(lv
->window
));
599 /* Label ************/
600 lv
->label
= GTK_LABEL(gtk_label_new(NULL
));
602 text
= g_strdup_printf("<span size='larger' weight='bold'>%s</span>", title
);
604 gtk_label_set_markup(lv
->label
, text
);
605 gtk_label_set_xalign(GTK_LABEL(lv
->label
), 0);
606 gtk_label_set_yalign(GTK_LABEL(lv
->label
), 0);
607 gtk_box_pack_start(GTK_BOX(title_box
), GTK_WIDGET(lv
->label
), FALSE
, FALSE
, 0);
610 /* Pane *************/
611 pane
= gtk_paned_new(GTK_ORIENTATION_HORIZONTAL
);
612 gtk_container_set_border_width(GTK_CONTAINER(pane
), PIDGIN_HIG_BOX_SPACE
);
613 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(lv
->window
))),
614 pane
, TRUE
, TRUE
, 0);
616 /* List *************/
617 lv
->treestore
= gtk_tree_store_new (2, G_TYPE_STRING
, G_TYPE_POINTER
);
618 lv
->treeview
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (lv
->treestore
));
619 g_object_unref(G_OBJECT(lv
->treestore
));
620 rend
= gtk_cell_renderer_text_new();
621 col
= gtk_tree_view_column_new_with_attributes ("time", rend
, "markup", 0, NULL
);
622 gtk_tree_view_append_column (GTK_TREE_VIEW(lv
->treeview
), col
);
623 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (lv
->treeview
), FALSE
);
624 gtk_paned_add1(GTK_PANED(pane
),
625 pidgin_make_scrollable(lv
->treeview
, GTK_POLICY_NEVER
, GTK_POLICY_ALWAYS
, GTK_SHADOW_IN
, -1, -1));
627 populate_log_tree(lv
);
629 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (lv
->treeview
));
630 g_signal_connect (G_OBJECT (sel
), "changed",
631 G_CALLBACK (log_select_cb
),
633 g_signal_connect (G_OBJECT(lv
->treeview
), "row-activated",
634 G_CALLBACK(log_row_activated_cb
),
636 pidgin_set_accessible_label(lv
->treeview
, lv
->label
);
638 g_signal_connect(lv
->treeview
, "button-press-event", G_CALLBACK(log_button_press_cb
), lv
);
639 g_signal_connect(lv
->treeview
, "popup-menu", G_CALLBACK(log_popup_menu_cb
), lv
);
641 /* Log size ************/
643 char *sz_txt
= purple_str_size_to_units(log_size
);
644 text
= g_strdup_printf("<span weight='bold'>%s</span> %s", _("Total log size:"), sz_txt
);
645 size_label
= gtk_label_new(NULL
);
646 gtk_label_set_markup(GTK_LABEL(size_label
), text
);
647 /* gtk_paned_add1(GTK_PANED(pane), size_label); */
648 gtk_label_set_xalign(GTK_LABEL(size_label
), 0);
649 gtk_label_set_yalign(GTK_LABEL(size_label
), 0);
650 gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(lv
->window
))),
651 size_label
, FALSE
, FALSE
, 0);
656 /* A fancy little box ************/
657 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, PIDGIN_HIG_BOX_SPACE
);
658 gtk_paned_add2(GTK_PANED(pane
), vbox
);
660 /* Viewer ************/
661 frame
= pidgin_create_webview(FALSE
, &lv
->web_view
, NULL
);
662 gtk_widget_set_name(lv
->web_view
, "pidgin_log_web_view");
663 gtk_widget_set_size_request(lv
->web_view
, 320, 200);
664 gtk_box_pack_start(GTK_BOX(vbox
), frame
, TRUE
, TRUE
, 0);
665 gtk_widget_show(frame
);
667 /* Search box **********/
668 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, PIDGIN_HIG_BOX_SPACE
);
669 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
670 lv
->entry
= gtk_entry_new();
671 gtk_box_pack_start(GTK_BOX(hbox
), lv
->entry
, TRUE
, TRUE
, 0);
672 find_button
= gtk_button_new_from_stock(GTK_STOCK_FIND
);
673 gtk_box_pack_start(GTK_BOX(hbox
), find_button
, FALSE
, FALSE
, 0);
674 g_signal_connect(GTK_ENTRY(lv
->entry
), "activate", G_CALLBACK(search_cb
), lv
);
675 g_signal_connect(GTK_BUTTON(find_button
), "clicked", G_CALLBACK(search_cb
), lv
);
677 select_first_log(lv
);
679 gtk_widget_show_all(lv
->window
);
684 void pidgin_log_show(PurpleLogType type
, const char *buddyname
, PurpleAccount
*account
) {
685 struct log_viewer_hash_t
*ht
;
686 PidginLogViewer
*lv
= NULL
;
687 const char *name
= buddyname
;
689 GdkPixbuf
*protocol_icon
;
691 g_return_if_fail(account
!= NULL
);
692 g_return_if_fail(buddyname
!= NULL
);
694 ht
= g_new0(struct log_viewer_hash_t
, 1);
697 ht
->buddyname
= g_strdup(buddyname
);
698 ht
->account
= account
;
700 if (log_viewers
== NULL
) {
701 log_viewers
= g_hash_table_new(log_viewer_hash
, log_viewer_equal
);
702 } else if ((lv
= g_hash_table_lookup(log_viewers
, ht
))) {
703 gtk_window_present(GTK_WINDOW(lv
->window
));
704 g_free(ht
->buddyname
);
709 if (type
== PURPLE_LOG_CHAT
) {
712 chat
= purple_blist_find_chat(account
, buddyname
);
714 name
= purple_chat_get_name(chat
);
716 title
= g_strdup_printf(_("Conversations in %s"), name
);
720 buddy
= purple_blist_find_buddy(account
, buddyname
);
722 name
= purple_buddy_get_contact_alias(buddy
);
724 title
= g_strdup_printf(_("Conversations with %s"), name
);
727 protocol_icon
= pidgin_create_protocol_icon(account
, PIDGIN_PROTOCOL_ICON_MEDIUM
);
729 display_log_viewer(ht
, purple_log_get_logs(type
, buddyname
, account
),
730 title
, gtk_image_new_from_pixbuf(protocol_icon
),
731 purple_log_get_total_size(type
, buddyname
, account
));
734 g_object_unref(protocol_icon
);
738 void pidgin_log_show_contact(PurpleContact
*contact
) {
739 struct log_viewer_hash_t
*ht
;
740 PurpleBlistNode
*child
;
741 PidginLogViewer
*lv
= NULL
;
745 const char *name
= NULL
;
747 int total_log_size
= 0;
749 g_return_if_fail(contact
!= NULL
);
751 ht
= g_new0(struct log_viewer_hash_t
, 1);
752 ht
->type
= PURPLE_LOG_IM
;
753 ht
->contact
= contact
;
755 if (log_viewers
== NULL
) {
756 log_viewers
= g_hash_table_new(log_viewer_hash
, log_viewer_equal
);
757 } else if ((lv
= g_hash_table_lookup(log_viewers
, ht
))) {
758 gtk_window_present(GTK_WINDOW(lv
->window
));
763 for (child
= purple_blist_node_get_first_child((PurpleBlistNode
*)contact
) ;
765 child
= purple_blist_node_get_sibling_next(child
)) {
766 const char *buddy_name
;
767 PurpleAccount
*account
;
769 if (!PURPLE_IS_BUDDY(child
))
772 buddy_name
= purple_buddy_get_name((PurpleBuddy
*)child
);
773 account
= purple_buddy_get_account((PurpleBuddy
*)child
);
774 logs
= g_list_concat(purple_log_get_logs(PURPLE_LOG_IM
, buddy_name
, account
), logs
);
775 total_log_size
+= purple_log_get_total_size(PURPLE_LOG_IM
, buddy_name
, account
);
777 logs
= g_list_sort(logs
, purple_log_compare
);
779 image
= gtk_image_new();
780 pixbuf
= gtk_widget_render_icon(image
, PIDGIN_STOCK_STATUS_PERSON
,
781 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL
), "GtkWindow");
783 gtk_image_set_from_pixbuf(GTK_IMAGE(image
), pixbuf
);
784 g_object_unref(pixbuf
);
786 gtk_widget_destroy(image
);
790 name
= purple_contact_get_alias(contact
);
792 /* This will happen if the contact doesn't have an alias,
793 * and none of the contact's buddies are online.
794 * There is probably a better way to deal with this. */
796 if (PURPLE_BLIST_NODE(contact
)->child
!= NULL
&&
797 PURPLE_IS_BUDDY(PURPLE_BLIST_NODE(contact
)->child
))
798 name
= purple_buddy_get_contact_alias(PURPLE_BUDDY(
799 PURPLE_BLIST_NODE(contact
)->child
));
804 title
= g_strdup_printf(_("Conversations with %s"), name
);
805 display_log_viewer(ht
, logs
, title
, image
, total_log_size
);
809 void pidgin_syslog_show()
811 GList
*accounts
= NULL
;
814 if (syslog_viewer
!= NULL
) {
815 gtk_window_present(GTK_WINDOW(syslog_viewer
->window
));
819 for(accounts
= purple_accounts_get_all(); accounts
!= NULL
; accounts
= accounts
->next
) {
821 PurpleAccount
*account
= (PurpleAccount
*)accounts
->data
;
822 if(purple_protocols_find(purple_account_get_protocol_id(account
)) == NULL
)
825 logs
= g_list_concat(purple_log_get_system_logs(account
), logs
);
827 logs
= g_list_sort(logs
, purple_log_compare
);
829 syslog_viewer
= display_log_viewer(NULL
, logs
, _("System Log"), NULL
, 0);
832 /****************************************************************************
833 * GTK+ LOG SUBSYSTEM *******************************************************
834 ****************************************************************************/
837 pidgin_log_get_handle(void)
844 void pidgin_log_init(void)
846 void *handle
= pidgin_log_get_handle();
848 purple_signal_register(handle
, "log-displaying",
849 purple_marshal_VOID__POINTER_POINTER
,
851 G_TYPE_POINTER
, /* (PidginLogViewer *) */
856 pidgin_log_uninit(void)
858 purple_signals_unregister_by_instance(pidgin_log_get_handle());