| 31 #include "request.h" |
31 #include "request.h" |
| 32 #include "util.h" |
32 #include "util.h" |
| 33 |
33 |
| 34 #include "gtkdebug.h" |
34 #include "gtkdebug.h" |
| 35 #include "gtkdialogs.h" |
35 #include "gtkdialogs.h" |
| 36 #include "gtkimhtml.h" |
|
| 37 #include "gtkutils.h" |
36 #include "gtkutils.h" |
| |
37 #include "gtkwebview.h" |
| 38 #include "pidginstock.h" |
38 #include "pidginstock.h" |
| 39 |
|
| 40 #ifdef HAVE_REGEX_H |
|
| 41 # include <regex.h> |
|
| 42 # define USE_REGEX 1 |
|
| 43 #else |
|
| 44 #if GLIB_CHECK_VERSION(2,14,0) |
|
| 45 # define USE_REGEX 1 |
|
| 46 #endif |
|
| 47 #endif /* HAVE_REGEX_H */ |
|
| 48 |
39 |
| 49 #include <gdk/gdkkeysyms.h> |
40 #include <gdk/gdkkeysyms.h> |
| 50 |
41 |
| 51 typedef struct |
42 typedef struct |
| 52 { |
43 { |
| 53 GtkWidget *window; |
44 GtkWidget *window; |
| 54 GtkWidget *text; |
45 GtkWidget *text; |
| 55 |
|
| 56 GtkListStore *store; |
|
| 57 |
|
| 58 gboolean paused; |
|
| 59 |
|
| 60 #ifdef USE_REGEX |
|
| 61 GtkWidget *filter; |
46 GtkWidget *filter; |
| 62 GtkWidget *expression; |
47 GtkWidget *expression; |
| |
48 GtkWidget *filterlevel; |
| |
49 |
| |
50 gboolean paused; |
| 63 |
51 |
| 64 gboolean invert; |
52 gboolean invert; |
| 65 gboolean highlight; |
53 gboolean highlight; |
| 66 |
|
| 67 guint timer; |
54 guint timer; |
| 68 # ifdef HAVE_REGEX_H |
|
| 69 regex_t regex; |
|
| 70 # else |
|
| 71 GRegex *regex; |
55 GRegex *regex; |
| 72 # endif /* HAVE_REGEX_H */ |
|
| 73 #else |
|
| 74 GtkWidget *find; |
|
| 75 #endif /* USE_REGEX */ |
|
| 76 GtkWidget *filterlevel; |
|
| 77 } DebugWindow; |
56 } DebugWindow; |
| 78 |
57 |
| 79 static const char debug_fg_colors[][8] = { |
58 #define EMPTY_HTML \ |
| 80 "#000000", /**< All debug levels. */ |
59 "<html><head><style>" \ |
| 81 "#666666", /**< Misc. */ |
60 "body{white-space:pre-wrap;}" \ |
| 82 "#000000", /**< Information. */ |
61 "div.l0{color:#000000;}" /* All debug levels. */ \ |
| 83 "#660000", /**< Warnings. */ |
62 "div.l1{color:#666666;}" /* Misc. */ \ |
| 84 "#FF0000", /**< Errors. */ |
63 "div.l2{color:#000000;}" /* Information. */ \ |
| 85 "#FF0000", /**< Fatal errors. */ |
64 "div.l3{color:#660000;}" /* Warnings. */ \ |
| 86 }; |
65 "div.l4{color:#FF0000;}" /* Errors. */ \ |
| |
66 "div.l5{color:#FF0000;font-weight:bold;}" /* Fatal errors. */ \ |
| |
67 /* Filter levels */ \ |
| |
68 "div#pause~div{display:none;}" \ |
| |
69 "body.l1 div.l0{display:none;}" \ |
| |
70 "body.l2 div.l0,body.l2 div.l1{display:none;}" \ |
| |
71 "body.l3 div.l0,body.l3 div.l1,body.l3 div.l2{display:none;}" \ |
| |
72 "body.l4 div.l0,body.l4 div.l1,body.l4 div.l2,body.l4 div.l3{display:none;}" \ |
| |
73 "body.l5 div.l0,body.l5 div.l1,body.l5 div.l2,body.l5 div.l3,body.l5 div.l4{display:none;}" \ |
| |
74 /* Regex */ \ |
| |
75 "div.hide{display:none;}" \ |
| |
76 "span.regex{background-color:#ffafaf;font-weight:bold;}" \ |
| |
77 "</style></head></html>" |
| 87 |
78 |
| 88 static DebugWindow *debug_win = NULL; |
79 static DebugWindow *debug_win = NULL; |
| 89 static guint debug_enabled_timer = 0; |
80 static guint debug_enabled_timer = 0; |
| 90 |
81 |
| 91 #ifdef USE_REGEX |
|
| 92 static void regex_filter_all(DebugWindow *win); |
|
| 93 static void regex_show_all(DebugWindow *win); |
|
| 94 #endif /* USE_REGEX */ |
|
| 95 |
|
| 96 static gint |
82 static gint |
| 97 debug_window_destroy(GtkWidget *w, GdkEvent *event, void *unused) |
83 debug_window_destroy(GtkWidget *w, GdkEvent *event, void *unused) |
| 98 { |
84 { |
| 99 purple_prefs_disconnect_by_handle(pidgin_debug_get_handle()); |
85 purple_prefs_disconnect_by_handle(pidgin_debug_get_handle()); |
| 100 |
86 |
| 101 #ifdef USE_REGEX |
|
| 102 if(debug_win->timer != 0) { |
87 if(debug_win->timer != 0) { |
| 103 const gchar *text; |
88 const gchar *text; |
| 104 |
89 |
| 105 purple_timeout_remove(debug_win->timer); |
90 purple_timeout_remove(debug_win->timer); |
| 106 |
91 |
| 107 text = gtk_entry_get_text(GTK_ENTRY(debug_win->expression)); |
92 text = gtk_entry_get_text(GTK_ENTRY(debug_win->expression)); |
| 108 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/debug/regex", text); |
93 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/debug/regex", text); |
| 109 } |
94 } |
| 110 #ifdef HAVE_REGEX_H |
|
| 111 regfree(&debug_win->regex); |
|
| 112 #else |
|
| 113 g_regex_unref(debug_win->regex); |
95 g_regex_unref(debug_win->regex); |
| 114 #endif /* HAVE_REGEX_H */ |
|
| 115 #endif /* USE_REGEX */ |
|
| 116 |
96 |
| 117 /* If the "Save Log" dialog is open then close it */ |
97 /* If the "Save Log" dialog is open then close it */ |
| 118 purple_request_close_with_handle(debug_win); |
98 purple_request_close_with_handle(debug_win); |
| 119 |
99 |
| 120 g_free(debug_win); |
100 g_free(debug_win); |
| 291 |
190 |
| 292 gtk_widget_modify_base(w, GTK_STATE_NORMAL, &color); |
191 gtk_widget_modify_base(w, GTK_STATE_NORMAL, &color); |
| 293 } |
192 } |
| 294 |
193 |
| 295 static void |
194 static void |
| 296 regex_highlight_clear(DebugWindow *win) { |
195 regex_toggle_div(WebKitDOMNode *div) |
| 297 GtkIMHtml *imhtml = GTK_IMHTML(win->text); |
196 { |
| 298 GtkTextIter s, e; |
197 WebKitDOMDOMTokenList *classes; |
| 299 |
198 |
| 300 gtk_text_buffer_get_start_iter(imhtml->text_buffer, &s); |
199 if (!WEBKIT_DOM_IS_HTML_ELEMENT(div)) |
| 301 gtk_text_buffer_get_end_iter(imhtml->text_buffer, &e); |
200 return; |
| 302 gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "regex", &s, &e); |
201 |
| 303 } |
202 classes = webkit_dom_html_element_get_class_list(WEBKIT_DOM_HTML_ELEMENT(div)); |
| 304 |
203 webkit_dom_dom_token_list_toggle(classes, "hide", NULL); |
| 305 static void |
204 } |
| 306 regex_match(DebugWindow *win, const gchar *text) { |
205 |
| 307 GtkIMHtml *imhtml = GTK_IMHTML(win->text); |
206 static void |
| 308 #ifdef HAVE_REGEX_H |
207 regex_highlight_clear(WebKitDOMDocument *dom) |
| 309 regmatch_t matches[4]; /* adjust if necessary */ |
208 { |
| 310 size_t n_matches = sizeof(matches) / sizeof(matches[0]); |
209 WebKitDOMNodeList *nodes; |
| 311 gint inverted; |
210 gulong i; |
| 312 #else |
211 |
| |
212 /* Remove highlighting SPANs */ |
| |
213 nodes = webkit_dom_document_get_elements_by_class_name(dom, "regex"); |
| |
214 i = webkit_dom_node_list_get_length(nodes); |
| |
215 while (i--) { |
| |
216 WebKitDOMNode *span, *parent; |
| |
217 char *content; |
| |
218 WebKitDOMText *text; |
| |
219 GError *err = NULL; |
| |
220 |
| |
221 span = webkit_dom_node_list_item(nodes, i); |
| |
222 parent = webkit_dom_node_get_parent_node(span); |
| |
223 |
| |
224 content = webkit_dom_node_get_text_content(span); |
| |
225 text = webkit_dom_document_create_text_node(dom, content); |
| |
226 g_free(content); |
| |
227 |
| |
228 webkit_dom_node_replace_child(parent, WEBKIT_DOM_NODE(text), span, &err); |
| |
229 } |
| |
230 } |
| |
231 |
| |
232 static void |
| |
233 regex_highlight_text_nodes(WebKitDOMDocument *dom, WebKitDOMNode *div, |
| |
234 gint start_pos, gint end_pos) |
| |
235 { |
| |
236 GSList *data = NULL; |
| |
237 WebKitDOMNode *node; |
| |
238 WebKitDOMRange *range; |
| |
239 WebKitDOMElement *span; |
| |
240 gint ind, end_ind; |
| |
241 gint this_start, this_end; |
| |
242 |
| |
243 ind = 0; |
| |
244 webkit_dom_node_normalize(div); |
| |
245 node = div; |
| |
246 |
| |
247 /* First, find the container nodes and offsets to apply highlighting. */ |
| |
248 do { |
| |
249 if (webkit_dom_node_get_node_type(node) == 3/*TEXT_NODE*/) { |
| |
250 /* The GObject model does not correctly reflect the type, hence the |
| |
251 regular cast. */ |
| |
252 end_ind = ind + webkit_dom_character_data_get_length((WebKitDOMCharacterData*)node); |
| |
253 |
| |
254 if (start_pos <= ind) |
| |
255 this_start = 0; |
| |
256 else if (start_pos < end_ind) |
| |
257 this_start = start_pos - ind; |
| |
258 else |
| |
259 this_start = -1; |
| |
260 |
| |
261 if (end_pos < end_ind) |
| |
262 this_end = end_pos - ind; |
| |
263 else |
| |
264 this_end = end_ind - ind; |
| |
265 |
| |
266 if (this_start != -1 && this_start < this_end) { |
| |
267 data = g_slist_prepend(data, GINT_TO_POINTER(this_end)); |
| |
268 data = g_slist_prepend(data, GINT_TO_POINTER(this_start)); |
| |
269 data = g_slist_prepend(data, node); |
| |
270 } |
| |
271 |
| |
272 ind = end_ind; |
| |
273 } |
| |
274 |
| |
275 if (webkit_dom_node_has_child_nodes(node)) { |
| |
276 node = webkit_dom_node_get_first_child(node); |
| |
277 } else { |
| |
278 while (node != div) { |
| |
279 WebKitDOMNode *next; |
| |
280 |
| |
281 next = webkit_dom_node_get_next_sibling(node); |
| |
282 if (next) { |
| |
283 node = next; |
| |
284 break; |
| |
285 } else { |
| |
286 node = webkit_dom_node_get_parent_node(node); |
| |
287 } |
| |
288 } |
| |
289 } |
| |
290 } while (node != div); |
| |
291 |
| |
292 /* Second, apply highlighting to saved sections. Changing the DOM is |
| |
293 automatically reflected in all WebKit API, so we have to do this after |
| |
294 finding the offsets, or things could get complicated. */ |
| |
295 while (data) { |
| |
296 node = WEBKIT_DOM_NODE(data->data); |
| |
297 data = g_slist_delete_link(data, data); |
| |
298 this_start = GPOINTER_TO_INT(data->data); |
| |
299 data = g_slist_delete_link(data, data); |
| |
300 this_end = GPOINTER_TO_INT(data->data); |
| |
301 data = g_slist_delete_link(data, data); |
| |
302 |
| |
303 range = webkit_dom_document_create_range(dom); |
| |
304 webkit_dom_range_set_start(range, node, this_start, NULL); |
| |
305 webkit_dom_range_set_end(range, node, this_end, NULL); |
| |
306 span = webkit_dom_document_create_element(dom, "span", NULL); |
| |
307 webkit_dom_html_element_set_class_name(WEBKIT_DOM_HTML_ELEMENT(span), |
| |
308 "regex"); |
| |
309 webkit_dom_range_surround_contents(range, WEBKIT_DOM_NODE(span), NULL); |
| |
310 } |
| |
311 } |
| |
312 |
| |
313 static void |
| |
314 regex_match(DebugWindow *win, WebKitDOMDocument *dom, WebKitDOMNode *div) |
| |
315 { |
| 313 GMatchInfo *match_info; |
316 GMatchInfo *match_info; |
| 314 #endif /* HAVE_REGEX_H */ |
317 gchar *text; |
| 315 gchar *plaintext; |
318 gchar *plaintext; |
| 316 |
319 |
| 317 if(!text) |
320 text = webkit_dom_node_get_text_content(div); |
| |
321 if (!text) |
| 318 return; |
322 return; |
| 319 |
323 |
| 320 /* I don't like having to do this, but we need it for highlighting. Plus |
324 /* I don't like having to do this, but we need it for highlighting. Plus |
| 321 * it makes the ^ and $ operators work :) |
325 * it makes the ^ and $ operators work :) |
| 322 */ |
326 */ |
| 323 plaintext = purple_markup_strip_html(text); |
327 plaintext = purple_markup_strip_html(text); |
| 324 |
328 |
| 325 /* we do a first pass to see if it matches at all. If it does we append |
329 /* We do a first pass to see if it matches at all. If it does we work out |
| 326 * it, and work out the offsets to highlight. |
330 * the offsets to highlight. |
| 327 */ |
331 */ |
| 328 #ifdef HAVE_REGEX_H |
332 if (g_regex_match(win->regex, plaintext, 0, &match_info) != win->invert) { |
| 329 inverted = (win->invert) ? REG_NOMATCH : 0; |
|
| 330 if(regexec(&win->regex, plaintext, n_matches, matches, 0) == inverted) { |
|
| 331 #else |
|
| 332 if(g_regex_match(win->regex, plaintext, 0, &match_info) != win->invert) { |
|
| 333 #endif /* HAVE_REGEX_H */ |
|
| 334 gchar *p = plaintext; |
|
| 335 GtkTextIter ins; |
|
| 336 gint i, offset = 0; |
|
| 337 |
|
| 338 gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &ins, |
|
| 339 gtk_text_buffer_get_insert(imhtml->text_buffer)); |
|
| 340 i = gtk_text_iter_get_offset(&ins); |
|
| 341 |
|
| 342 gtk_imhtml_append_text(imhtml, text, 0); |
|
| 343 |
|
| 344 /* If we're not highlighting or the expression is inverted, we're |
333 /* If we're not highlighting or the expression is inverted, we're |
| 345 * done and move on. |
334 * done and move on. |
| 346 */ |
335 */ |
| 347 if(!win->highlight || win->invert) { |
336 if (!win->highlight || win->invert) { |
| 348 g_free(plaintext); |
337 g_free(plaintext); |
| 349 #ifndef HAVE_REGEX_H |
|
| 350 g_match_info_free(match_info); |
338 g_match_info_free(match_info); |
| 351 #endif |
|
| 352 return; |
339 return; |
| 353 } |
340 } |
| 354 |
341 |
| 355 /* we use a do-while to highlight the first match, and then continue |
|
| 356 * if necessary... |
|
| 357 */ |
|
| 358 #ifdef HAVE_REGEX_H |
|
| 359 do { |
342 do { |
| 360 size_t m; |
343 gint m, count; |
| 361 |
|
| 362 for(m = 0; m < n_matches; m++) { |
|
| 363 GtkTextIter ms, me; |
|
| 364 |
|
| 365 if(matches[m].rm_eo == -1) |
|
| 366 break; |
|
| 367 |
|
| 368 i += offset; |
|
| 369 |
|
| 370 gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &ms, |
|
| 371 i + matches[m].rm_so); |
|
| 372 gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &me, |
|
| 373 i + matches[m].rm_eo); |
|
| 374 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "regex", |
|
| 375 &ms, &me); |
|
| 376 offset = matches[m].rm_eo; |
|
| 377 } |
|
| 378 |
|
| 379 p += offset; |
|
| 380 } while(regexec(&win->regex, p, n_matches, matches, REG_NOTBOL) == inverted); |
|
| 381 #else |
|
| 382 do |
|
| 383 { |
|
| 384 gint m; |
|
| 385 gint start_pos, end_pos; |
344 gint start_pos, end_pos; |
| 386 GtkTextIter ms, me; |
|
| 387 |
345 |
| 388 if (!g_match_info_matches(match_info)) |
346 if (!g_match_info_matches(match_info)) |
| 389 break; |
347 break; |
| 390 |
348 |
| 391 for (m = 0; m < g_match_info_get_match_count(match_info); m++) |
349 count = g_match_info_get_match_count(match_info); |
| |
350 if (count == 1) |
| |
351 m = 0; |
| |
352 else |
| |
353 m = 1; |
| |
354 |
| |
355 for (; m < count; m++) |
| 392 { |
356 { |
| 393 if (m == 1) |
|
| 394 continue; |
|
| 395 |
|
| 396 g_match_info_fetch_pos(match_info, m, &start_pos, &end_pos); |
357 g_match_info_fetch_pos(match_info, m, &start_pos, &end_pos); |
| 397 |
358 |
| 398 if (end_pos == -1) |
359 if (end_pos == -1) |
| 399 break; |
360 break; |
| 400 |
361 |
| 401 gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &ms, |
362 regex_highlight_text_nodes(dom, div, start_pos, end_pos); |
| 402 i + start_pos); |
|
| 403 gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &me, |
|
| 404 i + end_pos); |
|
| 405 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "regex", |
|
| 406 &ms, &me); |
|
| 407 offset = end_pos; |
|
| 408 } |
363 } |
| 409 |
364 } while (g_match_info_next(match_info, NULL)); |
| 410 g_match_info_free(match_info); |
365 |
| 411 p += offset; |
|
| 412 i += offset; |
|
| 413 } while (g_regex_match(win->regex, p, G_REGEX_MATCH_NOTBOL, &match_info) != win->invert); |
|
| 414 g_match_info_free(match_info); |
366 g_match_info_free(match_info); |
| 415 #endif /* HAVE_REGEX_H */ |
367 } else { |
| |
368 regex_toggle_div(div); |
| 416 } |
369 } |
| 417 |
370 |
| 418 g_free(plaintext); |
371 g_free(plaintext); |
| |
372 g_free(text); |
| |
373 } |
| |
374 |
| |
375 static void |
| |
376 regex_toggle_filter(DebugWindow *win, gboolean filter) |
| |
377 { |
| |
378 WebKitDOMDocument *dom; |
| |
379 WebKitDOMNodeList *list; |
| |
380 gulong i; |
| |
381 |
| |
382 dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(win->text)); |
| |
383 |
| |
384 if (win->highlight) |
| |
385 regex_highlight_clear(dom); |
| |
386 |
| |
387 /* Re-show debug lines that didn't match regex */ |
| |
388 list = webkit_dom_document_get_elements_by_class_name(dom, "hide"); |
| |
389 i = webkit_dom_node_list_get_length(list); |
| |
390 while (i--) { |
| |
391 WebKitDOMNode *div = webkit_dom_node_list_item(list, i); |
| |
392 regex_toggle_div(div); |
| |
393 } |
| |
394 |
| |
395 if (filter) { |
| |
396 list = webkit_dom_document_get_elements_by_tag_name(dom, "div"); |
| |
397 |
| |
398 for (i = 0; i < webkit_dom_node_list_get_length(list); i++) { |
| |
399 WebKitDOMNode *div = webkit_dom_node_list_item(list, i); |
| |
400 regex_match(win, dom, div); |
| |
401 } |
| |
402 } |
| |
403 } |
| |
404 |
| |
405 static void |
| |
406 regex_pref_filter_cb(const gchar *name, PurplePrefType type, |
| |
407 gconstpointer val, gpointer data) |
| |
408 { |
| |
409 DebugWindow *win = (DebugWindow *)data; |
| |
410 gboolean active = GPOINTER_TO_INT(val), current; |
| |
411 |
| |
412 if (!win || !win->window) |
| |
413 return; |
| |
414 |
| |
415 current = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)); |
| |
416 if (active != current) |
| |
417 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), active); |
| |
418 } |
| |
419 |
| |
420 static void |
| |
421 regex_pref_expression_cb(const gchar *name, PurplePrefType type, |
| |
422 gconstpointer val, gpointer data) |
| |
423 { |
| |
424 DebugWindow *win = (DebugWindow *)data; |
| |
425 const gchar *exp = (const gchar *)val; |
| |
426 |
| |
427 gtk_entry_set_text(GTK_ENTRY(win->expression), exp); |
| |
428 } |
| |
429 |
| |
430 static void |
| |
431 regex_pref_invert_cb(const gchar *name, PurplePrefType type, |
| |
432 gconstpointer val, gpointer data) |
| |
433 { |
| |
434 DebugWindow *win = (DebugWindow *)data; |
| |
435 gboolean active = GPOINTER_TO_INT(val); |
| |
436 |
| |
437 win->invert = active; |
| |
438 |
| |
439 if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) |
| |
440 regex_toggle_filter(win, TRUE); |
| |
441 } |
| |
442 |
| |
443 static void |
| |
444 regex_pref_highlight_cb(const gchar *name, PurplePrefType type, |
| |
445 gconstpointer val, gpointer data) |
| |
446 { |
| |
447 DebugWindow *win = (DebugWindow *)data; |
| |
448 gboolean active = GPOINTER_TO_INT(val); |
| |
449 |
| |
450 win->highlight = active; |
| |
451 |
| |
452 if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) |
| |
453 regex_toggle_filter(win, TRUE); |
| 419 } |
454 } |
| 420 |
455 |
| 421 static gboolean |
456 static gboolean |
| 422 regex_filter_all_cb(GtkTreeModel *m, GtkTreePath *p, GtkTreeIter *iter, |
457 regex_timer_cb(DebugWindow *win) { |
| 423 gpointer data) |
458 const gchar *text; |
| 424 { |
459 |
| 425 DebugWindow *win = (DebugWindow *)data; |
460 text = gtk_entry_get_text(GTK_ENTRY(win->expression)); |
| 426 gchar *text; |
461 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/debug/regex", text); |
| 427 PurpleDebugLevel level; |
462 |
| 428 |
463 win->timer = 0; |
| 429 gtk_tree_model_get(m, iter, 0, &text, 1, &level, -1); |
|
| 430 |
|
| 431 if (level >= purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel")) |
|
| 432 regex_match(win, text); |
|
| 433 |
|
| 434 g_free(text); |
|
| 435 |
464 |
| 436 return FALSE; |
465 return FALSE; |
| 437 } |
466 } |
| 438 |
467 |
| 439 static void |
468 static void |
| 440 regex_filter_all(DebugWindow *win) { |
469 regex_changed_cb(GtkWidget *w, DebugWindow *win) { |
| 441 gtk_imhtml_clear(GTK_IMHTML(win->text)); |
|
| 442 |
|
| 443 if(win->highlight) |
|
| 444 regex_highlight_clear(win); |
|
| 445 |
|
| 446 gtk_tree_model_foreach(GTK_TREE_MODEL(win->store), regex_filter_all_cb, |
|
| 447 win); |
|
| 448 } |
|
| 449 |
|
| 450 static gboolean |
|
| 451 regex_show_all_cb(GtkTreeModel *m, GtkTreePath *p, GtkTreeIter *iter, |
|
| 452 gpointer data) |
|
| 453 { |
|
| 454 DebugWindow *win = (DebugWindow *)data; |
|
| 455 gchar *text; |
|
| 456 PurpleDebugLevel level; |
|
| 457 |
|
| 458 gtk_tree_model_get(m, iter, 0, &text, 1, &level, -1); |
|
| 459 if (level >= purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel")) |
|
| 460 gtk_imhtml_append_text(GTK_IMHTML(win->text), text, 0); |
|
| 461 g_free(text); |
|
| 462 |
|
| 463 return FALSE; |
|
| 464 } |
|
| 465 |
|
| 466 static void |
|
| 467 regex_show_all(DebugWindow *win) { |
|
| 468 gtk_imhtml_clear(GTK_IMHTML(win->text)); |
|
| 469 |
|
| 470 if(win->highlight) |
|
| 471 regex_highlight_clear(win); |
|
| 472 |
|
| 473 gtk_tree_model_foreach(GTK_TREE_MODEL(win->store), regex_show_all_cb, |
|
| 474 win); |
|
| 475 } |
|
| 476 |
|
| 477 static void |
|
| 478 regex_compile(DebugWindow *win) { |
|
| 479 const gchar *text; |
470 const gchar *text; |
| 480 |
471 |
| |
472 if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) { |
| |
473 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), |
| |
474 FALSE); |
| |
475 } |
| |
476 |
| |
477 if (win->timer == 0) |
| |
478 win->timer = purple_timeout_add_seconds(5, (GSourceFunc)regex_timer_cb, win); |
| |
479 |
| 481 text = gtk_entry_get_text(GTK_ENTRY(win->expression)); |
480 text = gtk_entry_get_text(GTK_ENTRY(win->expression)); |
| 482 |
481 |
| 483 if(text == NULL || *text == '\0') { |
482 if (text == NULL || *text == '\0') { |
| 484 regex_clear_color(win->expression); |
483 regex_clear_color(win->expression); |
| 485 gtk_widget_set_sensitive(win->filter, FALSE); |
484 gtk_widget_set_sensitive(win->filter, FALSE); |
| 486 return; |
485 return; |
| 487 } |
486 } |
| 488 |
487 |
| 489 #ifdef HAVE_REGEX_H |
|
| 490 regfree(&win->regex); |
|
| 491 if(regcomp(&win->regex, text, REG_EXTENDED | REG_ICASE) != 0) { |
|
| 492 #else |
|
| 493 if (win->regex) |
488 if (win->regex) |
| 494 g_regex_unref(win->regex); |
489 g_regex_unref(win->regex); |
| 495 win->regex = g_regex_new(text, G_REGEX_EXTENDED | G_REGEX_CASELESS, 0, NULL); |
490 win->regex = g_regex_new(text, G_REGEX_CASELESS, 0, NULL); |
| 496 if(win->regex == NULL) { |
491 if (win->regex == NULL) { |
| 497 #endif |
|
| 498 /* failed to compile */ |
492 /* failed to compile */ |
| 499 regex_change_color(win->expression, 0xFFFF, 0xAFFF, 0xAFFF); |
493 regex_change_color(win->expression, 0xFFFF, 0xAFFF, 0xAFFF); |
| 500 gtk_widget_set_sensitive(win->filter, FALSE); |
494 gtk_widget_set_sensitive(win->filter, FALSE); |
| 501 } else { |
495 } else { |
| 502 /* compiled successfully */ |
496 /* compiled successfully */ |
| 503 regex_change_color(win->expression, 0xAFFF, 0xFFFF, 0xAFFF); |
497 regex_change_color(win->expression, 0xAFFF, 0xFFFF, 0xAFFF); |
| 504 gtk_widget_set_sensitive(win->filter, TRUE); |
498 gtk_widget_set_sensitive(win->filter, TRUE); |
| 505 } |
499 } |
| 506 |
|
| 507 /* we check if the filter is on in case it was only of the options that |
|
| 508 * got changed, and not the expression. |
|
| 509 */ |
|
| 510 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) |
|
| 511 regex_filter_all(win); |
|
| 512 } |
|
| 513 |
|
| 514 static void |
|
| 515 regex_pref_filter_cb(const gchar *name, PurplePrefType type, |
|
| 516 gconstpointer val, gpointer data) |
|
| 517 { |
|
| 518 DebugWindow *win = (DebugWindow *)data; |
|
| 519 gboolean active = GPOINTER_TO_INT(val), current; |
|
| 520 |
|
| 521 if(!win || !win->window) |
|
| 522 return; |
|
| 523 |
|
| 524 current = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)); |
|
| 525 if(active != current) |
|
| 526 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), active); |
|
| 527 } |
|
| 528 |
|
| 529 static void |
|
| 530 regex_pref_expression_cb(const gchar *name, PurplePrefType type, |
|
| 531 gconstpointer val, gpointer data) |
|
| 532 { |
|
| 533 DebugWindow *win = (DebugWindow *)data; |
|
| 534 const gchar *exp = (const gchar *)val; |
|
| 535 |
|
| 536 gtk_entry_set_text(GTK_ENTRY(win->expression), exp); |
|
| 537 } |
|
| 538 |
|
| 539 static void |
|
| 540 regex_pref_invert_cb(const gchar *name, PurplePrefType type, |
|
| 541 gconstpointer val, gpointer data) |
|
| 542 { |
|
| 543 DebugWindow *win = (DebugWindow *)data; |
|
| 544 gboolean active = GPOINTER_TO_INT(val); |
|
| 545 |
|
| 546 win->invert = active; |
|
| 547 |
|
| 548 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) |
|
| 549 regex_filter_all(win); |
|
| 550 } |
|
| 551 |
|
| 552 static void |
|
| 553 regex_pref_highlight_cb(const gchar *name, PurplePrefType type, |
|
| 554 gconstpointer val, gpointer data) |
|
| 555 { |
|
| 556 DebugWindow *win = (DebugWindow *)data; |
|
| 557 gboolean active = GPOINTER_TO_INT(val); |
|
| 558 |
|
| 559 win->highlight = active; |
|
| 560 |
|
| 561 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) |
|
| 562 regex_filter_all(win); |
|
| 563 } |
|
| 564 |
|
| 565 static void |
|
| 566 regex_row_changed_cb(GtkTreeModel *model, GtkTreePath *path, |
|
| 567 GtkTreeIter *iter, DebugWindow *win) |
|
| 568 { |
|
| 569 gchar *text; |
|
| 570 PurpleDebugLevel level; |
|
| 571 |
|
| 572 if(!win || !win->window) |
|
| 573 return; |
|
| 574 |
|
| 575 /* If the debug window is paused, we just return since it's in the store. |
|
| 576 * We don't call regex_match because it doesn't make sense to check the |
|
| 577 * string if it's paused. When we unpause we clear the imhtml and |
|
| 578 * reiterate over the store to handle matches that were outputted when |
|
| 579 * we were paused. |
|
| 580 */ |
|
| 581 if(win->paused) |
|
| 582 return; |
|
| 583 |
|
| 584 gtk_tree_model_get(model, iter, 0, &text, 1, &level, -1); |
|
| 585 |
|
| 586 if (level >= purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel")) { |
|
| 587 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) { |
|
| 588 regex_match(win, text); |
|
| 589 } else { |
|
| 590 gtk_imhtml_append_text(GTK_IMHTML(win->text), text, 0); |
|
| 591 } |
|
| 592 } |
|
| 593 |
|
| 594 g_free(text); |
|
| 595 } |
|
| 596 |
|
| 597 static gboolean |
|
| 598 regex_timer_cb(DebugWindow *win) { |
|
| 599 const gchar *text; |
|
| 600 |
|
| 601 text = gtk_entry_get_text(GTK_ENTRY(win->expression)); |
|
| 602 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/debug/regex", text); |
|
| 603 |
|
| 604 win->timer = 0; |
|
| 605 |
|
| 606 return FALSE; |
|
| 607 } |
|
| 608 |
|
| 609 static void |
|
| 610 regex_changed_cb(GtkWidget *w, DebugWindow *win) { |
|
| 611 if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) { |
|
| 612 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), |
|
| 613 FALSE); |
|
| 614 } |
|
| 615 |
|
| 616 if(win->timer == 0) |
|
| 617 win->timer = purple_timeout_add_seconds(5, (GSourceFunc)regex_timer_cb, win); |
|
| 618 |
|
| 619 regex_compile(win); |
|
| 620 } |
500 } |
| 621 |
501 |
| 622 static void |
502 static void |
| 623 regex_key_release_cb(GtkWidget *w, GdkEventKey *e, DebugWindow *win) { |
503 regex_key_release_cb(GtkWidget *w, GdkEventKey *e, DebugWindow *win) { |
| 624 if(e->keyval == GDK_KEY_Return && |
504 if(e->keyval == GDK_KEY_Return && |