summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Wasilczyk <tomkiewicz@cpw.pidgin.im>2013-05-09 13:15:30 +0200
committerTomasz Wasilczyk <tomkiewicz@cpw.pidgin.im>2013-05-09 13:15:30 +0200
commit91f702986e0674e85c823ee4b4e4b1c6f9be16f8 (patch)
tree70698955f02916f96a49377f9feddc5201293918
parent57e52ff7c35787f8527a4726245f64d92e3f8bf4 (diff)
parent4173a70045033ccc1498c61c0392c3f6b347f203 (diff)
downloadpidgin-91f702986e0674e85c823ee4b4e4b1c6f9be16f8.tar.gz
Merge from main
-rw-r--r--.hgignore1
-rw-r--r--configure.ac1
-rw-r--r--libpurple/cipher.c2
-rw-r--r--pidgin/Makefile.am8
-rw-r--r--pidgin/gtkdebug.c406
-rw-r--r--pidgin/gtkdebug.html251
-rw-r--r--pidgin/gtkmain.c28
-rw-r--r--pidgin/gtkprefs.c48
8 files changed, 420 insertions, 325 deletions
diff --git a/.hgignore b/.hgignore
index d8707ec018..65601cd8d8 100644
--- a/.hgignore
+++ b/.hgignore
@@ -89,6 +89,7 @@ pidgin-.*.tar.gz
pidgin.apspec$
pidgin.desktop$
pidgin.spec$
+pidgin/.*\.html\.h$
pidgin/pidgin$
pidgin/pixmaps/emotes/default/24/theme
pidgin/pixmaps/emotes/none/theme
diff --git a/configure.ac b/configure.ac
index 50958fce85..e33a8ecd8f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -102,6 +102,7 @@ GNT_LT_VERSION_INFO="gnt_lt_current:gnt_micro_version:gnt_minor_version"
AC_SUBST(GNT_LT_VERSION_INFO)
AC_PATH_PROG(sedpath, sed)
+AC_PATH_PROG(xxdpath, xxd)
dnl Storing configure arguments
AC_DEFINE_UNQUOTED(CONFIG_ARGS, "$ac_configure_args", [configure arguments])
diff --git a/libpurple/cipher.c b/libpurple/cipher.c
index ec1f20f47b..b44fbe39fe 100644
--- a/libpurple/cipher.c
+++ b/libpurple/cipher.c
@@ -513,7 +513,7 @@ purple_cipher_context_encrypt(PurpleCipherContext *context,
g_return_val_if_fail(context != NULL, -1);
g_return_val_if_fail(input != NULL, -1);
g_return_val_if_fail(output != NULL, -1);
- g_return_val_if_fail(out_size < in_len, -1);
+ g_return_val_if_fail(out_size >= in_len, -1);
cipher = context->cipher;
g_return_val_if_fail(cipher, -1);
diff --git a/pidgin/Makefile.am b/pidgin/Makefile.am
index 1ff1a23747..8df5a1b7e1 100644
--- a/pidgin/Makefile.am
+++ b/pidgin/Makefile.am
@@ -152,6 +152,14 @@ pidginincludedir=$(includedir)/pidgin
pidgininclude_HEADERS = \
$(pidgin_headers)
+pidgin_builtheaders = gtkdebug.html.h
+
+BUILT_SOURCES = $(pidgin_builtheaders)
+
+%.html.h: %.html
+ $(AM_V_GEN)echo "static const char $*_html[] = {" > $@
+ $(AM_V_at)$(sedpath) -e 's/^[ ]\+//g' -e 's/[ ]\+/ /g' $< | $(xxdpath) -i | sed -e 's/\(0x[0-9a-f][0-9a-f]\)$$/\1, 0x00/' >> $@
+ $(AM_V_at)echo "};" >> $@
pidgin_DEPENDENCIES = @LIBOBJS@
pidgin_LDFLAGS = -export-dynamic
diff --git a/pidgin/gtkdebug.c b/pidgin/gtkdebug.c
index 1b6a210015..d23e53cde6 100644
--- a/pidgin/gtkdebug.c
+++ b/pidgin/gtkdebug.c
@@ -41,6 +41,8 @@
#include "gtk3compat.h"
+#include "gtkdebug.html.h"
+
typedef struct
{
GtkWidget *window;
@@ -57,42 +59,6 @@ typedef struct
GRegex *regex;
} DebugWindow;
-#define EMPTY_HTML \
- "<html><head><style>" \
- "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. */ \
- /* Filter levels */ \
- "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;}" \
- /* Regex */ \
- "div.hide{display:none;}" \
- "span.regex{background-color:#ffafaf;font-weight:bold;}" \
- "</style><script>" \
- "function append(level, time, cat, msg) {" \
- "var div = document.createElement('div');" \
- "div.className = 'l' + level;" \
- "div.appendChild(document.createTextNode('(' + time + ') '));" \
- "if (cat) {" \
- "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));" \
- "document.body.appendChild(div);" \
- "alert('appended');" \
- "}" \
- "</script></head><body class=l%d></body></html>"
-
static DebugWindow *debug_win = NULL;
static guint debug_enabled_timer = 0;
@@ -166,13 +132,7 @@ save_cb(GtkWidget *w, DebugWindow *win)
static void
clear_cb(GtkWidget *w, DebugWindow *win)
{
- char *tmp;
- int level;
-
- level = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel");
- tmp = g_strdup_printf(EMPTY_HTML, level);
- gtk_webview_load_html_string(GTK_WEBVIEW(win->text), tmp);
- g_free(tmp);
+ gtk_webview_safe_execute_script(GTK_WEBVIEW(win->text), "clear();");
}
static void
@@ -180,20 +140,10 @@ pause_cb(GtkWidget *w, DebugWindow *win)
{
win->paused = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(w));
- if (win->paused) {
- gtk_webview_append_html(GTK_WEBVIEW(win->text), "<div id=pause></div>");
- } else {
- WebKitDOMDocument *dom;
- WebKitDOMElement *pause;
-
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(win->text));
- pause = webkit_dom_document_get_element_by_id(dom, "pause");
- if (pause) {
- WebKitDOMNode *parent;
- parent = webkit_dom_node_get_parent_node(WEBKIT_DOM_NODE(pause));
- webkit_dom_node_remove_child(parent, WEBKIT_DOM_NODE(pause), NULL);
- }
- }
+ if (win->paused)
+ gtk_webview_safe_execute_script(GTK_WEBVIEW(win->text), "pauseOutput();");
+ else
+ gtk_webview_safe_execute_script(GTK_WEBVIEW(win->text), "resumeOutput();");
}
/******************************************************************************
@@ -201,243 +151,63 @@ pause_cb(GtkWidget *w, DebugWindow *win)
*****************************************************************************/
static void
regex_clear_color(GtkWidget *w) {
- gtk_widget_modify_base(w, GTK_STATE_NORMAL, NULL);
-}
-
-static void
-regex_change_color(GtkWidget *w, guint16 r, guint16 g, guint16 b) {
- GdkColor color;
-
- color.red = r;
- color.green = g;
- color.blue = b;
-
- gtk_widget_modify_base(w, GTK_STATE_NORMAL, &color);
-}
-
-static void
-regex_toggle_div(WebKitDOMNode *div)
-{
- WebKitDOMDOMTokenList *classes;
-
- if (!WEBKIT_DOM_IS_HTML_ELEMENT(div))
- return;
-
-#if (WEBKIT_MAJOR_VERSION == 1 && \
- WEBKIT_MINOR_VERSION == 9 && \
- WEBKIT_MICRO_VERSION == 90)
- /* Workaround WebKit API bug. */
- classes = webkit_dom_element_get_class_list(WEBKIT_DOM_ELEMENT(div));
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context = gtk_widget_get_style_context(w);
+ gtk_style_context_remove_class(context, "good-filter");
+ gtk_style_context_remove_class(context, "bad-filter");
#else
- classes = webkit_dom_html_element_get_class_list(WEBKIT_DOM_HTML_ELEMENT(div));
+ gtk_widget_modify_base(w, GTK_STATE_NORMAL, NULL);
#endif
- webkit_dom_dom_token_list_toggle(classes, "hide", NULL);
- g_object_unref(classes);
}
static void
-regex_highlight_clear(WebKitDOMDocument *dom)
-{
- WebKitDOMNodeList *nodes;
- gulong i;
-
- /* Remove highlighting SPANs */
- nodes = webkit_dom_document_get_elements_by_class_name(dom, "regex");
- i = webkit_dom_node_list_get_length(nodes);
- while (i--) {
- WebKitDOMNode *span, *parent;
- char *content;
- WebKitDOMText *text;
- GError *err = NULL;
-
- span = webkit_dom_node_list_item(nodes, i);
- parent = webkit_dom_node_get_parent_node(span);
-
- content = webkit_dom_node_get_text_content(span);
- text = webkit_dom_document_create_text_node(dom, content);
- g_free(content);
-
- webkit_dom_node_replace_child(parent, WEBKIT_DOM_NODE(text), span, &err);
- }
+regex_change_color(GtkWidget *w, gboolean success) {
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context = gtk_widget_get_style_context(w);
- g_object_unref(nodes);
-}
-
-static void
-regex_highlight_text_nodes(WebKitDOMDocument *dom, WebKitDOMNode *div,
- gint start_pos, gint end_pos)
-{
- GSList *data = NULL;
- WebKitDOMNode *node;
- WebKitDOMRange *range;
- WebKitDOMElement *span;
- gint ind, end_ind;
- gint this_start, this_end;
-
- ind = 0;
- webkit_dom_node_normalize(div);
- node = div;
-
- /* First, find the container nodes and offsets to apply highlighting. */
- do {
- if (webkit_dom_node_get_node_type(node) == 3/*TEXT_NODE*/) {
- /* The GObject model does not correctly reflect the type, hence the
- regular cast. */
- end_ind = ind + webkit_dom_character_data_get_length((WebKitDOMCharacterData*)node);
-
- if (start_pos <= ind)
- this_start = 0;
- else if (start_pos < end_ind)
- this_start = start_pos - ind;
- else
- this_start = -1;
-
- if (end_pos < end_ind)
- this_end = end_pos - ind;
- else
- this_end = end_ind - ind;
-
- if (this_start != -1 && this_start < this_end) {
- data = g_slist_prepend(data, GINT_TO_POINTER(this_end));
- data = g_slist_prepend(data, GINT_TO_POINTER(this_start));
- data = g_slist_prepend(data, node);
- }
-
- ind = end_ind;
- }
-
- if (webkit_dom_node_has_child_nodes(node)) {
- node = webkit_dom_node_get_first_child(node);
- } else {
- while (node != div) {
- WebKitDOMNode *next;
-
- next = webkit_dom_node_get_next_sibling(node);
- if (next) {
- node = next;
- break;
- } else {
- node = webkit_dom_node_get_parent_node(node);
- }
- }
- }
- } while (node != div);
-
- /* 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. */
- while (data) {
- node = WEBKIT_DOM_NODE(data->data);
- data = g_slist_delete_link(data, data);
- this_start = GPOINTER_TO_INT(data->data);
- data = g_slist_delete_link(data, data);
- this_end = GPOINTER_TO_INT(data->data);
- data = g_slist_delete_link(data, data);
-
- range = webkit_dom_document_create_range(dom);
- webkit_dom_range_set_start(range, node, this_start, NULL);
- webkit_dom_range_set_end(range, node, this_end, NULL);
- span = webkit_dom_document_create_element(dom, "span", NULL);
- webkit_dom_html_element_set_class_name(WEBKIT_DOM_HTML_ELEMENT(span),
- "regex");
- webkit_dom_range_surround_contents(range, WEBKIT_DOM_NODE(span), NULL);
+ 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
-regex_match(DebugWindow *win, WebKitDOMDocument *dom, WebKitDOMNode *div)
-{
- GMatchInfo *match_info;
- gchar *text;
- gchar *plaintext;
-
- text = webkit_dom_node_get_text_content(div);
- if (!text)
- return;
-
- /* I don't like having to do this, but we need it for highlighting. Plus
- * it makes the ^ and $ operators work :)
- */
- plaintext = purple_markup_strip_html(text);
-
- /* We do a first pass to see if it matches at all. If it does we work out
- * the offsets to highlight.
- */
- if (g_regex_match(win->regex, plaintext, 0, &match_info) != win->invert) {
- /* If we're not highlighting or the expression is inverted, we're
- * done and move on.
- */
- if (!win->highlight || win->invert) {
- g_free(plaintext);
- g_match_info_free(match_info);
- return;
- }
-
- do {
- gint m, count;
- gint start_pos, end_pos;
-
- if (!g_match_info_matches(match_info))
- break;
-
- count = g_match_info_get_match_count(match_info);
- if (count == 1)
- m = 0;
- else
- m = 1;
-
- for (; m < count; m++)
- {
- g_match_info_fetch_pos(match_info, m, &start_pos, &end_pos);
-
- if (end_pos == -1)
- break;
-
- regex_highlight_text_nodes(dom, div, start_pos, end_pos);
- }
- } while (g_match_info_next(match_info, NULL));
+#else
+ GdkColor color;
- g_match_info_free(match_info);
+ if (success) {
+ color.red = 0xAFFF;
+ color.green = 0xFFFF;
+ color.blue = 0xAFFF;
} else {
- regex_toggle_div(div);
+ color.red = 0xFFFF;
+ color.green = 0xAFFF;
+ color.blue = 0xAFFF;
}
- g_free(plaintext);
- g_free(text);
+ gtk_widget_modify_base(w, GTK_STATE_NORMAL, &color);
+#endif
}
static void
regex_toggle_filter(DebugWindow *win, gboolean filter)
{
- WebKitDOMDocument *dom;
- WebKitDOMNodeList *list;
- gulong i;
-
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(win->text));
-
- if (win->highlight)
- regex_highlight_clear(dom);
-
- /* Re-show debug lines that didn't match regex */
- list = webkit_dom_document_get_elements_by_class_name(dom, "hide");
- i = webkit_dom_node_list_get_length(list);
-
- while (i--) {
- WebKitDOMNode *div = webkit_dom_node_list_item(list, i);
- regex_toggle_div(div);
- }
-
- g_object_unref(list);
+ gtk_webview_safe_execute_script(GTK_WEBVIEW(win->text), "regex.clear();");
if (filter) {
- list = webkit_dom_document_get_elements_by_tag_name(dom, "div");
-
- for (i = 0; i < webkit_dom_node_list_get_length(list); i++) {
- WebKitDOMNode *div = webkit_dom_node_list_item(list, i);
- regex_match(win, dom, div);
- }
-
- g_object_unref(list);
+ const char *text;
+ char *regex;
+ char *script;
+
+ text = gtk_entry_get_text(GTK_ENTRY(win->expression));
+ regex = gtk_webview_quote_js_string(text);
+ script = g_strdup_printf("regex.filterAll(%s, %s, %s);",
+ regex,
+ win->invert ? "true" : "false",
+ win->highlight ? "true" : "false");
+ gtk_webview_safe_execute_script(GTK_WEBVIEW(win->text), script);
+ g_free(script);
+ g_free(regex);
}
}
@@ -526,14 +296,18 @@ regex_changed_cb(GtkWidget *w, DebugWindow *win) {
if (win->regex)
g_regex_unref(win->regex);
+#if GLIB_CHECK_VERSION(2,34,0)
+ win->regex = g_regex_new(text, G_REGEX_CASELESS|G_REGEX_JAVASCRIPT_COMPAT, 0, NULL);
+#else
win->regex = g_regex_new(text, G_REGEX_CASELESS, 0, NULL);
+#endif
if (win->regex == NULL) {
/* failed to compile */
- regex_change_color(win->expression, 0xFFFF, 0xAFFF, 0xAFFF);
+ regex_change_color(win->expression, FALSE);
gtk_widget_set_sensitive(win->filter, FALSE);
} else {
/* compiled successfully */
- regex_change_color(win->expression, 0xAFFF, 0xFFFF, 0xAFFF);
+ regex_change_color(win->expression, TRUE);
gtk_widget_set_sensitive(win->filter, TRUE);
}
}
@@ -587,18 +361,14 @@ static void
filter_level_pref_changed(const char *name, PurplePrefType type, gconstpointer value, gpointer data)
{
DebugWindow *win = data;
- WebKitDOMDocument *dom;
- WebKitDOMHTMLElement *body;
int level = GPOINTER_TO_INT(value);
char *tmp;
if (level != gtk_combo_box_get_active(GTK_COMBO_BOX(win->filterlevel)))
gtk_combo_box_set_active(GTK_COMBO_BOX(win->filterlevel), level);
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(win->text));
- body = webkit_dom_document_get_body(dom);
- tmp = g_strdup_printf("l%d", level);
- webkit_dom_html_element_set_class_name(body, tmp);
+ tmp = g_strdup_printf("setFilterLevel('%d');", level);
+ gtk_webview_safe_execute_script(GTK_WEBVIEW(win->text), tmp);
g_free(tmp);
}
@@ -654,42 +424,6 @@ toolbar_context(GtkWidget *toolbar, GdkEventButton *event, gpointer null)
return FALSE;
}
-static void
-debug_window_appended(DebugWindow *win)
-{
- WebKitDOMDocument *dom;
- WebKitDOMHTMLElement *body;
- WebKitDOMNode *div;
-
- if (!gtk_toggle_tool_button_get_active(
- GTK_TOGGLE_TOOL_BUTTON(win->filter)))
- return;
-
- dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(win->text));
- body = webkit_dom_document_get_body(dom);
- div = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body));
-
- if (webkit_dom_element_webkit_matches_selector(
- WEBKIT_DOM_ELEMENT(div), "body>div:not(#pause)", NULL))
- regex_match(win, dom, div);
-}
-
-static gboolean debug_window_alert_cb(WebKitWebView *webview,
- WebKitWebFrame *frame, gchar *message, gpointer _win)
-{
- DebugWindow *win = _win;
-
- if (!win || !win->window)
- return FALSE;
-
- if (g_strcmp0(message, "appended") == 0) {
- debug_window_appended(win);
- return TRUE;
- }
-
- return FALSE;
-}
-
static DebugWindow *
debug_window_new(void)
{
@@ -700,6 +434,23 @@ debug_window_new(void)
gint width, height;
void *handle;
GtkToolItem *item;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+ GtkCssProvider *filter_css;
+ const gchar filter_style[] =
+ ".bad-filter {"
+ "color: @error_fg_color;"
+ "text-shadow: 0 1px @error_text_shadow;"
+ "background-image: none;"
+ "background-color: @error_bg_color;"
+ "}"
+ ".good-filter {"
+ "color: @question_fg_color;"
+ "text-shadow: 0 1px @question_text_shadow;"
+ "background-image: none;"
+ "background-color: @success_color;"
+ "}";
+#endif
win = g_new0(DebugWindow, 1);
@@ -793,6 +544,16 @@ debug_window_new(void)
gtk_container_add(GTK_CONTAINER(item), GTK_WIDGET(win->expression));
gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
+#if GTK_CHECK_VERSION(3,0,0)
+ /* TODO: implement it for GTK2 */
+ 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);
+#endif
+
/* this needs to be before the text is set from the pref if we want it
* to colorize a stored expression.
*/
@@ -848,13 +609,10 @@ debug_window_new(void)
frame = pidgin_create_webview(FALSE, &win->text, NULL, NULL);
gtk_webview_set_format_functions(GTK_WEBVIEW(win->text),
GTK_WEBVIEW_ALL ^ GTK_WEBVIEW_SMILEY ^ GTK_WEBVIEW_IMAGE);
- gtk_webview_set_autoscroll(GTK_WEBVIEW(win->text), TRUE);
+ gtk_webview_load_html_string(GTK_WEBVIEW(win->text), gtkdebug_html);
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
gtk_widget_show(frame);
- g_signal_connect(G_OBJECT(win->text), "script-alert",
- G_CALLBACK(debug_window_alert_cb), win);
-
clear_cb(NULL, win);
gtk_widget_show_all(win->window);
diff --git a/pidgin/gtkdebug.html b/pidgin/gtkdebug.html
new file mode 100644
index 0000000000..9d63d5fc32
--- /dev/null
+++ b/pidgin/gtkdebug.html
@@ -0,0 +1,251 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+ 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. */
+ /* Filter levels */
+ 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;}
+ /* Regex */
+ div.hide{display:none;}
+ span.regex{background-color:#ffafaf;font-weight:bold;}
+ </style>
+ <script>
+ function nearBottom() {
+ return ( document.body.scrollTop >= ( document.body.offsetHeight - ( window.innerHeight * 1.5 ) ) );
+ }
+
+ function scrollToBottom() {
+ document.body.scrollTop = document.body.offsetHeight;
+ }
+
+ regex = {
+ clear: function () {
+ var list, i;
+ var scroll = nearBottom();
+
+ /* Remove highlighting SPANs */
+ list = document.getElementsByClassName('regex');
+ i = list.length;
+ while (i--) {
+ var span = list[i];
+ var parent = span.parentNode;
+ var content = span.textContent;
+ var text = document.createTextNode(content);
+ parent.replaceChild(text, span);
+ }
+
+ /* Remove hidden DIVs */
+ list = document.getElementsByClassName('hide');
+ i = list.length;
+ while (i--) {
+ list[i].classList.remove('hide');
+ }
+
+ if (scroll)
+ scrollToBottom();
+ this.enabled = false;
+ },
+
+ highlightTextNodes: function (div, start_pos, end_pos) {
+ var data = [], node, range, span, contents;
+ var ind, end_ind
+ var this_start, this_end;
+
+ ind = 0;
+ div.normalize();
+ node = div;
+
+ /* First, find the container nodes and offsets to apply highlighting. */
+ do {
+ if (node.nodeType === Node.TEXT_NODE) {
+ end_ind = ind + node.length;
+
+ if (start_pos <= ind)
+ this_start = 0;
+ else if (start_pos < end_ind)
+ this_start = start_pos - ind;
+ else
+ this_start = -1;
+
+ if (end_pos < end_ind)
+ this_end = end_pos - ind;
+ else
+ this_end = end_ind - ind;
+
+ if (this_start != -1 && this_start < this_end) {
+ data.push(this_end, this_start, node);
+ }
+
+ ind = end_ind;
+ }
+
+ if (node.hasChildNodes()) {
+ node = node.firstChild;
+ } else {
+ while (node != div) {
+ var next = node.nextSibling;
+ if (next) {
+ node = next;
+ break;
+ } else {
+ node = node.parentNode;
+ }
+ }
+ }
+ } while (node != div);
+
+ /* 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. */
+ while (data.length) {
+ node = data.pop();
+ this_start = data.pop();
+ this_end = 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 text, match_info;
+ var m, count, start_pos, end_pos;
+
+ text = div.textContent;
+ if (!text)
+ return;
+
+ /* 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
+ * done and move on.
+ */
+ if (!this.highlight || this.invert)
+ return;
+
+ do {
+ if (match_info === null)
+ break;
+
+ count = match_info.length;
+ if (count === 1)
+ m = 0;
+ else
+ m = 1;
+
+ /* 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. */
+ m = 0; count = 1;
+
+ for (; m < count; m++) {
+ start_pos = match_info.index;
+ end_pos = this.regex.lastIndex;
+ if (end_pos == -1)
+ break;
+
+ 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));
+ } else {
+ div.classList.add('hide');
+ }
+ },
+
+ filterAll: function (str, inv, high) {
+ this.regex = new RegExp(str, 'gi');
+ this.invert = inv;
+ this.highlight = high;
+ this.enabled = true;
+ var list = document.getElementsByTagName('div');
+ for (var i = 0; i < list.length; i++)
+ this.match(list[i]);
+ },
+
+ highlight: false,
+ invert: false,
+ enabled: false,
+ regex: undefined
+ }
+
+ function append(level, time, cat, msg) {
+ var div = document.createElement('div');
+ div.className = 'l' + level;
+
+ div.appendChild(document.createTextNode('(' + time + ') '));
+
+ if (cat) {
+ 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));
+
+ if (regex.enabled)
+ regex.match(div);
+
+ var scroll = nearBottom();
+ document.body.appendChild(div);
+ if (scroll)
+ scrollToBottom();
+ }
+
+ function clear() {
+ document.body.innerHTML = '';
+ }
+
+ function pauseOutput() {
+ document.body.insertAdjacentHTML('beforeEnd', '<div id=pause></div>');
+ }
+
+ function resumeOutput() {
+ var pause = document.getElementById('pause');
+ if (pause) {
+ var parent = pause.parentNode;
+ parent.removeChild(pause);
+ scrollToBottom();
+ }
+ }
+
+ function setFilterLevel(l) {
+ var scroll = nearBottom();
+
+ document.body.className = 'l'+l;
+
+ if (scroll)
+ scrollToBottom();
+ }
+ </script>
+ </head>
+ <body class=l0></body>
+</html>
+
diff --git a/pidgin/gtkmain.c b/pidgin/gtkmain.c
index d71b733f57..10d61dbe23 100644
--- a/pidgin/gtkmain.c
+++ b/pidgin/gtkmain.c
@@ -429,6 +429,10 @@ int main(int argc, char *argv[])
char *opt_login_arg = NULL;
char *opt_session_arg = NULL;
char *search_path;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkCssProvider *provider;
+ GdkScreen *screen;
+#endif
GList *accounts;
#ifdef HAVE_SIGNAL_H
int sig_indx; /* for setting up signal catching */
@@ -440,6 +444,8 @@ int main(int argc, char *argv[])
#ifndef DEBUG
char *segfault_message_tmp;
#endif
+#endif
+#if defined(HAVE_SIGNAL_H) || GTK_CHECK_VERSION(3,0,0)
GError *error;
#endif
int opt;
@@ -690,9 +696,11 @@ int main(int argc, char *argv[])
purple_debug_set_enabled(debug_enabled);
+#if !GTK_CHECK_VERSION(3,0,0)
search_path = g_build_filename(purple_user_dir(), "gtkrc-2.0", NULL);
gtk_rc_add_default_file(search_path);
g_free(search_path);
+#endif
gui_check = gtk_init_check(&argc, &argv);
if (!gui_check) {
@@ -709,6 +717,26 @@ int main(int argc, char *argv[])
return 1;
}
+#if GTK_CHECK_VERSION(3,0,0)
+ search_path = g_build_filename(purple_user_dir(), "gtk-3.0.css", NULL);
+
+ error = NULL;
+ provider = gtk_css_provider_new();
+ gui_check = gtk_css_provider_load_from_path(provider, search_path, &error);
+
+ if (gui_check && !error) {
+ screen = gdk_screen_get_default();
+ gtk_style_context_add_provider_for_screen(screen,
+ GTK_STYLE_PROVIDER(provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+ } else {
+ purple_debug_error("gtk", "Unable to load custom gtk-3.0.css: %s\n",
+ error ? error->message : "(unknown error)");
+ }
+
+ g_free(search_path);
+#endif
+
g_set_application_name(PIDGIN_NAME);
#ifdef _WIN32
diff --git a/pidgin/gtkprefs.c b/pidgin/gtkprefs.c
index 9c4bccf991..65dc931067 100644
--- a/pidgin/gtkprefs.c
+++ b/pidgin/gtkprefs.c
@@ -1948,6 +1948,26 @@ conv_page(void)
static void
network_ip_changed(GtkEntry *entry, gpointer data)
{
+#if GTK_CHECK_VERSION(3,0,0)
+ const gchar *text = gtk_entry_get_text(entry);
+ GtkStyleContext *context = gtk_widget_get_style_context(GTK_WIDGET(entry));
+
+ if (text && *text) {
+ if (purple_ip_address_is_valid(text)) {
+ purple_network_set_public_ip(text);
+ gtk_style_context_add_class(context, "good-ip");
+ gtk_style_context_remove_class(context, "bad-ip");
+ } else {
+ gtk_style_context_add_class(context, "bad-ip");
+ gtk_style_context_remove_class(context, "good-ip");
+ }
+
+ } else {
+ purple_network_set_public_ip("");
+ gtk_style_context_remove_class(context, "bad-ip");
+ gtk_style_context_remove_class(context, "good-ip");
+ }
+#else
const gchar *text = gtk_entry_get_text(entry);
GdkColor color;
@@ -1970,6 +1990,7 @@ network_ip_changed(GtkEntry *entry, gpointer data)
purple_network_set_public_ip("");
gtk_widget_modify_base(GTK_WIDGET(entry), GTK_STATE_NORMAL, NULL);
}
+#endif
}
static gboolean
@@ -2092,6 +2113,23 @@ network_page(void)
GtkWidget *vbox, *hbox, *entry;
GtkWidget *label, *auto_ip_checkbox, *ports_checkbox, *spin_button;
GtkSizeGroup *sg;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+ GtkCssProvider *ip_css;
+ const gchar ip_style[] =
+ ".bad-ip {"
+ "color: @error_fg_color;"
+ "text-shadow: 0 1px @error_text_shadow;"
+ "background-image: none;"
+ "background-color: @error_bg_color;"
+ "}"
+ ".good-ip {"
+ "color: @question_fg_color;"
+ "text-shadow: 0 1px @question_text_shadow;"
+ "background-image: none;"
+ "background-color: @success_color;"
+ "}";
+#endif
ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
@@ -2133,6 +2171,16 @@ network_page(void)
g_signal_connect(G_OBJECT(entry), "changed",
G_CALLBACK(network_ip_changed), NULL);
+#if GTK_CHECK_VERSION(3,0,0)
+ /* TODO: implement it for GTK2 */
+ ip_css = gtk_css_provider_new();
+ gtk_css_provider_load_from_data(ip_css, ip_style, -1, NULL);
+ context = gtk_widget_get_style_context(entry);
+ gtk_style_context_add_provider(context,
+ GTK_STYLE_PROVIDER(ip_css),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+#endif
+
hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Public _IP:"),
sg, entry, TRUE, NULL);