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_foreach(lv
->logs
, (GFunc
)purple_log_free
, NULL
);
271 g_list_free(lv
->logs
);
275 gtk_widget_destroy(w
);
278 static void log_row_activated_cb(GtkTreeView
*tv
, GtkTreePath
*path
, GtkTreeViewColumn
*col
, PidginLogViewer
*viewer
) {
279 if (gtk_tree_view_row_expanded(tv
, path
))
280 gtk_tree_view_collapse_row(tv
, path
);
282 gtk_tree_view_expand_row(tv
, path
, FALSE
);
285 static void delete_log_cleanup_cb(gpointer
*data
)
287 g_free(data
[1]); /* iter */
291 static void delete_log_cb(gpointer
*data
)
293 if (!purple_log_delete((PurpleLog
*)data
[2]))
295 purple_notify_error(NULL
, NULL
, _("Log Deletion Failed"),
296 _("Check permissions and try again."), NULL
);
300 GtkTreeStore
*treestore
= data
[0];
301 GtkTreeIter
*iter
= (GtkTreeIter
*)data
[1];
302 GtkTreePath
*path
= gtk_tree_model_get_path(GTK_TREE_MODEL(treestore
), iter
);
303 gboolean first
= !gtk_tree_path_prev(path
);
305 if (!gtk_tree_store_remove(treestore
, iter
) && first
)
307 /* iter was the last child at its level */
309 if (gtk_tree_path_up(path
))
311 gtk_tree_model_get_iter(GTK_TREE_MODEL(treestore
), iter
, path
);
312 gtk_tree_store_remove(treestore
, iter
);
316 gtk_tree_path_free(path
);
319 delete_log_cleanup_cb(data
);
322 static void log_delete_log_cb(GtkWidget
*menuitem
, gpointer
*data
)
324 PidginLogViewer
*lv
= data
[0];
325 PurpleLog
*log
= data
[1];
326 GtkTreeIter
*iter
= data
[2];
327 gchar
*time
= log_get_date(log
);
332 if (log
->type
== PURPLE_LOG_IM
)
334 PurpleBuddy
*buddy
= purple_blist_find_buddy(log
->account
, log
->name
);
336 name
= purple_buddy_get_contact_alias(buddy
);
340 tmp
= g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
341 "conversation with %s which started at %s?"), name
, time
);
343 else if (log
->type
== PURPLE_LOG_CHAT
)
345 PurpleChat
*chat
= purple_blist_find_chat(log
->account
, log
->name
);
347 name
= purple_chat_get_name(chat
);
351 tmp
= g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
352 "conversation in %s which started at %s?"), name
, time
);
354 else if (log
->type
== PURPLE_LOG_SYSTEM
)
356 tmp
= g_strdup_printf(_("Are you sure you want to permanently delete the system log "
357 "which started at %s?"), time
);
362 g_return_if_reached();
365 /* The only way to free data in all cases is to tie it to the menuitem with
366 * g_object_set_data_full(). But, since we need to get some data down to
367 * delete_log_cb() to delete the log from the log viewer after the file is
368 * deleted, we have to allocate a new data array and make sure it gets freed
370 data2
= g_new(gpointer
, 3);
371 data2
[0] = lv
->treestore
;
374 purple_request_action(lv
, NULL
, _("Delete Log?"), tmp
, 0,
377 _("Delete"), delete_log_cb
,
378 _("Cancel"), delete_log_cleanup_cb
);
384 log_create_popup_menu(GtkWidget
*treeview
, PidginLogViewer
*lv
, GtkTreeIter
*iter
)
392 gtk_tree_model_get_value(GTK_TREE_MODEL(lv
->treestore
), iter
, 1, &val
);
393 log
= g_value_get_pointer(&val
);
399 menu
= gtk_menu_new();
400 menuitem
= gtk_menu_item_new_with_label(_("Delete Log..."));
402 if (purple_log_is_deletable(log
)) {
403 gpointer
*data
= g_new(gpointer
, 3);
408 g_signal_connect(menuitem
, "activate", G_CALLBACK(log_delete_log_cb
), data
);
409 g_object_set_data_full(G_OBJECT(menuitem
), "log-viewer-data", data
, g_free
);
411 gtk_widget_set_sensitive(menuitem
, FALSE
);
413 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
414 gtk_widget_show_all(menu
);
419 static gboolean
log_button_press_cb(GtkWidget
*treeview
, GdkEventButton
*event
, PidginLogViewer
*lv
)
421 if (gdk_event_triggers_context_menu((GdkEvent
*)event
)) {
426 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview
), event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
))
428 iter
= g_new(GtkTreeIter
, 1);
429 gtk_tree_model_get_iter(GTK_TREE_MODEL(lv
->treestore
), iter
, path
);
430 gtk_tree_path_free(path
);
432 menu
= log_create_popup_menu(treeview
, lv
, iter
);
434 gtk_menu_popup_at_pointer(GTK_MENU(menu
), (GdkEvent
*)event
);
444 static gboolean
log_popup_menu_cb(GtkWidget
*treeview
, PidginLogViewer
*lv
)
446 GtkTreeSelection
*sel
;
450 iter
= g_new(GtkTreeIter
, 1);
451 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(lv
->treeview
));
452 if (!gtk_tree_selection_get_selected(sel
, NULL
, iter
)) {
457 menu
= log_create_popup_menu(treeview
, lv
, iter
);
459 pidgin_menu_popup_at_treeview_selection(menu
, treeview
);
466 #if 0 /* FIXME: Add support in Talkatu for highlighting search terms. */
467 static gboolean
search_find_cb(gpointer data
)
469 PidginLogViewer
*viewer
= data
;
470 webkit_web_view_mark_text_matches(WEBKIT_WEB_VIEW(viewer
->log_view
), viewer
->search
, FALSE
, 0);
471 webkit_web_view_set_highlight_text_matches(WEBKIT_WEB_VIEW(viewer
->log_view
), TRUE
);
472 webkit_web_view_search_text(WEBKIT_WEB_VIEW(viewer
->log_view
), viewer
->search
, FALSE
, TRUE
, TRUE
);
477 static void log_select_cb(GtkTreeSelection
*sel
, PidginLogViewer
*viewer
) {
480 GtkTreeModel
*model
= GTK_TREE_MODEL(viewer
->treestore
);
481 PurpleLog
*log
= NULL
;
482 PurpleLogReadFlags flags
;
485 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
))
489 gtk_tree_model_get_value (model
, &iter
, 1, &val
);
490 log
= g_value_get_pointer(&val
);
496 pidgin_set_cursor(GTK_WIDGET(viewer
), GDK_WATCH
);
498 if (log
->type
!= PURPLE_LOG_SYSTEM
) {
499 gchar
*log_date
= log_get_date(log
);
501 if (log
->type
== PURPLE_LOG_CHAT
) {
502 title
= g_strdup_printf(_("Conversation in %s on %s"),
503 log
->name
, log_date
);
505 title
= g_strdup_printf(_("Conversation with %s on %s"),
506 log
->name
, log_date
);
509 gtk_label_set_markup(viewer
->label
, title
);
514 read
= purple_log_read(log
, &flags
);
516 talkatu_buffer_clear(TALKATU_BUFFER(viewer
->log_buffer
));
518 purple_signal_emit(pidgin_log_get_handle(), "log-displaying", viewer
, log
);
520 /* plaintext log (html one starts with <html> tag) */
523 char *newRead
= purple_strreplace(read
, "\n", "<br>");
528 talkatu_markup_set_html(TALKATU_BUFFER(viewer
->log_buffer
), read
, -1);
531 if (viewer
->search
!= NULL
) {
532 #if 0 /* FIXME: Add support in Talkatu for highlighting search terms. */
533 webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(viewer
->log_view
));
534 g_idle_add(search_find_cb
, viewer
);
538 pidgin_clear_cursor(GTK_WIDGET(viewer
));
541 /* I want to make this smarter, but haven't come up with a cool algorithm to do so, yet.
542 * I want the tree to be divided into groups like "Today," "Yesterday," "Last week,"
543 * "August," "2002," etc. based on how many conversation took place in each subdivision.
545 * For now, I'll just make it a flat list.
547 static void populate_log_tree(PidginLogViewer
*lv
)
548 /* Logs are made from trees in real life.
549 This is a tree made from logs */
552 char prev_top_month
[30] = "";
553 GtkTreeIter toplevel
, child
;
554 GList
*logs
= lv
->logs
;
556 while (logs
!= NULL
) {
557 PurpleLog
*log
= logs
->data
;
561 dt
= g_date_time_to_local(log
->time
);
562 month
= g_date_time_format(dt
, _("%B %Y"));
564 if (!purple_strequal(month
, prev_top_month
)) {
566 gtk_tree_store_append(lv
->treestore
, &toplevel
, NULL
);
567 gtk_tree_store_set(lv
->treestore
, &toplevel
, 0, month
, 1, NULL
, -1);
569 g_strlcpy(prev_top_month
, month
, sizeof(prev_top_month
));
573 log_date
= g_date_time_format(dt
, "%c");
574 gtk_tree_store_append(lv
->treestore
, &child
, &toplevel
);
575 gtk_tree_store_set(lv
->treestore
, &child
,
582 g_date_time_unref(dt
);
587 static PidginLogViewer
*
588 display_log_viewer(struct log_viewer_hash
*ht
, GList
*logs
, const char *title
,
589 GtkWidget
*icon
, int log_size
)
595 /* No logs were found. */
596 const char *log_preferences
= NULL
;
599 if (!purple_prefs_get_bool("/purple/logging/log_system"))
600 log_preferences
= _("System events will only be logged if the \"Log all status changes to system log\" preference is enabled.");
602 if (ht
->type
== PURPLE_LOG_IM
) {
603 if (!purple_prefs_get_bool("/purple/logging/log_ims"))
604 log_preferences
= _("Instant messages will only be logged if the \"Log all instant messages\" preference is enabled.");
605 } else if (ht
->type
== PURPLE_LOG_CHAT
) {
606 if (!purple_prefs_get_bool("/purple/logging/log_chats"))
607 log_preferences
= _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
609 g_free(ht
->buddyname
);
614 gtk_widget_destroy(icon
);
616 purple_notify_info(NULL
, title
, _("No logs were found"), log_preferences
, NULL
);
620 /* Window ***********/
621 lv
= g_object_new(PIDGIN_TYPE_LOG_VIEWER
, NULL
);
622 gtk_window_set_title(GTK_WINDOW(lv
), title
);
627 g_hash_table_insert(log_viewers
, ht
, lv
);
630 gtk_widget_hide(lv
->browse_button
);
633 g_signal_connect(G_OBJECT(lv
), "response", G_CALLBACK(destroy_cb
), ht
);
635 /* Icon *************/
637 gtk_box_pack_start(GTK_BOX(lv
->title_box
), icon
, FALSE
, FALSE
,
641 /* Label ************/
642 gtk_label_set_markup(lv
->label
, title
);
644 /* List *************/
645 populate_log_tree(lv
);
647 /* Log size ************/
649 char *sz_txt
= g_format_size(log_size
);
650 char *text
= g_strdup_printf("<span weight='bold'>%s</span> %s",
651 _("Total log size:"), sz_txt
);
652 gtk_label_set_markup(GTK_LABEL(lv
->size_label
), text
);
656 gtk_widget_hide(lv
->size_label
);
659 select_first_log(lv
);
661 gtk_widget_show(GTK_WIDGET(lv
));
666 /****************************************************************************
667 * GObject Implementation
668 ****************************************************************************/
670 pidgin_log_viewer_class_init(PidginLogViewerClass
*klass
)
672 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
674 gtk_widget_class_set_template_from_resource(
675 widget_class
, "/im/pidgin/Pidgin/Log/log-viewer.ui");
677 gtk_widget_class_bind_template_child_internal(
678 widget_class
, PidginLogViewer
, browse_button
);
680 gtk_widget_class_bind_template_child_internal(
681 widget_class
, PidginLogViewer
, title_box
);
682 gtk_widget_class_bind_template_child_internal(widget_class
,
683 PidginLogViewer
, label
);
685 gtk_widget_class_bind_template_child_internal(
686 widget_class
, PidginLogViewer
, treeview
);
687 gtk_widget_class_bind_template_child_internal(
688 widget_class
, PidginLogViewer
, treestore
);
689 gtk_widget_class_bind_template_callback(widget_class
, log_select_cb
);
690 gtk_widget_class_bind_template_callback(widget_class
,
691 log_row_activated_cb
);
692 gtk_widget_class_bind_template_callback(widget_class
,
693 log_button_press_cb
);
694 gtk_widget_class_bind_template_callback(widget_class
,
697 gtk_widget_class_bind_template_child_internal(widget_class
,
698 PidginLogViewer
, entry
);
699 gtk_widget_class_bind_template_callback(widget_class
,
700 entry_search_changed_cb
);
701 gtk_widget_class_bind_template_callback(widget_class
,
702 entry_stop_search_cb
);
704 gtk_widget_class_bind_template_child_internal(
705 widget_class
, PidginLogViewer
, log_view
);
706 gtk_widget_class_bind_template_child_internal(
707 widget_class
, PidginLogViewer
, log_buffer
);
709 gtk_widget_class_bind_template_child_internal(
710 widget_class
, PidginLogViewer
, size_label
);
714 pidgin_log_viewer_init(PidginLogViewer
*self
)
716 gtk_widget_init_template(GTK_WIDGET(self
));
719 /****************************************************************************
721 ****************************************************************************/
723 void pidgin_log_show(PurpleLogType type
, const char *buddyname
, PurpleAccount
*account
) {
724 struct log_viewer_hash
*ht
;
725 PidginLogViewer
*lv
= NULL
;
726 const char *name
= buddyname
;
728 GdkPixbuf
*protocol_icon
;
730 g_return_if_fail(account
!= NULL
);
731 g_return_if_fail(buddyname
!= NULL
);
733 ht
= g_new0(struct log_viewer_hash
, 1);
736 ht
->buddyname
= g_strdup(buddyname
);
737 ht
->account
= account
;
739 if (log_viewers
== NULL
) {
740 log_viewers
= g_hash_table_new(log_viewer_hash
, log_viewer_equal
);
741 } else if ((lv
= g_hash_table_lookup(log_viewers
, ht
))) {
742 gtk_window_present(GTK_WINDOW(lv
));
743 g_free(ht
->buddyname
);
748 if (type
== PURPLE_LOG_CHAT
) {
751 chat
= purple_blist_find_chat(account
, buddyname
);
753 name
= purple_chat_get_name(chat
);
755 title
= g_strdup_printf(_("Conversations in %s"), name
);
759 buddy
= purple_blist_find_buddy(account
, buddyname
);
761 name
= purple_buddy_get_contact_alias(buddy
);
763 title
= g_strdup_printf(_("Conversations with %s"), name
);
766 protocol_icon
= pidgin_create_protocol_icon(account
, PIDGIN_PROTOCOL_ICON_MEDIUM
);
768 display_log_viewer(ht
, purple_log_get_logs(type
, buddyname
, account
),
769 title
, gtk_image_new_from_pixbuf(protocol_icon
),
770 purple_log_get_total_size(type
, buddyname
, account
));
773 g_object_unref(protocol_icon
);
777 void pidgin_log_show_contact(PurpleContact
*contact
) {
778 struct log_viewer_hash
*ht
;
779 PurpleBlistNode
*child
;
780 PidginLogViewer
*lv
= NULL
;
784 const char *name
= NULL
;
786 int total_log_size
= 0;
788 g_return_if_fail(contact
!= NULL
);
790 ht
= g_new0(struct log_viewer_hash
, 1);
791 ht
->type
= PURPLE_LOG_IM
;
792 ht
->contact
= contact
;
794 if (log_viewers
== NULL
) {
795 log_viewers
= g_hash_table_new(log_viewer_hash
, log_viewer_equal
);
796 } else if ((lv
= g_hash_table_lookup(log_viewers
, ht
))) {
797 gtk_window_present(GTK_WINDOW(lv
));
802 for (child
= purple_blist_node_get_first_child((PurpleBlistNode
*)contact
) ;
804 child
= purple_blist_node_get_sibling_next(child
)) {
805 const char *buddy_name
;
806 PurpleAccount
*account
;
808 if (!PURPLE_IS_BUDDY(child
))
811 buddy_name
= purple_buddy_get_name((PurpleBuddy
*)child
);
812 account
= purple_buddy_get_account((PurpleBuddy
*)child
);
813 logs
= g_list_concat(purple_log_get_logs(PURPLE_LOG_IM
, buddy_name
, account
), logs
);
814 total_log_size
+= purple_log_get_total_size(PURPLE_LOG_IM
, buddy_name
, account
);
816 logs
= g_list_sort(logs
, purple_log_compare
);
818 image
= gtk_image_new();
819 pixbuf
= gtk_widget_render_icon(image
, PIDGIN_STOCK_STATUS_PERSON
,
820 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL
), "GtkWindow");
822 gtk_image_set_from_pixbuf(GTK_IMAGE(image
), pixbuf
);
823 g_object_unref(pixbuf
);
825 gtk_widget_destroy(image
);
829 name
= purple_contact_get_alias(contact
);
831 /* This will happen if the contact doesn't have an alias,
832 * and none of the contact's buddies are online.
833 * There is probably a better way to deal with this. */
835 if (PURPLE_BLIST_NODE(contact
)->child
!= NULL
&&
836 PURPLE_IS_BUDDY(PURPLE_BLIST_NODE(contact
)->child
))
837 name
= purple_buddy_get_contact_alias(PURPLE_BUDDY(
838 PURPLE_BLIST_NODE(contact
)->child
));
843 title
= g_strdup_printf(_("Conversations with %s"), name
);
844 display_log_viewer(ht
, logs
, title
, image
, total_log_size
);
848 void pidgin_syslog_show()
850 GList
*accounts
= NULL
;
853 if (syslog_viewer
!= NULL
) {
854 gtk_window_present(GTK_WINDOW(syslog_viewer
));
858 for(accounts
= purple_accounts_get_all(); accounts
!= NULL
; accounts
= accounts
->next
) {
860 PurpleAccount
*account
= (PurpleAccount
*)accounts
->data
;
861 if(purple_protocols_find(purple_account_get_protocol_id(account
)) == NULL
)
864 logs
= g_list_concat(purple_log_get_system_logs(account
), logs
);
866 logs
= g_list_sort(logs
, purple_log_compare
);
868 syslog_viewer
= display_log_viewer(NULL
, logs
, _("System Log"), NULL
, 0);
871 /****************************************************************************
872 * PIDGIN LOG SUBSYSTEM *****************************************************
873 ****************************************************************************/
876 pidgin_log_get_handle(void)
883 void pidgin_log_init(void)
885 void *handle
= pidgin_log_get_handle();
887 purple_signal_register(handle
, "log-displaying",
888 purple_marshal_VOID__POINTER_POINTER
,
890 G_TYPE_POINTER
, /* (PidginLogViewer *) */
895 pidgin_log_uninit(void)
897 purple_signals_unregister_by_instance(pidgin_log_get_handle());