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
29 #include "gtkdialogs.h"
31 #include "pidgindebug.h"
32 #include "pidginstock.h"
34 #ifdef ENABLE_GLIBTRACE
38 #include <gdk/gdkkeysyms.h>
40 #include "gtk3compat.h"
42 #include "pidginresources.h"
44 struct _PidginDebugWindow
{
49 GtkTextBuffer
*buffer
;
50 GtkTextMark
*start_mark
;
51 GtkTextMark
*end_mark
;
53 GtkTextTag
*level
[PURPLE_DEBUG_FATAL
+ 1];
55 GtkTextTag
*filtered_invisible
;
56 GtkTextTag
*filtered_visible
;
61 GtkWidget
*expression
;
62 GtkWidget
*filterlevel
;
67 GtkWidget
*popover_invert
;
68 GtkWidget
*popover_highlight
;
74 static PidginDebugWindow
*debug_win
= NULL
;
80 /* Other members, including private data. */
81 guint debug_enabled_timer
;
84 static void pidgin_debug_ui_finalize(GObject
*gobject
);
85 static void pidgin_debug_ui_interface_init(PurpleDebugUiInterface
*iface
);
87 G_DEFINE_TYPE_WITH_CODE(PidginDebugUi
, pidgin_debug_ui
, G_TYPE_OBJECT
,
88 G_IMPLEMENT_INTERFACE(PURPLE_TYPE_DEBUG_UI
,
89 pidgin_debug_ui_interface_init
));
90 G_DEFINE_TYPE(PidginDebugWindow
, pidgin_debug_window
, GTK_TYPE_WINDOW
);
93 debug_window_destroy(GtkWidget
*w
, GdkEvent
*event
, void *unused
)
95 purple_prefs_disconnect_by_handle(pidgin_debug_get_handle());
97 if (debug_win
->regex
!= NULL
)
98 g_regex_unref(debug_win
->regex
);
100 /* If the "Save Log" dialog is open then close it */
101 purple_request_close_with_handle(debug_win
);
105 purple_prefs_set_bool(PIDGIN_PREFS_ROOT
"/debug/enabled", FALSE
);
111 configure_cb(GtkWidget
*w
, GdkEventConfigure
*event
, void *unused
)
113 if (gtk_widget_get_visible(w
)) {
114 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/debug/width", event
->width
);
115 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/debug/height", event
->height
);
122 view_near_bottom(PidginDebugWindow
*win
)
124 GtkAdjustment
*adj
= gtk_scrollable_get_vadjustment(
125 GTK_SCROLLABLE(win
->textview
));
126 return (gtk_adjustment_get_value(adj
) >=
127 (gtk_adjustment_get_upper(adj
) -
128 gtk_adjustment_get_page_size(adj
) * 1.5));
132 save_writefile_cb(void *user_data
, const char *filename
)
134 PidginDebugWindow
*win
= (PidginDebugWindow
*)user_data
;
136 GtkTextIter start
, end
;
139 if ((fp
= g_fopen(filename
, "w+")) == NULL
) {
140 purple_notify_error(win
, NULL
, _("Unable to open file."), NULL
, NULL
);
144 gtk_text_buffer_get_bounds(win
->buffer
, &start
, &end
);
145 tmp
= gtk_text_buffer_get_text(win
->buffer
, &start
, &end
, TRUE
);
146 fprintf(fp
, "Pidgin Debug Log : %s\n", purple_date_format_full(NULL
));
147 fprintf(fp
, "%s", tmp
);
154 save_cb(GtkWidget
*w
, PidginDebugWindow
*win
)
156 purple_request_file(win
, _("Save Debug Log"), "purple-debug.log", TRUE
,
157 G_CALLBACK(save_writefile_cb
), NULL
, NULL
, win
);
161 clear_cb(GtkWidget
*w
, PidginDebugWindow
*win
)
163 gtk_text_buffer_set_text(win
->buffer
, "", 0);
167 pause_cb(GtkWidget
*w
, PidginDebugWindow
*win
)
169 win
->paused
= gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(w
));
172 GtkTextIter start
, end
;
173 gtk_text_buffer_get_bounds(win
->buffer
, &start
, &end
);
174 gtk_text_buffer_remove_tag(win
->buffer
, win
->tags
.paused
,
176 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(win
->textview
),
177 win
->end_mark
, 0, TRUE
, 0, 1);
181 /******************************************************************************
183 *****************************************************************************/
185 regex_clear_color(GtkWidget
*w
) {
186 GtkStyleContext
*context
= gtk_widget_get_style_context(w
);
187 gtk_style_context_remove_class(context
, "good-filter");
188 gtk_style_context_remove_class(context
, "bad-filter");
192 regex_change_color(GtkWidget
*w
, gboolean success
) {
193 GtkStyleContext
*context
= gtk_widget_get_style_context(w
);
196 gtk_style_context_add_class(context
, "good-filter");
197 gtk_style_context_remove_class(context
, "bad-filter");
199 gtk_style_context_add_class(context
, "bad-filter");
200 gtk_style_context_remove_class(context
, "good-filter");
205 do_regex(PidginDebugWindow
*win
, GtkTextIter
*start
, GtkTextIter
*end
)
207 GError
*error
= NULL
;
209 gint initial_position
;
210 gint start_pos
, end_pos
;
211 GtkTextIter match_start
, match_end
;
217 initial_position
= gtk_text_iter_get_offset(start
);
220 /* First hide everything. */
221 gtk_text_buffer_apply_tag(win
->buffer
,
222 win
->tags
.filtered_invisible
, start
, end
);
225 text
= gtk_text_buffer_get_text(win
->buffer
, start
, end
, TRUE
);
226 g_regex_match(win
->regex
, text
, 0, &match
);
227 while (g_match_info_matches(match
)) {
228 g_match_info_fetch_pos(match
, 0, &start_pos
, &end_pos
);
229 start_pos
+= initial_position
;
230 end_pos
+= initial_position
;
232 /* Expand match to full line of message. */
233 gtk_text_buffer_get_iter_at_offset(win
->buffer
,
234 &match_start
, start_pos
);
235 gtk_text_iter_set_line_index(&match_start
, 0);
236 gtk_text_buffer_get_iter_at_offset(win
->buffer
,
237 &match_end
, end_pos
);
238 gtk_text_iter_forward_line(&match_end
);
241 /* Make invisible. */
242 gtk_text_buffer_apply_tag(win
->buffer
,
243 win
->tags
.filtered_invisible
,
244 &match_start
, &match_end
);
246 /* Make visible again (with higher priority.) */
247 gtk_text_buffer_apply_tag(win
->buffer
,
248 win
->tags
.filtered_visible
,
249 &match_start
, &match_end
);
251 if (win
->highlight
) {
252 gtk_text_buffer_get_iter_at_offset(
256 gtk_text_buffer_get_iter_at_offset(
260 gtk_text_buffer_apply_tag(win
->buffer
,
267 g_match_info_next(match
, &error
);
270 g_match_info_free(match
);
275 regex_toggle_filter(PidginDebugWindow
*win
, gboolean filter
)
277 GtkTextIter start
, end
;
279 gtk_text_buffer_get_bounds(win
->buffer
, &start
, &end
);
280 gtk_text_buffer_remove_tag(win
->buffer
, win
->tags
.match
, &start
, &end
);
281 gtk_text_buffer_remove_tag(win
->buffer
, win
->tags
.filtered_invisible
,
283 gtk_text_buffer_remove_tag(win
->buffer
, win
->tags
.filtered_visible
,
287 do_regex(win
, &start
, &end
);
292 regex_pref_filter_cb(const gchar
*name
, PurplePrefType type
,
293 gconstpointer val
, gpointer data
)
295 PidginDebugWindow
*win
= (PidginDebugWindow
*)data
;
296 gboolean active
= GPOINTER_TO_INT(val
), current
;
301 current
= gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
));
302 if (active
!= current
)
303 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
), active
);
307 regex_pref_expression_cb(const gchar
*name
, PurplePrefType type
,
308 gconstpointer val
, gpointer data
)
310 PidginDebugWindow
*win
= (PidginDebugWindow
*)data
;
311 const gchar
*exp
= (const gchar
*)val
;
313 gtk_entry_set_text(GTK_ENTRY(win
->expression
), exp
);
317 regex_pref_invert_cb(const gchar
*name
, PurplePrefType type
,
318 gconstpointer val
, gpointer data
)
320 PidginDebugWindow
*win
= (PidginDebugWindow
*)data
;
321 gboolean active
= GPOINTER_TO_INT(val
);
323 win
->invert
= active
;
325 if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
326 regex_toggle_filter(win
, TRUE
);
330 regex_pref_highlight_cb(const gchar
*name
, PurplePrefType type
,
331 gconstpointer val
, gpointer data
)
333 PidginDebugWindow
*win
= (PidginDebugWindow
*)data
;
334 gboolean active
= GPOINTER_TO_INT(val
);
336 win
->highlight
= active
;
338 if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
339 regex_toggle_filter(win
, TRUE
);
343 regex_changed_cb(GtkWidget
*w
, PidginDebugWindow
*win
) {
346 if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
))) {
347 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
),
351 text
= gtk_entry_get_text(GTK_ENTRY(win
->expression
));
352 purple_prefs_set_string(PIDGIN_PREFS_ROOT
"/debug/regex", text
);
354 if (text
== NULL
|| *text
== '\0') {
355 regex_clear_color(win
->expression
);
356 gtk_widget_set_sensitive(win
->filter
, FALSE
);
361 g_regex_unref(win
->regex
);
363 win
->regex
= g_regex_new(text
, G_REGEX_CASELESS
|G_REGEX_JAVASCRIPT_COMPAT
, 0, NULL
);
365 if (win
->regex
== NULL
) {
366 /* failed to compile */
367 regex_change_color(win
->expression
, FALSE
);
368 gtk_widget_set_sensitive(win
->filter
, FALSE
);
370 /* compiled successfully */
371 regex_change_color(win
->expression
, TRUE
);
372 gtk_widget_set_sensitive(win
->filter
, TRUE
);
377 regex_key_release_cb(GtkWidget
*w
, GdkEventKey
*e
, PidginDebugWindow
*win
) {
378 if (gtk_widget_is_sensitive(win
->filter
)) {
379 GtkToggleToolButton
*tb
= GTK_TOGGLE_TOOL_BUTTON(win
->filter
);
380 if ((e
->keyval
== GDK_KEY_Return
|| e
->keyval
== GDK_KEY_KP_Enter
) &&
381 !gtk_toggle_tool_button_get_active(tb
))
383 gtk_toggle_tool_button_set_active(tb
, TRUE
);
385 if (e
->keyval
== GDK_KEY_Escape
&&
386 gtk_toggle_tool_button_get_active(tb
))
388 gtk_toggle_tool_button_set_active(tb
, FALSE
);
394 regex_menu_cb(GtkWidget
*item
, PidginDebugWindow
*win
)
398 active
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(item
));
400 if (item
== win
->popover_highlight
) {
401 purple_prefs_set_bool(PIDGIN_PREFS_ROOT
"/debug/highlight", active
);
402 } else if (item
== win
->popover_invert
) {
403 purple_prefs_set_bool(PIDGIN_PREFS_ROOT
"/debug/invert", active
);
408 regex_popup_cb(GtkEntry
*entry
, GtkEntryIconPosition icon_pos
, GdkEvent
*event
,
409 PidginDebugWindow
*win
)
412 if (icon_pos
!= GTK_ENTRY_ICON_PRIMARY
) {
416 gtk_entry_get_icon_area(entry
, icon_pos
, &rect
);
417 gtk_popover_set_pointing_to(GTK_POPOVER(win
->popover
), &rect
);
418 gtk_popover_popup(GTK_POPOVER(win
->popover
));
422 regex_filter_toggled_cb(GtkToggleToolButton
*button
, PidginDebugWindow
*win
)
426 active
= gtk_toggle_tool_button_get_active(button
);
428 purple_prefs_set_bool(PIDGIN_PREFS_ROOT
"/debug/filter", active
);
430 regex_toggle_filter(win
, active
);
434 debug_window_set_filter_level(PidginDebugWindow
*win
, int level
)
439 if (level
!= gtk_combo_box_get_active(GTK_COMBO_BOX(win
->filterlevel
)))
440 gtk_combo_box_set_active(GTK_COMBO_BOX(win
->filterlevel
), level
);
442 scroll
= view_near_bottom(win
);
443 for (i
= 0; i
<= PURPLE_DEBUG_FATAL
; i
++) {
444 g_object_set(G_OBJECT(win
->tags
.level
[i
]),
445 "invisible", i
< level
,
449 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(win
->textview
),
450 win
->end_mark
, 0, TRUE
, 0, 1);
455 filter_level_pref_changed(const char *name
, PurplePrefType type
, gconstpointer value
, gpointer data
)
457 PidginDebugWindow
*win
= data
;
458 int level
= GPOINTER_TO_INT(value
);
460 debug_window_set_filter_level(win
, level
);
464 filter_level_changed_cb(GtkWidget
*combo
, gpointer null
)
466 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel",
467 gtk_combo_box_get_active(GTK_COMBO_BOX(combo
)));
471 toolbar_style_pref_changed_cb(const char *name
, PurplePrefType type
, gconstpointer value
, gpointer data
)
473 gtk_toolbar_set_style(GTK_TOOLBAR(data
), GPOINTER_TO_INT(value
));
477 toolbar_icon_pref_changed(GtkWidget
*item
, GtkWidget
*toolbar
)
479 int style
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item
), "user_data"));
480 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/debug/style", style
);
484 toolbar_context(GtkWidget
*toolbar
, gint x
, gint y
, gint button
, gpointer null
)
486 GtkWidget
*menu
, *item
;
488 GtkToolbarStyle value
[3];
491 text
[0] = _("_Icon Only"); value
[0] = GTK_TOOLBAR_ICONS
;
492 text
[1] = _("_Text Only"); value
[1] = GTK_TOOLBAR_TEXT
;
493 text
[2] = _("_Both Icon & Text"); value
[2] = GTK_TOOLBAR_BOTH_HORIZ
;
495 menu
= gtk_menu_new();
497 for (i
= 0; i
< 3; i
++) {
498 item
= gtk_check_menu_item_new_with_mnemonic(text
[i
]);
499 g_object_set_data(G_OBJECT(item
), "user_data", GINT_TO_POINTER(value
[i
]));
500 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(toolbar_icon_pref_changed
), toolbar
);
501 if (value
[i
] == (GtkToolbarStyle
)purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/style"))
502 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
503 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
506 gtk_widget_show_all(menu
);
508 gtk_menu_popup_at_pointer(GTK_MENU(menu
), NULL
);
513 pidgin_debug_window_class_init(PidginDebugWindowClass
*klass
) {
514 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
516 gtk_widget_class_set_template_from_resource(
518 "/im/pidgin/Pidgin/Debug/debug.ui"
521 gtk_widget_class_bind_template_child(
522 widget_class
, PidginDebugWindow
, toolbar
);
523 gtk_widget_class_bind_template_child(
524 widget_class
, PidginDebugWindow
, textview
);
525 gtk_widget_class_bind_template_child(
526 widget_class
, PidginDebugWindow
, buffer
);
527 gtk_widget_class_bind_template_child(
528 widget_class
, PidginDebugWindow
, tags
.category
);
529 gtk_widget_class_bind_template_child(
530 widget_class
, PidginDebugWindow
, tags
.filtered_invisible
);
531 gtk_widget_class_bind_template_child(
532 widget_class
, PidginDebugWindow
, tags
.filtered_visible
);
533 gtk_widget_class_bind_template_child(
534 widget_class
, PidginDebugWindow
, tags
.level
[0]);
535 gtk_widget_class_bind_template_child(
536 widget_class
, PidginDebugWindow
, tags
.level
[1]);
537 gtk_widget_class_bind_template_child(
538 widget_class
, PidginDebugWindow
, tags
.level
[2]);
539 gtk_widget_class_bind_template_child(
540 widget_class
, PidginDebugWindow
, tags
.level
[3]);
541 gtk_widget_class_bind_template_child(
542 widget_class
, PidginDebugWindow
, tags
.level
[4]);
543 gtk_widget_class_bind_template_child(
544 widget_class
, PidginDebugWindow
, tags
.level
[5]);
545 gtk_widget_class_bind_template_child(
546 widget_class
, PidginDebugWindow
, tags
.paused
);
547 gtk_widget_class_bind_template_child(
548 widget_class
, PidginDebugWindow
, filter
);
549 gtk_widget_class_bind_template_child(
550 widget_class
, PidginDebugWindow
, filterlevel
);
551 gtk_widget_class_bind_template_child(
552 widget_class
, PidginDebugWindow
, expression
);
553 gtk_widget_class_bind_template_child(
554 widget_class
, PidginDebugWindow
, tags
.match
);
555 gtk_widget_class_bind_template_child(
556 widget_class
, PidginDebugWindow
, popover
);
557 gtk_widget_class_bind_template_child(
558 widget_class
, PidginDebugWindow
, popover_invert
);
559 gtk_widget_class_bind_template_child(
560 widget_class
, PidginDebugWindow
, popover_highlight
);
561 gtk_widget_class_bind_template_callback(widget_class
, toolbar_context
);
562 gtk_widget_class_bind_template_callback(widget_class
, save_cb
);
563 gtk_widget_class_bind_template_callback(widget_class
, clear_cb
);
564 gtk_widget_class_bind_template_callback(widget_class
, pause_cb
);
565 gtk_widget_class_bind_template_callback(widget_class
,
566 regex_filter_toggled_cb
);
567 gtk_widget_class_bind_template_callback(widget_class
,
569 gtk_widget_class_bind_template_callback(widget_class
, regex_popup_cb
);
570 gtk_widget_class_bind_template_callback(widget_class
, regex_menu_cb
);
571 gtk_widget_class_bind_template_callback(widget_class
,
572 regex_key_release_cb
);
573 gtk_widget_class_bind_template_callback(widget_class
,
574 filter_level_changed_cb
);
578 pidgin_debug_window_init(PidginDebugWindow
*win
)
583 GtkStyleContext
*context
;
584 GtkCssProvider
*filter_css
;
585 const gchar filter_style
[] =
587 "color: @error_fg_color;"
588 "text-shadow: 0 1px @error_text_shadow;"
589 "background-image: none;"
590 "background-color: @error_bg_color;"
593 "color: @question_fg_color;"
594 "text-shadow: 0 1px @question_text_shadow;"
595 "background-image: none;"
596 "background-color: @success_color;"
599 gtk_widget_init_template(GTK_WIDGET(win
));
601 width
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/width");
602 height
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/height");
604 purple_debug_info("gtkdebug", "Setting dimensions to %d, %d\n",
607 gtk_window_set_default_size(GTK_WINDOW(win
), width
, height
);
609 g_signal_connect(G_OBJECT(win
), "delete_event",
610 G_CALLBACK(debug_window_destroy
), NULL
);
611 g_signal_connect(G_OBJECT(win
), "configure_event",
612 G_CALLBACK(configure_cb
), NULL
);
614 handle
= pidgin_debug_get_handle();
616 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/toolbar")) {
617 /* Setup our top button bar thingie. */
618 gtk_toolbar_set_style(GTK_TOOLBAR(win
->toolbar
),
619 purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/style"));
620 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/style",
621 toolbar_style_pref_changed_cb
, win
->toolbar
);
623 /* we purposely disable the toggle button here in case
624 * /purple/gtk/debug/expression has an empty string. If it does not have
625 * an empty string, the change signal will get called and make the
626 * toggle button sensitive.
628 gtk_widget_set_sensitive(win
->filter
, FALSE
);
629 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
),
630 purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/filter"));
631 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/filter",
632 regex_pref_filter_cb
, win
);
635 filter_css
= gtk_css_provider_new();
636 gtk_css_provider_load_from_data(filter_css
, filter_style
, -1, NULL
);
637 context
= gtk_widget_get_style_context(win
->expression
);
638 gtk_style_context_add_provider(context
,
639 GTK_STYLE_PROVIDER(filter_css
),
640 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
642 gtk_entry_set_text(GTK_ENTRY(win
->expression
),
643 purple_prefs_get_string(PIDGIN_PREFS_ROOT
"/debug/regex"));
644 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/regex",
645 regex_pref_expression_cb
, win
);
647 /* connect the rest of our pref callbacks */
648 win
->invert
= purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/invert");
649 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/invert",
650 regex_pref_invert_cb
, win
);
652 win
->highlight
= purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/highlight");
653 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/highlight",
654 regex_pref_highlight_cb
, win
);
656 gtk_combo_box_set_active(GTK_COMBO_BOX(win
->filterlevel
),
657 purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel"));
659 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/filterlevel",
660 filter_level_pref_changed
, win
);
662 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win
->popover_invert
),
664 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win
->popover_highlight
),
668 /* The *start* and *end* marks bound the beginning and end of an
669 insertion, used for filtering. The *end* mark is also used for
671 gtk_text_buffer_get_end_iter(win
->buffer
, &end
);
672 win
->start_mark
= gtk_text_buffer_create_mark(win
->buffer
,
673 "start", &end
, TRUE
);
674 win
->end_mark
= gtk_text_buffer_create_mark(win
->buffer
,
677 /* Set active filter level in textview */
678 debug_window_set_filter_level(win
,
679 purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel"));
685 debug_enabled_timeout_cb(gpointer data
)
687 PidginDebugUi
*ui
= PIDGIN_DEBUG_UI(data
);
689 ui
->debug_enabled_timer
= 0;
691 pidgin_debug_window_show();
697 debug_disabled_timeout_cb(gpointer data
)
699 PidginDebugUi
*ui
= PIDGIN_DEBUG_UI(data
);
701 ui
->debug_enabled_timer
= 0;
703 pidgin_debug_window_hide();
709 debug_enabled_cb(const char *name
, PurplePrefType type
,
710 gconstpointer value
, gpointer data
)
712 PidginDebugUi
*ui
= PIDGIN_DEBUG_UI(data
);
714 if (GPOINTER_TO_INT(value
))
715 ui
->debug_enabled_timer
= g_timeout_add(0, debug_enabled_timeout_cb
, data
);
717 ui
->debug_enabled_timer
= g_timeout_add(0, debug_disabled_timeout_cb
, data
);
721 pidgin_glib_log_handler(const gchar
*domain
, GLogLevelFlags flags
,
722 const gchar
*msg
, gpointer user_data
)
724 PurpleDebugLevel level
;
725 char *new_msg
= NULL
;
726 char *new_domain
= NULL
;
728 if ((flags
& G_LOG_LEVEL_ERROR
) == G_LOG_LEVEL_ERROR
)
729 level
= PURPLE_DEBUG_ERROR
;
730 else if ((flags
& G_LOG_LEVEL_CRITICAL
) == G_LOG_LEVEL_CRITICAL
)
731 level
= PURPLE_DEBUG_FATAL
;
732 else if ((flags
& G_LOG_LEVEL_WARNING
) == G_LOG_LEVEL_WARNING
)
733 level
= PURPLE_DEBUG_WARNING
;
734 else if ((flags
& G_LOG_LEVEL_MESSAGE
) == G_LOG_LEVEL_MESSAGE
)
735 level
= PURPLE_DEBUG_INFO
;
736 else if ((flags
& G_LOG_LEVEL_INFO
) == G_LOG_LEVEL_INFO
)
737 level
= PURPLE_DEBUG_INFO
;
738 else if ((flags
& G_LOG_LEVEL_DEBUG
) == G_LOG_LEVEL_DEBUG
)
739 level
= PURPLE_DEBUG_MISC
;
742 purple_debug_warning("gtkdebug",
743 "Unknown glib logging level in %d\n", flags
);
745 level
= PURPLE_DEBUG_MISC
; /* This will never happen. */
749 new_msg
= purple_utf8_try_convert(msg
);
752 new_domain
= purple_utf8_try_convert(domain
);
756 #ifdef ENABLE_GLIBTRACE
760 bt_size
= backtrace(bt_buff
, 20);
761 fprintf(stderr
, "\nBacktrace for \"%s\" (%s):\n", new_msg
,
762 new_domain
!= NULL
? new_domain
: "g_log");
763 backtrace_symbols_fd(bt_buff
, bt_size
, STDERR_FILENO
);
764 fprintf(stderr
, "\n");
767 purple_debug(level
, (new_domain
!= NULL
? new_domain
: "g_log"),
778 pidgin_glib_dummy_print_handler(const gchar
*string
)
784 pidgin_debug_ui_init(PidginDebugUi
*self
)
786 /* Debug window preferences. */
788 * NOTE: This must be set before prefs are loaded, and the callbacks
789 * set after they are loaded, since prefs sets the enabled
790 * preference here and that loads the window, which calls the
791 * configure event, which overrides the width and height! :P
794 purple_prefs_add_none(PIDGIN_PREFS_ROOT
"/debug");
796 /* Controls printing to the debug window */
797 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/enabled", FALSE
);
798 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel", PURPLE_DEBUG_ALL
);
799 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/debug/style", GTK_TOOLBAR_BOTH_HORIZ
);
801 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/toolbar", TRUE
);
802 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/debug/width", 450);
803 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/debug/height", 250);
805 purple_prefs_add_string(PIDGIN_PREFS_ROOT
"/debug/regex", "");
806 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/filter", FALSE
);
807 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/invert", FALSE
);
808 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/case_insensitive", FALSE
);
809 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/highlight", FALSE
);
811 purple_prefs_connect_callback(NULL
, PIDGIN_PREFS_ROOT
"/debug/enabled",
812 debug_enabled_cb
, self
);
814 #define REGISTER_G_LOG_HANDLER(name) \
815 g_log_set_handler((name), G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL \
816 | G_LOG_FLAG_RECURSION, \
817 pidgin_glib_log_handler, NULL)
819 /* Register the glib/gtk log handlers. */
820 REGISTER_G_LOG_HANDLER(NULL
);
821 REGISTER_G_LOG_HANDLER("Gdk");
822 REGISTER_G_LOG_HANDLER("GdkPixbuf");
823 REGISTER_G_LOG_HANDLER("GLib");
824 REGISTER_G_LOG_HANDLER("GLib-GObject");
825 REGISTER_G_LOG_HANDLER("GModule");
826 REGISTER_G_LOG_HANDLER("Gnt"); /* just in case we find a gnt plugin */
827 REGISTER_G_LOG_HANDLER("GPlugin");
828 REGISTER_G_LOG_HANDLER("GPluginGtk");
829 REGISTER_G_LOG_HANDLER("GThread");
830 REGISTER_G_LOG_HANDLER("Gtk");
831 REGISTER_G_LOG_HANDLER("Json");
832 REGISTER_G_LOG_HANDLER("Talkatu");
834 REGISTER_G_LOG_HANDLER("GStreamer");
838 if (!purple_debug_is_enabled())
839 g_set_print_handler(pidgin_glib_dummy_print_handler
);
844 pidgin_debug_ui_finalize(GObject
*gobject
)
846 PidginDebugUi
*self
= PIDGIN_DEBUG_UI(gobject
);
848 if (self
->debug_enabled_timer
!= 0)
849 g_source_remove(self
->debug_enabled_timer
);
850 self
->debug_enabled_timer
= 0;
852 G_OBJECT_CLASS(pidgin_debug_ui_parent_class
)->finalize(gobject
);
856 pidgin_debug_window_show(void)
858 if (debug_win
== NULL
) {
859 debug_win
= PIDGIN_DEBUG_WINDOW(
860 g_object_new(PIDGIN_TYPE_DEBUG_WINDOW
, NULL
));
863 gtk_widget_show(GTK_WIDGET(debug_win
));
865 purple_prefs_set_bool(PIDGIN_PREFS_ROOT
"/debug/enabled", TRUE
);
869 pidgin_debug_window_hide(void)
871 if (debug_win
!= NULL
) {
872 gtk_widget_destroy(GTK_WIDGET(debug_win
));
873 debug_window_destroy(NULL
, NULL
, NULL
);
878 pidgin_debug_print(PurpleDebugUi
*self
,
879 PurpleDebugLevel level
, const char *category
,
882 GtkTextTag
*level_tag
;
888 if (debug_win
== NULL
)
890 if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/enabled"))
893 scroll
= view_near_bottom(debug_win
);
894 gtk_text_buffer_get_end_iter(debug_win
->buffer
, &end
);
895 gtk_text_buffer_move_mark(debug_win
->buffer
, debug_win
->start_mark
,
898 level_tag
= debug_win
->tags
.level
[level
];
901 mdate
= purple_utf8_strftime("(%H:%M:%S) ", localtime(&mtime
));
902 gtk_text_buffer_insert_with_tags(
908 debug_win
->paused
? debug_win
->tags
.paused
: NULL
,
911 if (category
&& *category
) {
912 gtk_text_buffer_insert_with_tags(
918 debug_win
->tags
.category
,
919 debug_win
->paused
? debug_win
->tags
.paused
: NULL
,
921 gtk_text_buffer_insert_with_tags(
927 debug_win
->tags
.category
,
928 debug_win
->paused
? debug_win
->tags
.paused
: NULL
,
932 gtk_text_buffer_insert_with_tags(
938 debug_win
->paused
? debug_win
->tags
.paused
: NULL
,
940 gtk_text_buffer_insert_with_tags(
946 debug_win
->paused
? debug_win
->tags
.paused
: NULL
,
949 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/filter") && debug_win
->regex
) {
950 /* Filter out any new messages. */
953 gtk_text_buffer_get_iter_at_mark(debug_win
->buffer
, &start
,
954 debug_win
->start_mark
);
955 gtk_text_buffer_get_iter_at_mark(debug_win
->buffer
, &end
,
956 debug_win
->end_mark
);
958 do_regex(debug_win
, &start
, &end
);
962 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(debug_win
->textview
),
963 debug_win
->end_mark
, 0, TRUE
, 0, 1);
968 pidgin_debug_is_enabled(PurpleDebugUi
*self
, PurpleDebugLevel level
, const char *category
)
970 return (debug_win
!= NULL
&&
971 purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/enabled"));
975 pidgin_debug_ui_interface_init(PurpleDebugUiInterface
*iface
)
977 iface
->print
= pidgin_debug_print
;
978 iface
->is_enabled
= pidgin_debug_is_enabled
;
982 pidgin_debug_ui_class_init(PidginDebugUiClass
*klass
)
984 GObjectClass
*object_class
= G_OBJECT_CLASS(klass
);
986 object_class
->finalize
= pidgin_debug_ui_finalize
;
990 pidgin_debug_ui_new(void)
992 return g_object_new(PIDGIN_TYPE_DEBUG_UI
, NULL
);
996 pidgin_debug_get_handle() {