pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Use Meson summary() function.
2021-07-27, Elliott Sales de Andrade
cb640ea0f315
Use Meson summary() function.
Now that we require at least 0.52, we can use Meson's builtin summary printing to display the results of configuration.
Testing Done:
Configured with defaults, and with pixmaps disabled to trigger the warning: https://asciinema.org/a/mV2oxOoVCJNdmrPwgqqUJ3mkU?t=17
Reviewed at https://reviews.imfreedom.org/r/848/
/* pidgin
*
* 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
* source distribution.
*
* 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
*/
#ifdef HAVE_CONFIG_H
#
include
<config.h>
#endif
#include
<glib/gi18n-lib.h>
#include
<glib/gstdio.h>
#include
<gtk/gtk.h>
#include
<purple.h>
#include
"gtkdialogs.h"
#include
"gtkutils.h"
#include
"pidgincore.h"
#include
"pidgindebug.h"
#include
"pidginstock.h"
#include
<gdk/gdkkeysyms.h>
#include
"pidginresources.h"
struct
_PidginDebugWindow
{
GtkWindow
parent
;
GtkWidget
*
toolbar
;
GtkWidget
*
textview
;
GtkTextBuffer
*
buffer
;
GtkTextMark
*
start_mark
;
GtkTextMark
*
end_mark
;
struct
{
GtkTextTag
*
level
[
PURPLE_DEBUG_FATAL
+
1
];
GtkTextTag
*
category
;
GtkTextTag
*
filtered_invisible
;
GtkTextTag
*
filtered_visible
;
GtkTextTag
*
match
;
GtkTextTag
*
paused
;
}
tags
;
GtkWidget
*
filter
;
GtkWidget
*
expression
;
GtkWidget
*
filterlevel
;
gboolean
paused
;
GtkWidget
*
popover
;
GtkWidget
*
popover_invert
;
GtkWidget
*
popover_highlight
;
gboolean
invert
;
gboolean
highlight
;
GRegex
*
regex
;
};
static
PidginDebugWindow
*
debug_win
=
NULL
;
struct
_PidginDebugUi
{
GObject
parent
;
/* 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
);
static
gint
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
);
debug_win
=
NULL
;
purple_prefs_set_bool
(
PIDGIN_PREFS_ROOT
"/debug/enabled"
,
FALSE
);
return
FALSE
;
}
static
gboolean
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
);
}
return
FALSE
;
}
static
gboolean
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
));
}
static
void
save_writefile_cb
(
void
*
user_data
,
const
char
*
filename
)
{
PidginDebugWindow
*
win
=
(
PidginDebugWindow
*
)
user_data
;
FILE
*
fp
;
GtkTextIter
start
,
end
;
char
*
tmp
;
if
((
fp
=
g_fopen
(
filename
,
"w+"
))
==
NULL
)
{
purple_notify_error
(
win
,
NULL
,
_
(
"Unable to open file."
),
NULL
,
NULL
);
return
;
}
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
);
g_free
(
tmp
);
fclose
(
fp
);
}
static
void
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
);
}
static
void
clear_cb
(
GtkWidget
*
w
,
PidginDebugWindow
*
win
)
{
gtk_text_buffer_set_text
(
win
->
buffer
,
""
,
0
);
}
static
void
pause_cb
(
GtkWidget
*
w
,
PidginDebugWindow
*
win
)
{
win
->
paused
=
gtk_toggle_tool_button_get_active
(
GTK_TOGGLE_TOOL_BUTTON
(
w
));
if
(
!
win
->
paused
)
{
GtkTextIter
start
,
end
;
gtk_text_buffer_get_bounds
(
win
->
buffer
,
&
start
,
&
end
);
gtk_text_buffer_remove_tag
(
win
->
buffer
,
win
->
tags
.
paused
,
&
start
,
&
end
);
gtk_text_view_scroll_to_mark
(
GTK_TEXT_VIEW
(
win
->
textview
),
win
->
end_mark
,
0
,
TRUE
,
0
,
1
);
}
}
/******************************************************************************
* regex stuff
*****************************************************************************/
static
void
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"
);
}
static
void
regex_change_color
(
GtkWidget
*
w
,
gboolean
success
)
{
GtkStyleContext
*
context
=
gtk_widget_get_style_context
(
w
);
if
(
success
)
{
gtk_style_context_add_class
(
context
,
"good-filter"
);
gtk_style_context_remove_class
(
context
,
"bad-filter"
);
}
else
{
gtk_style_context_add_class
(
context
,
"bad-filter"
);
gtk_style_context_remove_class
(
context
,
"good-filter"
);
}
}
static
void
do_regex
(
PidginDebugWindow
*
win
,
GtkTextIter
*
start
,
GtkTextIter
*
end
)
{
GError
*
error
=
NULL
;
GMatchInfo
*
match
;
gint
initial_position
;
gint
start_pos
,
end_pos
;
GtkTextIter
match_start
,
match_end
;
gchar
*
text
;
if
(
!
win
->
regex
)
return
;
initial_position
=
gtk_text_iter_get_offset
(
start
);
if
(
!
win
->
invert
)
{
/* 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
,
&
match_end
,
end_pos
);
gtk_text_iter_forward_line
(
&
match_end
);
if
(
win
->
invert
)
{
/* Make invisible. */
gtk_text_buffer_apply_tag
(
win
->
buffer
,
win
->
tags
.
filtered_invisible
,
&
match_start
,
&
match_end
);
}
else
{
/* Make visible again (with higher priority.) */
gtk_text_buffer_apply_tag
(
win
->
buffer
,
win
->
tags
.
filtered_visible
,
&
match_start
,
&
match_end
);
if
(
win
->
highlight
)
{
gtk_text_buffer_get_iter_at_offset
(
win
->
buffer
,
&
match_start
,
start_pos
);
gtk_text_buffer_get_iter_at_offset
(
win
->
buffer
,
&
match_end
,
end_pos
);
gtk_text_buffer_apply_tag
(
win
->
buffer
,
win
->
tags
.
match
,
&
match_start
,
&
match_end
);
}
}
g_match_info_next
(
match
,
&
error
);
}
g_match_info_free
(
match
);
g_free
(
text
);
}
static
void
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
,
&
start
,
&
end
);
gtk_text_buffer_remove_tag
(
win
->
buffer
,
win
->
tags
.
filtered_visible
,
&
start
,
&
end
);
if
(
filter
)
{
do_regex
(
win
,
&
start
,
&
end
);
}
}
static
void
regex_pref_filter_cb
(
const
gchar
*
name
,
PurplePrefType
type
,
gconstpointer
val
,
gpointer
data
)
{
PidginDebugWindow
*
win
=
(
PidginDebugWindow
*
)
data
;
gboolean
active
=
GPOINTER_TO_INT
(
val
),
current
;
if
(
!
win
)
return
;
current
=
gtk_toggle_tool_button_get_active
(
GTK_TOGGLE_TOOL_BUTTON
(
win
->
filter
));
if
(
active
!=
current
)
gtk_toggle_tool_button_set_active
(
GTK_TOGGLE_TOOL_BUTTON
(
win
->
filter
),
active
);
}
static
void
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
);
}
static
void
regex_pref_invert_cb
(
const
gchar
*
name
,
PurplePrefType
type
,
gconstpointer
val
,
gpointer
data
)
{
PidginDebugWindow
*
win
=
(
PidginDebugWindow
*
)
data
;
gboolean
active
=
GPOINTER_TO_INT
(
val
);
win
->
invert
=
active
;
if
(
gtk_toggle_tool_button_get_active
(
GTK_TOGGLE_TOOL_BUTTON
(
win
->
filter
)))
regex_toggle_filter
(
win
,
TRUE
);
}
static
void
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
);
}
static
void
regex_changed_cb
(
GtkWidget
*
w
,
PidginDebugWindow
*
win
)
{
const
gchar
*
text
;
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
),
FALSE
);
}
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
);
return
;
}
if
(
win
->
regex
)
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
);
}
else
{
/* compiled successfully */
regex_change_color
(
win
->
expression
,
TRUE
);
gtk_widget_set_sensitive
(
win
->
filter
,
TRUE
);
}
}
static
void
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
);
}
}
}
static
void
regex_menu_cb
(
GtkWidget
*
item
,
PidginDebugWindow
*
win
)
{
gboolean
active
;
active
=
gtk_toggle_button_get_active
(
GTK_TOGGLE_BUTTON
(
item
));
if
(
item
==
win
->
popover_highlight
)
{
purple_prefs_set_bool
(
PIDGIN_PREFS_ROOT
"/debug/highlight"
,
active
);
}
else
if
(
item
==
win
->
popover_invert
)
{
purple_prefs_set_bool
(
PIDGIN_PREFS_ROOT
"/debug/invert"
,
active
);
}
}
static
void
regex_popup_cb
(
GtkEntry
*
entry
,
GtkEntryIconPosition
icon_pos
,
GdkEvent
*
event
,
PidginDebugWindow
*
win
)
{
GdkRectangle
rect
;
if
(
icon_pos
!=
GTK_ENTRY_ICON_PRIMARY
)
{
return
;
}
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
));
}
static
void
regex_filter_toggled_cb
(
GtkToggleToolButton
*
button
,
PidginDebugWindow
*
win
)
{
gboolean
active
;
active
=
gtk_toggle_tool_button_get_active
(
button
);
purple_prefs_set_bool
(
PIDGIN_PREFS_ROOT
"/debug/filter"
,
active
);
regex_toggle_filter
(
win
,
active
);
}
static
void
debug_window_set_filter_level
(
PidginDebugWindow
*
win
,
int
level
)
{
gboolean
scroll
;
int
i
;
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
,
NULL
);
}
if
(
scroll
)
{
gtk_text_view_scroll_to_mark
(
GTK_TEXT_VIEW
(
win
->
textview
),
win
->
end_mark
,
0
,
TRUE
,
0
,
1
);
}
}
static
void
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
);
}
static
void
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
)));
}
static
void
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
));
}
static
void
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
);
}
static
gboolean
toolbar_context
(
GtkWidget
*
toolbar
,
gint
x
,
gint
y
,
gint
button
,
gpointer
null
)
{
GtkWidget
*
menu
,
*
item
;
const
char
*
text
[
3
];
GtkToolbarStyle
value
[
3
];
int
i
;
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
;
menu
=
gtk_menu_new
();
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
);
return
FALSE
;
}
static
void
pidgin_debug_window_class_init
(
PidginDebugWindowClass
*
klass
)
{
GtkWidgetClass
*
widget_class
=
GTK_WIDGET_CLASS
(
klass
);
gtk_widget_class_set_template_from_resource
(
widget_class
,
"/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_child
(
widget_class
,
PidginDebugWindow
,
popover
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginDebugWindow
,
popover_invert
);
gtk_widget_class_bind_template_child
(
widget_class
,
PidginDebugWindow
,
popover_highlight
);
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
,
regex_changed_cb
);
gtk_widget_class_bind_template_callback
(
widget_class
,
regex_popup_cb
);
gtk_widget_class_bind_template_callback
(
widget_class
,
regex_menu_cb
);
gtk_widget_class_bind_template_callback
(
widget_class
,
regex_key_release_cb
);
gtk_widget_class_bind_template_callback
(
widget_class
,
filter_level_changed_cb
);
}
static
void
pidgin_debug_window_init
(
PidginDebugWindow
*
win
)
{
gint
width
,
height
;
void
*
handle
;
GtkTextIter
end
;
GtkStyleContext
*
context
;
GtkCssProvider
*
filter_css
;
const
gchar
*
res
=
"/im/pidgin/Pidgin/Debug/filter.css"
;
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
(
"pidgindebug"
,
"Setting dimensions to %d, %d
\n
"
,
width
,
height
);
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
);
/* regex entry */
filter_css
=
gtk_css_provider_new
();
gtk_css_provider_load_from_resource
(
filter_css
,
res
);
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
);
gtk_toggle_button_set_active
(
GTK_TOGGLE_BUTTON
(
win
->
popover_invert
),
win
->
invert
);
gtk_toggle_button_set_active
(
GTK_TOGGLE_BUTTON
(
win
->
popover_highlight
),
win
->
highlight
);
}
/* The *start* and *end* marks bound the beginning and end of an
insertion, used for filtering. The *end* mark is also used for
auto-scrolling. */
gtk_text_buffer_get_end_iter
(
win
->
buffer
,
&
end
);
win
->
start_mark
=
gtk_text_buffer_create_mark
(
win
->
buffer
,
"start"
,
&
end
,
TRUE
);
win
->
end_mark
=
gtk_text_buffer_create_mark
(
win
->
buffer
,
"end"
,
&
end
,
FALSE
);
/* Set active filter level in textview */
debug_window_set_filter_level
(
win
,
purple_prefs_get_int
(
PIDGIN_PREFS_ROOT
"/debug/filterlevel"
));
clear_cb
(
NULL
,
win
);
}
static
gboolean
debug_enabled_timeout_cb
(
gpointer
data
)
{
PidginDebugUi
*
ui
=
PIDGIN_DEBUG_UI
(
data
);
ui
->
debug_enabled_timer
=
0
;
pidgin_debug_window_show
();
return
FALSE
;
}
static
gboolean
debug_disabled_timeout_cb
(
gpointer
data
)
{
PidginDebugUi
*
ui
=
PIDGIN_DEBUG_UI
(
data
);
ui
->
debug_enabled_timer
=
0
;
pidgin_debug_window_hide
();
return
FALSE
;
}
static
void
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
);
else
ui
->
debug_enabled_timer
=
g_timeout_add
(
0
,
debug_disabled_timeout_cb
,
data
);
}
static
void
pidgin_debug_g_log_handler
(
const
gchar
*
domain
,
GLogLevelFlags
flags
,
const
gchar
*
msg
,
gpointer
user_data
)
{
PurpleDebugLevel
level
;
GString
*
category
=
g_string_new
(
"GLog-"
);
if
(
domain
!=
NULL
)
{
g_string_append_printf
(
category
,
"%s-"
,
domain
);
}
if
((
flags
&
G_LOG_LEVEL_ERROR
)
!=
0
)
{
g_string_append
(
category
,
"Error"
);
level
=
PURPLE_DEBUG_ERROR
;
}
else
if
((
flags
&
G_LOG_LEVEL_CRITICAL
)
!=
0
)
{
g_string_append
(
category
,
"Fatal"
);
level
=
PURPLE_DEBUG_FATAL
;
}
else
if
((
flags
&
G_LOG_LEVEL_WARNING
)
!=
0
)
{
g_string_append
(
category
,
"Warning"
);
level
=
PURPLE_DEBUG_WARNING
;
}
else
if
((
flags
&
G_LOG_LEVEL_MESSAGE
)
!=
0
)
{
g_string_append
(
category
,
"Message"
);
level
=
PURPLE_DEBUG_INFO
;
}
else
if
((
flags
&
G_LOG_LEVEL_INFO
)
!=
0
)
{
g_string_append
(
category
,
"Info"
);
level
=
PURPLE_DEBUG_INFO
;
}
else
if
((
flags
&
G_LOG_LEVEL_DEBUG
)
!=
0
)
{
g_string_append
(
category
,
"Debug"
);
level
=
PURPLE_DEBUG_MISC
;
}
else
{
g_string_append
(
category
,
"Unknown"
);
level
=
PURPLE_DEBUG_MISC
;
}
purple_debug
(
level
,
category
->
str
,
"%s"
,
msg
);
g_string_free
(
category
,
TRUE
);
}
static
void
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
);
g_log_set_default_handler
(
pidgin_debug_g_log_handler
,
NULL
);
}
static
void
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
);
}
void
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
);
}
void
pidgin_debug_window_hide
(
void
)
{
if
(
debug_win
!=
NULL
)
{
gtk_widget_destroy
(
GTK_WIDGET
(
debug_win
));
debug_window_destroy
(
NULL
,
NULL
,
NULL
);
}
}
static
void
pidgin_debug_print
(
PurpleDebugUi
*
self
,
PurpleDebugLevel
level
,
const
char
*
category
,
const
char
*
arg_s
)
{
GtkTextTag
*
level_tag
;
const
char
*
mdate
;
time_t
mtime
;
GtkTextIter
end
;
gboolean
scroll
;
if
(
debug_win
==
NULL
)
return
;
if
(
!
purple_prefs_get_bool
(
PIDGIN_PREFS_ROOT
"/debug/enabled"
))
return
;
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
,
&
end
);
level_tag
=
debug_win
->
tags
.
level
[
level
];
mtime
=
time
(
NULL
);
mdate
=
purple_utf8_strftime
(
"(%H:%M:%S) "
,
localtime
(
&
mtime
));
gtk_text_buffer_insert_with_tags
(
debug_win
->
buffer
,
&
end
,
mdate
,
-1
,
level_tag
,
debug_win
->
paused
?
debug_win
->
tags
.
paused
:
NULL
,
NULL
);
if
(
category
&&
*
category
)
{
gtk_text_buffer_insert_with_tags
(
debug_win
->
buffer
,
&
end
,
category
,
-1
,
level_tag
,
debug_win
->
tags
.
category
,
debug_win
->
paused
?
debug_win
->
tags
.
paused
:
NULL
,
NULL
);
gtk_text_buffer_insert_with_tags
(
debug_win
->
buffer
,
&
end
,
": "
,
2
,
level_tag
,
debug_win
->
tags
.
category
,
debug_win
->
paused
?
debug_win
->
tags
.
paused
:
NULL
,
NULL
);
}
gtk_text_buffer_insert_with_tags
(
debug_win
->
buffer
,
&
end
,
arg_s
,
-1
,
level_tag
,
debug_win
->
paused
?
debug_win
->
tags
.
paused
:
NULL
,
NULL
);
gtk_text_buffer_insert_with_tags
(
debug_win
->
buffer
,
&
end
,
"
\n
"
,
1
,
level_tag
,
debug_win
->
paused
?
debug_win
->
tags
.
paused
:
NULL
,
NULL
);
if
(
purple_prefs_get_bool
(
PIDGIN_PREFS_ROOT
"/debug/filter"
)
&&
debug_win
->
regex
)
{
/* Filter out any new messages. */
GtkTextIter
start
;
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
,
debug_win
->
end_mark
);
do_regex
(
debug_win
,
&
start
,
&
end
);
}
if
(
scroll
)
{
gtk_text_view_scroll_to_mark
(
GTK_TEXT_VIEW
(
debug_win
->
textview
),
debug_win
->
end_mark
,
0
,
TRUE
,
0
,
1
);
}
}
static
gboolean
pidgin_debug_is_enabled
(
PurpleDebugUi
*
self
,
PurpleDebugLevel
level
,
const
char
*
category
)
{
return
(
debug_win
!=
NULL
&&
purple_prefs_get_bool
(
PIDGIN_PREFS_ROOT
"/debug/enabled"
));
}
static
void
pidgin_debug_ui_interface_init
(
PurpleDebugUiInterface
*
iface
)
{
iface
->
print
=
pidgin_debug_print
;
iface
->
is_enabled
=
pidgin_debug_is_enabled
;
}
static
void
pidgin_debug_ui_class_init
(
PidginDebugUiClass
*
klass
)
{
GObjectClass
*
object_class
=
G_OBJECT_CLASS
(
klass
);
object_class
->
finalize
=
pidgin_debug_ui_finalize
;
}
PidginDebugUi
*
pidgin_debug_ui_new
(
void
)
{
return
g_object_new
(
PIDGIN_TYPE_DEBUG_UI
,
NULL
);
}
void
*
pidgin_debug_get_handle
()
{
static
int
handle
;
return
&
handle
;
}