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
33 #include "pidginstock.h"
36 #include "pidginlog.h"
38 #include "gtk3compat.h"
40 #define PIDGIN_TYPE_LOG_VIEWER pidgin_log_viewer_get_type()
43 * @logs: The list of logs viewed in this viewer
44 * @browse_button: The button for opening a log folder externally
45 * @treestore: The treestore containing said logs
46 * @treeview: The treeview representing said treestore
47 * @log_view: The talkatu view to display said logs
48 * @log_buffer: The talkatu buffer to hold said logs
49 * @title_box: The box containing the title (and optional icon)
50 * @label: The label at the top of the log viewer
51 * @size_label: The label to show the size of the logs
52 * @entry: The search entry, in which search terms are entered
53 * @search: The string currently being searched for
55 * A Pidgin Log Viewer. You can look at logs with it.
57 G_DECLARE_FINAL_TYPE(PidginLogViewer
, pidgin_log_viewer
, PIDGIN
, LOG_VIEWER
, GtkDialog
)
59 struct _PidginLogViewer
{
64 GtkWidget
*browse_button
;
66 GtkTreeStore
*treestore
;
69 TalkatuHtmlBuffer
*log_buffer
;
74 GtkWidget
*size_label
;
80 G_DEFINE_TYPE(PidginLogViewer
, pidgin_log_viewer
, GTK_TYPE_DIALOG
)
82 static GHashTable
*log_viewers
= NULL
;
83 static void populate_log_tree(PidginLogViewer
*lv
);
84 static PidginLogViewer
*syslog_viewer
= NULL
;
86 struct log_viewer_hash
{
89 PurpleAccount
*account
;
90 PurpleContact
*contact
;
93 static guint
log_viewer_hash(gconstpointer data
)
95 const struct log_viewer_hash
*viewer
= data
;
97 if (viewer
->contact
!= NULL
)
98 return g_direct_hash(viewer
->contact
);
100 return g_str_hash(viewer
->buddyname
) +
101 g_str_hash(purple_account_get_username(viewer
->account
));
104 static gboolean
log_viewer_equal(gconstpointer y
, gconstpointer z
)
106 const struct log_viewer_hash
*a
, *b
;
113 if (a
->contact
!= NULL
) {
114 if (b
->contact
!= NULL
)
115 return (a
->contact
== b
->contact
);
119 if (b
->contact
!= NULL
)
123 normal
= g_strdup(purple_normalize(a
->account
, a
->buddyname
));
124 ret
= (a
->account
== b
->account
) &&
125 purple_strequal(normal
, purple_normalize(b
->account
, b
->buddyname
));
131 static void select_first_log(PidginLogViewer
*lv
)
134 GtkTreeIter iter
, it
;
137 model
= GTK_TREE_MODEL(lv
->treestore
);
139 if (!gtk_tree_model_get_iter_first(model
, &iter
))
142 path
= gtk_tree_model_get_path(model
, &iter
);
143 if (gtk_tree_model_iter_children(model
, &it
, &iter
))
145 gtk_tree_view_expand_row(GTK_TREE_VIEW(lv
->treeview
), path
, TRUE
);
146 path
= gtk_tree_model_get_path(model
, &it
);
149 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(lv
->treeview
)), path
);
151 gtk_tree_path_free(path
);
154 static gchar
*log_get_date(PurpleLog
*log
)
158 dt
= g_date_time_to_local(log
->time
);
159 ret
= g_date_time_format(dt
, "%c");
160 g_date_time_unref(dt
);
165 entry_stop_search_cb(GtkWidget
*entry
, PidginLogViewer
*lv
)
168 gtk_tree_store_clear(lv
->treestore
);
169 populate_log_tree(lv
);
170 g_clear_pointer(&lv
->search
, g_free
);
172 webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(lv
->log_view
));
174 select_first_log(lv
);
178 entry_search_changed_cb(GtkWidget
*button
, PidginLogViewer
*lv
)
180 const char *search_term
= gtk_entry_get_text(GTK_ENTRY(lv
->entry
));
183 if (lv
->search
!= NULL
&& purple_strequal(lv
->search
, search_term
))
186 /* Searching for the same term acts as "Find Next" */
187 webkit_web_view_search_text(WEBKIT_WEB_VIEW(lv
->log_view
), lv
->search
, FALSE
, TRUE
, TRUE
);
192 pidgin_set_cursor(GTK_WIDGET(lv
), GDK_WATCH
);
195 lv
->search
= g_strdup(search_term
);
197 gtk_tree_store_clear(lv
->treestore
);
198 talkatu_buffer_clear(TALKATU_BUFFER(lv
->log_buffer
));
200 for (logs
= lv
->logs
; logs
!= NULL
; logs
= logs
->next
) {
201 char *read
= purple_log_read((PurpleLog
*)logs
->data
, NULL
);
202 if (read
&& *read
&& purple_strcasestr(read
, search_term
)) {
204 PurpleLog
*log
= logs
->data
;
205 gchar
*log_date
= log_get_date(log
);
207 gtk_tree_store_append (lv
->treestore
, &iter
, NULL
);
208 gtk_tree_store_set(lv
->treestore
, &iter
,
216 select_first_log(lv
);
217 pidgin_clear_cursor(GTK_WIDGET(lv
));
221 destroy_cb(GtkWidget
*w
, gint resp
, struct log_viewer_hash
*ht
)
223 PidginLogViewer
*lv
= syslog_viewer
;
226 if (resp
== GTK_RESPONSE_HELP
) {
227 GtkTreeSelection
*sel
;
230 PurpleLog
*log
= NULL
;
234 lv
= g_hash_table_lookup(log_viewers
, ht
);
235 model
= GTK_TREE_MODEL(lv
->treestore
);
237 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(lv
->treeview
));
238 if (gtk_tree_selection_get_selected(sel
, &model
, &iter
)) {
242 gtk_tree_model_get_value (model
, &iter
, 1, &val
);
243 log
= g_value_get_pointer(&val
);
249 logdir
= g_build_filename(purple_data_dir(), "logs", NULL
);
251 logdir
= purple_log_get_log_dir(log
->type
, log
->name
, log
->account
);
253 winpidgin_shell_execute(logdir
, "explore", NULL
);
260 lv
= g_hash_table_lookup(log_viewers
, ht
);
261 g_hash_table_remove(log_viewers
, ht
);
263 g_free(ht
->buddyname
);
266 syslog_viewer
= NULL
;
268 purple_request_close_with_handle(lv
);
270 g_list_free_full(lv
->logs
, (GDestroyNotify
)purple_log_free
);
274 gtk_widget_destroy(w
);
277 static void log_row_activated_cb(GtkTreeView
*tv
, GtkTreePath
*path
, GtkTreeViewColumn
*col
, PidginLogViewer
*viewer
) {
278 if (gtk_tree_view_row_expanded(tv
, path
))
279 gtk_tree_view_collapse_row(tv
, path
);
281 gtk_tree_view_expand_row(tv
, path
, FALSE
);
284 static void delete_log_cleanup_cb(gpointer
*data
)
286 g_free(data
[1]); /* iter */
290 static void delete_log_cb(gpointer
*data
)
292 if (!purple_log_delete((PurpleLog
*)data
[2]))
294 purple_notify_error(NULL
, NULL
, _("Log Deletion Failed"),
295 _("Check permissions and try again."), NULL
);
299 GtkTreeStore
*treestore
= data
[0];
300 GtkTreeIter
*iter
= (GtkTreeIter
*)data
[1];
301 GtkTreePath
*path
= gtk_tree_model_get_path(GTK_TREE_MODEL(treestore
), iter
);
302 gboolean first
= !gtk_tree_path_prev(path
);
304 if (!gtk_tree_store_remove(treestore
, iter
) && first
)
306 /* iter was the last child at its level */
308 if (gtk_tree_path_up(path
))
310 gtk_tree_model_get_iter(GTK_TREE_MODEL(treestore
), iter
, path
);
311 gtk_tree_store_remove(treestore
, iter
);
315 gtk_tree_path_free(path
);
318 delete_log_cleanup_cb(data
);
321 static void log_delete_log_cb(GtkWidget
*menuitem
, gpointer
*data
)
323 PidginLogViewer
*lv
= data
[0];
324 PurpleLog
*log
= data
[1];
325 GtkTreeIter
*iter
= data
[2];
326 gchar
*time
= log_get_date(log
);
331 if (log
->type
== PURPLE_LOG_IM
)
333 PurpleBuddy
*buddy
= purple_blist_find_buddy(log
->account
, log
->name
);
335 name
= purple_buddy_get_contact_alias(buddy
);
339 tmp
= g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
340 "conversation with %s which started at %s?"), name
, time
);
342 else if (log
->type
== PURPLE_LOG_CHAT
)
344 PurpleChat
*chat
= purple_blist_find_chat(log
->account
, log
->name
);
346 name
= purple_chat_get_name(chat
);
350 tmp
= g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
351 "conversation in %s which started at %s?"), name
, time
);
353 else if (log
->type
== PURPLE_LOG_SYSTEM
)
355 tmp
= g_strdup_printf(_("Are you sure you want to permanently delete the system log "
356 "which started at %s?"), time
);
361 g_return_if_reached();
364 /* The only way to free data in all cases is to tie it to the menuitem with
365 * g_object_set_data_full(). But, since we need to get some data down to
366 * delete_log_cb() to delete the log from the log viewer after the file is
367 * deleted, we have to allocate a new data array and make sure it gets freed
369 data2
= g_new(gpointer
, 3);
370 data2
[0] = lv
->treestore
;
373 purple_request_action(lv
, NULL
, _("Delete Log?"), tmp
, 0,
376 _("Delete"), delete_log_cb
,
377 _("Cancel"), delete_log_cleanup_cb
);
383 log_create_popup_menu(GtkWidget
*treeview
, PidginLogViewer
*lv
, GtkTreeIter
*iter
)
391 gtk_tree_model_get_value(GTK_TREE_MODEL(lv
->treestore
), iter
, 1, &val
);
392 log
= g_value_get_pointer(&val
);
398 menu
= gtk_menu_new();
399 menuitem
= gtk_menu_item_new_with_label(_("Delete Log..."));
401 if (purple_log_is_deletable(log
)) {
402 gpointer
*data
= g_new(gpointer
, 3);
407 g_signal_connect(menuitem
, "activate", G_CALLBACK(log_delete_log_cb
), data
);
408 g_object_set_data_full(G_OBJECT(menuitem
), "log-viewer-data", data
, g_free
);
410 gtk_widget_set_sensitive(menuitem
, FALSE
);
412 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
413 gtk_widget_show_all(menu
);
418 static gboolean
log_button_press_cb(GtkWidget
*treeview
, GdkEventButton
*event
, PidginLogViewer
*lv
)
420 if (gdk_event_triggers_context_menu((GdkEvent
*)event
)) {
425 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview
), event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
))
427 iter
= g_new(GtkTreeIter
, 1);
428 gtk_tree_model_get_iter(GTK_TREE_MODEL(lv
->treestore
), iter
, path
);
429 gtk_tree_path_free(path
);
431 menu
= log_create_popup_menu(treeview
, lv
, iter
);
433 gtk_menu_popup_at_pointer(GTK_MENU(menu
), (GdkEvent
*)event
);
443 static gboolean
log_popup_menu_cb(GtkWidget
*treeview
, PidginLogViewer
*lv
)
445 GtkTreeSelection
*sel
;
449 iter
= g_new(GtkTreeIter
, 1);
450 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(lv
->treeview
));
451 if (!gtk_tree_selection_get_selected(sel
, NULL
, iter
)) {
456 menu
= log_create_popup_menu(treeview
, lv
, iter
);
458 pidgin_menu_popup_at_treeview_selection(menu
, treeview
);
465 #if 0 /* FIXME: Add support in Talkatu for highlighting search terms. */
466 static gboolean
search_find_cb(gpointer data
)
468 PidginLogViewer
*viewer
= data
;
469 webkit_web_view_mark_text_matches(WEBKIT_WEB_VIEW(viewer
->log_view
), viewer
->search
, FALSE
, 0);
470 webkit_web_view_set_highlight_text_matches(WEBKIT_WEB_VIEW(viewer
->log_view
), TRUE
);
471 webkit_web_view_search_text(WEBKIT_WEB_VIEW(viewer
->log_view
), viewer
->search
, FALSE
, TRUE
, TRUE
);
476 static void log_select_cb(GtkTreeSelection
*sel
, PidginLogViewer
*viewer
) {
479 GtkTreeModel
*model
= GTK_TREE_MODEL(viewer
->treestore
);
480 PurpleLog
*log
= NULL
;
481 PurpleLogReadFlags flags
;
484 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
))
488 gtk_tree_model_get_value (model
, &iter
, 1, &val
);
489 log
= g_value_get_pointer(&val
);
495 pidgin_set_cursor(GTK_WIDGET(viewer
), GDK_WATCH
);
497 if (log
->type
!= PURPLE_LOG_SYSTEM
) {
498 gchar
*log_date
= log_get_date(log
);
500 if (log
->type
== PURPLE_LOG_CHAT
) {
501 title
= g_strdup_printf(_("Conversation in %s on %s"),
502 log
->name
, log_date
);
504 title
= g_strdup_printf(_("Conversation with %s on %s"),
505 log
->name
, log_date
);
508 gtk_label_set_markup(viewer
->label
, title
);
513 read
= purple_log_read(log
, &flags
);
515 talkatu_buffer_clear(TALKATU_BUFFER(viewer
->log_buffer
));
517 purple_signal_emit(pidgin_log_get_handle(), "log-displaying", viewer
, log
);
519 /* plaintext log (html one starts with <html> tag) */
522 char *newRead
= purple_strreplace(read
, "\n", "<br>");
527 talkatu_markup_set_html(TALKATU_BUFFER(viewer
->log_buffer
), read
, -1);
530 if (viewer
->search
!= NULL
) {
531 #if 0 /* FIXME: Add support in Talkatu for highlighting search terms. */
532 webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(viewer
->log_view
));
533 g_idle_add(search_find_cb
, viewer
);
537 pidgin_clear_cursor(GTK_WIDGET(viewer
));
540 /* I want to make this smarter, but haven't come up with a cool algorithm to do so, yet.
541 * I want the tree to be divided into groups like "Today," "Yesterday," "Last week,"
542 * "August," "2002," etc. based on how many conversation took place in each subdivision.
544 * For now, I'll just make it a flat list.
546 static void populate_log_tree(PidginLogViewer
*lv
)
547 /* Logs are made from trees in real life.
548 This is a tree made from logs */
551 char prev_top_month
[30] = "";
552 GtkTreeIter toplevel
, child
;
553 GList
*logs
= lv
->logs
;
555 while (logs
!= NULL
) {
556 PurpleLog
*log
= logs
->data
;
560 dt
= g_date_time_to_local(log
->time
);
561 month
= g_date_time_format(dt
, _("%B %Y"));
563 if (!purple_strequal(month
, prev_top_month
)) {
565 gtk_tree_store_append(lv
->treestore
, &toplevel
, NULL
);
566 gtk_tree_store_set(lv
->treestore
, &toplevel
, 0, month
, 1, NULL
, -1);
568 g_strlcpy(prev_top_month
, month
, sizeof(prev_top_month
));
572 log_date
= g_date_time_format(dt
, "%c");
573 gtk_tree_store_append(lv
->treestore
, &child
, &toplevel
);
574 gtk_tree_store_set(lv
->treestore
, &child
,
581 g_date_time_unref(dt
);
586 static PidginLogViewer
*
587 display_log_viewer(struct log_viewer_hash
*ht
, GList
*logs
, const char *title
,
588 GtkWidget
*icon
, int log_size
)
594 /* No logs were found. */
595 const char *log_preferences
= NULL
;
598 if (!purple_prefs_get_bool("/purple/logging/log_system"))
599 log_preferences
= _("System events will only be logged if the \"Log all status changes to system log\" preference is enabled.");
601 if (ht
->type
== PURPLE_LOG_IM
) {
602 if (!purple_prefs_get_bool("/purple/logging/log_ims"))
603 log_preferences
= _("Instant messages will only be logged if the \"Log all instant messages\" preference is enabled.");
604 } else if (ht
->type
== PURPLE_LOG_CHAT
) {
605 if (!purple_prefs_get_bool("/purple/logging/log_chats"))
606 log_preferences
= _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
608 g_free(ht
->buddyname
);
613 gtk_widget_destroy(icon
);
615 purple_notify_info(NULL
, title
, _("No logs were found"), log_preferences
, NULL
);
619 /* Window ***********/
620 lv
= g_object_new(PIDGIN_TYPE_LOG_VIEWER
, NULL
);
621 gtk_window_set_title(GTK_WINDOW(lv
), title
);
626 g_hash_table_insert(log_viewers
, ht
, lv
);
629 gtk_widget_hide(lv
->browse_button
);
632 g_signal_connect(G_OBJECT(lv
), "response", G_CALLBACK(destroy_cb
), ht
);
634 /* Icon *************/
636 gtk_box_pack_start(GTK_BOX(lv
->title_box
), icon
, FALSE
, FALSE
,
640 /* Label ************/
641 gtk_label_set_markup(lv
->label
, title
);
643 /* List *************/
644 populate_log_tree(lv
);
646 /* Log size ************/
648 char *sz_txt
= g_format_size(log_size
);
649 char *text
= g_strdup_printf("<span weight='bold'>%s</span> %s",
650 _("Total log size:"), sz_txt
);
651 gtk_label_set_markup(GTK_LABEL(lv
->size_label
), text
);
655 gtk_widget_hide(lv
->size_label
);
658 select_first_log(lv
);
660 gtk_widget_show(GTK_WIDGET(lv
));
665 /****************************************************************************
666 * GObject Implementation
667 ****************************************************************************/
669 pidgin_log_viewer_class_init(PidginLogViewerClass
*klass
)
671 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
673 gtk_widget_class_set_template_from_resource(
674 widget_class
, "/im/pidgin/Pidgin/Log/log-viewer.ui");
676 gtk_widget_class_bind_template_child_internal(
677 widget_class
, PidginLogViewer
, browse_button
);
679 gtk_widget_class_bind_template_child_internal(
680 widget_class
, PidginLogViewer
, title_box
);
681 gtk_widget_class_bind_template_child_internal(widget_class
,
682 PidginLogViewer
, label
);
684 gtk_widget_class_bind_template_child_internal(
685 widget_class
, PidginLogViewer
, treeview
);
686 gtk_widget_class_bind_template_child_internal(
687 widget_class
, PidginLogViewer
, treestore
);
688 gtk_widget_class_bind_template_callback(widget_class
, log_select_cb
);
689 gtk_widget_class_bind_template_callback(widget_class
,
690 log_row_activated_cb
);
691 gtk_widget_class_bind_template_callback(widget_class
,
692 log_button_press_cb
);
693 gtk_widget_class_bind_template_callback(widget_class
,
696 gtk_widget_class_bind_template_child_internal(widget_class
,
697 PidginLogViewer
, entry
);
698 gtk_widget_class_bind_template_callback(widget_class
,
699 entry_search_changed_cb
);
700 gtk_widget_class_bind_template_callback(widget_class
,
701 entry_stop_search_cb
);
703 gtk_widget_class_bind_template_child_internal(
704 widget_class
, PidginLogViewer
, log_view
);
705 gtk_widget_class_bind_template_child_internal(
706 widget_class
, PidginLogViewer
, log_buffer
);
708 gtk_widget_class_bind_template_child_internal(
709 widget_class
, PidginLogViewer
, size_label
);
713 pidgin_log_viewer_init(PidginLogViewer
*self
)
715 gtk_widget_init_template(GTK_WIDGET(self
));
718 /****************************************************************************
720 ****************************************************************************/
722 void pidgin_log_show(PurpleLogType type
, const char *buddyname
, PurpleAccount
*account
) {
723 struct log_viewer_hash
*ht
;
724 PidginLogViewer
*lv
= NULL
;
725 const char *name
= buddyname
;
727 GdkPixbuf
*protocol_icon
;
729 g_return_if_fail(account
!= NULL
);
730 g_return_if_fail(buddyname
!= NULL
);
732 ht
= g_new0(struct log_viewer_hash
, 1);
735 ht
->buddyname
= g_strdup(buddyname
);
736 ht
->account
= account
;
738 if (log_viewers
== NULL
) {
739 log_viewers
= g_hash_table_new(log_viewer_hash
, log_viewer_equal
);
740 } else if ((lv
= g_hash_table_lookup(log_viewers
, ht
))) {
741 gtk_window_present(GTK_WINDOW(lv
));
742 g_free(ht
->buddyname
);
747 if (type
== PURPLE_LOG_CHAT
) {
750 chat
= purple_blist_find_chat(account
, buddyname
);
752 name
= purple_chat_get_name(chat
);
754 title
= g_strdup_printf(_("Conversations in %s"), name
);
758 buddy
= purple_blist_find_buddy(account
, buddyname
);
760 name
= purple_buddy_get_contact_alias(buddy
);
762 title
= g_strdup_printf(_("Conversations with %s"), name
);
765 protocol_icon
= pidgin_create_protocol_icon(account
, PIDGIN_PROTOCOL_ICON_MEDIUM
);
767 display_log_viewer(ht
, purple_log_get_logs(type
, buddyname
, account
),
768 title
, gtk_image_new_from_pixbuf(protocol_icon
),
769 purple_log_get_total_size(type
, buddyname
, account
));
772 g_object_unref(protocol_icon
);
776 void pidgin_log_show_contact(PurpleContact
*contact
) {
777 struct log_viewer_hash
*ht
;
778 PurpleBlistNode
*child
;
779 PidginLogViewer
*lv
= NULL
;
783 const char *name
= NULL
;
785 int total_log_size
= 0;
787 g_return_if_fail(contact
!= NULL
);
789 ht
= g_new0(struct log_viewer_hash
, 1);
790 ht
->type
= PURPLE_LOG_IM
;
791 ht
->contact
= contact
;
793 if (log_viewers
== NULL
) {
794 log_viewers
= g_hash_table_new(log_viewer_hash
, log_viewer_equal
);
795 } else if ((lv
= g_hash_table_lookup(log_viewers
, ht
))) {
796 gtk_window_present(GTK_WINDOW(lv
));
801 for (child
= purple_blist_node_get_first_child((PurpleBlistNode
*)contact
) ;
803 child
= purple_blist_node_get_sibling_next(child
)) {
804 const char *buddy_name
;
805 PurpleAccount
*account
;
807 if (!PURPLE_IS_BUDDY(child
))
810 buddy_name
= purple_buddy_get_name((PurpleBuddy
*)child
);
811 account
= purple_buddy_get_account((PurpleBuddy
*)child
);
812 logs
= g_list_concat(purple_log_get_logs(PURPLE_LOG_IM
, buddy_name
, account
), logs
);
813 total_log_size
+= purple_log_get_total_size(PURPLE_LOG_IM
, buddy_name
, account
);
815 logs
= g_list_sort(logs
, purple_log_compare
);
817 image
= gtk_image_new();
818 pixbuf
= gtk_widget_render_icon(image
, PIDGIN_STOCK_STATUS_PERSON
,
819 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL
), "GtkWindow");
821 gtk_image_set_from_pixbuf(GTK_IMAGE(image
), pixbuf
);
822 g_object_unref(pixbuf
);
824 gtk_widget_destroy(image
);
828 name
= purple_contact_get_alias(contact
);
830 /* This will happen if the contact doesn't have an alias,
831 * and none of the contact's buddies are online.
832 * There is probably a better way to deal with this. */
834 if (PURPLE_BLIST_NODE(contact
)->child
!= NULL
&&
835 PURPLE_IS_BUDDY(PURPLE_BLIST_NODE(contact
)->child
))
836 name
= purple_buddy_get_contact_alias(PURPLE_BUDDY(
837 PURPLE_BLIST_NODE(contact
)->child
));
842 title
= g_strdup_printf(_("Conversations with %s"), name
);
843 display_log_viewer(ht
, logs
, title
, image
, total_log_size
);
847 void pidgin_syslog_show()
849 GList
*accounts
= NULL
;
852 if (syslog_viewer
!= NULL
) {
853 gtk_window_present(GTK_WINDOW(syslog_viewer
));
857 for(accounts
= purple_accounts_get_all(); accounts
!= NULL
; accounts
= accounts
->next
) {
859 PurpleAccount
*account
= (PurpleAccount
*)accounts
->data
;
860 if(purple_protocols_find(purple_account_get_protocol_id(account
)) == NULL
)
863 logs
= g_list_concat(purple_log_get_system_logs(account
), logs
);
865 logs
= g_list_sort(logs
, purple_log_compare
);
867 syslog_viewer
= display_log_viewer(NULL
, logs
, _("System Log"), NULL
, 0);
870 /****************************************************************************
871 * PIDGIN LOG SUBSYSTEM *****************************************************
872 ****************************************************************************/
875 pidgin_log_get_handle(void)
882 void pidgin_log_init(void)
884 void *handle
= pidgin_log_get_handle();
886 purple_signal_register(handle
, "log-displaying",
887 purple_marshal_VOID__POINTER_POINTER
,
889 G_TYPE_POINTER
, /* (PidginLogViewer *) */
894 pidgin_log_uninit(void)
896 purple_signals_unregister_by_instance(pidgin_log_get_handle());