Merged in qulogic/pidgin (pull request #378)
Convert debug window to GtkTextView and Glade
Approved-by: Gary Kramlich
--- a/pidgin/gtkblist.c Wed Jun 13 03:18:40 2018 +0000
+++ b/pidgin/gtkblist.c Fri Jun 15 02:41:45 2018 +0000
@@ -42,7 +42,6 @@
#include "gtkcellrendererexpander.h"
@@ -61,6 +60,7 @@
#include "pidgin/minidialog.h"
#include "pidgin/pidginabout.h"
+#include "pidgin/pidgindebug.h" #include "pidgin/pidgintooltip.h"
#include <gdk/gdkkeysyms.h>
--- a/pidgin/gtkdebug.c Wed Jun 13 03:18:40 2018 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,908 +0,0 @@
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
-#include "pidginstock.h"
-#include <gdk/gdkkeysyms.h>
-#include "pidginresources.h"
- GtkWidget *filterlevel;
-static DebugWindow *debug_win = NULL;
- /* Other members, including private data. */
- guint debug_enabled_timer;
-static void pidgin_debug_ui_finalize(GObject *gobject);
-static void pidgin_debug_ui_interface_init(PurpleDebugUiInterface *iface);
-G_DEFINE_TYPE_WITH_CODE(PidginDebugUi, pidgin_debug_ui, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE(PURPLE_TYPE_DEBUG_UI,
- pidgin_debug_ui_interface_init));
-debug_window_destroy(GtkWidget *w, GdkEvent *event, void *unused)
- purple_prefs_disconnect_by_handle(pidgin_debug_get_handle());
- if(debug_win->timer != 0) {
- g_source_remove(debug_win->timer);
- text = gtk_entry_get_text(GTK_ENTRY(debug_win->expression));
- purple_prefs_set_string(PIDGIN_PREFS_ROOT "/debug/regex", text);
- if (debug_win->regex != NULL)
- g_regex_unref(debug_win->regex);
- /* If the "Save Log" dialog is open then close it */
- purple_request_close_with_handle(debug_win);
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/enabled", FALSE);
-configure_cb(GtkWidget *w, GdkEventConfigure *event, DebugWindow *win)
- if (gtk_widget_get_visible(w)) {
- purple_prefs_set_int(PIDGIN_PREFS_ROOT "/debug/width", event->width);
- purple_prefs_set_int(PIDGIN_PREFS_ROOT "/debug/height", event->height);
-save_writefile_cb(void *user_data, const char *filename)
- DebugWindow *win = (DebugWindow *)user_data;
- if ((fp = g_fopen(filename, "w+")) == NULL) {
- purple_notify_error(win, NULL, _("Unable to open file."), NULL, NULL);
- tmp = pidgin_webview_get_body_text(PIDGIN_WEBVIEW(win->text));
- fprintf(fp, "Pidgin Debug Log : %s\n", purple_date_format_full(NULL));
- fprintf(fp, "%s", tmp);
-save_cb(GtkWidget *w, DebugWindow *win)
- purple_request_file(win, _("Save Debug Log"), "purple-debug.log", TRUE,
- G_CALLBACK(save_writefile_cb), NULL, NULL, win);
-clear_cb(GtkWidget *w, DebugWindow *win)
- pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(win->text), "clear();");
-pause_cb(GtkWidget *w, DebugWindow *win)
- win->paused = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(w));
- pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(win->text), "pauseOutput();");
- pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(win->text), "resumeOutput();");
-/******************************************************************************
- *****************************************************************************/
-regex_clear_color(GtkWidget *w) {
- GtkStyleContext *context = gtk_widget_get_style_context(w);
- gtk_style_context_remove_class(context, "good-filter");
- gtk_style_context_remove_class(context, "bad-filter");
-regex_change_color(GtkWidget *w, gboolean success) {
- GtkStyleContext *context = gtk_widget_get_style_context(w);
- gtk_style_context_add_class(context, "good-filter");
- gtk_style_context_remove_class(context, "bad-filter");
- gtk_style_context_add_class(context, "bad-filter");
- gtk_style_context_remove_class(context, "good-filter");
-regex_toggle_filter(DebugWindow *win, gboolean filter)
- pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(win->text), "regex.clear();");
- text = gtk_entry_get_text(GTK_ENTRY(win->expression));
- regex = pidgin_webview_quote_js_string(text);
- script = g_strdup_printf("regex.filterAll(%s, %s, %s);",
- win->invert ? "true" : "false",
- win->highlight ? "true" : "false");
- pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(win->text), script);
-regex_pref_filter_cb(const gchar *name, PurplePrefType type,
- gconstpointer val, gpointer data)
- DebugWindow *win = (DebugWindow *)data;
- gboolean active = GPOINTER_TO_INT(val), current;
- if (!win || !win->window)
- current = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter));
- gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), active);
-regex_pref_expression_cb(const gchar *name, PurplePrefType type,
- gconstpointer val, gpointer data)
- DebugWindow *win = (DebugWindow *)data;
- const gchar *exp = (const gchar *)val;
- gtk_entry_set_text(GTK_ENTRY(win->expression), exp);
-regex_pref_invert_cb(const gchar *name, PurplePrefType type,
- gconstpointer val, gpointer data)
- DebugWindow *win = (DebugWindow *)data;
- gboolean active = GPOINTER_TO_INT(val);
- if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
- regex_toggle_filter(win, TRUE);
-regex_pref_highlight_cb(const gchar *name, PurplePrefType type,
- gconstpointer val, gpointer data)
- DebugWindow *win = (DebugWindow *)data;
- gboolean active = GPOINTER_TO_INT(val);
- win->highlight = active;
- if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
- regex_toggle_filter(win, TRUE);
-regex_timer_cb(DebugWindow *win) {
- text = gtk_entry_get_text(GTK_ENTRY(win->expression));
- purple_prefs_set_string(PIDGIN_PREFS_ROOT "/debug/regex", text);
-regex_changed_cb(GtkWidget *w, DebugWindow *win) {
- if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) {
- gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter),
- win->timer = g_timeout_add_seconds(5, (GSourceFunc)regex_timer_cb, win);
- text = gtk_entry_get_text(GTK_ENTRY(win->expression));
- if (text == NULL || *text == '\0') {
- regex_clear_color(win->expression);
- gtk_widget_set_sensitive(win->filter, FALSE);
- g_regex_unref(win->regex);
- win->regex = g_regex_new(text, G_REGEX_CASELESS|G_REGEX_JAVASCRIPT_COMPAT, 0, NULL);
- if (win->regex == NULL) {
- /* failed to compile */
- regex_change_color(win->expression, FALSE);
- gtk_widget_set_sensitive(win->filter, FALSE);
- /* compiled successfully */
- regex_change_color(win->expression, TRUE);
- gtk_widget_set_sensitive(win->filter, TRUE);
-regex_key_release_cb(GtkWidget *w, GdkEventKey *e, DebugWindow *win) {
- if (gtk_widget_is_sensitive(win->filter)) {
- GtkToggleToolButton *tb = GTK_TOGGLE_TOOL_BUTTON(win->filter);
- if ((e->keyval == GDK_KEY_Return || e->keyval == GDK_KEY_KP_Enter) &&
- !gtk_toggle_tool_button_get_active(tb))
- gtk_toggle_tool_button_set_active(tb, TRUE);
- if (e->keyval == GDK_KEY_Escape &&
- gtk_toggle_tool_button_get_active(tb))
- gtk_toggle_tool_button_set_active(tb, FALSE);
-regex_menu_cb(GtkWidget *item, const gchar *pref) {
- active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item));
- purple_prefs_set_bool(pref, active);
-regex_popup_cb(GtkEntry *entry, GtkWidget *menu, DebugWindow *win) {
- pidgin_separator(menu);
- pidgin_new_check_item(menu, _("Invert"),
- G_CALLBACK(regex_menu_cb),
- PIDGIN_PREFS_ROOT "/debug/invert", win->invert);
- pidgin_new_check_item(menu, _("Highlight matches"),
- G_CALLBACK(regex_menu_cb),
- PIDGIN_PREFS_ROOT "/debug/highlight", win->highlight);
-regex_filter_toggled_cb(GtkToggleToolButton *button, DebugWindow *win)
- active = gtk_toggle_tool_button_get_active(button);
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/filter", active);
- if (!PIDGIN_IS_WEBVIEW(win->text))
- regex_toggle_filter(win, active);
-debug_window_set_filter_level(DebugWindow *win, int level)
- if (level != gtk_combo_box_get_active(GTK_COMBO_BOX(win->filterlevel)))
- gtk_combo_box_set_active(GTK_COMBO_BOX(win->filterlevel), level);
- tmp = g_strdup_printf("setFilterLevel('%d');", level);
- pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(win->text), tmp);
-filter_level_pref_changed(const char *name, PurplePrefType type, gconstpointer value, gpointer data)
- DebugWindow *win = data;
- int level = GPOINTER_TO_INT(value);
- debug_window_set_filter_level(win, level);
-filter_level_changed_cb(GtkWidget *combo, gpointer null)
- purple_prefs_set_int(PIDGIN_PREFS_ROOT "/debug/filterlevel",
- gtk_combo_box_get_active(GTK_COMBO_BOX(combo)));
-toolbar_style_pref_changed_cb(const char *name, PurplePrefType type, gconstpointer value, gpointer data)
- gtk_toolbar_set_style(GTK_TOOLBAR(data), GPOINTER_TO_INT(value));
-toolbar_icon_pref_changed(GtkWidget *item, GtkWidget *toolbar)
- int style = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "user_data"));
- purple_prefs_set_int(PIDGIN_PREFS_ROOT "/debug/style", style);
-toolbar_context(GtkWidget *toolbar, GdkEventButton *event, gpointer null)
- GtkWidget *menu, *item;
- GtkToolbarStyle value[3];
- if (!gdk_event_triggers_context_menu((GdkEvent *)event))
- text[0] = _("_Icon Only"); value[0] = GTK_TOOLBAR_ICONS;
- text[1] = _("_Text Only"); value[1] = GTK_TOOLBAR_TEXT;
- text[2] = _("_Both Icon & Text"); value[2] = GTK_TOOLBAR_BOTH_HORIZ;
- for (i = 0; i < 3; i++) {
- item = gtk_check_menu_item_new_with_mnemonic(text[i]);
- g_object_set_data(G_OBJECT(item), "user_data", GINT_TO_POINTER(value[i]));
- g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(toolbar_icon_pref_changed), toolbar);
- if (value[i] == (GtkToolbarStyle)purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/style"))
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- gtk_widget_show_all(menu);
- gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
- GBytes *resource_bytes;
- GtkStyleContext *context;
- GtkCssProvider *filter_css;
- const gchar filter_style[] =
- "color: @error_fg_color;"
- "text-shadow: 0 1px @error_text_shadow;"
- "background-image: none;"
- "background-color: @error_bg_color;"
- "color: @question_fg_color;"
- "text-shadow: 0 1px @question_text_shadow;"
- "background-image: none;"
- "background-color: @success_color;"
- win = g_new0(DebugWindow, 1);
- width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/width");
- height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/height");
- win->window = pidgin_create_window(_("Debug Window"), 0, "debug", TRUE);
- purple_debug_info("gtkdebug", "Setting dimensions to %d, %d\n",
- gtk_window_set_default_size(GTK_WINDOW(win->window), width, height);
- g_signal_connect(G_OBJECT(win->window), "delete_event",
- G_CALLBACK(debug_window_destroy), NULL);
- g_signal_connect(G_OBJECT(win->window), "configure_event",
- G_CALLBACK(configure_cb), win);
- handle = pidgin_debug_get_handle();
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- gtk_container_add(GTK_CONTAINER(win->window), vbox);
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/toolbar")) {
- /* Setup our top button bar thingie. */
- toolbar = gtk_toolbar_new();
- gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolbar), TRUE);
- g_signal_connect(G_OBJECT(toolbar), "button-press-event", G_CALLBACK(toolbar_context), win);
- gtk_toolbar_set_style(GTK_TOOLBAR(toolbar),
- purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/style"));
- purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/style",
- toolbar_style_pref_changed_cb, toolbar);
- gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar),
- GTK_ICON_SIZE_SMALL_TOOLBAR);
- gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
- item = gtk_tool_button_new(NULL, _("_Save..."));
- gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(item), TRUE);
- gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item),
- gtk_tool_item_set_is_important(item, TRUE);
- gtk_tool_item_set_tooltip_text(item, _("Save"));
- g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(save_cb), win);
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
- item = gtk_tool_button_new(NULL, _("_Clear"));
- gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(item), TRUE);
- gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item),
- gtk_tool_item_set_is_important(item, TRUE);
- gtk_tool_item_set_tooltip_text(item, _("Clear"));
- g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(clear_cb), win);
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
- item = gtk_separator_tool_item_new();
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
- item = gtk_toggle_tool_button_new();
- gtk_tool_button_set_label(GTK_TOOL_BUTTON(item), _("_Pause"));
- gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(item), TRUE);
- gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item),
- "media-playback-pause");
- gtk_tool_item_set_is_important(item, TRUE);
- gtk_tool_item_set_tooltip_text(item, _("Pause"));
- g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(pause_cb), win);
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
- item = gtk_separator_tool_item_new();
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
- /* regex toggle button */
- item = gtk_toggle_tool_button_new();
- gtk_tool_button_set_label(GTK_TOOL_BUTTON(item), _("_Filter"));
- gtk_tool_button_set_use_underline(GTK_TOOL_BUTTON(item), TRUE);
- gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item),
- gtk_tool_item_set_is_important(item, TRUE);
- win->filter = GTK_WIDGET(item);
- gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(win->filter), _("Filter"));
- g_signal_connect(G_OBJECT(win->filter), "clicked", G_CALLBACK(regex_filter_toggled_cb), win);
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(win->filter));
- /* we purposely disable the toggle button here in case
- * /purple/gtk/debug/expression has an empty string. If it does not have
- * an empty string, the change signal will get called and make the
- * toggle button sensitive.
- gtk_widget_set_sensitive(win->filter, FALSE);
- gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter),
- purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/filter"));
- purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/filter",
- regex_pref_filter_cb, win);
- win->expression = gtk_entry_new();
- item = gtk_tool_item_new();
- gtk_widget_set_tooltip_text(win->expression, _("Right click for more options."));
- gtk_container_add(GTK_CONTAINER(item), GTK_WIDGET(win->expression));
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
- filter_css = gtk_css_provider_new();
- gtk_css_provider_load_from_data(filter_css, filter_style, -1, NULL);
- context = gtk_widget_get_style_context(win->expression);
- gtk_style_context_add_provider(context,
- GTK_STYLE_PROVIDER(filter_css),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
- /* this needs to be before the text is set from the pref if we want it
- * to colorize a stored expression.
- g_signal_connect(G_OBJECT(win->expression), "changed",
- G_CALLBACK(regex_changed_cb), win);
- gtk_entry_set_text(GTK_ENTRY(win->expression),
- purple_prefs_get_string(PIDGIN_PREFS_ROOT "/debug/regex"));
- g_signal_connect(G_OBJECT(win->expression), "populate-popup",
- G_CALLBACK(regex_popup_cb), win);
- g_signal_connect(G_OBJECT(win->expression), "key-release-event",
- G_CALLBACK(regex_key_release_cb), win);
- purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/regex",
- regex_pref_expression_cb, win);
- /* connect the rest of our pref callbacks */
- win->invert = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/invert");
- purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/invert",
- regex_pref_invert_cb, win);
- win->highlight = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/highlight");
- purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/highlight",
- regex_pref_highlight_cb, win);
- item = gtk_separator_tool_item_new();
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
- item = gtk_tool_item_new();
- gtk_container_add(GTK_CONTAINER(item), gtk_label_new(_("Level ")));
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
- win->filterlevel = gtk_combo_box_text_new();
- item = gtk_tool_item_new();
- gtk_widget_set_tooltip_text(win->filterlevel, _("Select the debug filter level."));
- gtk_container_add(GTK_CONTAINER(item), win->filterlevel);
- gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
- gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->filterlevel), _("All"));
- gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->filterlevel), _("Misc"));
- gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->filterlevel), _("Info"));
- gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->filterlevel), _("Warning"));
- gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->filterlevel), _("Error "));
- gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(win->filterlevel), _("Fatal Error"));
- gtk_combo_box_set_active(GTK_COMBO_BOX(win->filterlevel),
- purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel"));
- purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/filterlevel",
- filter_level_pref_changed, win);
- g_signal_connect(G_OBJECT(win->filterlevel), "changed",
- G_CALLBACK(filter_level_changed_cb), NULL);
- /* Add the gtkwebview */
- frame = pidgin_create_webview(FALSE, &win->text, NULL);
- pidgin_webview_set_format_functions(PIDGIN_WEBVIEW(win->text),
- PIDGIN_WEBVIEW_ALL ^ PIDGIN_WEBVIEW_SMILEY ^ PIDGIN_WEBVIEW_IMAGE);
- resource = pidgin_get_resource();
- resource_bytes = g_resource_lookup_data(resource,
- "/im/pidgin/Pidgin/Debug/gtkdebug.html",
- G_RESOURCE_LOOKUP_FLAGS_NONE,
- if (G_UNLIKELY(resource_bytes == NULL || error != NULL)) {
- gchar *msg = g_strdup_printf("Unable to load debug window HTML: %s\n",
- error ? error->message : "Unknown error");
- pidgin_webview_load_html_string(PIDGIN_WEBVIEW(win->text), msg);
- gconstpointer gtkdebug_html;
- gtkdebug_html = g_bytes_get_data(resource_bytes, NULL);
- pidgin_webview_load_html_string(PIDGIN_WEBVIEW(win->text),
- /* Set active filter level in webview */
- debug_window_set_filter_level(win, purple_prefs_get_int(
- PIDGIN_PREFS_ROOT "/debug/filterlevel"));
- g_bytes_unref(resource_bytes);
- gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
- gtk_widget_show(frame);
- gtk_widget_show_all(win->window);
-debug_enabled_timeout_cb(gpointer data)
- PidginDebugUi *ui = PIDGIN_DEBUG_UI(data);
- ui->debug_enabled_timer = 0;
- pidgin_debug_window_show();
-debug_disabled_timeout_cb(gpointer data)
- PidginDebugUi *ui = PIDGIN_DEBUG_UI(data);
- ui->debug_enabled_timer = 0;
- pidgin_debug_window_hide();
-debug_enabled_cb(const char *name, PurplePrefType type,
- gconstpointer value, gpointer data)
- PidginDebugUi *ui = PIDGIN_DEBUG_UI(data);
- if (GPOINTER_TO_INT(value))
- ui->debug_enabled_timer = g_timeout_add(0, debug_enabled_timeout_cb, data);
- ui->debug_enabled_timer = g_timeout_add(0, debug_disabled_timeout_cb, data);
-pidgin_glib_log_handler(const gchar *domain, GLogLevelFlags flags,
- const gchar *msg, gpointer user_data)
- PurpleDebugLevel level;
- char *new_domain = NULL;
- if ((flags & G_LOG_LEVEL_ERROR) == G_LOG_LEVEL_ERROR)
- level = PURPLE_DEBUG_ERROR;
- else if ((flags & G_LOG_LEVEL_CRITICAL) == G_LOG_LEVEL_CRITICAL)
- level = PURPLE_DEBUG_FATAL;
- else if ((flags & G_LOG_LEVEL_WARNING) == G_LOG_LEVEL_WARNING)
- level = PURPLE_DEBUG_WARNING;
- else if ((flags & G_LOG_LEVEL_MESSAGE) == G_LOG_LEVEL_MESSAGE)
- level = PURPLE_DEBUG_INFO;
- else if ((flags & G_LOG_LEVEL_INFO) == G_LOG_LEVEL_INFO)
- level = PURPLE_DEBUG_INFO;
- else if ((flags & G_LOG_LEVEL_DEBUG) == G_LOG_LEVEL_DEBUG)
- level = PURPLE_DEBUG_MISC;
- purple_debug_warning("gtkdebug",
- "Unknown glib logging level in %d\n", flags);
- level = PURPLE_DEBUG_MISC; /* This will never happen. */
- new_msg = purple_utf8_try_convert(msg);
- new_domain = purple_utf8_try_convert(domain);
- bt_size = backtrace(bt_buff, 20);
- fprintf(stderr, "\nBacktrace for \"%s\" (%s):\n", new_msg,
- new_domain != NULL ? new_domain : "g_log");
- backtrace_symbols_fd(bt_buff, bt_size, STDERR_FILENO);
- purple_debug(level, (new_domain != NULL ? new_domain : "g_log"),
-pidgin_glib_dummy_print_handler(const gchar *string)
-pidgin_debug_ui_init(PidginDebugUi *self)
- /* Debug window preferences. */
- * NOTE: This must be set before prefs are loaded, and the callbacks
- * set after they are loaded, since prefs sets the enabled
- * preference here and that loads the window, which calls the
- * configure event, which overrides the width and height! :P
- purple_prefs_add_none(PIDGIN_PREFS_ROOT "/debug");
- /* Controls printing to the debug window */
- purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/enabled", FALSE);
- purple_prefs_add_int(PIDGIN_PREFS_ROOT "/debug/filterlevel", PURPLE_DEBUG_ALL);
- purple_prefs_add_int(PIDGIN_PREFS_ROOT "/debug/style", GTK_TOOLBAR_BOTH_HORIZ);
- purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/toolbar", TRUE);
- purple_prefs_add_int(PIDGIN_PREFS_ROOT "/debug/width", 450);
- purple_prefs_add_int(PIDGIN_PREFS_ROOT "/debug/height", 250);
- purple_prefs_add_string(PIDGIN_PREFS_ROOT "/debug/regex", "");
- purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/filter", FALSE);
- purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/invert", FALSE);
- purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/case_insensitive", FALSE);
- purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/highlight", FALSE);
- purple_prefs_connect_callback(NULL, PIDGIN_PREFS_ROOT "/debug/enabled",
- debug_enabled_cb, self);
-#define REGISTER_G_LOG_HANDLER(name) \
- g_log_set_handler((name), G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL \
- | G_LOG_FLAG_RECURSION, \
- pidgin_glib_log_handler, NULL)
- /* Register the glib/gtk log handlers. */
- REGISTER_G_LOG_HANDLER(NULL);
- REGISTER_G_LOG_HANDLER("Gdk");
- REGISTER_G_LOG_HANDLER("Gtk");
- REGISTER_G_LOG_HANDLER("GdkPixbuf");
- REGISTER_G_LOG_HANDLER("GLib");
- REGISTER_G_LOG_HANDLER("GModule");
- REGISTER_G_LOG_HANDLER("GLib-GObject");
- REGISTER_G_LOG_HANDLER("GThread");
- REGISTER_G_LOG_HANDLER("Json");
- REGISTER_G_LOG_HANDLER("GStreamer");
- if (!purple_debug_is_enabled())
- g_set_print_handler(pidgin_glib_dummy_print_handler);
-pidgin_debug_ui_finalize(GObject *gobject)
- PidginDebugUi *self = PIDGIN_DEBUG_UI(gobject);
- if (self->debug_enabled_timer != 0)
- g_source_remove(self->debug_enabled_timer);
- self->debug_enabled_timer = 0;
- G_OBJECT_CLASS(pidgin_debug_ui_parent_class)->finalize(gobject);
-pidgin_debug_window_show(void)
- debug_win = debug_window_new();
- gtk_widget_show(debug_win->window);
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/enabled", TRUE);
-pidgin_debug_window_hide(void)
- if (debug_win != NULL) {
- gtk_widget_destroy(debug_win->window);
- debug_window_destroy(NULL, NULL, NULL);
-pidgin_debug_print(PurpleDebugUi *self,
- PurpleDebugLevel level, const char *category,
- if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"))
- mdate = purple_utf8_strftime("%H:%M:%S", localtime(&mtime));
- esc_s = purple_escape_js(arg_s);
- js = g_strdup_printf("append(%d, '%s', '%s', %s);",
- level, mdate, category ? category : "", esc_s);
- pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(debug_win->text), js);
-pidgin_debug_is_enabled(PurpleDebugUi *self, PurpleDebugLevel level, const char *category)
- return (debug_win != NULL &&
- purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"));
-pidgin_debug_ui_interface_init(PurpleDebugUiInterface *iface)
- iface->print = pidgin_debug_print;
- iface->is_enabled = pidgin_debug_is_enabled;
-pidgin_debug_ui_class_init(PidginDebugUiClass *klass)
- GObjectClass *object_class = G_OBJECT_CLASS(klass);
- object_class->finalize = pidgin_debug_ui_finalize;
-pidgin_debug_ui_new(void)
- return g_object_new(PIDGIN_TYPE_DEBUG_UI, NULL);
-pidgin_debug_get_handle() {
--- a/pidgin/gtkdebug.h Wed Jun 13 03:18:40 2018 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- * @section_id: pidgin-gtkdebug
- * @short_description: <filename>gtkdebug.h</filename>
-#define PIDGIN_TYPE_DEBUG_UI (pidgin_debug_ui_get_type())
-#if GLIB_CHECK_VERSION(2,44,0)
-G_DECLARE_FINAL_TYPE(PidginDebugUi, pidgin_debug_ui, PIDGIN, DEBUG_UI, GObject)
-GType pidgin_debug_ui_get_type(void);
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-typedef struct _PidginDebugUi PidginDebugUi;
-typedef struct { GObjectClass parent_class; } PidginDebugUiClass;
-static inline PidginDebugUi *
-PIDGIN_DEBUG_UI(gpointer ptr)
- return G_TYPE_CHECK_INSTANCE_CAST(ptr, pidgin_debug_ui_get_type(), PidginDebugUi);
-PIDGIN_IS_DEBUG_UI(gpointer ptr)
- return G_TYPE_CHECK_INSTANCE_TYPE(ptr, pidgin_debug_ui_get_type());
-G_GNUC_END_IGNORE_DEPRECATIONS
- * Initializes the GTK+ debug system.
-PidginDebugUi *pidgin_debug_ui_new(void);
- * pidgin_debug_get_handle:
- * Get the handle for the GTK+ debug system.
- * Returns: the handle to the debug system
-void *pidgin_debug_get_handle(void);
- * pidgin_debug_window_show:
- * Shows the debug window.
-void pidgin_debug_window_show(void);
- * pidgin_debug_window_hide:
- * Hides the debug window.
-void pidgin_debug_window_hide(void);
-#endif /* _PIDGINDEBUG_H_ */
--- a/pidgin/gtkprefs.c Wed Jun 13 03:18:40 2018 +0000
+++ b/pidgin/gtkprefs.c Fri Jun 15 02:41:45 2018 +0000
@@ -44,7 +44,6 @@
#include "gtkconv-theme.h"
#include "gtksavedstatuses.h"
@@ -53,6 +52,7 @@
#include "gtkstatus-icon-theme.h"
+#include "pidgindebug.h" --- a/pidgin/libpidgin.c Wed Jun 13 03:18:40 2018 +0000
+++ b/pidgin/libpidgin.c Fri Jun 15 02:41:45 2018 +0000
@@ -47,7 +47,6 @@
@@ -67,6 +66,7 @@
#include "gtkwhiteboard.h"
+#include "pidgindebug.h" --- a/pidgin/meson.build Wed Jun 13 03:18:40 2018 +0000
+++ b/pidgin/meson.build Fri Jun 15 02:41:45 2018 +0000
@@ -10,7 +10,6 @@
'gtkconv-theme-loader.c',
@@ -44,6 +43,7 @@
@@ -59,7 +59,6 @@
'gtkconv-theme-loader.h',
@@ -94,6 +93,7 @@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgindebug.c Fri Jun 15 02:41:45 2018 +0000
@@ -0,0 +1,1028 @@
+ * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA +#include "pidgindebug.h" +#include "pidginstock.h" +#include <gdk/gdkkeysyms.h> +#include "pidginresources.h" +struct _PidginDebugWindow { + GtkTextMark *start_mark; + GtkTextTag *level[PURPLE_DEBUG_FATAL + 1]; + GtkTextTag *filtered_invisible; + GtkTextTag *filtered_visible; + GtkWidget *filterlevel; +#if GTK_CHECK_VERSION(3,12,0) +static PidginDebugWindow *debug_win = NULL; + /* Other members, including private data. */ + guint debug_enabled_timer; +static void pidgin_debug_ui_finalize(GObject *gobject); +static void pidgin_debug_ui_interface_init(PurpleDebugUiInterface *iface); +G_DEFINE_TYPE_WITH_CODE(PidginDebugUi, pidgin_debug_ui, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(PURPLE_TYPE_DEBUG_UI, + pidgin_debug_ui_interface_init)); +G_DEFINE_TYPE(PidginDebugWindow, pidgin_debug_window, GTK_TYPE_WINDOW); +debug_window_destroy(GtkWidget *w, GdkEvent *event, void *unused) + purple_prefs_disconnect_by_handle(pidgin_debug_get_handle()); + if (debug_win->regex != NULL) + g_regex_unref(debug_win->regex); + /* If the "Save Log" dialog is open then close it */ + purple_request_close_with_handle(debug_win); + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/enabled", FALSE); +configure_cb(GtkWidget *w, GdkEventConfigure *event, void *unused) + if (gtk_widget_get_visible(w)) { + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/debug/width", event->width); + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/debug/height", event->height); +view_near_bottom(PidginDebugWindow *win) + GtkAdjustment *adj = gtk_scrollable_get_vadjustment( + GTK_SCROLLABLE(win->textview)); + return (gtk_adjustment_get_value(adj) >= + (gtk_adjustment_get_upper(adj) - + gtk_adjustment_get_page_size(adj) * 1.5)); +save_writefile_cb(void *user_data, const char *filename) + PidginDebugWindow *win = (PidginDebugWindow *)user_data; + GtkTextIter start, end; + if ((fp = g_fopen(filename, "w+")) == NULL) { + purple_notify_error(win, NULL, _("Unable to open file."), NULL, NULL); + gtk_text_buffer_get_bounds(win->buffer, &start, &end); + tmp = gtk_text_buffer_get_text(win->buffer, &start, &end, TRUE); + fprintf(fp, "Pidgin Debug Log : %s\n", purple_date_format_full(NULL)); + fprintf(fp, "%s", tmp); +save_cb(GtkWidget *w, PidginDebugWindow *win) + purple_request_file(win, _("Save Debug Log"), "purple-debug.log", TRUE, + G_CALLBACK(save_writefile_cb), NULL, NULL, win); +clear_cb(GtkWidget *w, PidginDebugWindow *win) + gtk_text_buffer_set_text(win->buffer, "", 0); +pause_cb(GtkWidget *w, PidginDebugWindow *win) + win->paused = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(w)); + GtkTextIter start, end; + gtk_text_buffer_get_bounds(win->buffer, &start, &end); + gtk_text_buffer_remove_tag(win->buffer, win->tags.paused, + gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(win->textview), + win->end_mark, 0, TRUE, 0, 1); +/****************************************************************************** + *****************************************************************************/ +regex_clear_color(GtkWidget *w) { + GtkStyleContext *context = gtk_widget_get_style_context(w); + gtk_style_context_remove_class(context, "good-filter"); + gtk_style_context_remove_class(context, "bad-filter"); +regex_change_color(GtkWidget *w, gboolean success) { + GtkStyleContext *context = gtk_widget_get_style_context(w); + gtk_style_context_add_class(context, "good-filter"); + gtk_style_context_remove_class(context, "bad-filter"); + gtk_style_context_add_class(context, "bad-filter"); + gtk_style_context_remove_class(context, "good-filter"); +do_regex(PidginDebugWindow *win, GtkTextIter *start, GtkTextIter *end) + gint start_pos, end_pos; + GtkTextIter match_start, match_end; + initial_position = gtk_text_iter_get_offset(start); + /* First hide everything. */ + gtk_text_buffer_apply_tag(win->buffer, + win->tags.filtered_invisible, start, end); + text = gtk_text_buffer_get_text(win->buffer, start, end, TRUE); + g_regex_match(win->regex, text, 0, &match); + while (g_match_info_matches(match)) { + g_match_info_fetch_pos(match, 0, &start_pos, &end_pos); + start_pos += initial_position; + end_pos += initial_position; + /* Expand match to full line of message. */ + gtk_text_buffer_get_iter_at_offset(win->buffer, + &match_start, start_pos); + gtk_text_iter_set_line_index(&match_start, 0); + gtk_text_buffer_get_iter_at_offset(win->buffer, + gtk_text_iter_forward_line(&match_end); + gtk_text_buffer_apply_tag(win->buffer, + win->tags.filtered_invisible, + &match_start, &match_end); + /* Make visible again (with higher priority.) */ + gtk_text_buffer_apply_tag(win->buffer, + win->tags.filtered_visible, + &match_start, &match_end); + gtk_text_buffer_get_iter_at_offset( + gtk_text_buffer_get_iter_at_offset( + gtk_text_buffer_apply_tag(win->buffer, + g_match_info_next(match, &error); + g_match_info_free(match); +regex_toggle_filter(PidginDebugWindow *win, gboolean filter) + GtkTextIter start, end; + gtk_text_buffer_get_bounds(win->buffer, &start, &end); + gtk_text_buffer_remove_tag(win->buffer, win->tags.match, &start, &end); + gtk_text_buffer_remove_tag(win->buffer, win->tags.filtered_invisible, + gtk_text_buffer_remove_tag(win->buffer, win->tags.filtered_visible, + do_regex(win, &start, &end); +regex_pref_filter_cb(const gchar *name, PurplePrefType type, + gconstpointer val, gpointer data) + PidginDebugWindow *win = (PidginDebugWindow *)data; + gboolean active = GPOINTER_TO_INT(val), current; + current = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)); + gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), active); +regex_pref_expression_cb(const gchar *name, PurplePrefType type, + gconstpointer val, gpointer data) + PidginDebugWindow *win = (PidginDebugWindow *)data; + const gchar *exp = (const gchar *)val; + gtk_entry_set_text(GTK_ENTRY(win->expression), exp); +regex_pref_invert_cb(const gchar *name, PurplePrefType type, + gconstpointer val, gpointer data) + PidginDebugWindow *win = (PidginDebugWindow *)data; + gboolean active = GPOINTER_TO_INT(val); + if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) + regex_toggle_filter(win, TRUE); +regex_pref_highlight_cb(const gchar *name, PurplePrefType type, + gconstpointer val, gpointer data) + PidginDebugWindow *win = (PidginDebugWindow *)data; + gboolean active = GPOINTER_TO_INT(val); + win->highlight = active; + if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) + regex_toggle_filter(win, TRUE); +regex_changed_cb(GtkWidget *w, PidginDebugWindow *win) { + if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) { + gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), + text = gtk_entry_get_text(GTK_ENTRY(win->expression)); + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/debug/regex", text); + if (text == NULL || *text == '\0') { + regex_clear_color(win->expression); + gtk_widget_set_sensitive(win->filter, FALSE); + g_regex_unref(win->regex); + win->regex = g_regex_new(text, G_REGEX_CASELESS|G_REGEX_JAVASCRIPT_COMPAT, 0, NULL); + if (win->regex == NULL) { + /* failed to compile */ + regex_change_color(win->expression, FALSE); + gtk_widget_set_sensitive(win->filter, FALSE); + /* compiled successfully */ + regex_change_color(win->expression, TRUE); + gtk_widget_set_sensitive(win->filter, TRUE); +regex_key_release_cb(GtkWidget *w, GdkEventKey *e, PidginDebugWindow *win) { + if (gtk_widget_is_sensitive(win->filter)) { + GtkToggleToolButton *tb = GTK_TOGGLE_TOOL_BUTTON(win->filter); + if ((e->keyval == GDK_KEY_Return || e->keyval == GDK_KEY_KP_Enter) && + !gtk_toggle_tool_button_get_active(tb)) + gtk_toggle_tool_button_set_active(tb, TRUE); + if (e->keyval == GDK_KEY_Escape && + gtk_toggle_tool_button_get_active(tb)) + gtk_toggle_tool_button_set_active(tb, FALSE); +regex_menu_cb(GtkWidget *item, const gchar *pref) { +#if GTK_CHECK_VERSION(3,12,0) + active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(item)); + active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item)); + purple_prefs_set_bool(pref, active); +regex_popup_cb(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event, + PidginDebugWindow *win) +#if GTK_CHECK_VERSION(3,12,0) + gtk_entry_get_icon_area(entry, icon_pos, &rect); + gtk_popover_set_pointing_to(GTK_POPOVER(win->popover), &rect); + gtk_popover_popup(GTK_POPOVER(win->popover)); + pidgin_new_check_item(menu, _("Invert"), + G_CALLBACK(regex_menu_cb), + PIDGIN_PREFS_ROOT "/debug/invert", win->invert); + pidgin_new_check_item(menu, _("Highlight matches"), + G_CALLBACK(regex_menu_cb), + PIDGIN_PREFS_ROOT "/debug/highlight", win->highlight); + gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(entry), + GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, event); +regex_filter_toggled_cb(GtkToggleToolButton *button, PidginDebugWindow *win) + active = gtk_toggle_tool_button_get_active(button); + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/filter", active); + regex_toggle_filter(win, active); +debug_window_set_filter_level(PidginDebugWindow *win, int level) + if (level != gtk_combo_box_get_active(GTK_COMBO_BOX(win->filterlevel))) + gtk_combo_box_set_active(GTK_COMBO_BOX(win->filterlevel), level); + scroll = view_near_bottom(win); + for (i = 0; i <= PURPLE_DEBUG_FATAL; i++) { + g_object_set(G_OBJECT(win->tags.level[i]), + "invisible", i < level, + gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(win->textview), + win->end_mark, 0, TRUE, 0, 1); +filter_level_pref_changed(const char *name, PurplePrefType type, gconstpointer value, gpointer data) + PidginDebugWindow *win = data; + int level = GPOINTER_TO_INT(value); + debug_window_set_filter_level(win, level); +filter_level_changed_cb(GtkWidget *combo, gpointer null) + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/debug/filterlevel", + gtk_combo_box_get_active(GTK_COMBO_BOX(combo))); +toolbar_style_pref_changed_cb(const char *name, PurplePrefType type, gconstpointer value, gpointer data) + gtk_toolbar_set_style(GTK_TOOLBAR(data), GPOINTER_TO_INT(value)); +toolbar_icon_pref_changed(GtkWidget *item, GtkWidget *toolbar) + int style = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "user_data")); + purple_prefs_set_int(PIDGIN_PREFS_ROOT "/debug/style", style); +toolbar_context(GtkWidget *toolbar, gint x, gint y, gint button, gpointer null) + GtkWidget *menu, *item; + GtkToolbarStyle value[3]; + text[0] = _("_Icon Only"); value[0] = GTK_TOOLBAR_ICONS; + text[1] = _("_Text Only"); value[1] = GTK_TOOLBAR_TEXT; + text[2] = _("_Both Icon & Text"); value[2] = GTK_TOOLBAR_BOTH_HORIZ; + for (i = 0; i < 3; i++) { + item = gtk_check_menu_item_new_with_mnemonic(text[i]); + g_object_set_data(G_OBJECT(item), "user_data", GINT_TO_POINTER(value[i])); + g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(toolbar_icon_pref_changed), toolbar); + if (value[i] == (GtkToolbarStyle)purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/style")) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show_all(menu); + gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL); +pidgin_debug_window_class_init(PidginDebugWindowClass *klass) { + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + gtk_widget_class_set_template_from_resource( + "/im/pidgin/Pidgin/Debug/debug.ui" + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, toolbar); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, textview); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, buffer); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, tags.category); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, tags.filtered_invisible); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, tags.filtered_visible); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, tags.level[0]); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, tags.level[1]); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, tags.level[2]); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, tags.level[3]); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, tags.level[4]); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, tags.level[5]); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, tags.paused); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, filter); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, filterlevel); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, expression); + gtk_widget_class_bind_template_child( + widget_class, PidginDebugWindow, tags.match); + gtk_widget_class_bind_template_callback(widget_class, toolbar_context); + gtk_widget_class_bind_template_callback(widget_class, save_cb); + gtk_widget_class_bind_template_callback(widget_class, clear_cb); + gtk_widget_class_bind_template_callback(widget_class, pause_cb); + gtk_widget_class_bind_template_callback(widget_class, + regex_filter_toggled_cb); + gtk_widget_class_bind_template_callback(widget_class, + gtk_widget_class_bind_template_callback(widget_class, regex_popup_cb); + gtk_widget_class_bind_template_callback(widget_class, + gtk_widget_class_bind_template_callback(widget_class, + filter_level_changed_cb); +pidgin_debug_window_init(PidginDebugWindow *win) + GtkStyleContext *context; + GtkCssProvider *filter_css; + const gchar filter_style[] = + "color: @error_fg_color;" + "text-shadow: 0 1px @error_text_shadow;" + "background-image: none;" + "background-color: @error_bg_color;" + "color: @question_fg_color;" + "text-shadow: 0 1px @question_text_shadow;" + "background-image: none;" + "background-color: @success_color;" +#if GTK_CHECK_VERSION(3,12,0) + GtkWidget *popover_invert; + GtkWidget *popover_highlight; + gtk_widget_init_template(GTK_WIDGET(win)); + width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/width"); + height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/height"); + purple_debug_info("gtkdebug", "Setting dimensions to %d, %d\n", + gtk_window_set_default_size(GTK_WINDOW(win), width, height); + g_signal_connect(G_OBJECT(win), "delete_event", + G_CALLBACK(debug_window_destroy), NULL); + g_signal_connect(G_OBJECT(win), "configure_event", + G_CALLBACK(configure_cb), NULL); + handle = pidgin_debug_get_handle(); + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/toolbar")) { + /* Setup our top button bar thingie. */ + gtk_toolbar_set_style(GTK_TOOLBAR(win->toolbar), + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/style")); + purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/style", + toolbar_style_pref_changed_cb, win->toolbar); + /* we purposely disable the toggle button here in case + * /purple/gtk/debug/expression has an empty string. If it does not have + * an empty string, the change signal will get called and make the + * toggle button sensitive. + gtk_widget_set_sensitive(win->filter, FALSE); + gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), + purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/filter")); + purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/filter", + regex_pref_filter_cb, win); + filter_css = gtk_css_provider_new(); + gtk_css_provider_load_from_data(filter_css, filter_style, -1, NULL); + context = gtk_widget_get_style_context(win->expression); + gtk_style_context_add_provider(context, + GTK_STYLE_PROVIDER(filter_css), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + gtk_entry_set_text(GTK_ENTRY(win->expression), + purple_prefs_get_string(PIDGIN_PREFS_ROOT "/debug/regex")); + purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/regex", + regex_pref_expression_cb, win); + /* connect the rest of our pref callbacks */ + win->invert = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/invert"); + purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/invert", + regex_pref_invert_cb, win); + win->highlight = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/highlight"); + purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/highlight", + regex_pref_highlight_cb, win); + gtk_combo_box_set_active(GTK_COMBO_BOX(win->filterlevel), + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel")); + purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/filterlevel", + filter_level_pref_changed, win); +#if GTK_CHECK_VERSION(3,12,0) + builder = gtk_builder_new_from_resource( + "/im/pidgin/Pidgin/Debug/filter-popover.ui"); + win->popover = GTK_WIDGET(gtk_builder_get_object(builder, + gtk_popover_set_relative_to(GTK_POPOVER(win->popover), + popover_invert = GTK_WIDGET(gtk_builder_get_object(builder, + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(popover_invert), + g_signal_connect(G_OBJECT(popover_invert), "toggled", + G_CALLBACK(regex_menu_cb), + PIDGIN_PREFS_ROOT "/debug/invert"); + popover_highlight = GTK_WIDGET(gtk_builder_get_object(builder, + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(popover_highlight), + g_signal_connect(G_OBJECT(popover_highlight), "toggled", + G_CALLBACK(regex_menu_cb), + PIDGIN_PREFS_ROOT "/debug/highlight"); + g_object_unref(builder); + /* The *start* and *end* marks bound the beginning and end of an + insertion, used for filtering. The *end* mark is also used for + gtk_text_buffer_get_end_iter(win->buffer, &end); + win->start_mark = gtk_text_buffer_create_mark(win->buffer, + win->end_mark = gtk_text_buffer_create_mark(win->buffer, + /* Set active filter level in textview */ + debug_window_set_filter_level(win, + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel")); +debug_enabled_timeout_cb(gpointer data) + PidginDebugUi *ui = PIDGIN_DEBUG_UI(data); + ui->debug_enabled_timer = 0; + pidgin_debug_window_show(); +debug_disabled_timeout_cb(gpointer data) + PidginDebugUi *ui = PIDGIN_DEBUG_UI(data); + ui->debug_enabled_timer = 0; + pidgin_debug_window_hide(); +debug_enabled_cb(const char *name, PurplePrefType type, + gconstpointer value, gpointer data) + PidginDebugUi *ui = PIDGIN_DEBUG_UI(data); + if (GPOINTER_TO_INT(value)) + ui->debug_enabled_timer = g_timeout_add(0, debug_enabled_timeout_cb, data); + ui->debug_enabled_timer = g_timeout_add(0, debug_disabled_timeout_cb, data); +pidgin_glib_log_handler(const gchar *domain, GLogLevelFlags flags, + const gchar *msg, gpointer user_data) + PurpleDebugLevel level; + char *new_domain = NULL; + if ((flags & G_LOG_LEVEL_ERROR) == G_LOG_LEVEL_ERROR) + level = PURPLE_DEBUG_ERROR; + else if ((flags & G_LOG_LEVEL_CRITICAL) == G_LOG_LEVEL_CRITICAL) + level = PURPLE_DEBUG_FATAL; + else if ((flags & G_LOG_LEVEL_WARNING) == G_LOG_LEVEL_WARNING) + level = PURPLE_DEBUG_WARNING; + else if ((flags & G_LOG_LEVEL_MESSAGE) == G_LOG_LEVEL_MESSAGE) + level = PURPLE_DEBUG_INFO; + else if ((flags & G_LOG_LEVEL_INFO) == G_LOG_LEVEL_INFO) + level = PURPLE_DEBUG_INFO; + else if ((flags & G_LOG_LEVEL_DEBUG) == G_LOG_LEVEL_DEBUG) + level = PURPLE_DEBUG_MISC; + purple_debug_warning("gtkdebug", + "Unknown glib logging level in %d\n", flags); + level = PURPLE_DEBUG_MISC; /* This will never happen. */ + new_msg = purple_utf8_try_convert(msg); + new_domain = purple_utf8_try_convert(domain); + bt_size = backtrace(bt_buff, 20); + fprintf(stderr, "\nBacktrace for \"%s\" (%s):\n", new_msg, + new_domain != NULL ? new_domain : "g_log"); + backtrace_symbols_fd(bt_buff, bt_size, STDERR_FILENO); + purple_debug(level, (new_domain != NULL ? new_domain : "g_log"), +pidgin_glib_dummy_print_handler(const gchar *string) +pidgin_debug_ui_init(PidginDebugUi *self) + /* Debug window preferences. */ + * NOTE: This must be set before prefs are loaded, and the callbacks + * set after they are loaded, since prefs sets the enabled + * preference here and that loads the window, which calls the + * configure event, which overrides the width and height! :P + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/debug"); + /* Controls printing to the debug window */ + purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/enabled", FALSE); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/debug/filterlevel", PURPLE_DEBUG_ALL); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/debug/style", GTK_TOOLBAR_BOTH_HORIZ); + purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/toolbar", TRUE); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/debug/width", 450); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/debug/height", 250); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/debug/regex", ""); + purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/filter", FALSE); + purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/invert", FALSE); + purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/case_insensitive", FALSE); + purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/debug/highlight", FALSE); + purple_prefs_connect_callback(NULL, PIDGIN_PREFS_ROOT "/debug/enabled", + debug_enabled_cb, self); +#define REGISTER_G_LOG_HANDLER(name) \ + g_log_set_handler((name), G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL \ + | G_LOG_FLAG_RECURSION, \ + pidgin_glib_log_handler, NULL) + /* Register the glib/gtk log handlers. */ + REGISTER_G_LOG_HANDLER(NULL); + REGISTER_G_LOG_HANDLER("Gdk"); + REGISTER_G_LOG_HANDLER("Gtk"); + REGISTER_G_LOG_HANDLER("GdkPixbuf"); + REGISTER_G_LOG_HANDLER("GLib"); + REGISTER_G_LOG_HANDLER("GModule"); + REGISTER_G_LOG_HANDLER("GLib-GObject"); + REGISTER_G_LOG_HANDLER("GThread"); + REGISTER_G_LOG_HANDLER("Json"); + REGISTER_G_LOG_HANDLER("GStreamer"); + if (!purple_debug_is_enabled()) + g_set_print_handler(pidgin_glib_dummy_print_handler); +pidgin_debug_ui_finalize(GObject *gobject) + PidginDebugUi *self = PIDGIN_DEBUG_UI(gobject); + if (self->debug_enabled_timer != 0) + g_source_remove(self->debug_enabled_timer); + self->debug_enabled_timer = 0; + G_OBJECT_CLASS(pidgin_debug_ui_parent_class)->finalize(gobject); +pidgin_debug_window_show(void) + if (debug_win == NULL) { + debug_win = PIDGIN_DEBUG_WINDOW( + g_object_new(PIDGIN_TYPE_DEBUG_WINDOW, NULL)); + gtk_widget_show(GTK_WIDGET(debug_win)); + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/enabled", TRUE); +pidgin_debug_window_hide(void) + if (debug_win != NULL) { + gtk_widget_destroy(GTK_WIDGET(debug_win)); + debug_window_destroy(NULL, NULL, NULL); +pidgin_debug_print(PurpleDebugUi *self, + PurpleDebugLevel level, const char *category, + if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled")) + scroll = view_near_bottom(debug_win); + gtk_text_buffer_get_end_iter(debug_win->buffer, &end); + gtk_text_buffer_move_mark(debug_win->buffer, debug_win->start_mark, + level_tag = debug_win->tags.level[level]; + mdate = purple_utf8_strftime("(%H:%M:%S) ", localtime(&mtime)); + gtk_text_buffer_insert_with_tags( + debug_win->paused ? debug_win->tags.paused : NULL, + if (category && *category) { + gtk_text_buffer_insert_with_tags( + debug_win->tags.category, + debug_win->paused ? debug_win->tags.paused : NULL, + gtk_text_buffer_insert_with_tags( + debug_win->tags.category, + debug_win->paused ? debug_win->tags.paused : NULL, + gtk_text_buffer_insert_with_tags( + debug_win->paused ? debug_win->tags.paused : NULL, + gtk_text_buffer_insert_with_tags( + debug_win->paused ? debug_win->tags.paused : NULL, + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/filter") && debug_win->regex) { + /* Filter out any new messages. */ + gtk_text_buffer_get_iter_at_mark(debug_win->buffer, &start, + debug_win->start_mark); + gtk_text_buffer_get_iter_at_mark(debug_win->buffer, &end, + do_regex(debug_win, &start, &end); + gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(debug_win->textview), + debug_win->end_mark, 0, TRUE, 0, 1); +pidgin_debug_is_enabled(PurpleDebugUi *self, PurpleDebugLevel level, const char *category) + return (debug_win != NULL && + purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled")); +pidgin_debug_ui_interface_init(PurpleDebugUiInterface *iface) + iface->print = pidgin_debug_print; + iface->is_enabled = pidgin_debug_is_enabled; +pidgin_debug_ui_class_init(PidginDebugUiClass *klass) + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = pidgin_debug_ui_finalize; +pidgin_debug_ui_new(void) + return g_object_new(PIDGIN_TYPE_DEBUG_UI, NULL); +pidgin_debug_get_handle() { --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgindebug.h Fri Jun 15 02:41:45 2018 +0000
@@ -0,0 +1,103 @@
+ * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * @section_id: pidgin-gtkdebug + * @short_description: <filename>pidgindebug.h</filename> +#define PIDGIN_TYPE_DEBUG_UI (pidgin_debug_ui_get_type()) +#define PIDGIN_TYPE_DEBUG_WINDOW (pidgin_debug_window_get_type()) +#if GLIB_CHECK_VERSION(2,44,0) +G_DECLARE_FINAL_TYPE(PidginDebugUi, pidgin_debug_ui, PIDGIN, DEBUG_UI, GObject) +G_DECLARE_FINAL_TYPE(PidginDebugWindow, pidgin_debug_window, PIDGIN, DEBUG_WINDOW, GtkWindow) +GType pidgin_debug_ui_get_type(void); +GType pidgin_debug_window_get_type(void); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS +typedef struct _PidginDebugUi PidginDebugUi; +typedef struct { GObjectClass parent_class; } PidginDebugUiClass; +static inline PidginDebugUi * +PIDGIN_DEBUG_UI(gpointer ptr) + return G_TYPE_CHECK_INSTANCE_CAST(ptr, pidgin_debug_ui_get_type(), PidginDebugUi); +PIDGIN_IS_DEBUG_UI(gpointer ptr) + return G_TYPE_CHECK_INSTANCE_TYPE(ptr, pidgin_debug_ui_get_type()); +typedef struct _PidginDebugWindow PidginDebugWindow; +typedef struct { GtkWindowClass parent_class; } PidginDebugWindowClass; +static inline PidginDebugWindow * +PIDGIN_DEBUG_WINDOW(gpointer ptr) + return G_TYPE_CHECK_INSTANCE_CAST(ptr, pidgin_debug_window_get_type(), PidginDebugWindow); +PIDGIN_IS_DEBUG_WINDOW(gpointer ptr) + return G_TYPE_CHECK_INSTANCE_TYPE(ptr, pidgin_debug_window_get_type()); +G_GNUC_END_IGNORE_DEPRECATIONS + * Initializes the GTK+ debug system. +PidginDebugUi *pidgin_debug_ui_new(void); + * pidgin_debug_get_handle: + * Get the handle for the GTK+ debug system. + * Returns: the handle to the debug system +void *pidgin_debug_get_handle(void); + * pidgin_debug_window_show: + * Shows the debug window. +void pidgin_debug_window_show(void); + * pidgin_debug_window_hide: + * Hides the debug window. +void pidgin_debug_window_hide(void); +#endif /* PIDGIN_DEBUG_H */ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/Debug/debug.ui Fri Jun 15 02:41:45 2018 +0000
@@ -0,0 +1,279 @@
+<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.1 --> + <requires lib="gtk+" version="3.10"/> + <object class="GtkTextTagTable" id="message-format"> + <object class="GtkTextTag" id="tags.level[0]"> + <property name="foreground_rgba">rgb(0,0,0)</property> + <object class="GtkTextTag" id="tags.level[1]"> + <property name="foreground_rgba">rgb(102,102,102)</property> + <object class="GtkTextTag" id="tags.level[2]"> + <property name="foreground_rgba">rgb(0,0,0)</property> + <object class="GtkTextTag" id="tags.level[3]"> + <property name="foreground_rgba">rgb(102,0,0)</property> + <object class="GtkTextTag" id="tags.level[4]"> + <property name="foreground_rgba">rgb(255,0,0)</property> + <object class="GtkTextTag" id="tags.level[5]"> + <property name="foreground_rgba">rgb(255,0,0)</property> + <property name="weight">700</property> + <object class="GtkTextTag" id="tags.category"> + <property name="weight">700</property> + <object class="GtkTextTag" id="tags.filtered_invisible"> + <property name="invisible">True</property> + <object class="GtkTextTag" id="tags.filtered_visible"> + <property name="invisible">False</property> + <object class="GtkTextTag" id="tags.match"> + <property name="background_rgba">rgb(255,175,175)</property> + <property name="weight">700</property> + <object class="GtkTextTag" id="tags.paused"> + <property name="invisible">True</property> + <object class="GtkTextBuffer" id="buffer"> + <property name="tag_table">message-format</property> + <template class="PidginDebugWindow" parent="GtkWindow"> + <property name="can_focus">False</property> + <property name="title" translatable="yes">Debug Window</property> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <object class="GtkToolbar" id="toolbar"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_size">2</property> + <signal name="popup-context-menu" handler="toolbar_context" swapped="no"/> + <object class="GtkToolButton" id="save"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="tooltip_text" translatable="yes">Save</property> + <property name="is_important">True</property> + <property name="label" translatable="yes">_Save...</property> + <property name="use_underline">True</property> + <property name="icon_name">document-save</property> + <signal name="clicked" handler="save_cb" object="PidginDebugWindow" swapped="no"/> + <property name="expand">False</property> + <property name="homogeneous">True</property> + <object class="GtkToolButton" id="clear"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="tooltip_text" translatable="yes">Clear</property> + <property name="is_important">True</property> + <property name="label" translatable="yes">_Clear</property> + <property name="use_underline">True</property> + <property name="icon_name">edit-clear</property> + <signal name="clicked" handler="clear_cb" object="PidginDebugWindow" swapped="no"/> + <property name="expand">False</property> + <property name="homogeneous">True</property> + <object class="GtkSeparatorToolItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="expand">False</property> + <property name="homogeneous">True</property> + <object class="GtkToggleToolButton" id="pause"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="tooltip_text" translatable="yes">Pause</property> + <property name="is_important">True</property> + <property name="label" translatable="yes">_Pause</property> + <property name="use_underline">True</property> + <property name="icon_name">media-playback-pause</property> + <signal name="clicked" handler="pause_cb" object="PidginDebugWindow" swapped="no"/> + <property name="expand">False</property> + <property name="homogeneous">True</property> + <object class="GtkSeparatorToolItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="expand">False</property> + <property name="homogeneous">True</property> + <object class="GtkToggleToolButton" id="filter"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="tooltip_text" translatable="yes">Filter</property> + <property name="is_important">True</property> + <property name="label" translatable="yes">_Filter</property> + <property name="use_underline">True</property> + <property name="icon_name">edit-find</property> + <signal name="clicked" handler="regex_filter_toggled_cb" object="PidginDebugWindow" swapped="no"/> + <property name="expand">False</property> + <property name="homogeneous">True</property> + <object class="GtkToolItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <object class="GtkSearchEntry" id="expression"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="primary_icon_name">edit-find-symbolic</property> + <property name="primary_icon_activatable">True</property> + <property name="primary_icon_sensitive">True</property> + <property name="primary_icon_tooltip_text" translatable="yes">Click for more options.</property> + <signal name="icon-press" handler="regex_popup_cb" object="PidginDebugWindow" swapped="no"/> + <signal name="key-release-event" handler="regex_key_release_cb" object="PidginDebugWindow" swapped="no"/> + <signal name="search-changed" handler="regex_changed_cb" object="PidginDebugWindow" swapped="no"/> + <property name="expand">False</property> + <property name="homogeneous">True</property> + <object class="GtkSeparatorToolItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="expand">False</property> + <property name="homogeneous">True</property> + <object class="GtkToolItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Level </property> + <relation type="label-for" target="filterlevel"/> + <property name="expand">False</property> + <property name="homogeneous">True</property> + <object class="GtkToolItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <object class="GtkComboBoxText" id="filterlevel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="tooltip_text" translatable="yes">Select the debug filter level.</property> + <property name="active">0</property> + <item translatable="yes">All</item> + <item translatable="yes">Misc</item> + <item translatable="yes">Info</item> + <item translatable="yes">Warning</item> + <item translatable="yes">Error</item> + <item translatable="yes">Fatal Error</item> + <signal name="changed" handler="filter_level_changed_cb" object="PidginDebugWindow" swapped="no"/> + <property name="expand">False</property> + <property name="homogeneous">True</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + <object class="GtkScrolledWindow"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="shadow_type">in</property> + <object class="GtkTextView" id="textview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">False</property> + <property name="wrap_mode">word</property> + <property name="buffer">buffer</property> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/Debug/filter-popover.ui Fri Jun 15 02:41:45 2018 +0000
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.1 --> + <requires lib="gtk+" version="3.12"/> + <object class="GtkPopover" id="popover"> + <property name="can_focus">False</property> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <object class="GtkCheckButton" id="popover.invert"> + <property name="label" translatable="yes">Invert</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + <object class="GtkCheckButton" id="popover.highlight"> + <property name="label" translatable="yes">Highlight matches</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> --- a/pidgin/resources/Debug/gtkdebug.html Wed Jun 13 03:18:40 2018 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,251 +0,0 @@
- body{white-space:pre-wrap;}
- div.l0{color:#000000;} /* All debug levels. */
- div.l1{color:#666666;} /* Misc. */
- div.l2{color:#000000;} /* Information. */
- div.l3{color:#660000;} /* Warnings. */
- div.l4{color:#FF0000;} /* Errors. */
- div.l5{color:#FF0000;font-weight:bold;} /* Fatal errors. */
- div#pause~div{display:none;}
- body.l1 div.l0{display:none;}
- body.l2 div.l0,body.l2 div.l1{display:none;}
- body.l3 div.l0,body.l3 div.l1,body.l3 div.l2{display:none;}
- body.l4 div.l0,body.l4 div.l1,body.l4 div.l2,body.l4 div.l3{display:none;}
- body.l5 div.l0,body.l5 div.l1,body.l5 div.l2,body.l5 div.l3,body.l5 div.l4{display:none;}
- div.hide{display:none;}
- span.regex{background-color:#ffafaf;font-weight:bold;}
- function nearBottom() {
- return ( document.body.scrollTop >= ( document.body.offsetHeight - ( window.innerHeight * 1.5 ) ) );
- function scrollToBottom() {
- document.body.scrollTop = document.body.offsetHeight;
- var scroll = nearBottom();
- /* Remove highlighting SPANs */
- list = document.getElementsByClassName('regex');
- var parent = span.parentNode;
- var content = span.textContent;
- var text = document.createTextNode(content);
- parent.replaceChild(text, span);
- /* Remove hidden DIVs */
- list = document.getElementsByClassName('hide');
- list[i].classList.remove('hide');
- highlightTextNodes: function (div, start_pos, end_pos) {
- var data = [], node, range, span, contents;
- var this_start, this_end;
- /* First, find the container nodes and offsets to apply highlighting. */
- if (node.nodeType === Node.TEXT_NODE) {
- end_ind = ind + node.length;
- else if (start_pos < end_ind)
- this_start = start_pos - ind;
- this_end = end_pos - ind;
- this_end = end_ind - ind;
- if (this_start != -1 && this_start < this_end) {
- data.push(this_end, this_start, node);
- if (node.hasChildNodes()) {
- node = node.firstChild;
- var next = node.nextSibling;
- node = node.parentNode;
- /* Second, apply highlighting to saved sections. Changing the DOM is
- automatically reflected in all WebKit API, so we have to do this after
- finding the offsets, or things could get complicated. */
- this_start = data.pop();
- range = document.createRange();
- range.setStart(node, this_start);
- range.setEnd(node, this_end);
- span = document.createElement('span');
- span.className = 'regex';
- contents = range.extractContents();
- range.insertNode(span);
- span.appendChild(contents);
- match: function (div) {
- var m, count, start_pos, end_pos;
- text = div.textContent;
- /* We do a first pass to see if it matches at all. If it does we work out
- * the offsets to highlight.
- this.regex.lastIndex = 0;
- var match_info = this.regex.exec(text);
- if ((match_info != null) != this.invert) {
- /* If we're not highlighting or the expression is inverted, we're
- if (!this.highlight || this.invert)
- if (match_info === null)
- count = match_info.length;
- /* We do this because JS doesn't provide a sufficient means to
- determine the indices of matched groups. So we're just going
- to highlight the entire match instead. */
- for (; m < count; m++) {
- start_pos = match_info.index;
- end_pos = this.regex.lastIndex;
- this.highlightTextNodes(div, start_pos, end_pos);
- /* Workaround broken API for empty matches */
- if (match_info.index == this.regex.lastIndex)
- this.regex.lastIndex++;
- } while (match_info = this.regex.exec(text));
- div.classList.add('hide');
- filterAll: function (str, inv, high) {
- this.regex = new RegExp(str, 'gi');
- var list = document.getElementsByTagName('div');
- for (var i = 0; i < list.length; i++)
- function append(level, time, cat, msg) {
- var div = document.createElement('div');
- div.className = 'l' + level;
- div.appendChild(document.createTextNode('(' + time + ') '));
- var cat_n = document.createElement('b');
- cat_n.appendChild(document.createTextNode(cat + ':'));
- div.appendChild(cat_n);
- div.appendChild(document.createTextNode(' '));
- div.appendChild(document.createTextNode(msg));
- var scroll = nearBottom();
- document.body.appendChild(div);
- document.body.innerHTML = '';
- function pauseOutput() {
- document.body.insertAdjacentHTML('beforeEnd', '<div id=pause></div>');
- function resumeOutput() {
- var pause = document.getElementById('pause');
- var parent = pause.parentNode;
- parent.removeChild(pause);
- function setFilterLevel(l) {
- var scroll = nearBottom();
- document.body.className = 'l'+l;
--- a/pidgin/resources/pidgin.gresource.xml Wed Jun 13 03:18:40 2018 +0000
+++ b/pidgin/resources/pidgin.gresource.xml Fri Jun 15 02:41:45 2018 +0000
@@ -5,6 +5,7 @@
<file compressed="true">About/about.ui</file>
<file compressed="true">About/about.html</file>
<file compressed="true">About/credits.json</file>
- <file compressed="true">Debug/gtkdebug.html</file>
+ <file compressed="true">Debug/debug.ui</file> + <file compressed="true">Debug/filter-popover.ui</file>