2 * @file gtklog.c GTK+ Log viewer
8 * Pidgin is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
36 #include "pidginstock.h"
38 #include "gtkimhtml.h"
42 static GHashTable
*log_viewers
= NULL
;
43 static void populate_log_tree(PidginLogViewer
*lv
);
44 static PidginLogViewer
*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 return g_str_hash(viewer
->buddyname
) +
61 g_str_hash(purple_account_get_username(viewer
->account
));
64 static gboolean
log_viewer_equal(gconstpointer y
, gconstpointer z
)
66 const struct log_viewer_hash_t
*a
, *b
;
73 if (a
->contact
!= NULL
) {
74 if (b
->contact
!= NULL
)
75 return (a
->contact
== b
->contact
);
79 if (b
->contact
!= NULL
)
83 normal
= g_strdup(purple_normalize(a
->account
, a
->buddyname
));
84 ret
= (a
->account
== b
->account
) &&
85 !strcmp(normal
, purple_normalize(b
->account
, b
->buddyname
));
91 static void select_first_log(PidginLogViewer
*lv
)
97 model
= GTK_TREE_MODEL(lv
->treestore
);
99 if (!gtk_tree_model_get_iter_first(model
, &iter
))
102 path
= gtk_tree_model_get_path(model
, &iter
);
103 if (gtk_tree_model_iter_children(model
, &it
, &iter
))
105 gtk_tree_view_expand_row(GTK_TREE_VIEW(lv
->treeview
), path
, TRUE
);
106 path
= gtk_tree_model_get_path(model
, &it
);
109 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(lv
->treeview
)), path
);
111 gtk_tree_path_free(path
);
114 static const char *log_get_date(PurpleLog
*log
)
117 return purple_date_format_full(log
->tm
);
119 return purple_date_format_full(localtime(&log
->time
));
122 static void search_cb(GtkWidget
*button
, PidginLogViewer
*lv
)
124 const char *search_term
= gtk_entry_get_text(GTK_ENTRY(lv
->entry
));
127 if (!(*search_term
)) {
129 gtk_tree_store_clear(lv
->treestore
);
130 populate_log_tree(lv
);
133 gtk_imhtml_search_clear(GTK_IMHTML(lv
->imhtml
));
134 select_first_log(lv
);
138 if (lv
->search
!= NULL
&& !strcmp(lv
->search
, search_term
))
140 /* Searching for the same term acts as "Find Next" */
141 gtk_imhtml_search_find(GTK_IMHTML(lv
->imhtml
), lv
->search
);
145 pidgin_set_cursor(lv
->window
, GDK_WATCH
);
148 lv
->search
= g_strdup(search_term
);
150 gtk_tree_store_clear(lv
->treestore
);
151 gtk_imhtml_clear(GTK_IMHTML(lv
->imhtml
));
153 for (logs
= lv
->logs
; logs
!= NULL
; logs
= logs
->next
) {
154 char *read
= purple_log_read((PurpleLog
*)logs
->data
, NULL
);
155 if (read
&& *read
&& purple_strcasestr(read
, search_term
)) {
157 PurpleLog
*log
= logs
->data
;
159 gtk_tree_store_append (lv
->treestore
, &iter
, NULL
);
160 gtk_tree_store_set(lv
->treestore
, &iter
,
161 0, log_get_date(log
),
167 select_first_log(lv
);
168 pidgin_clear_cursor(lv
->window
);
171 static void destroy_cb(GtkWidget
*w
, gint resp
, struct log_viewer_hash_t
*ht
) {
172 PidginLogViewer
*lv
= syslog_viewer
;
175 if (resp
== GTK_RESPONSE_HELP
) {
176 GtkTreeSelection
*sel
;
179 PurpleLog
*log
= NULL
;
183 lv
= g_hash_table_lookup(log_viewers
, ht
);
184 model
= GTK_TREE_MODEL(lv
->treestore
);
186 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(lv
->treeview
));
187 if (gtk_tree_selection_get_selected(sel
, &model
, &iter
)) {
191 gtk_tree_model_get_value (model
, &iter
, 1, &val
);
192 log
= g_value_get_pointer(&val
);
198 logdir
= g_build_filename(purple_user_dir(), "logs", NULL
);
200 logdir
= purple_log_get_log_dir(log
->type
, log
->name
, log
->account
);
202 winpidgin_shell_execute(logdir
, "explore", NULL
);
209 lv
= g_hash_table_lookup(log_viewers
, ht
);
210 g_hash_table_remove(log_viewers
, ht
);
212 g_free(ht
->buddyname
);
215 syslog_viewer
= NULL
;
217 purple_request_close_with_handle(lv
);
219 g_list_foreach(lv
->logs
, (GFunc
)purple_log_free
, NULL
);
220 g_list_free(lv
->logs
);
225 gtk_widget_destroy(w
);
228 static void log_row_activated_cb(GtkTreeView
*tv
, GtkTreePath
*path
, GtkTreeViewColumn
*col
, PidginLogViewer
*viewer
) {
229 if (gtk_tree_view_row_expanded(tv
, path
))
230 gtk_tree_view_collapse_row(tv
, path
);
232 gtk_tree_view_expand_row(tv
, path
, FALSE
);
235 static void delete_log_cleanup_cb(gpointer
*data
)
237 g_free(data
[1]); /* iter */
241 static void delete_log_cb(gpointer
*data
)
243 if (!purple_log_delete((PurpleLog
*)data
[2]))
245 purple_notify_error(NULL
, NULL
, _("Log Deletion Failed"),
246 _("Check permissions and try again."));
250 GtkTreeStore
*treestore
= data
[0];
251 GtkTreeIter
*iter
= (GtkTreeIter
*)data
[1];
252 GtkTreePath
*path
= gtk_tree_model_get_path(GTK_TREE_MODEL(treestore
), iter
);
253 gboolean first
= !gtk_tree_path_prev(path
);
255 if (!gtk_tree_store_remove(treestore
, iter
) && first
)
257 /* iter was the last child at its level */
259 if (gtk_tree_path_up(path
))
261 gtk_tree_model_get_iter(GTK_TREE_MODEL(treestore
), iter
, path
);
262 gtk_tree_store_remove(treestore
, iter
);
266 gtk_tree_path_free(path
);
269 delete_log_cleanup_cb(data
);
272 static void log_delete_log_cb(GtkWidget
*menuitem
, gpointer
*data
)
274 PidginLogViewer
*lv
= data
[0];
275 PurpleLog
*log
= data
[1];
276 const char *time
= log_get_date(log
);
281 if (log
->type
== PURPLE_LOG_IM
)
283 PurpleBuddy
*buddy
= purple_find_buddy(log
->account
, log
->name
);
285 name
= purple_buddy_get_contact_alias(buddy
);
289 tmp
= g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
290 "conversation with %s which started at %s?"), name
, time
);
292 else if (log
->type
== PURPLE_LOG_CHAT
)
294 PurpleChat
*chat
= purple_blist_find_chat(log
->account
, log
->name
);
296 name
= purple_chat_get_name(chat
);
300 tmp
= g_strdup_printf(_("Are you sure you want to permanently delete the log of the "
301 "conversation in %s which started at %s?"), name
, time
);
303 else if (log
->type
== PURPLE_LOG_SYSTEM
)
305 tmp
= g_strdup_printf(_("Are you sure you want to permanently delete the system log "
306 "which started at %s?"), time
);
309 g_return_if_reached();
311 /* The only way to free data in all cases is to tie it to the menuitem with
312 * g_object_set_data_full(). But, since we need to get some data down to
313 * delete_log_cb() to delete the log from the log viewer after the file is
314 * deleted, we have to allocate a new data array and make sure it gets freed
316 data2
= g_new(gpointer
, 3);
317 data2
[0] = lv
->treestore
;
318 data2
[1] = data
[3]; /* iter */
320 purple_request_action(lv
, NULL
, _("Delete Log?"), tmp
, 0,
323 _("Delete"), delete_log_cb
,
324 _("Cancel"), delete_log_cleanup_cb
);
328 static void log_show_popup_menu(GtkWidget
*treeview
, GdkEventButton
*event
, gpointer
*data
)
330 GtkWidget
*menu
= gtk_menu_new();
331 GtkWidget
*menuitem
= gtk_menu_item_new_with_label(_("Delete Log..."));
333 if (!purple_log_is_deletable((PurpleLog
*)data
[1]))
334 gtk_widget_set_sensitive(menuitem
, FALSE
);
336 g_signal_connect(menuitem
, "activate", G_CALLBACK(log_delete_log_cb
), data
);
337 g_object_set_data_full(G_OBJECT(menuitem
), "log-viewer-data", data
, g_free
);
338 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menuitem
);
339 gtk_widget_show_all(menu
);
341 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, (GtkMenuPositionFunc
)data
[2], NULL
,
342 (event
!= NULL
) ? event
->button
: 0,
343 gdk_event_get_time((GdkEvent
*)event
));
346 static gboolean
log_button_press_cb(GtkWidget
*treeview
, GdkEventButton
*event
, PidginLogViewer
*lv
)
348 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3)
356 if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview
), event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
))
358 iter
= g_new(GtkTreeIter
, 1);
359 gtk_tree_model_get_iter(GTK_TREE_MODEL(lv
->treestore
), iter
, path
);
361 gtk_tree_model_get_value(GTK_TREE_MODEL(lv
->treestore
), iter
, 1, &val
);
362 gtk_tree_path_free(path
);
364 log
= g_value_get_pointer(&val
);
372 data
= g_new(gpointer
, 4);
378 log_show_popup_menu(treeview
, event
, data
);
385 static gboolean
log_popup_menu_cb(GtkWidget
*treeview
, PidginLogViewer
*lv
)
387 GtkTreeSelection
*sel
;
393 iter
= g_new(GtkTreeIter
, 1);
394 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(lv
->treeview
));
395 if (!gtk_tree_selection_get_selected(sel
, NULL
, iter
))
401 gtk_tree_model_get_value(GTK_TREE_MODEL(lv
->treestore
),
402 iter
, NODE_COLUMN
, &val
);
404 log
= g_value_get_pointer(&val
);
409 data
= g_new(gpointer
, 4);
412 data
[2] = pidgin_treeview_popup_menu_position_func
;
415 log_show_popup_menu(treeview
, NULL
, data
);
419 static gboolean
search_find_cb(gpointer data
)
421 PidginLogViewer
*viewer
= data
;
422 gtk_imhtml_search_find(GTK_IMHTML(viewer
->imhtml
), viewer
->search
);
423 g_object_steal_data(G_OBJECT(viewer
->entry
), "search-find-cb");
427 static void log_select_cb(GtkTreeSelection
*sel
, PidginLogViewer
*viewer
) {
430 GtkTreeModel
*model
= GTK_TREE_MODEL(viewer
->treestore
);
431 PurpleLog
*log
= NULL
;
432 PurpleLogReadFlags flags
;
435 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
))
439 gtk_tree_model_get_value (model
, &iter
, 1, &val
);
440 log
= g_value_get_pointer(&val
);
446 pidgin_set_cursor(viewer
->window
, GDK_WATCH
);
448 if (log
->type
!= PURPLE_LOG_SYSTEM
) {
450 if (log
->type
== PURPLE_LOG_CHAT
)
451 title
= g_strdup_printf(_("<span size='larger' weight='bold'>Conversation in %s on %s</span>"),
452 log
->name
, log_get_date(log
));
454 title
= g_strdup_printf(_("<span size='larger' weight='bold'>Conversation with %s on %s</span>"),
455 log
->name
, log_get_date(log
));
457 gtk_label_set_markup(GTK_LABEL(viewer
->label
), title
);
461 read
= purple_log_read(log
, &flags
);
462 viewer
->flags
= flags
;
464 gtk_imhtml_clear(GTK_IMHTML(viewer
->imhtml
));
465 gtk_imhtml_set_protocol_name(GTK_IMHTML(viewer
->imhtml
),
466 purple_account_get_protocol_name(log
->account
));
468 purple_signal_emit(pidgin_log_get_handle(), "log-displaying", viewer
, log
);
470 gtk_imhtml_append_text(GTK_IMHTML(viewer
->imhtml
), read
,
471 GTK_IMHTML_NO_COMMENTS
| GTK_IMHTML_NO_TITLE
| GTK_IMHTML_NO_SCROLL
|
472 ((flags
& PURPLE_LOG_READ_NO_NEWLINE
) ? GTK_IMHTML_NO_NEWLINE
: 0));
475 if (viewer
->search
!= NULL
) {
477 gtk_imhtml_search_clear(GTK_IMHTML(viewer
->imhtml
));
478 source
= g_idle_add(search_find_cb
, viewer
);
479 g_object_set_data_full(G_OBJECT(viewer
->entry
), "search-find-cb",
480 GINT_TO_POINTER(source
), (GDestroyNotify
)g_source_remove
);
483 pidgin_clear_cursor(viewer
->window
);
486 /* I want to make this smarter, but haven't come up with a cool algorithm to do so, yet.
487 * I want the tree to be divided into groups like "Today," "Yesterday," "Last week,"
488 * "August," "2002," etc. based on how many conversation took place in each subdivision.
490 * For now, I'll just make it a flat list.
492 static void populate_log_tree(PidginLogViewer
*lv
)
493 /* Logs are made from trees in real life.
494 This is a tree made from logs */
497 char prev_top_month
[30] = "";
498 GtkTreeIter toplevel
, child
;
499 GList
*logs
= lv
->logs
;
501 while (logs
!= NULL
) {
502 PurpleLog
*log
= logs
->data
;
504 month
= purple_utf8_strftime(_("%B %Y"),
505 log
->tm
? log
->tm
: localtime(&log
->time
));
507 if (strcmp(month
, prev_top_month
) != 0)
510 gtk_tree_store_append(lv
->treestore
, &toplevel
, NULL
);
511 gtk_tree_store_set(lv
->treestore
, &toplevel
, 0, month
, 1, NULL
, -1);
513 strncpy(prev_top_month
, month
, sizeof(prev_top_month
));
517 gtk_tree_store_append(lv
->treestore
, &child
, &toplevel
);
518 gtk_tree_store_set(lv
->treestore
, &child
,
519 0, log_get_date(log
),
527 static PidginLogViewer
*display_log_viewer(struct log_viewer_hash_t
*ht
, GList
*logs
,
528 const char *title
, GtkWidget
*icon
, int log_size
)
531 GtkWidget
*title_box
;
534 GtkCellRenderer
*rend
;
535 GtkTreeViewColumn
*col
;
536 GtkTreeSelection
*sel
;
540 GtkWidget
*find_button
;
541 GtkWidget
*size_label
;
545 /* No logs were found. */
546 const char *log_preferences
= NULL
;
549 if (!purple_prefs_get_bool("/purple/logging/log_system"))
550 log_preferences
= _("System events will only be logged if the \"Log all status changes to system log\" preference is enabled.");
552 if (ht
->type
== PURPLE_LOG_IM
) {
553 if (!purple_prefs_get_bool("/purple/logging/log_ims"))
554 log_preferences
= _("Instant messages will only be logged if the \"Log all instant messages\" preference is enabled.");
555 } else if (ht
->type
== PURPLE_LOG_CHAT
) {
556 if (!purple_prefs_get_bool("/purple/logging/log_chats"))
557 log_preferences
= _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
559 g_free(ht
->buddyname
);
564 gtk_widget_destroy(icon
);
566 purple_notify_info(NULL
, title
, _("No logs were found"), log_preferences
);
570 lv
= g_new0(PidginLogViewer
, 1);
574 g_hash_table_insert(log_viewers
, ht
, lv
);
576 /* Window ***********/
577 lv
->window
= gtk_dialog_new_with_buttons(title
, NULL
, 0,
578 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
, NULL
);
580 /* Steal the "HELP" response and use it to trigger browsing to the logs folder */
581 gtk_dialog_add_button(GTK_DIALOG(lv
->window
), _("_Browse logs folder"), GTK_RESPONSE_HELP
);
583 gtk_container_set_border_width (GTK_CONTAINER(lv
->window
), PIDGIN_HIG_BOX_SPACE
);
584 gtk_dialog_set_has_separator(GTK_DIALOG(lv
->window
), FALSE
);
585 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(lv
->window
)->vbox
), 0);
586 g_signal_connect(G_OBJECT(lv
->window
), "response",
587 G_CALLBACK(destroy_cb
), ht
);
588 gtk_window_set_role(GTK_WINDOW(lv
->window
), "log_viewer");
590 /* Icon *************/
592 title_box
= gtk_hbox_new(FALSE
, PIDGIN_HIG_BOX_SPACE
);
593 gtk_container_set_border_width(GTK_CONTAINER(title_box
), PIDGIN_HIG_BOX_SPACE
);
594 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(lv
->window
)->vbox
), title_box
, FALSE
, FALSE
, 0);
596 gtk_box_pack_start(GTK_BOX(title_box
), icon
, FALSE
, FALSE
, 0);
598 title_box
= GTK_DIALOG(lv
->window
)->vbox
;
600 /* Label ************/
601 lv
->label
= gtk_label_new(NULL
);
603 text
= g_strdup_printf("<span size='larger' weight='bold'>%s</span>", title
);
605 gtk_label_set_markup(GTK_LABEL(lv
->label
), text
);
606 gtk_misc_set_alignment(GTK_MISC(lv
->label
), 0, 0);
607 gtk_box_pack_start(GTK_BOX(title_box
), lv
->label
, FALSE
, FALSE
, 0);
610 /* Pane *************/
611 pane
= gtk_hpaned_new();
612 gtk_container_set_border_width(GTK_CONTAINER(pane
), PIDGIN_HIG_BOX_SPACE
);
613 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(lv
->window
)->vbox
), pane
, TRUE
, TRUE
, 0);
615 /* List *************/
616 lv
->treestore
= gtk_tree_store_new (2, G_TYPE_STRING
, G_TYPE_POINTER
);
617 lv
->treeview
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (lv
->treestore
));
618 g_object_unref(G_OBJECT(lv
->treestore
));
619 rend
= gtk_cell_renderer_text_new();
620 col
= gtk_tree_view_column_new_with_attributes ("time", rend
, "markup", 0, NULL
);
621 gtk_tree_view_append_column (GTK_TREE_VIEW(lv
->treeview
), col
);
622 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (lv
->treeview
), FALSE
);
623 gtk_paned_add1(GTK_PANED(pane
),
624 pidgin_make_scrollable(lv
->treeview
, GTK_POLICY_NEVER
, GTK_POLICY_ALWAYS
, GTK_SHADOW_IN
, -1, -1));
626 populate_log_tree(lv
);
628 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (lv
->treeview
));
629 g_signal_connect (G_OBJECT (sel
), "changed",
630 G_CALLBACK (log_select_cb
),
632 g_signal_connect (G_OBJECT(lv
->treeview
), "row-activated",
633 G_CALLBACK(log_row_activated_cb
),
635 pidgin_set_accessible_label(lv
->treeview
, lv
->label
);
637 g_signal_connect(lv
->treeview
, "button-press-event", G_CALLBACK(log_button_press_cb
), lv
);
638 g_signal_connect(lv
->treeview
, "popup-menu", G_CALLBACK(log_popup_menu_cb
), lv
);
640 /* Log size ************/
642 char *sz_txt
= purple_str_size_to_units(log_size
);
643 text
= g_strdup_printf("<span weight='bold'>%s</span> %s", _("Total log size:"), sz_txt
);
644 size_label
= gtk_label_new(NULL
);
645 gtk_label_set_markup(GTK_LABEL(size_label
), text
);
646 /* gtk_paned_add1(GTK_PANED(pane), size_label); */
647 gtk_misc_set_alignment(GTK_MISC(size_label
), 0, 0);
648 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(lv
->window
)->vbox
), size_label
, FALSE
, FALSE
, 0);
653 /* A fancy little box ************/
654 vbox
= gtk_vbox_new(FALSE
, PIDGIN_HIG_BOX_SPACE
);
655 gtk_paned_add2(GTK_PANED(pane
), vbox
);
657 /* Viewer ************/
658 frame
= pidgin_create_imhtml(FALSE
, &lv
->imhtml
, NULL
, NULL
);
659 gtk_widget_set_name(lv
->imhtml
, "pidgin_log_imhtml");
660 gtk_widget_set_size_request(lv
->imhtml
, 320, 200);
661 gtk_box_pack_start(GTK_BOX(vbox
), frame
, TRUE
, TRUE
, 0);
662 gtk_widget_show(frame
);
664 /* Search box **********/
665 hbox
= gtk_hbox_new(FALSE
, PIDGIN_HIG_BOX_SPACE
);
666 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
667 lv
->entry
= gtk_entry_new();
668 gtk_box_pack_start(GTK_BOX(hbox
), lv
->entry
, TRUE
, TRUE
, 0);
669 find_button
= gtk_button_new_from_stock(GTK_STOCK_FIND
);
670 gtk_box_pack_start(GTK_BOX(hbox
), find_button
, FALSE
, FALSE
, 0);
671 g_signal_connect(GTK_ENTRY(lv
->entry
), "activate", G_CALLBACK(search_cb
), lv
);
672 g_signal_connect(GTK_BUTTON(find_button
), "clicked", G_CALLBACK(search_cb
), lv
);
674 select_first_log(lv
);
676 gtk_widget_show_all(lv
->window
);
681 void pidgin_log_show(PurpleLogType type
, const char *buddyname
, PurpleAccount
*account
) {
682 struct log_viewer_hash_t
*ht
;
683 PidginLogViewer
*lv
= NULL
;
684 const char *name
= buddyname
;
686 GdkPixbuf
*prpl_icon
;
688 g_return_if_fail(account
!= NULL
);
689 g_return_if_fail(buddyname
!= NULL
);
691 ht
= g_new0(struct log_viewer_hash_t
, 1);
694 ht
->buddyname
= g_strdup(buddyname
);
695 ht
->account
= account
;
697 if (log_viewers
== NULL
) {
698 log_viewers
= g_hash_table_new(log_viewer_hash
, log_viewer_equal
);
699 } else if ((lv
= g_hash_table_lookup(log_viewers
, ht
))) {
700 gtk_window_present(GTK_WINDOW(lv
->window
));
701 g_free(ht
->buddyname
);
706 if (type
== PURPLE_LOG_CHAT
) {
709 chat
= purple_blist_find_chat(account
, buddyname
);
711 name
= purple_chat_get_name(chat
);
713 title
= g_strdup_printf(_("Conversations in %s"), name
);
717 buddy
= purple_find_buddy(account
, buddyname
);
719 name
= purple_buddy_get_contact_alias(buddy
);
721 title
= g_strdup_printf(_("Conversations with %s"), name
);
724 prpl_icon
= pidgin_create_prpl_icon(account
, PIDGIN_PRPL_ICON_MEDIUM
);
726 display_log_viewer(ht
, purple_log_get_logs(type
, buddyname
, account
),
727 title
, gtk_image_new_from_pixbuf(prpl_icon
),
728 purple_log_get_total_size(type
, buddyname
, account
));
731 g_object_unref(prpl_icon
);
735 void pidgin_log_show_contact(PurpleContact
*contact
) {
736 struct log_viewer_hash_t
*ht
;
737 PurpleBlistNode
*child
;
738 PidginLogViewer
*lv
= NULL
;
742 const char *name
= NULL
;
744 int total_log_size
= 0;
746 g_return_if_fail(contact
!= NULL
);
748 ht
= g_new0(struct log_viewer_hash_t
, 1);
749 ht
->type
= PURPLE_LOG_IM
;
750 ht
->contact
= contact
;
752 if (log_viewers
== NULL
) {
753 log_viewers
= g_hash_table_new(log_viewer_hash
, log_viewer_equal
);
754 } else if ((lv
= g_hash_table_lookup(log_viewers
, ht
))) {
755 gtk_window_present(GTK_WINDOW(lv
->window
));
760 for (child
= purple_blist_node_get_first_child((PurpleBlistNode
*)contact
) ;
762 child
= purple_blist_node_get_sibling_next(child
)) {
763 const char *buddy_name
;
764 PurpleAccount
*account
;
766 if (!PURPLE_BLIST_NODE_IS_BUDDY(child
))
769 buddy_name
= purple_buddy_get_name((PurpleBuddy
*)child
);
770 account
= purple_buddy_get_account((PurpleBuddy
*)child
);
771 logs
= g_list_concat(purple_log_get_logs(PURPLE_LOG_IM
, buddy_name
, account
), logs
);
772 total_log_size
+= purple_log_get_total_size(PURPLE_LOG_IM
, buddy_name
, account
);
774 logs
= g_list_sort(logs
, purple_log_compare
);
776 image
= gtk_image_new();
777 pixbuf
= gtk_widget_render_icon(image
, PIDGIN_STOCK_STATUS_PERSON
,
778 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL
), "GtkWindow");
780 gtk_image_set_from_pixbuf(GTK_IMAGE(image
), pixbuf
);
781 g_object_unref(pixbuf
);
783 gtk_widget_destroy(image
);
787 if (contact
->alias
!= NULL
)
788 name
= contact
->alias
;
789 else if (contact
->priority
!= NULL
)
790 name
= purple_buddy_get_contact_alias(contact
->priority
);
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 (contact
->node
.child
!= NULL
&& PURPLE_BLIST_NODE_IS_BUDDY(contact
->node
.child
))
797 name
= purple_buddy_get_contact_alias((PurpleBuddy
*) contact
->node
.child
);
802 title
= g_strdup_printf(_("Conversations with %s"), name
);
803 display_log_viewer(ht
, logs
, title
, image
, total_log_size
);
807 void pidgin_syslog_show()
809 GList
*accounts
= NULL
;
812 if (syslog_viewer
!= NULL
) {
813 gtk_window_present(GTK_WINDOW(syslog_viewer
->window
));
817 for(accounts
= purple_accounts_get_all(); accounts
!= NULL
; accounts
= accounts
->next
) {
819 PurpleAccount
*account
= (PurpleAccount
*)accounts
->data
;
820 if(purple_find_prpl(purple_account_get_protocol_id(account
)) == NULL
)
823 logs
= g_list_concat(purple_log_get_system_logs(account
), logs
);
825 logs
= g_list_sort(logs
, purple_log_compare
);
827 syslog_viewer
= display_log_viewer(NULL
, logs
, _("System Log"), NULL
, 0);
830 /****************************************************************************
831 * GTK+ LOG SUBSYSTEM *******************************************************
832 ****************************************************************************/
835 pidgin_log_get_handle(void)
842 void pidgin_log_init(void)
844 void *handle
= pidgin_log_get_handle();
846 purple_signal_register(handle
, "log-displaying",
847 purple_marshal_VOID__POINTER_POINTER
,
849 purple_value_new(PURPLE_TYPE_BOXED
,
850 "PidginLogViewer *"),
851 purple_value_new(PURPLE_TYPE_SUBTYPE
,
852 PURPLE_SUBTYPE_LOG
));
856 pidgin_log_uninit(void)
858 purple_signals_unregister_by_instance(pidgin_log_get_handle());