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"
42 #endif /* HAVE_REGEX_H */
44 #include <gdk/gdkkeysyms.h>
57 GtkWidget
*expression
;
67 #endif /* HAVE_REGEX_H */
68 GtkWidget
*filterlevel
;
71 static const char debug_fg_colors
[][8] = {
72 "#000000", /**< All debug levels. */
73 "#666666", /**< Misc. */
74 "#000000", /**< Information. */
75 "#660000", /**< Warnings. */
76 "#FF0000", /**< Errors. */
77 "#FF0000", /**< Fatal errors. */
80 static DebugWindow
*debug_win
= NULL
;
81 static guint debug_enabled_timer
= 0;
84 static void regex_filter_all(DebugWindow
*win
);
85 static void regex_show_all(DebugWindow
*win
);
86 #endif /* HAVE_REGEX_H */
89 debug_window_destroy(GtkWidget
*w
, GdkEvent
*event
, void *unused
)
91 purple_prefs_disconnect_by_handle(pidgin_debug_get_handle());
94 if(debug_win
->timer
!= 0) {
97 purple_timeout_remove(debug_win
->timer
);
99 text
= gtk_entry_get_text(GTK_ENTRY(debug_win
->expression
));
100 purple_prefs_set_string(PIDGIN_PREFS_ROOT
"/debug/regex", text
);
103 regfree(&debug_win
->regex
);
106 /* If the "Save Log" dialog is open then close it */
107 purple_request_close_with_handle(debug_win
);
112 purple_prefs_set_bool(PIDGIN_PREFS_ROOT
"/debug/enabled", FALSE
);
118 configure_cb(GtkWidget
*w
, GdkEventConfigure
*event
, DebugWindow
*win
)
120 if (GTK_WIDGET_VISIBLE(w
)) {
121 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/debug/width", event
->width
);
122 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/debug/height", event
->height
);
135 do_find_cb(GtkWidget
*widget
, gint response
, struct _find
*f
)
138 case GTK_RESPONSE_OK
:
139 gtk_imhtml_search_find(GTK_IMHTML(f
->window
->text
),
140 gtk_entry_get_text(GTK_ENTRY(f
->entry
)));
143 case GTK_RESPONSE_DELETE_EVENT
:
144 case GTK_RESPONSE_CLOSE
:
145 gtk_imhtml_search_clear(GTK_IMHTML(f
->window
->text
));
146 gtk_widget_destroy(f
->window
->find
);
147 f
->window
->find
= NULL
;
154 find_cb(GtkWidget
*w
, DebugWindow
*win
)
156 GtkWidget
*hbox
, *img
, *label
;
161 gtk_window_present(GTK_WINDOW(win
->find
));
165 f
= g_malloc(sizeof(struct _find
));
167 win
->find
= gtk_dialog_new_with_buttons(_("Find"),
168 GTK_WINDOW(win
->window
), GTK_DIALOG_DESTROY_WITH_PARENT
,
169 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
,
170 GTK_STOCK_FIND
, GTK_RESPONSE_OK
, NULL
);
171 gtk_dialog_set_default_response(GTK_DIALOG(win
->find
),
173 g_signal_connect(G_OBJECT(win
->find
), "response",
174 G_CALLBACK(do_find_cb
), f
);
176 gtk_container_set_border_width(GTK_CONTAINER(win
->find
), PIDGIN_HIG_BOX_SPACE
);
177 gtk_window_set_resizable(GTK_WINDOW(win
->find
), FALSE
);
178 gtk_dialog_set_has_separator(GTK_DIALOG(win
->find
), FALSE
);
179 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(win
->find
)->vbox
), PIDGIN_HIG_BORDER
);
180 gtk_container_set_border_width(
181 GTK_CONTAINER(GTK_DIALOG(win
->find
)->vbox
), PIDGIN_HIG_BOX_SPACE
);
183 hbox
= gtk_hbox_new(FALSE
, PIDGIN_HIG_BORDER
);
184 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(win
->find
)->vbox
),
186 img
= gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION
,
187 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE
));
188 gtk_box_pack_start(GTK_BOX(hbox
), img
, FALSE
, FALSE
, 0);
190 gtk_misc_set_alignment(GTK_MISC(img
), 0, 0);
191 gtk_dialog_set_response_sensitive(GTK_DIALOG(win
->find
),
192 GTK_RESPONSE_OK
, FALSE
);
194 label
= gtk_label_new(NULL
);
195 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label
), _("_Search for:"));
196 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
198 f
->entry
= gtk_entry_new();
199 gtk_entry_set_activates_default(GTK_ENTRY(f
->entry
), TRUE
);
200 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), GTK_WIDGET(f
->entry
));
201 g_signal_connect(G_OBJECT(f
->entry
), "changed",
202 G_CALLBACK(pidgin_set_sensitive_if_input
),
204 gtk_box_pack_start(GTK_BOX(hbox
), f
->entry
, FALSE
, FALSE
, 0);
206 gtk_widget_show_all(win
->find
);
207 gtk_widget_grab_focus(f
->entry
);
209 #endif /* HAVE_REGEX_H */
212 save_writefile_cb(void *user_data
, const char *filename
)
214 DebugWindow
*win
= (DebugWindow
*)user_data
;
218 if ((fp
= g_fopen(filename
, "w+")) == NULL
) {
219 purple_notify_error(win
, NULL
, _("Unable to open file."), NULL
);
223 tmp
= gtk_imhtml_get_text(GTK_IMHTML(win
->text
), NULL
, NULL
);
224 fprintf(fp
, "Pidgin Debug Log : %s\n", purple_date_format_full(NULL
));
225 fprintf(fp
, "%s", tmp
);
232 save_cb(GtkWidget
*w
, DebugWindow
*win
)
234 purple_request_file(win
, _("Save Debug Log"), "purple-debug.log", TRUE
,
235 G_CALLBACK(save_writefile_cb
), NULL
,
241 clear_cb(GtkWidget
*w
, DebugWindow
*win
)
243 gtk_imhtml_clear(GTK_IMHTML(win
->text
));
246 gtk_list_store_clear(win
->store
);
247 #endif /* HAVE_REGEX_H */
251 pause_cb(GtkWidget
*w
, DebugWindow
*win
)
253 win
->paused
= gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(w
));
257 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
258 regex_filter_all(win
);
262 #endif /* HAVE_REGEX_H */
265 /******************************************************************************
267 *****************************************************************************/
270 regex_clear_color(GtkWidget
*w
) {
271 gtk_widget_modify_base(w
, GTK_STATE_NORMAL
, NULL
);
275 regex_change_color(GtkWidget
*w
, guint16 r
, guint16 g
, guint16 b
) {
282 gtk_widget_modify_base(w
, GTK_STATE_NORMAL
, &color
);
286 regex_highlight_clear(DebugWindow
*win
) {
287 GtkIMHtml
*imhtml
= GTK_IMHTML(win
->text
);
290 gtk_text_buffer_get_start_iter(imhtml
->text_buffer
, &s
);
291 gtk_text_buffer_get_end_iter(imhtml
->text_buffer
, &e
);
292 gtk_text_buffer_remove_tag_by_name(imhtml
->text_buffer
, "regex", &s
, &e
);
296 regex_match(DebugWindow
*win
, const gchar
*text
) {
297 GtkIMHtml
*imhtml
= GTK_IMHTML(win
->text
);
298 regmatch_t matches
[4]; /* adjust if necessary */
299 size_t n_matches
= sizeof(matches
) / sizeof(matches
[0]);
306 inverted
= (win
->invert
) ? REG_NOMATCH
: 0;
308 /* I don't like having to do this, but we need it for highlighting. Plus
309 * it makes the ^ and $ operators work :)
311 plaintext
= purple_markup_strip_html(text
);
313 /* we do a first pass to see if it matches at all. If it does we append
314 * it, and work out the offsets to highlight.
316 if(regexec(&win
->regex
, plaintext
, n_matches
, matches
, 0) == inverted
) {
318 gchar
*p
= plaintext
;
321 gtk_text_buffer_get_iter_at_mark(imhtml
->text_buffer
, &ins
,
322 gtk_text_buffer_get_insert(imhtml
->text_buffer
));
323 i
= gtk_text_iter_get_offset(&ins
);
325 gtk_imhtml_append_text(imhtml
, text
, 0);
327 /* If we're not highlighting or the expression is inverted, we're
330 if(!win
->highlight
|| inverted
== REG_NOMATCH
) {
335 /* we use a do-while to highlight the first match, and then continue
341 for(m
= 0; m
< n_matches
; m
++) {
344 if(matches
[m
].rm_eo
== -1)
349 gtk_text_buffer_get_iter_at_offset(imhtml
->text_buffer
, &ms
,
350 i
+ matches
[m
].rm_so
);
351 gtk_text_buffer_get_iter_at_offset(imhtml
->text_buffer
, &me
,
352 i
+ matches
[m
].rm_eo
);
353 gtk_text_buffer_apply_tag_by_name(imhtml
->text_buffer
, "regex",
355 offset
= matches
[m
].rm_eo
;
359 } while(regexec(&win
->regex
, p
, n_matches
, matches
, REG_NOTBOL
) == inverted
);
366 regex_filter_all_cb(GtkTreeModel
*m
, GtkTreePath
*p
, GtkTreeIter
*iter
,
369 DebugWindow
*win
= (DebugWindow
*)data
;
371 PurpleDebugLevel level
;
373 gtk_tree_model_get(m
, iter
, 0, &text
, 1, &level
, -1);
375 if (level
>= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel"))
376 regex_match(win
, text
);
384 regex_filter_all(DebugWindow
*win
) {
385 gtk_imhtml_clear(GTK_IMHTML(win
->text
));
388 regex_highlight_clear(win
);
390 gtk_tree_model_foreach(GTK_TREE_MODEL(win
->store
), regex_filter_all_cb
,
395 regex_show_all_cb(GtkTreeModel
*m
, GtkTreePath
*p
, GtkTreeIter
*iter
,
398 DebugWindow
*win
= (DebugWindow
*)data
;
400 PurpleDebugLevel level
;
402 gtk_tree_model_get(m
, iter
, 0, &text
, 1, &level
, -1);
403 if (level
>= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel"))
404 gtk_imhtml_append_text(GTK_IMHTML(win
->text
), text
, 0);
411 regex_show_all(DebugWindow
*win
) {
412 gtk_imhtml_clear(GTK_IMHTML(win
->text
));
415 regex_highlight_clear(win
);
417 gtk_tree_model_foreach(GTK_TREE_MODEL(win
->store
), regex_show_all_cb
,
422 regex_compile(DebugWindow
*win
) {
425 text
= gtk_entry_get_text(GTK_ENTRY(win
->expression
));
427 if(text
== NULL
|| *text
== '\0') {
428 regex_clear_color(win
->expression
);
429 gtk_widget_set_sensitive(win
->filter
, FALSE
);
433 regfree(&win
->regex
);
435 if(regcomp(&win
->regex
, text
, REG_EXTENDED
| REG_ICASE
) != 0) {
436 /* failed to compile */
437 regex_change_color(win
->expression
, 0xFFFF, 0xAFFF, 0xAFFF);
438 gtk_widget_set_sensitive(win
->filter
, FALSE
);
440 /* compiled successfully */
441 regex_change_color(win
->expression
, 0xAFFF, 0xFFFF, 0xAFFF);
442 gtk_widget_set_sensitive(win
->filter
, TRUE
);
445 /* we check if the filter is on in case it was only of the options that
446 * got changed, and not the expression.
448 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
449 regex_filter_all(win
);
453 regex_pref_filter_cb(const gchar
*name
, PurplePrefType type
,
454 gconstpointer val
, gpointer data
)
456 DebugWindow
*win
= (DebugWindow
*)data
;
457 gboolean active
= GPOINTER_TO_INT(val
), current
;
459 if(!win
|| !win
->window
)
462 current
= gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
));
463 if(active
!= current
)
464 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
), active
);
468 regex_pref_expression_cb(const gchar
*name
, PurplePrefType type
,
469 gconstpointer val
, gpointer data
)
471 DebugWindow
*win
= (DebugWindow
*)data
;
472 const gchar
*exp
= (const gchar
*)val
;
474 gtk_entry_set_text(GTK_ENTRY(win
->expression
), exp
);
478 regex_pref_invert_cb(const gchar
*name
, PurplePrefType type
,
479 gconstpointer val
, gpointer data
)
481 DebugWindow
*win
= (DebugWindow
*)data
;
482 gboolean active
= GPOINTER_TO_INT(val
);
484 win
->invert
= active
;
486 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
487 regex_filter_all(win
);
491 regex_pref_highlight_cb(const gchar
*name
, PurplePrefType type
,
492 gconstpointer val
, gpointer data
)
494 DebugWindow
*win
= (DebugWindow
*)data
;
495 gboolean active
= GPOINTER_TO_INT(val
);
497 win
->highlight
= active
;
499 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
500 regex_filter_all(win
);
504 regex_row_changed_cb(GtkTreeModel
*model
, GtkTreePath
*path
,
505 GtkTreeIter
*iter
, DebugWindow
*win
)
508 PurpleDebugLevel level
;
510 if(!win
|| !win
->window
)
513 /* If the debug window is paused, we just return since it's in the store.
514 * We don't call regex_match because it doesn't make sense to check the
515 * string if it's paused. When we unpause we clear the imhtml and
516 * reiterate over the store to handle matches that were outputted when
522 gtk_tree_model_get(model
, iter
, 0, &text
, 1, &level
, -1);
524 if (level
>= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel")) {
525 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
))) {
526 regex_match(win
, text
);
528 gtk_imhtml_append_text(GTK_IMHTML(win
->text
), text
, 0);
536 regex_timer_cb(DebugWindow
*win
) {
539 text
= gtk_entry_get_text(GTK_ENTRY(win
->expression
));
540 purple_prefs_set_string(PIDGIN_PREFS_ROOT
"/debug/regex", text
);
548 regex_changed_cb(GtkWidget
*w
, DebugWindow
*win
) {
549 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
))) {
550 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
),
555 win
->timer
= purple_timeout_add_seconds(5, (GSourceFunc
)regex_timer_cb
, win
);
561 regex_key_release_cb(GtkWidget
*w
, GdkEventKey
*e
, DebugWindow
*win
) {
562 if(e
->keyval
== GDK_Return
&&
563 GTK_WIDGET_IS_SENSITIVE(win
->filter
) &&
564 !gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
566 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
), TRUE
);
571 regex_menu_cb(GtkWidget
*item
, const gchar
*pref
) {
574 active
= gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item
));
576 purple_prefs_set_bool(pref
, active
);
580 regex_popup_cb(GtkEntry
*entry
, GtkWidget
*menu
, DebugWindow
*win
) {
581 pidgin_separator(menu
);
582 pidgin_new_check_item(menu
, _("Invert"),
583 G_CALLBACK(regex_menu_cb
),
584 PIDGIN_PREFS_ROOT
"/debug/invert", win
->invert
);
585 pidgin_new_check_item(menu
, _("Highlight matches"),
586 G_CALLBACK(regex_menu_cb
),
587 PIDGIN_PREFS_ROOT
"/debug/highlight", win
->highlight
);
591 regex_filter_toggled_cb(GtkToggleToolButton
*button
, DebugWindow
*win
) {
594 active
= gtk_toggle_tool_button_get_active(button
);
596 purple_prefs_set_bool(PIDGIN_PREFS_ROOT
"/debug/filter", active
);
598 if(!GTK_IS_IMHTML(win
->text
))
602 regex_filter_all(win
);
608 filter_level_pref_changed(const char *name
, PurplePrefType type
, gconstpointer value
, gpointer data
)
610 DebugWindow
*win
= data
;
612 if (GPOINTER_TO_INT(value
) != gtk_combo_box_get_active(GTK_COMBO_BOX(win
->filterlevel
)))
613 gtk_combo_box_set_active(GTK_COMBO_BOX(win
->filterlevel
), GPOINTER_TO_INT(value
));
614 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
)))
615 regex_filter_all(win
);
619 #endif /* HAVE_REGEX_H */
622 filter_level_changed_cb(GtkWidget
*combo
, gpointer null
)
624 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel",
625 gtk_combo_box_get_active(GTK_COMBO_BOX(combo
)));
629 toolbar_style_pref_changed_cb(const char *name
, PurplePrefType type
, gconstpointer value
, gpointer data
)
631 gtk_toolbar_set_style(GTK_TOOLBAR(data
), GPOINTER_TO_INT(value
));
635 toolbar_icon_pref_changed(GtkWidget
*item
, GtkWidget
*toolbar
)
637 int style
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item
), "user_data"));
638 purple_prefs_set_int(PIDGIN_PREFS_ROOT
"/debug/style", style
);
642 toolbar_context(GtkWidget
*toolbar
, GdkEventButton
*event
, gpointer null
)
644 GtkWidget
*menu
, *item
;
646 GtkToolbarStyle value
[3];
649 if (!(event
->button
== 3 && event
->type
== GDK_BUTTON_PRESS
))
652 text
[0] = _("_Icon Only"); value
[0] = GTK_TOOLBAR_ICONS
;
653 text
[1] = _("_Text Only"); value
[1] = GTK_TOOLBAR_TEXT
;
654 text
[2] = _("_Both Icon & Text"); value
[2] = GTK_TOOLBAR_BOTH_HORIZ
;
656 menu
= gtk_menu_new();
658 for (i
= 0; i
< 3; i
++) {
659 item
= gtk_check_menu_item_new_with_mnemonic(text
[i
]);
660 g_object_set_data(G_OBJECT(item
), "user_data", GINT_TO_POINTER(value
[i
]));
661 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(toolbar_icon_pref_changed
), toolbar
);
662 if (value
[i
] == purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/style"))
663 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
664 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
667 gtk_widget_show_all(menu
);
669 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
, 0, gtk_get_current_event_time());
674 debug_window_new(void)
683 #if !GTK_CHECK_VERSION(2,12,0)
684 GtkTooltips
*tooltips
;
687 win
= g_new0(DebugWindow
, 1);
689 width
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/width");
690 height
= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/height");
692 win
->window
= pidgin_create_window(_("Debug Window"), 0, "debug", TRUE
);
693 purple_debug_info("gtkdebug", "Setting dimensions to %d, %d\n",
696 gtk_window_set_default_size(GTK_WINDOW(win
->window
), width
, height
);
698 g_signal_connect(G_OBJECT(win
->window
), "delete_event",
699 G_CALLBACK(debug_window_destroy
), NULL
);
700 g_signal_connect(G_OBJECT(win
->window
), "configure_event",
701 G_CALLBACK(configure_cb
), win
);
703 handle
= pidgin_debug_get_handle();
706 /* the list store for all the messages */
707 win
->store
= gtk_list_store_new(2, G_TYPE_STRING
, G_TYPE_INT
);
709 /* row-changed gets called when we do gtk_list_store_set, and row-inserted
710 * gets called with gtk_list_store_append, which is a
711 * completely empty row. So we just ignore row-inserted, and deal with row
714 g_signal_connect(G_OBJECT(win
->store
), "row-changed",
715 G_CALLBACK(regex_row_changed_cb
), win
);
717 #endif /* HAVE_REGEX_H */
720 vbox
= gtk_vbox_new(FALSE
, 0);
721 gtk_container_add(GTK_CONTAINER(win
->window
), vbox
);
723 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/toolbar")) {
724 /* Setup our top button bar thingie. */
725 toolbar
= gtk_toolbar_new();
726 #if !GTK_CHECK_VERSION(2,12,0)
727 tooltips
= gtk_tooltips_new();
729 #if !GTK_CHECK_VERSION(2,14,0)
730 gtk_toolbar_set_tooltips(GTK_TOOLBAR(toolbar
), TRUE
);
732 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolbar
), TRUE
);
733 g_signal_connect(G_OBJECT(toolbar
), "button-press-event", G_CALLBACK(toolbar_context
), win
);
735 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar
),
736 purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/style"));
737 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/style",
738 toolbar_style_pref_changed_cb
, toolbar
);
739 gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar
),
740 GTK_ICON_SIZE_SMALL_TOOLBAR
);
742 gtk_box_pack_start(GTK_BOX(vbox
), toolbar
, FALSE
, FALSE
, 0);
746 item
= gtk_tool_button_new_from_stock(GTK_STOCK_FIND
);
747 gtk_tool_item_set_is_important(item
, TRUE
);
748 #if GTK_CHECK_VERSION(2,12,0)
749 gtk_tool_item_set_tooltip_text(item
, _("Find"));
751 gtk_tool_item_set_tooltip(item
, tooltips
, _("Find"), NULL
);
753 g_signal_connect(G_OBJECT(item
), "clicked", G_CALLBACK(find_cb
), win
);
754 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
755 #endif /* HAVE_REGEX_H */
758 item
= gtk_tool_button_new_from_stock(GTK_STOCK_SAVE
);
759 gtk_tool_item_set_is_important(item
, TRUE
);
760 #if GTK_CHECK_VERSION(2,12,0)
761 gtk_tool_item_set_tooltip_text(item
, _("Save"));
763 gtk_tool_item_set_tooltip(item
, tooltips
, _("Save"), NULL
);
765 g_signal_connect(G_OBJECT(item
), "clicked", G_CALLBACK(save_cb
), win
);
766 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
769 item
= gtk_tool_button_new_from_stock(GTK_STOCK_CLEAR
);
770 gtk_tool_item_set_is_important(item
, TRUE
);
771 #if GTK_CHECK_VERSION(2,12,0)
772 gtk_tool_item_set_tooltip_text(item
, _("Clear"));
774 gtk_tool_item_set_tooltip(item
, tooltips
, _("Clear"), NULL
);
776 g_signal_connect(G_OBJECT(item
), "clicked", G_CALLBACK(clear_cb
), win
);
777 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
779 item
= gtk_separator_tool_item_new();
780 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
783 item
= gtk_toggle_tool_button_new_from_stock(PIDGIN_STOCK_PAUSE
);
784 gtk_tool_item_set_is_important(item
, TRUE
);
785 #if GTK_CHECK_VERSION(2,12,0)
786 gtk_tool_item_set_tooltip_text(item
, _("Pause"));
788 gtk_tool_item_set_tooltip(item
, tooltips
, _("Pause"), NULL
);
790 g_signal_connect(G_OBJECT(item
), "clicked", G_CALLBACK(pause_cb
), win
);
791 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
795 item
= gtk_separator_tool_item_new();
796 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
798 /* regex toggle button */
799 item
= gtk_toggle_tool_button_new_from_stock(GTK_STOCK_FIND
);
800 gtk_tool_item_set_is_important(item
, TRUE
);
801 win
->filter
= GTK_WIDGET(item
);
802 gtk_tool_button_set_label(GTK_TOOL_BUTTON(win
->filter
), _("Filter"));
803 #if GTK_CHECK_VERSION(2,12,0)
804 gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(win
->filter
), _("Filter"));
806 gtk_tooltips_set_tip(tooltips
, win
->filter
, _("Filter"), NULL
);
808 g_signal_connect(G_OBJECT(win
->filter
), "clicked", G_CALLBACK(regex_filter_toggled_cb
), win
);
809 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(win
->filter
));
811 /* we purposely disable the toggle button here in case
812 * /purple/gtk/debug/expression has an empty string. If it does not have
813 * an empty string, the change signal will get called and make the
814 * toggle button sensitive.
816 gtk_widget_set_sensitive(win
->filter
, FALSE
);
817 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win
->filter
),
818 purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/filter"));
819 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/filter",
820 regex_pref_filter_cb
, win
);
823 win
->expression
= gtk_entry_new();
824 item
= gtk_tool_item_new();
825 #if GTK_CHECK_VERSION(2,12,0)
826 gtk_widget_set_tooltip_text(win
->expression
, _("Right click for more options."));
828 gtk_tooltips_set_tip(tooltips
, win
->expression
, _("Right click for more options."), NULL
);
830 gtk_container_add(GTK_CONTAINER(item
), GTK_WIDGET(win
->expression
));
831 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
833 /* this needs to be before the text is set from the pref if we want it
834 * to colorize a stored expression.
836 g_signal_connect(G_OBJECT(win
->expression
), "changed",
837 G_CALLBACK(regex_changed_cb
), win
);
838 gtk_entry_set_text(GTK_ENTRY(win
->expression
),
839 purple_prefs_get_string(PIDGIN_PREFS_ROOT
"/debug/regex"));
840 g_signal_connect(G_OBJECT(win
->expression
), "populate-popup",
841 G_CALLBACK(regex_popup_cb
), win
);
842 g_signal_connect(G_OBJECT(win
->expression
), "key-release-event",
843 G_CALLBACK(regex_key_release_cb
), win
);
844 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/regex",
845 regex_pref_expression_cb
, win
);
847 /* connect the rest of our pref callbacks */
848 win
->invert
= purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/invert");
849 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/invert",
850 regex_pref_invert_cb
, win
);
852 win
->highlight
= purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/highlight");
853 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/highlight",
854 regex_pref_highlight_cb
, win
);
856 #endif /* HAVE_REGEX_H */
858 item
= gtk_separator_tool_item_new();
859 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
861 item
= gtk_tool_item_new();
862 gtk_container_add(GTK_CONTAINER(item
), gtk_label_new(_("Level ")));
863 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
865 win
->filterlevel
= gtk_combo_box_new_text();
866 item
= gtk_tool_item_new();
867 #if GTK_CHECK_VERSION(2,12,0)
868 gtk_widget_set_tooltip_text(win
->filterlevel
, _("Select the debug filter level."));
870 gtk_tooltips_set_tip(tooltips
, win
->filterlevel
, _("Select the debug filter level."), NULL
);
872 gtk_container_add(GTK_CONTAINER(item
), win
->filterlevel
);
873 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(item
));
875 gtk_combo_box_append_text(GTK_COMBO_BOX(win
->filterlevel
), _("All"));
876 gtk_combo_box_append_text(GTK_COMBO_BOX(win
->filterlevel
), _("Misc"));
877 gtk_combo_box_append_text(GTK_COMBO_BOX(win
->filterlevel
), _("Info"));
878 gtk_combo_box_append_text(GTK_COMBO_BOX(win
->filterlevel
), _("Warning"));
879 gtk_combo_box_append_text(GTK_COMBO_BOX(win
->filterlevel
), _("Error "));
880 gtk_combo_box_append_text(GTK_COMBO_BOX(win
->filterlevel
), _("Fatal Error"));
881 gtk_combo_box_set_active(GTK_COMBO_BOX(win
->filterlevel
),
882 purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel"));
884 purple_prefs_connect_callback(handle
, PIDGIN_PREFS_ROOT
"/debug/filterlevel",
885 filter_level_pref_changed
, win
);
887 g_signal_connect(G_OBJECT(win
->filterlevel
), "changed",
888 G_CALLBACK(filter_level_changed_cb
), NULL
);
891 /* Add the gtkimhtml */
892 frame
= pidgin_create_imhtml(FALSE
, &win
->text
, NULL
, NULL
);
893 gtk_imhtml_set_format_functions(GTK_IMHTML(win
->text
),
894 GTK_IMHTML_ALL
^ GTK_IMHTML_SMILEY
^ GTK_IMHTML_IMAGE
);
895 gtk_box_pack_start(GTK_BOX(vbox
), frame
, TRUE
, TRUE
, 0);
896 gtk_widget_show(frame
);
899 /* add the tag for regex highlighting */
900 gtk_text_buffer_create_tag(GTK_IMHTML(win
->text
)->text_buffer
, "regex",
901 "background", "#FFAFAF",
904 #endif /* HAVE_REGEX_H */
906 gtk_widget_show_all(win
->window
);
912 debug_enabled_timeout_cb(gpointer data
)
914 debug_enabled_timer
= 0;
917 pidgin_debug_window_show();
919 pidgin_debug_window_hide();
925 debug_enabled_cb(const char *name
, PurplePrefType type
,
926 gconstpointer value
, gpointer data
)
928 debug_enabled_timer
= g_timeout_add(0, debug_enabled_timeout_cb
, GINT_TO_POINTER(GPOINTER_TO_INT(value
)));
932 pidgin_glib_log_handler(const gchar
*domain
, GLogLevelFlags flags
,
933 const gchar
*msg
, gpointer user_data
)
935 PurpleDebugLevel level
;
936 char *new_msg
= NULL
;
937 char *new_domain
= NULL
;
939 if ((flags
& G_LOG_LEVEL_ERROR
) == G_LOG_LEVEL_ERROR
)
940 level
= PURPLE_DEBUG_ERROR
;
941 else if ((flags
& G_LOG_LEVEL_CRITICAL
) == G_LOG_LEVEL_CRITICAL
)
942 level
= PURPLE_DEBUG_FATAL
;
943 else if ((flags
& G_LOG_LEVEL_WARNING
) == G_LOG_LEVEL_WARNING
)
944 level
= PURPLE_DEBUG_WARNING
;
945 else if ((flags
& G_LOG_LEVEL_MESSAGE
) == G_LOG_LEVEL_MESSAGE
)
946 level
= PURPLE_DEBUG_INFO
;
947 else if ((flags
& G_LOG_LEVEL_INFO
) == G_LOG_LEVEL_INFO
)
948 level
= PURPLE_DEBUG_INFO
;
949 else if ((flags
& G_LOG_LEVEL_DEBUG
) == G_LOG_LEVEL_DEBUG
)
950 level
= PURPLE_DEBUG_MISC
;
953 purple_debug_warning("gtkdebug",
954 "Unknown glib logging level in %d\n", flags
);
956 level
= PURPLE_DEBUG_MISC
; /* This will never happen. */
960 new_msg
= purple_utf8_try_convert(msg
);
963 new_domain
= purple_utf8_try_convert(domain
);
967 purple_debug(level
, (new_domain
!= NULL
? new_domain
: "g_log"),
978 pidgin_glib_dummy_print_handler(const gchar
*string
)
984 pidgin_debug_init(void)
986 /* Debug window preferences. */
988 * NOTE: This must be set before prefs are loaded, and the callbacks
989 * set after they are loaded, since prefs sets the enabled
990 * preference here and that loads the window, which calls the
991 * configure event, which overrides the width and height! :P
994 purple_prefs_add_none(PIDGIN_PREFS_ROOT
"/debug");
996 /* Controls printing to the debug window */
997 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/enabled", FALSE
);
998 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel", PURPLE_DEBUG_ALL
);
999 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/debug/style", GTK_TOOLBAR_BOTH_HORIZ
);
1001 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/toolbar", TRUE
);
1002 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/debug/width", 450);
1003 purple_prefs_add_int(PIDGIN_PREFS_ROOT
"/debug/height", 250);
1006 purple_prefs_add_string(PIDGIN_PREFS_ROOT
"/debug/regex", "");
1007 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/filter", FALSE
);
1008 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/invert", FALSE
);
1009 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/case_insensitive", FALSE
);
1010 purple_prefs_add_bool(PIDGIN_PREFS_ROOT
"/debug/highlight", FALSE
);
1011 #endif /* HAVE_REGEX_H */
1013 purple_prefs_connect_callback(NULL
, PIDGIN_PREFS_ROOT
"/debug/enabled",
1014 debug_enabled_cb
, NULL
);
1016 #define REGISTER_G_LOG_HANDLER(name) \
1017 g_log_set_handler((name), G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL \
1018 | G_LOG_FLAG_RECURSION, \
1019 pidgin_glib_log_handler, NULL)
1021 /* Register the glib/gtk log handlers. */
1022 REGISTER_G_LOG_HANDLER(NULL
);
1023 REGISTER_G_LOG_HANDLER("Gdk");
1024 REGISTER_G_LOG_HANDLER("Gtk");
1025 REGISTER_G_LOG_HANDLER("GdkPixbuf");
1026 REGISTER_G_LOG_HANDLER("GLib");
1027 REGISTER_G_LOG_HANDLER("GModule");
1028 REGISTER_G_LOG_HANDLER("GLib-GObject");
1029 REGISTER_G_LOG_HANDLER("GThread");
1030 #ifdef USE_GSTREAMER
1031 REGISTER_G_LOG_HANDLER("GStreamer");
1035 if (!purple_debug_is_enabled())
1036 g_set_print_handler(pidgin_glib_dummy_print_handler
);
1041 pidgin_debug_uninit(void)
1043 purple_debug_set_ui_ops(NULL
);
1045 if (debug_enabled_timer
!= 0)
1046 g_source_remove(debug_enabled_timer
);
1050 pidgin_debug_window_show(void)
1052 if (debug_win
== NULL
)
1053 debug_win
= debug_window_new();
1055 gtk_widget_show(debug_win
->window
);
1057 purple_prefs_set_bool(PIDGIN_PREFS_ROOT
"/debug/enabled", TRUE
);
1061 pidgin_debug_window_hide(void)
1063 if (debug_win
!= NULL
) {
1064 gtk_widget_destroy(debug_win
->window
);
1065 debug_window_destroy(NULL
, NULL
, NULL
);
1070 pidgin_debug_print(PurpleDebugLevel level
, const char *category
,
1075 #endif /* HAVE_REGEX_H */
1077 gchar
*esc_s
, *cat_s
, *tmp
, *s
;
1081 if (debug_win
== NULL
||
1082 !purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/enabled"))
1088 mdate
= purple_utf8_strftime("%H:%M:%S", localtime(&mtime
));
1089 ts_s
= g_strdup_printf("(%s) ", mdate
);
1090 if (category
== NULL
)
1091 cat_s
= g_strdup("");
1093 cat_s
= g_strdup_printf("<b>%s:</b> ", category
);
1095 esc_s
= g_markup_escape_text(arg_s
, -1);
1097 s
= g_strdup_printf("<font color=\"%s\">%s%s%s</font>",
1098 debug_fg_colors
[level
], ts_s
, cat_s
, esc_s
);
1104 tmp
= purple_utf8_try_convert(s
);
1108 if (level
== PURPLE_DEBUG_FATAL
) {
1109 tmp
= g_strdup_printf("<b>%s</b>", s
);
1115 /* add the text to the list store */
1116 gtk_list_store_append(debug_win
->store
, &iter
);
1117 gtk_list_store_set(debug_win
->store
, &iter
, 0, s
, 1, level
, -1);
1118 #else /* HAVE_REGEX_H */
1119 if(!debug_win
->paused
&& level
>= purple_prefs_get_int(PIDGIN_PREFS_ROOT
"/debug/filterlevel"))
1120 gtk_imhtml_append_text(GTK_IMHTML(debug_win
->text
), s
, 0);
1121 #endif /* !HAVE_REGEX_H */
1127 pidgin_debug_is_enabled(PurpleDebugLevel level
, const char *category
)
1129 return (debug_win
!= NULL
&&
1130 purple_prefs_get_bool(PIDGIN_PREFS_ROOT
"/debug/enabled"));
1133 static PurpleDebugUiOps ops
=
1136 pidgin_debug_is_enabled
,
1144 pidgin_debug_get_ui_ops(void)
1150 pidgin_debug_get_handle() {