2 * @file gtkdebug.c GTK+ Debug API
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
35 #include "gtkdialogs.h"
36 #include "gtkimhtml.h"
38 #include "pidginstock.h"
44 #if GLIB_CHECK_VERSION(2,14,0)
47 #endif /* HAVE_REGEX_H */
49 #include <gdk/gdkkeysyms.h>
62 GtkWidget
*expression
;
72 # endif /* HAVE_REGEX_H */
75 #endif /* USE_REGEX */
76 GtkWidget
*filterlevel
;
79 static const char debug_fg_colors
[][8] = {
80 "#000000", /**< All debug levels. */
81 "#666666", /**< Misc. */
82 "#000000", /**< Information. */
83 "#660000", /**< Warnings. */
84 "#FF0000", /**< Errors. */
85 "#FF0000", /**< Fatal errors. */
88 static DebugWindow
*debug_win
= NULL
;
89 static guint debug_enabled_timer
= 0;
92 static void regex_filter_all(DebugWindow
*win
);
93 static void regex_show_all(DebugWindow
*win
);
94 #endif /* USE_REGEX */
97 debug_window_destroy(GtkWidget
*w
, GdkEvent
*event
, void *unused
)
99 purple_prefs_disconnect_by_handle(pidgin_debug_get_handle());
102 if(debug_win
->timer
!= 0) {
105 purple_timeout_remove(debug_win
->timer
);
107 text
= gtk_entry_get_text(GTK_ENTRY(debug_win
->expression
));
108 purple_prefs_set_string(PIDGIN_PREFS_ROOT
"/debug/regex", text
);
111 regfree(&debug_win
->regex
);
113 g_regex_unref(debug_win
->regex
);
114 #endif /* HAVE_REGEX_H */
115 #endif /* USE_REGEX */
117 /* If the "Save Log" dialog is open then close it */
118 purple_request_close_with_handle(debug_win
);
123 purple_prefs_set_bool(PIDGIN_PREFS_ROOT
"/debug/enabled", FALSE
);
129 configure_cb(GtkWidget
*w
, GdkEventConfigure
*event
, DebugWindow
*win
)
131 if (GTK_WIDGET_VISIBLE(w
)) {
132 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/debug/width", event
->width
);
133 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/debug/height", event
->height
);
146 do_find_cb(GtkWidget
*widget
, gint response
, struct _find
*f
)
149 case GTK_RESPONSE_OK
:
150 gtk_imhtml_search_find(GTK_IMHTML(f
->window
->text
),
151 gtk_entry_get_text(GTK_ENTRY(f
->entry
)));
154 case GTK_RESPONSE_DELETE_EVENT
:
155 case GTK_RESPONSE_CLOSE
:
156 gtk_imhtml_search_clear(GTK_IMHTML(f
->window
->text
));
157 gtk_widget_destroy(f
->window
->find
);
158 f
->window
->find
= NULL
;
165 find_cb(GtkWidget
*w
, DebugWindow
*win
)
167 GtkWidget
*hbox
, *img
, *label
;
172 gtk_window_present(GTK_WINDOW(win
->find
));
176 f
= g_malloc(sizeof(struct _find
));
178 win
->find
= gtk_dialog_new_with_buttons(_("Find"),
179 GTK_WINDOW(win
->window
), GTK_DIALOG_DESTROY_WITH_PARENT
,
180 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
,
181 GTK_STOCK_FIND
, GTK_RESPONSE_OK
, NULL
);
182 gtk_dialog_set_default_response(GTK_DIALOG(win
->find
),
184 g_signal_connect(G_OBJECT(win
->find
), "response",
185 G_CALLBACK(do_find_cb
), f
);
187 gtk_container_set_border_width(GTK_CONTAINER(win
->find
), PIDGIN_HIG_BOX_SPACE
);
188 gtk_window_set_resizable(GTK_WINDOW(win
->find
), FALSE
);
189 gtk_dialog_set_has_separator(GTK_DIALOG(win
->find
), FALSE
);
190 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(win
->find
)->vbox
), PIDGIN_HIG_BORDER
);
191 gtk_container_set_border_width(
192 GTK_CONTAINER(GTK_DIALOG(win
->find
)->vbox
), PIDGIN_HIG_BOX_SPACE
);
194 hbox
= gtk_hbox_new(FALSE
, PIDGIN_HIG_BORDER
);
195 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(win
->find
)->vbox
),
197 img
= gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION
,
198 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE
));
199 gtk_box_pack_start(GTK_BOX(hbox
), img
, FALSE
, FALSE
, 0);
201 gtk_misc_set_alignment(GTK_MISC(img
), 0, 0);
202 gtk_dialog_set_response_sensitive(GTK_DIALOG(win
->find
),
203 GTK_RESPONSE_OK
, FALSE
);
205 label
= gtk_label_new(NULL
);
206 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label
), _("_Search for:"));
207 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
209 f
->entry
= gtk_entry_new();
210 gtk_entry_set_activates_default(GTK_ENTRY(f
->entry
), TRUE
);
211 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), GTK_WIDGET(f
->entry
));
212 g_signal_connect(G_OBJECT(f
->entry
), "changed",
213 G_CALLBACK(pidgin_set_sensitive_if_input
),
215 gtk_box_pack_start(GTK_BOX(hbox
), f
->entry
, FALSE
, FALSE
, 0);
217 gtk_widget_show_all(win
->find
);
218 gtk_widget_grab_focus(f
->entry
);
220 #endif /* USE_REGEX */
223 save_writefile_cb(void *user_data
, const char *filename
)
225 DebugWindow
*win
= (DebugWindow
*)user_data
;
229 if ((fp
= g_fopen(filename
, "w+")) == NULL
) {
230 purple_notify_error(win
, NULL
, _("Unable to open file."), NULL
);
234 tmp
= gtk_imhtml_get_text(GTK_IMHTML(win
->text
), NULL
, NULL
);
235 fprintf(fp
, "Pidgin Debug Log : %s\n", purple_date_format_full(NULL
));
236 fprintf(fp
, "%s", tmp
);
243 save_cb(GtkWidget
*w
, DebugWindow
*win
)
245 purple_request_file(win
, _("Save Debug Log"), "purple-debug.log", TRUE
,
246 G_CALLBACK(save_writefile_cb
), NULL
,
252 clear_cb(GtkWidget
*w
, DebugWindow
*win
)
254 gtk_imhtml_clear(GTK_IMHTML(win
->text
));
257 gtk_list_store_clear(win
->store
);
258 #endif /* USE_REGEX */
262 pause_cb(GtkWidget
*w
, DebugWindow
*win
)
264 win
->paused
= gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(w
));
268 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
269 regex_filter_all(win
);
273 #endif /* USE_REGEX */
276 /******************************************************************************
278 *****************************************************************************/
281 regex_clear_color(GtkWidget
*w
) {
282 gtk_widget_modify_base(w
, GTK_STATE_NORMAL
, NULL
);
286 regex_change_color(GtkWidget
*w
, guint16 r
, guint16 g
, guint16 b
) {
293 gtk_widget_modify_base(w
, GTK_STATE_NORMAL
, &color
);
297 regex_highlight_clear(DebugWindow
*win
) {
298 GtkIMHtml
*imhtml
= GTK_IMHTML(win
->text
);
301 gtk_text_buffer_get_start_iter(imhtml
->text_buffer
, &s
);
302 gtk_text_buffer_get_end_iter(imhtml
->text_buffer
, &e
);
303 gtk_text_buffer_remove_tag_by_name(imhtml
->text_buffer
, "regex", &s
, &e
);
307 regex_match(DebugWindow
*win
, const gchar
*text
) {
308 GtkIMHtml
*imhtml
= GTK_IMHTML(win
->text
);
310 regmatch_t matches
[4]; /* adjust if necessary */
311 size_t n_matches
= sizeof(matches
) / sizeof(matches
[0]);
314 GMatchInfo
*match_info
;
315 #endif /* HAVE_REGEX_H */
321 /* I don't like having to do this, but we need it for highlighting. Plus
322 * it makes the ^ and $ operators work :)
324 plaintext
= purple_markup_strip_html(text
);
326 /* we do a first pass to see if it matches at all. If it does we append
327 * it, and work out the offsets to highlight.
330 inverted
= (win
->invert
) ? REG_NOMATCH
: 0;
331 if(regexec(&win
->regex
, plaintext
, n_matches
, matches
, 0) == inverted
) {
333 if(g_regex_match(win
->regex
, plaintext
, 0, &match_info
) != win
->invert
) {
334 #endif /* HAVE_REGEX_H */
335 gchar
*p
= plaintext
;
339 gtk_text_buffer_get_iter_at_mark(imhtml
->text_buffer
, &ins
,
340 gtk_text_buffer_get_insert(imhtml
->text_buffer
));
341 i
= gtk_text_iter_get_offset(&ins
);
343 gtk_imhtml_append_text(imhtml
, text
, 0);
345 /* If we're not highlighting or the expression is inverted, we're
348 if(!win
->highlight
|| win
->invert
) {
351 g_match_info_free(match_info
);
356 /* we use a do-while to highlight the first match, and then continue
363 for(m
= 0; m
< n_matches
; m
++) {
366 if(matches
[m
].rm_eo
== -1)
371 gtk_text_buffer_get_iter_at_offset(imhtml
->text_buffer
, &ms
,
372 i
+ matches
[m
].rm_so
);
373 gtk_text_buffer_get_iter_at_offset(imhtml
->text_buffer
, &me
,
374 i
+ matches
[m
].rm_eo
);
375 gtk_text_buffer_apply_tag_by_name(imhtml
->text_buffer
, "regex",
377 offset
= matches
[m
].rm_eo
;
381 } while(regexec(&win
->regex
, p
, n_matches
, matches
, REG_NOTBOL
) == inverted
);
386 gint start_pos
, end_pos
;
389 if (!g_match_info_matches(match_info
))
392 for (m
= 0; m
< g_match_info_get_match_count(match_info
); m
++)
397 g_match_info_fetch_pos(match_info
, m
, &start_pos
, &end_pos
);
402 gtk_text_buffer_get_iter_at_offset(imhtml
->text_buffer
, &ms
,
404 gtk_text_buffer_get_iter_at_offset(imhtml
->text_buffer
, &me
,
406 gtk_text_buffer_apply_tag_by_name(imhtml
->text_buffer
, "regex",
411 g_match_info_free(match_info
);
414 } while (g_regex_match(win
->regex
, p
, G_REGEX_MATCH_NOTBOL
, &match_info
) != win
->invert
);
415 g_match_info_free(match_info
);
416 #endif /* HAVE_REGEX_H */
423 regex_filter_all_cb(GtkTreeModel
*m
, GtkTreePath
*p
, GtkTreeIter
*iter
,
426 DebugWindow
*win
= (DebugWindow
*)data
;
428 PurpleDebugLevel level
;
430 gtk_tree_model_get(m
, iter
, 0, &text
, 1, &level
, -1);
432 if (level
>= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel"))
433 regex_match(win
, text
);
441 regex_filter_all(DebugWindow
*win
) {
442 gtk_imhtml_clear(GTK_IMHTML(win
->text
));
445 regex_highlight_clear(win
);
447 gtk_tree_model_foreach(GTK_TREE_MODEL(win
->store
), regex_filter_all_cb
,
452 regex_show_all_cb(GtkTreeModel
*m
, GtkTreePath
*p
, GtkTreeIter
*iter
,
455 DebugWindow
*win
= (DebugWindow
*)data
;
457 PurpleDebugLevel level
;
459 gtk_tree_model_get(m
, iter
, 0, &text
, 1, &level
, -1);
460 if (level
>= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel"))
461 gtk_imhtml_append_text(GTK_IMHTML(win
->text
), text
, 0);
468 regex_show_all(DebugWindow
*win
) {
469 gtk_imhtml_clear(GTK_IMHTML(win
->text
));
472 regex_highlight_clear(win
);
474 gtk_tree_model_foreach(GTK_TREE_MODEL(win
->store
), regex_show_all_cb
,
479 regex_compile(DebugWindow
*win
) {
482 text
= gtk_entry_get_text(GTK_ENTRY(win
->expression
));
484 if(text
== NULL
|| *text
== '\0') {
485 regex_clear_color(win
->expression
);
486 gtk_widget_set_sensitive(win
->filter
, FALSE
);
491 regfree(&win
->regex
);
492 if(regcomp(&win
->regex
, text
, REG_EXTENDED
| REG_ICASE
) != 0) {
495 g_regex_unref(win
->regex
);
496 win
->regex
= g_regex_new(text
, G_REGEX_EXTENDED
| G_REGEX_CASELESS
, 0, NULL
);
497 if(win
->regex
== NULL
) {
499 /* failed to compile */
500 regex_change_color(win
->expression
, 0xFFFF, 0xAFFF, 0xAFFF);
501 gtk_widget_set_sensitive(win
->filter
, FALSE
);
503 /* compiled successfully */
504 regex_change_color(win
->expression
, 0xAFFF, 0xFFFF, 0xAFFF);
505 gtk_widget_set_sensitive(win
->filter
, TRUE
);
508 /* we check if the filter is on in case it was only of the options that
509 * got changed, and not the expression.
511 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
512 regex_filter_all(win
);
516 regex_pref_filter_cb(const gchar
*name
, PurplePrefType type
,
517 gconstpointer val
, gpointer data
)
519 DebugWindow
*win
= (DebugWindow
*)data
;
520 gboolean active
= GPOINTER_TO_INT(val
), current
;
522 if(!win
|| !win
->window
)
525 current
= gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
));
526 if(active
!= current
)
527 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
), active
);
531 regex_pref_expression_cb(const gchar
*name
, PurplePrefType type
,
532 gconstpointer val
, gpointer data
)
534 DebugWindow
*win
= (DebugWindow
*)data
;
535 const gchar
*exp
= (const gchar
*)val
;
537 gtk_entry_set_text(GTK_ENTRY(win
->expression
), exp
);
541 regex_pref_invert_cb(const gchar
*name
, PurplePrefType type
,
542 gconstpointer val
, gpointer data
)
544 DebugWindow
*win
= (DebugWindow
*)data
;
545 gboolean active
= GPOINTER_TO_INT(val
);
547 win
->invert
= active
;
549 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
550 regex_filter_all(win
);
554 regex_pref_highlight_cb(const gchar
*name
, PurplePrefType type
,
555 gconstpointer val
, gpointer data
)
557 DebugWindow
*win
= (DebugWindow
*)data
;
558 gboolean active
= GPOINTER_TO_INT(val
);
560 win
->highlight
= active
;
562 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
563 regex_filter_all(win
);
567 regex_row_changed_cb(GtkTreeModel
*model
, GtkTreePath
*path
,
568 GtkTreeIter
*iter
, DebugWindow
*win
)
571 PurpleDebugLevel level
;
573 if(!win
|| !win
->window
)
576 /* If the debug window is paused, we just return since it's in the store.
577 * We don't call regex_match because it doesn't make sense to check the
578 * string if it's paused. When we unpause we clear the imhtml and
579 * reiterate over the store to handle matches that were outputted when
585 gtk_tree_model_get(model
, iter
, 0, &text
, 1, &level
, -1);
587 if (level
>= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel")) {
588 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
))) {
589 regex_match(win
, text
);
591 gtk_imhtml_append_text(GTK_IMHTML(win
->text
), text
, 0);
599 regex_timer_cb(DebugWindow
*win
) {
602 text
= gtk_entry_get_text(GTK_ENTRY(win
->expression
));
603 purple_prefs_set_string(PIDGIN_PREFS_ROOT
"/debug/regex", text
);
611 regex_changed_cb(GtkWidget
*w
, DebugWindow
*win
) {
612 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
))) {
613 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
),
618 win
->timer
= purple_timeout_add_seconds(5, (GSourceFunc
)regex_timer_cb
, win
);
624 regex_key_release_cb(GtkWidget
*w
, GdkEventKey
*e
, DebugWindow
*win
) {
625 if(e
->keyval
== GDK_Return
&&
626 GTK_WIDGET_IS_SENSITIVE(win
->filter
) &&
627 !gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
629 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
), TRUE
);
634 regex_menu_cb(GtkWidget
*item
, const gchar
*pref
) {
637 active
= gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item
));
639 purple_prefs_set_bool(pref
, active
);
643 regex_popup_cb(GtkEntry
*entry
, GtkWidget
*menu
, DebugWindow
*win
) {
644 pidgin_separator(menu
);
645 pidgin_new_check_item(menu
, _("Invert"),
646 G_CALLBACK(regex_menu_cb
),
647 PIDGIN_PREFS_ROOT
"/debug/invert", win
->invert
);
648 pidgin_new_check_item(menu
, _("Highlight matches"),
649 G_CALLBACK(regex_menu_cb
),
650 PIDGIN_PREFS_ROOT
"/debug/highlight", win
->highlight
);
654 regex_filter_toggled_cb(GtkToggleToolButton
*button
, DebugWindow
*win
) {
657 active
= gtk_toggle_tool_button_get_active(button
);
659 purple_prefs_set_bool(PIDGIN_PREFS_ROOT
"/debug/filter", active
);
661 if(!GTK_IS_IMHTML(win
->text
))
665 regex_filter_all(win
);
671 filter_level_pref_changed(const char *name
, PurplePrefType type
, gconstpointer value
, gpointer data
)
673 DebugWindow
*win
= data
;
675 if (GPOINTER_TO_INT(value
) != gtk_combo_box_get_active(GTK_COMBO_BOX(win
->filterlevel
)))
676 gtk_combo_box_set_active(GTK_COMBO_BOX(win
->filterlevel
), GPOINTER_TO_INT(value
));
677 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
678 regex_filter_all(win
);
682 #endif /* USE_REGEX */
685 filter_level_changed_cb(GtkWidget
*combo
, gpointer null
)
687 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel",
688 gtk_combo_box_get_active(GTK_COMBO_BOX(combo
)));
692 toolbar_style_pref_changed_cb(const char *name
, PurplePrefType type
, gconstpointer value
, gpointer data
)
694 gtk_toolbar_set_style(GTK_TOOLBAR(data
), GPOINTER_TO_INT(value
));
698 toolbar_icon_pref_changed(GtkWidget
*item
, GtkWidget
*toolbar
)
700 int style
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item
), "user_data"));
701 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/debug/style", style
);
705 toolbar_context(GtkWidget
*toolbar
, GdkEventButton
*event
, gpointer null
)
707 GtkWidget
*menu
, *item
;
709 GtkToolbarStyle value
[3];
712 if (!(event
->button
== 3 && event
->type
== GDK_BUTTON_PRESS
))
715 text
[0] = _("_Icon Only"); value
[0] = GTK_TOOLBAR_ICONS
;
716 text
[1] = _("_Text Only"); value
[1] = GTK_TOOLBAR_TEXT
;
717 text
[2] = _("_Both Icon & Text"); value
[2] = GTK_TOOLBAR_BOTH_HORIZ
;
719 menu
= gtk_menu_new();
721 for (i
= 0; i
< 3; i
++) {
722 item
= gtk_check_menu_item_new_with_mnemonic(text
[i
]);
723 g_object_set_data(G_OBJECT(item
), "user_data", GINT_TO_POINTER(value
[i
]));
724 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(toolbar_icon_pref_changed
), toolbar
);
725 if (value
[i
] == purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/style"))
726 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
727 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
730 gtk_widget_show_all(menu
);
732 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
, 0, gtk_get_current_event_time());
737 debug_window_new(void)
746 #if !GTK_CHECK_VERSION(2,12,0)
747 GtkTooltips
*tooltips
;
750 win
= g_new0(DebugWindow
, 1);
752 width
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/width");
753 height
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/height");
755 win
->window
= pidgin_create_window(_("Debug Window"), 0, "debug", TRUE
);
756 purple_debug_info("gtkdebug", "Setting dimensions to %d, %d\n",
759 gtk_window_set_default_size(GTK_WINDOW(win
->window
), width
, height
);
761 g_signal_connect(G_OBJECT(win
->window
), "delete_event",
762 G_CALLBACK(debug_window_destroy
), NULL
);
763 g_signal_connect(G_OBJECT(win
->window
), "configure_event",
764 G_CALLBACK(configure_cb
), win
);
766 handle
= pidgin_debug_get_handle();
769 /* the list store for all the messages */
770 win
->store
= gtk_list_store_new(2, G_TYPE_STRING
, G_TYPE_INT
);
772 /* row-changed gets called when we do gtk_list_store_set, and row-inserted
773 * gets called with gtk_list_store_append, which is a
774 * completely empty row. So we just ignore row-inserted, and deal with row
777 g_signal_connect(G_OBJECT(win
->store
), "row-changed",
778 G_CALLBACK(regex_row_changed_cb
), win
);
780 #endif /* USE_REGEX */
783 vbox
= gtk_vbox_new(FALSE
, 0);
784 gtk_container_add(GTK_CONTAINER(win
->window
), vbox
);
786 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/toolbar")) {
787 /* Setup our top button bar thingie. */
788 toolbar
= gtk_toolbar_new();
789 #if !GTK_CHECK_VERSION(2,12,0)
790 tooltips
= gtk_tooltips_new();
792 #if !GTK_CHECK_VERSION(2,14,0)
793 gtk_toolbar_set_tooltips(GTK_TOOLBAR(toolbar
), TRUE
);
795 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolbar
), TRUE
);
796 g_signal_connect(G_OBJECT(toolbar
), "button-press-event", G_CALLBACK(toolbar_context
), win
);
798 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar
),
799 purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/style"));
800 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/style",
801 toolbar_style_pref_changed_cb
, toolbar
);
802 gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar
),
803 GTK_ICON_SIZE_SMALL_TOOLBAR
);
805 gtk_box_pack_start(GTK_BOX(vbox
), toolbar
, FALSE
, FALSE
, 0);
809 item
= gtk_tool_button_new_from_stock(GTK_STOCK_FIND
);
810 gtk_tool_item_set_is_important(item
, TRUE
);
811 #if GTK_CHECK_VERSION(2,12,0)
812 gtk_tool_item_set_tooltip_text(item
, _("Find"));
814 gtk_tool_item_set_tooltip(item
, tooltips
, _("Find"), NULL
);
816 g_signal_connect(G_OBJECT(item
), "clicked", G_CALLBACK(find_cb
), win
);
817 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
818 #endif /* USE_REGEX */
821 item
= gtk_tool_button_new_from_stock(GTK_STOCK_SAVE
);
822 gtk_tool_item_set_is_important(item
, TRUE
);
823 #if GTK_CHECK_VERSION(2,12,0)
824 gtk_tool_item_set_tooltip_text(item
, _("Save"));
826 gtk_tool_item_set_tooltip(item
, tooltips
, _("Save"), NULL
);
828 g_signal_connect(G_OBJECT(item
), "clicked", G_CALLBACK(save_cb
), win
);
829 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
832 item
= gtk_tool_button_new_from_stock(GTK_STOCK_CLEAR
);
833 gtk_tool_item_set_is_important(item
, TRUE
);
834 #if GTK_CHECK_VERSION(2,12,0)
835 gtk_tool_item_set_tooltip_text(item
, _("Clear"));
837 gtk_tool_item_set_tooltip(item
, tooltips
, _("Clear"), NULL
);
839 g_signal_connect(G_OBJECT(item
), "clicked", G_CALLBACK(clear_cb
), win
);
840 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
842 item
= gtk_separator_tool_item_new();
843 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
846 item
= gtk_toggle_tool_button_new_from_stock(PIDGIN_STOCK_PAUSE
);
847 gtk_tool_item_set_is_important(item
, TRUE
);
848 #if GTK_CHECK_VERSION(2,12,0)
849 gtk_tool_item_set_tooltip_text(item
, _("Pause"));
851 gtk_tool_item_set_tooltip(item
, tooltips
, _("Pause"), NULL
);
853 g_signal_connect(G_OBJECT(item
), "clicked", G_CALLBACK(pause_cb
), win
);
854 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
858 item
= gtk_separator_tool_item_new();
859 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
861 /* regex toggle button */
862 item
= gtk_toggle_tool_button_new_from_stock(GTK_STOCK_FIND
);
863 gtk_tool_item_set_is_important(item
, TRUE
);
864 win
->filter
= GTK_WIDGET(item
);
865 gtk_tool_button_set_label(GTK_TOOL_BUTTON(win
->filter
), _("Filter"));
866 #if GTK_CHECK_VERSION(2,12,0)
867 gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(win
->filter
), _("Filter"));
869 gtk_tooltips_set_tip(tooltips
, win
->filter
, _("Filter"), NULL
);
871 g_signal_connect(G_OBJECT(win
->filter
), "clicked", G_CALLBACK(regex_filter_toggled_cb
), win
);
872 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(win
->filter
));
874 /* we purposely disable the toggle button here in case
875 * /purple/gtk/debug/expression has an empty string. If it does not have
876 * an empty string, the change signal will get called and make the
877 * toggle button sensitive.
879 gtk_widget_set_sensitive(win
->filter
, FALSE
);
880 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
),
881 purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/filter"));
882 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/filter",
883 regex_pref_filter_cb
, win
);
886 win
->expression
= gtk_entry_new();
887 item
= gtk_tool_item_new();
888 #if GTK_CHECK_VERSION(2,12,0)
889 gtk_widget_set_tooltip_text(win
->expression
, _("Right click for more options."));
891 gtk_tooltips_set_tip(tooltips
, win
->expression
, _("Right click for more options."), NULL
);
893 gtk_container_add(GTK_CONTAINER(item
), GTK_WIDGET(win
->expression
));
894 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
896 /* this needs to be before the text is set from the pref if we want it
897 * to colorize a stored expression.
899 g_signal_connect(G_OBJECT(win
->expression
), "changed",
900 G_CALLBACK(regex_changed_cb
), win
);
901 gtk_entry_set_text(GTK_ENTRY(win
->expression
),
902 purple_prefs_get_string(PIDGIN_PREFS_ROOT
"/debug/regex"));
903 g_signal_connect(G_OBJECT(win
->expression
), "populate-popup",
904 G_CALLBACK(regex_popup_cb
), win
);
905 g_signal_connect(G_OBJECT(win
->expression
), "key-release-event",
906 G_CALLBACK(regex_key_release_cb
), win
);
907 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/regex",
908 regex_pref_expression_cb
, win
);
910 /* connect the rest of our pref callbacks */
911 win
->invert
= purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/invert");
912 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/invert",
913 regex_pref_invert_cb
, win
);
915 win
->highlight
= purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/highlight");
916 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/highlight",
917 regex_pref_highlight_cb
, win
);
919 #endif /* USE_REGEX */
921 item
= gtk_separator_tool_item_new();
922 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
924 item
= gtk_tool_item_new();
925 gtk_container_add(GTK_CONTAINER(item
), gtk_label_new(_("Level ")));
926 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
928 win
->filterlevel
= gtk_combo_box_new_text();
929 item
= gtk_tool_item_new();
930 #if GTK_CHECK_VERSION(2,12,0)
931 gtk_widget_set_tooltip_text(win
->filterlevel
, _("Select the debug filter level."));
933 gtk_tooltips_set_tip(tooltips
, win
->filterlevel
, _("Select the debug filter level."), NULL
);
935 gtk_container_add(GTK_CONTAINER(item
), win
->filterlevel
);
936 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
938 gtk_combo_box_append_text(GTK_COMBO_BOX(win
->filterlevel
), _("All"));
939 gtk_combo_box_append_text(GTK_COMBO_BOX(win
->filterlevel
), _("Misc"));
940 gtk_combo_box_append_text(GTK_COMBO_BOX(win
->filterlevel
), _("Info"));
941 gtk_combo_box_append_text(GTK_COMBO_BOX(win
->filterlevel
), _("Warning"));
942 gtk_combo_box_append_text(GTK_COMBO_BOX(win
->filterlevel
), _("Error "));
943 gtk_combo_box_append_text(GTK_COMBO_BOX(win
->filterlevel
), _("Fatal Error"));
944 gtk_combo_box_set_active(GTK_COMBO_BOX(win
->filterlevel
),
945 purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel"));
947 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/filterlevel",
948 filter_level_pref_changed
, win
);
950 g_signal_connect(G_OBJECT(win
->filterlevel
), "changed",
951 G_CALLBACK(filter_level_changed_cb
), NULL
);
954 /* Add the gtkimhtml */
955 frame
= pidgin_create_imhtml(FALSE
, &win
->text
, NULL
, NULL
);
956 gtk_imhtml_set_format_functions(GTK_IMHTML(win
->text
),
957 GTK_IMHTML_ALL
^ GTK_IMHTML_SMILEY
^ GTK_IMHTML_IMAGE
);
958 gtk_box_pack_start(GTK_BOX(vbox
), frame
, TRUE
, TRUE
, 0);
959 gtk_widget_show(frame
);
962 /* add the tag for regex highlighting */
963 gtk_text_buffer_create_tag(GTK_IMHTML(win
->text
)->text_buffer
, "regex",
964 "background", "#FFAFAF",
967 #endif /* USE_REGEX */
969 gtk_widget_show_all(win
->window
);
975 debug_enabled_timeout_cb(gpointer data
)
977 debug_enabled_timer
= 0;
980 pidgin_debug_window_show();
982 pidgin_debug_window_hide();
988 debug_enabled_cb(const char *name
, PurplePrefType type
,
989 gconstpointer value
, gpointer data
)
991 debug_enabled_timer
= g_timeout_add(0, debug_enabled_timeout_cb
, GINT_TO_POINTER(GPOINTER_TO_INT(value
)));
995 pidgin_glib_log_handler(const gchar
*domain
, GLogLevelFlags flags
,
996 const gchar
*msg
, gpointer user_data
)
998 PurpleDebugLevel level
;
999 char *new_msg
= NULL
;
1000 char *new_domain
= NULL
;
1002 if ((flags
& G_LOG_LEVEL_ERROR
) == G_LOG_LEVEL_ERROR
)
1003 level
= PURPLE_DEBUG_ERROR
;
1004 else if ((flags
& G_LOG_LEVEL_CRITICAL
) == G_LOG_LEVEL_CRITICAL
)
1005 level
= PURPLE_DEBUG_FATAL
;
1006 else if ((flags
& G_LOG_LEVEL_WARNING
) == G_LOG_LEVEL_WARNING
)
1007 level
= PURPLE_DEBUG_WARNING
;
1008 else if ((flags
& G_LOG_LEVEL_MESSAGE
) == G_LOG_LEVEL_MESSAGE
)
1009 level
= PURPLE_DEBUG_INFO
;
1010 else if ((flags
& G_LOG_LEVEL_INFO
) == G_LOG_LEVEL_INFO
)
1011 level
= PURPLE_DEBUG_INFO
;
1012 else if ((flags
& G_LOG_LEVEL_DEBUG
) == G_LOG_LEVEL_DEBUG
)
1013 level
= PURPLE_DEBUG_MISC
;
1016 purple_debug_warning("gtkdebug",
1017 "Unknown glib logging level in %d\n", flags
);
1019 level
= PURPLE_DEBUG_MISC
; /* This will never happen. */
1023 new_msg
= purple_utf8_try_convert(msg
);
1026 new_domain
= purple_utf8_try_convert(domain
);
1028 if (new_msg
!= NULL
)
1030 purple_debug(level
, (new_domain
!= NULL
? new_domain
: "g_log"),
1041 pidgin_glib_dummy_print_handler(const gchar
*string
)
1047 pidgin_debug_init(void)
1049 /* Debug window preferences. */
1051 * NOTE: This must be set before prefs are loaded, and the callbacks
1052 * set after they are loaded, since prefs sets the enabled
1053 * preference here and that loads the window, which calls the
1054 * configure event, which overrides the width and height! :P
1057 purple_prefs_add_none(PIDGIN_PREFS_ROOT
"/debug");
1059 /* Controls printing to the debug window */
1060 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/enabled", FALSE
);
1061 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel", PURPLE_DEBUG_ALL
);
1062 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/debug/style", GTK_TOOLBAR_BOTH_HORIZ
);
1064 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/toolbar", TRUE
);
1065 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/debug/width", 450);
1066 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/debug/height", 250);
1069 purple_prefs_add_string(PIDGIN_PREFS_ROOT
"/debug/regex", "");
1070 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/filter", FALSE
);
1071 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/invert", FALSE
);
1072 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/case_insensitive", FALSE
);
1073 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/highlight", FALSE
);
1074 #endif /* USE_REGEX */
1076 purple_prefs_connect_callback(NULL
, PIDGIN_PREFS_ROOT
"/debug/enabled",
1077 debug_enabled_cb
, NULL
);
1079 #define REGISTER_G_LOG_HANDLER(name) \
1080 g_log_set_handler((name), G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL \
1081 | G_LOG_FLAG_RECURSION, \
1082 pidgin_glib_log_handler, NULL)
1084 /* Register the glib/gtk log handlers. */
1085 REGISTER_G_LOG_HANDLER(NULL
);
1086 REGISTER_G_LOG_HANDLER("Gdk");
1087 REGISTER_G_LOG_HANDLER("Gtk");
1088 REGISTER_G_LOG_HANDLER("GdkPixbuf");
1089 REGISTER_G_LOG_HANDLER("GLib");
1090 REGISTER_G_LOG_HANDLER("GModule");
1091 REGISTER_G_LOG_HANDLER("GLib-GObject");
1092 REGISTER_G_LOG_HANDLER("GThread");
1093 #ifdef USE_GSTREAMER
1094 REGISTER_G_LOG_HANDLER("GStreamer");
1098 if (!purple_debug_is_enabled())
1099 g_set_print_handler(pidgin_glib_dummy_print_handler
);
1104 pidgin_debug_uninit(void)
1106 purple_debug_set_ui_ops(NULL
);
1108 if (debug_enabled_timer
!= 0)
1109 g_source_remove(debug_enabled_timer
);
1113 pidgin_debug_window_show(void)
1115 if (debug_win
== NULL
)
1116 debug_win
= debug_window_new();
1118 gtk_widget_show(debug_win
->window
);
1120 purple_prefs_set_bool(PIDGIN_PREFS_ROOT
"/debug/enabled", TRUE
);
1124 pidgin_debug_window_hide(void)
1126 if (debug_win
!= NULL
) {
1127 gtk_widget_destroy(debug_win
->window
);
1128 debug_window_destroy(NULL
, NULL
, NULL
);
1133 pidgin_debug_print(PurpleDebugLevel level
, const char *category
,
1138 #endif /* USE_REGEX */
1140 gchar
*esc_s
, *cat_s
, *tmp
, *s
;
1144 if (debug_win
== NULL
||
1145 !purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/enabled"))
1151 mdate
= purple_utf8_strftime("%H:%M:%S", localtime(&mtime
));
1152 ts_s
= g_strdup_printf("(%s) ", mdate
);
1153 if (category
== NULL
)
1154 cat_s
= g_strdup("");
1156 cat_s
= g_strdup_printf("<b>%s:</b> ", category
);
1158 esc_s
= g_markup_escape_text(arg_s
, -1);
1160 s
= g_strdup_printf("<font color=\"%s\">%s%s%s</font>",
1161 debug_fg_colors
[level
], ts_s
, cat_s
, esc_s
);
1167 tmp
= purple_utf8_try_convert(s
);
1171 if (level
== PURPLE_DEBUG_FATAL
) {
1172 tmp
= g_strdup_printf("<b>%s</b>", s
);
1178 /* add the text to the list store */
1179 gtk_list_store_append(debug_win
->store
, &iter
);
1180 gtk_list_store_set(debug_win
->store
, &iter
, 0, s
, 1, level
, -1);
1181 #else /* USE_REGEX */
1182 if(!debug_win
->paused
&& level
>= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel"))
1183 gtk_imhtml_append_text(GTK_IMHTML(debug_win
->text
), s
, 0);
1184 #endif /* !USE_REGEX */
1190 pidgin_debug_is_enabled(PurpleDebugLevel level
, const char *category
)
1192 return (debug_win
!= NULL
&&
1193 purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/enabled"));
1196 static PurpleDebugUiOps ops
=
1199 pidgin_debug_is_enabled
,
1207 pidgin_debug_get_ui_ops(void)
1213 pidgin_debug_get_handle() {