comparison src/gtkdebug.c @ 11256:bb0d7b719af2

[gaim-migrate @ 13430] I give you regex filtering in the debug window. We keep a buffer of all the text, so when unpausing all the messages that were output when paused will be displayed, as well as when you change the filter. This _should_ be alright on systems that don't have regex.h but I haven't gotten anyone to test it recently, if it's busted, just #ifdef HAVE_REGEX_H it. committer: Tailor Script <tailor@pidgin.im>
author Gary Kramlich <grim@reaperworld.com>
date Sat, 13 Aug 2005 22:09:34 +0000
parents a511b77a368b
children ecbbe6d18b0d
comparison
equal deleted inserted replaced
11255:1b1d63602d77 11256:bb0d7b719af2
34 #include "gtkdialogs.h" 34 #include "gtkdialogs.h"
35 #include "gtkimhtml.h" 35 #include "gtkimhtml.h"
36 #include "gtkutils.h" 36 #include "gtkutils.h"
37 #include "gtkstock.h" 37 #include "gtkstock.h"
38 38
39 #ifdef HAVE_REGEX_H
40 # include <regex.h>
41 #endif /* HAVE_REGEX_H */
42
39 typedef struct 43 typedef struct
40 { 44 {
41 GtkWidget *window; 45 GtkWidget *window;
42 GtkWidget *text; 46 GtkWidget *text;
43 GtkWidget *find; 47
44 48 GtkListStore *store;
45 /* The category filter tree view. */
46 GtkWidget *treeview;
47 49
48 gboolean timestamps; 50 gboolean timestamps;
49 gboolean paused; 51 gboolean paused;
50 52
53 #ifdef HAVE_REGEX_H
54 GtkWidget *filter;
55 GtkWidget *expression;
56
57 gboolean invert;
58 gboolean highlight;
59
60 guint timer;
61
62 regex_t regex;
63 #else
64 GtkWidget *find;
65 #endif /* HAVE_REGEX_H */
51 } DebugWindow; 66 } DebugWindow;
52 67
53 static char debug_fg_colors[][8] = { 68 static char debug_fg_colors[][8] = {
54 "#000000", /**< All debug levels. */ 69 "#000000", /**< All debug levels. */
55 "#666666", /**< Misc. */ 70 "#666666", /**< Misc. */
59 "#FF0000", /**< Fatal errors. */ 74 "#FF0000", /**< Fatal errors. */
60 }; 75 };
61 76
62 static DebugWindow *debug_win = NULL; 77 static DebugWindow *debug_win = NULL;
63 78
64 static GHashTable *debug_categories = NULL; 79 #ifdef HAVE_REGEX_H
65 static gboolean filter_enabled = FALSE; 80 static void regex_filter_all(DebugWindow *win);
66 81 static void regex_show_all(DebugWindow *win);
82 #endif /* HAVE_REGEX_H */
83
84 static gint
85 debug_window_destroy(GtkWidget *w, GdkEvent *event, void *unused)
86 {
87 gaim_prefs_disconnect_by_handle(gaim_gtk_debug_get_handle());
88
89 #ifdef HAVE_REGEX_H
90 if(debug_win->timer != 0) {
91 const gchar *text;
92
93 g_source_remove(debug_win->timer);
94
95 text = gtk_entry_get_text(GTK_ENTRY(debug_win->expression));
96 gaim_prefs_set_string("/gaim/gtk/debug/regex", text);
97 }
98
99 regfree(&debug_win->regex);
100 #endif
101
102 /* If the "Save Log" dialog is open then close it */
103 gaim_request_close_with_handle(debug_win);
104
105 g_free(debug_win);
106 debug_win = NULL;
107
108 gaim_prefs_set_bool("/gaim/gtk/debug/enabled", FALSE);
109
110 return FALSE;
111 }
112
113 static gboolean
114 configure_cb(GtkWidget *w, GdkEventConfigure *event, DebugWindow *win)
115 {
116 if (GTK_WIDGET_VISIBLE(w)) {
117 gaim_prefs_set_int("/gaim/gtk/debug/width", event->width);
118 gaim_prefs_set_int("/gaim/gtk/debug/height", event->height);
119 }
120
121 return FALSE;
122 }
123
124 #ifndef HAVE_REGEX_H
67 struct _find { 125 struct _find {
68 DebugWindow *window; 126 DebugWindow *window;
69 GtkWidget *entry; 127 GtkWidget *entry;
70 }; 128 };
71 129
72 static gint
73 debug_window_destroy(GtkWidget *w, GdkEvent *event, void *unused)
74 {
75 gaim_prefs_disconnect_by_handle(gaim_gtk_debug_get_handle());
76
77 /* If the "Save Log" dialog is open then close it */
78 gaim_request_close_with_handle(debug_win);
79
80 g_free(debug_win);
81 debug_win = NULL;
82
83 gaim_prefs_set_bool("/gaim/gtk/debug/enabled", FALSE);
84
85 return FALSE;
86 }
87
88 static gboolean
89 configure_cb(GtkWidget *w, GdkEventConfigure *event, DebugWindow *win)
90 {
91 if (GTK_WIDGET_VISIBLE(w)) {
92 gaim_prefs_set_int("/gaim/gtk/debug/width", event->width);
93 gaim_prefs_set_int("/gaim/gtk/debug/height", event->height);
94 }
95
96 return FALSE;
97 }
98
99 static void 130 static void
100 do_find_cb(GtkWidget *widget, gint response, struct _find *f) 131 do_find_cb(GtkWidget *widget, gint response, struct _find *f)
101 { 132 {
102 switch (response) { 133 switch (response) {
103 case GTK_RESPONSE_OK: 134 case GTK_RESPONSE_OK:
104 gtk_imhtml_search_find(GTK_IMHTML(f->window->text), 135 gtk_imhtml_search_find(GTK_IMHTML(f->window->text),
105 gtk_entry_get_text(GTK_ENTRY(f->entry))); 136 gtk_entry_get_text(GTK_ENTRY(f->entry)));
106 break; 137 break;
107 138
108 case GTK_RESPONSE_DELETE_EVENT: 139 case GTK_RESPONSE_DELETE_EVENT:
109 case GTK_RESPONSE_CLOSE: 140 case GTK_RESPONSE_CLOSE:
169 gtk_box_pack_start(GTK_BOX(hbox), f->entry, FALSE, FALSE, 0); 200 gtk_box_pack_start(GTK_BOX(hbox), f->entry, FALSE, FALSE, 0);
170 201
171 gtk_widget_show_all(win->find); 202 gtk_widget_show_all(win->find);
172 gtk_widget_grab_focus(f->entry); 203 gtk_widget_grab_focus(f->entry);
173 } 204 }
205 #endif /* HAVE_REGEX_H */
174 206
175 static void 207 static void
176 save_writefile_cb(void *user_data, const char *filename) 208 save_writefile_cb(void *user_data, const char *filename)
177 { 209 {
178 DebugWindow *win = (DebugWindow *)user_data; 210 DebugWindow *win = (DebugWindow *)user_data;
201 233
202 static void 234 static void
203 clear_cb(GtkWidget *w, DebugWindow *win) 235 clear_cb(GtkWidget *w, DebugWindow *win)
204 { 236 {
205 gtk_imhtml_clear(GTK_IMHTML(win->text)); 237 gtk_imhtml_clear(GTK_IMHTML(win->text));
238
239 #ifdef HAVE_REGEX_H
240 gtk_list_store_clear(win->store);
241 #endif /* HAVE_REGEX_H */
206 } 242 }
207 243
208 static void 244 static void
209 pause_cb(GtkWidget *w, DebugWindow *win) 245 pause_cb(GtkWidget *w, DebugWindow *win)
210 { 246 {
211 win->paused = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); 247 win->paused = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
248
249 #ifdef HAVE_REGEX_H
250 if(!win->paused) {
251 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter)))
252 regex_filter_all(win);
253 else
254 regex_show_all(win);
255 }
256 #endif /* HAVE_REGEX_H */
212 } 257 }
213 258
214 static void 259 static void
215 timestamps_cb(GtkWidget *w, DebugWindow *win) 260 timestamps_cb(GtkWidget *w, DebugWindow *win)
216 { 261 {
224 gpointer data) 269 gpointer data)
225 { 270 {
226 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data), GPOINTER_TO_INT(value)); 271 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data), GPOINTER_TO_INT(value));
227 } 272 }
228 273
229 static void 274 /******************************************************************************
230 filter_cb(GtkToggleButton *button, DebugWindow *win) 275 * regex stuff
231 { 276 *****************************************************************************/
232 if (gtk_toggle_button_get_active(button)) { 277 #ifdef HAVE_REGEX_H
233 filter_enabled = TRUE; 278 static void
279 regex_clear_color(GtkWidget *w) {
280 gtk_widget_modify_base(w, GTK_STATE_NORMAL, NULL);
281 }
282
283 static void
284 regex_change_color(GtkWidget *w, guint16 r, guint16 g, guint16 b) {
285 GdkColor color;
286
287 color.red = r;
288 color.green = g;
289 color.blue = b;
290
291 gtk_widget_modify_base(w, GTK_STATE_NORMAL, &color);
292 }
293
294 static void
295 regex_highlight_clear(DebugWindow *win) {
296 GtkIMHtml *imhtml = GTK_IMHTML(win->text);
297 GtkTextIter s, e;
298
299 gtk_text_buffer_get_start_iter(imhtml->text_buffer, &s);
300 gtk_text_buffer_get_end_iter(imhtml->text_buffer, &e);
301 gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "regex", &s, &e);
302 }
303
304 static void
305 regex_match(DebugWindow *win, const gchar *text) {
306 GtkIMHtml *imhtml = GTK_IMHTML(win->text);
307 regmatch_t matches[4]; /* adjust if necessary */
308 size_t n_matches = sizeof(matches) / sizeof(matches[0]);
309 gchar *plaintext;
310 gint inverted;
311
312 if(!text)
313 return;
314
315 inverted = (win->invert) ? REG_NOMATCH : 0;
316
317 /* I don't like having to do this, but we need it for highlighting. Plus
318 * it makes the ^ and $ operators work :)
319 */
320 plaintext = gaim_markup_strip_html(text);
321
322 /* we do a first pass to see if it matches at all. If it does we append
323 * it, and work out the offsets to highlight.
324 */
325 if(regexec(&win->regex, plaintext, n_matches, matches, 0) == inverted) {
326 GtkTextIter ins;
327 gchar *p = plaintext;
328 gint i, offset = 0;
329
330 gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &ins,
331 gtk_text_buffer_get_insert(imhtml->text_buffer));
332 i = gtk_text_iter_get_offset(&ins);
333
334 gtk_imhtml_append_text(imhtml, text, 0);
335
336 if(!win->highlight) {
337 g_free(plaintext);
338 return;
339 }
340
341 /* we use a do-while to highlight the first match, and then continue
342 * if necessary...
343 */
344 do {
345 gint m;
346
347 for(m = 0; m < n_matches; m++) {
348 GtkTextIter ms, me;
349
350 if(matches[m].rm_eo == -1)
351 break;
352
353 i += offset;
354
355 gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &ms,
356 i + matches[m].rm_so);
357 gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &me,
358 i + matches[m].rm_eo);
359 gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "regex",
360 &ms, &me);
361 offset = matches[m].rm_eo;
362 }
363
364 p += offset;
365 } while(regexec(&win->regex, p, n_matches, matches, REG_NOTBOL) == inverted);
366 }
367
368 g_free(plaintext);
369 }
370
371 static gboolean
372 regex_filter_all_cb(GtkTreeModel *m, GtkTreePath *p, GtkTreeIter *iter,
373 gpointer data)
374 {
375 DebugWindow *win = (DebugWindow *)data;
376 gchar *text;
377
378 gtk_tree_model_get(m, iter, 0, &text, -1);
379
380 regex_match(win, text);
381
382 g_free(text);
383
384 return FALSE;
385 }
386
387 static void
388 regex_filter_all(DebugWindow *win) {
389 gtk_imhtml_clear(GTK_IMHTML(win->text));
390
391 if(win->highlight)
392 regex_highlight_clear(win);
393
394 gtk_tree_model_foreach(GTK_TREE_MODEL(win->store), regex_filter_all_cb,
395 win);
396 }
397
398 static gboolean
399 regex_show_all_cb(GtkTreeModel *m, GtkTreePath *p, GtkTreeIter *iter,
400 gpointer data)
401 {
402 DebugWindow *win = (DebugWindow *)data;
403 gchar *text;
404
405 gtk_tree_model_get(m, iter, 0, &text, -1);
406 gtk_imhtml_append_text(GTK_IMHTML(win->text), text, 0);
407 g_free(text);
408
409 return FALSE;
410 }
411
412 static void
413 regex_show_all(DebugWindow *win) {
414 gtk_imhtml_clear(GTK_IMHTML(win->text));
415
416 if(win->highlight)
417 regex_highlight_clear(win);
418
419 gtk_tree_model_foreach(GTK_TREE_MODEL(win->store), regex_show_all_cb,
420 win);
421 }
422
423 static void
424 regex_compile(DebugWindow *win) {
425 const gchar *text;
426
427 text = gtk_entry_get_text(GTK_ENTRY(win->expression));
428
429 if(text == NULL || *text == '\0') {
430 regex_clear_color(win->expression);
431 gtk_widget_set_sensitive(win->filter, FALSE);
432 return;
433 }
434
435 regfree(&win->regex);
436
437 if(regcomp(&win->regex, text, REG_EXTENDED | REG_ICASE) != 0) {
438 /* failed to compile */
439 regex_change_color(win->expression, 0xFFFF, 0xAFFF, 0xAFFF);
440 gtk_widget_set_sensitive(win->filter, FALSE);
234 } else { 441 } else {
235 filter_enabled = FALSE; 442 /* compiled successfully */
236 } 443 regex_change_color(win->expression, 0xAFFF, 0xFFFF, 0xAFFF);
237 } 444 gtk_widget_set_sensitive(win->filter, TRUE);
238 445 }
239 static void 446
240 debug_liststore_append(gpointer key, gpointer value, gpointer user_data) 447 /* we check if the filter is on in case it was only of the options that
241 { 448 * got changed, and not the expression.
242 GtkTreeIter iter; 449 */
243 GtkListStore **liststore = (GtkListStore **)user_data; 450 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter)))
244 451 regex_filter_all(win);
245 gtk_list_store_append(*liststore, &iter); 452 }
246 gtk_list_store_set(*liststore, &iter, 0, key, -1); 453
247 } 454 static void
455 regex_pref_filter_cb(const gchar *name, GaimPrefType type,
456 gpointer val, gpointer data)
457 {
458 DebugWindow *win = (DebugWindow *)data;
459 gboolean active = GPOINTER_TO_INT(val), current;
460
461 if(!win || !win->window)
462 return;
463
464 current = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter));
465 if(active != current)
466 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->filter), active);
467 }
468
469 static void
470 regex_pref_expression_cb(const gchar *name, GaimPrefType type,
471 gpointer val, gpointer data)
472 {
473 DebugWindow *win = (DebugWindow *)data;
474 const gchar *exp = (const gchar *)val;
475
476 gtk_entry_set_text(GTK_ENTRY(win->expression), exp);
477 }
478
479 static void
480 regex_pref_invert_cb(const gchar *name, GaimPrefType type,
481 gpointer val, gpointer data)
482 {
483 DebugWindow *win = (DebugWindow *)data;
484 gboolean active = GPOINTER_TO_INT(val);
485
486 win->invert = active;
487
488 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter)))
489 regex_filter_all(win);
490 }
491
492 static void
493 regex_pref_highlight_cb(const gchar *name, GaimPrefType type,
494 gpointer val, gpointer data)
495 {
496 DebugWindow *win = (DebugWindow *)data;
497 gboolean active = GPOINTER_TO_INT(val);
498
499 win->highlight = active;
500
501 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter)))
502 regex_filter_all(win);
503 }
504
505 static void
506 regex_row_changed_cb(GtkTreeModel *model, GtkTreePath *path,
507 GtkTreeIter *iter, DebugWindow *win)
508 {
509 gchar *text;
510
511 if(!win || !win->window)
512 return;
513
514 /* If the debug window is paused, we just return since it's in the store.
515 * We don't call regex_match because it doesn't make sense to check the
516 * string if it's paused. When we unpause we clear the imhtml and
517 * reiterate over the store to handle matches that were outputted when
518 * we were paused.
519 */
520 if(win->paused)
521 return;
522
523 gtk_tree_model_get(model, iter, 0, &text, -1);
524
525 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter))) {
526 regex_match(win, text);
527 } else {
528 gtk_imhtml_append_text(GTK_IMHTML(win->text), text, 0);
529 }
530
531 g_free(text);
532 }
533
534 static gboolean
535 regex_timer_cb(DebugWindow *win) {
536 const gchar *text;
537
538 text = gtk_entry_get_text(GTK_ENTRY(win->expression));
539 gaim_prefs_set_string("/gaim/gtk/debug/regex", text);
540
541 win->timer = 0;
542
543 return FALSE;
544 }
545
546 static void
547 regex_changed_cb(GtkWidget *w, DebugWindow *win) {
548 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter))) {
549 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->filter),
550 FALSE);
551 }
552
553 if(win->timer == 0)
554 win->timer = gaim_timeout_add(5000, (GSourceFunc)regex_timer_cb, win);
555
556 regex_compile(win);
557 }
558
559 static void
560 regex_menu_cb(GtkWidget *item, const gchar *pref) {
561 gboolean active;
562
563 active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item));
564
565 gaim_prefs_set_bool(pref, active);
566 }
567
568 static void
569 regex_popup_cb(GtkEntry *entry, GtkWidget *menu, DebugWindow *win) {
570 gaim_separator(menu);
571 gaim_new_check_item(menu, _("Invert"),
572 G_CALLBACK(regex_menu_cb),
573 "/gaim/gtk/debug/invert", win->invert);
574 gaim_new_check_item(menu, _("Highlight matches"),
575 G_CALLBACK(regex_menu_cb),
576 "/gaim/gtk/debug/highlight", win->highlight);
577 }
578
579 static void
580 regex_filter_toggled_cb(GtkToggleButton *button, DebugWindow *win) {
581 gboolean active;
582
583 active = gtk_toggle_button_get_active(button);
584
585 gaim_prefs_set_bool("/gaim/gtk/debug/filter", active);
586
587 if(!GTK_IS_IMHTML(win->text))
588 return;
589
590 if(active)
591 regex_filter_all(win);
592 else
593 regex_show_all(win);
594 }
595
596 #endif /* HAVE_REGEX_H */
248 597
249 static DebugWindow * 598 static DebugWindow *
250 debug_window_new(void) 599 debug_window_new(void)
251 { 600 {
252 DebugWindow *win; 601 DebugWindow *win;
253 GtkWidget *vbox; 602 GtkWidget *vbox;
254 GtkWidget *toolbar; 603 GtkWidget *toolbar;
255 GtkWidget *frame; 604 GtkWidget *frame;
256 GtkWidget *button; 605 GtkWidget *button;
257 GtkWidget *image; 606 GtkWidget *image;
258 GtkListStore *liststore = NULL; 607 gint width, height;
259 GtkCellRenderer *renderer = NULL; 608 void *handle;
260 GtkTreeSelection *selection = NULL;
261 GtkTreeViewColumn *column = NULL;
262 int width, height;
263 609
264 win = g_new0(DebugWindow, 1); 610 win = g_new0(DebugWindow, 1);
265 611
266 width = gaim_prefs_get_int("/gaim/gtk/debug/width"); 612 width = gaim_prefs_get_int("/gaim/gtk/debug/width");
267 height = gaim_prefs_get_int("/gaim/gtk/debug/height"); 613 height = gaim_prefs_get_int("/gaim/gtk/debug/height");
268 614
269 GAIM_DIALOG(win->window); 615 GAIM_DIALOG(win->window);
270 gaim_debug_info("gtkdebug", "Setting dimensions to %d, %d\n", 616 gaim_debug_info("gtkdebug", "Setting dimensions to %d, %d\n",
271 width, height); 617 width, height);
272 618
273 gtk_window_set_default_size(GTK_WINDOW(win->window), width, height); 619 gtk_window_set_default_size(GTK_WINDOW(win->window), width, height);
274 gtk_window_set_role(GTK_WINDOW(win->window), "debug"); 620 gtk_window_set_role(GTK_WINDOW(win->window), "debug");
275 gtk_window_set_title(GTK_WINDOW(win->window), _("Debug Window")); 621 gtk_window_set_title(GTK_WINDOW(win->window), _("Debug Window"));
276 622
277 g_signal_connect(G_OBJECT(win->window), "delete_event", 623 g_signal_connect(G_OBJECT(win->window), "delete_event",
278 G_CALLBACK(debug_window_destroy), NULL); 624 G_CALLBACK(debug_window_destroy), NULL);
279 g_signal_connect(G_OBJECT(win->window), "configure_event", 625 g_signal_connect(G_OBJECT(win->window), "configure_event",
280 G_CALLBACK(configure_cb), win); 626 G_CALLBACK(configure_cb), win);
281 627
628 handle = gaim_gtk_debug_get_handle();
629
630 #ifdef HAVE_REGEX_H
631 /* the list store for all the messages */
632 win->store = gtk_list_store_new(1, G_TYPE_STRING);
633
634 /* row-changed gets called when we do gtk_list_store_set, and row-inserted
635 * gets called with gtk_list_store_append, which is a
636 * completely empty row. So we just ignore row-inserted, and deal with row
637 * changed. -Gary
638 */
639 g_signal_connect(G_OBJECT(win->store), "row-changed",
640 G_CALLBACK(regex_row_changed_cb), win);
641
642 #endif /* HAVE_REGEX_H */
643
282 /* Setup the vbox */ 644 /* Setup the vbox */
283 vbox = gtk_vbox_new(FALSE, 0); 645 vbox = gtk_vbox_new(FALSE, 0);
284 gtk_container_add(GTK_CONTAINER(win->window), vbox); 646 gtk_container_add(GTK_CONTAINER(win->window), vbox);
285 647
286 if (gaim_prefs_get_bool("/gaim/gtk/debug/toolbar")) { 648 if (gaim_prefs_get_bool("/gaim/gtk/debug/toolbar")) {
287 /* Setup our top button bar thingie. */ 649 /* Setup our top button bar thingie. */
288 toolbar = gtk_toolbar_new(); 650 toolbar = gtk_toolbar_new();
651 gtk_toolbar_set_tooltips(GTK_TOOLBAR(toolbar), TRUE);
652
289 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), 653 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar),
290 GTK_TOOLBAR_BOTH_HORIZ); 654 GTK_TOOLBAR_BOTH_HORIZ);
291 gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar), 655 gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar),
292 GTK_ICON_SIZE_SMALL_TOOLBAR); 656 GTK_ICON_SIZE_SMALL_TOOLBAR);
293 657
294 gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); 658 gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
295 659
660 #ifndef HAVE_REGEX_H
296 /* Find button */ 661 /* Find button */
297 gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar), GTK_STOCK_FIND, 662 gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar), GTK_STOCK_FIND,
298 NULL, NULL, G_CALLBACK(find_cb), 663 _("Find"), NULL, G_CALLBACK(find_cb),
299 win, -1); 664 win, -1);
665 #endif /* HAVE_REGEX_H */
300 666
301 /* Save */ 667 /* Save */
302 gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar), GTK_STOCK_SAVE, 668 gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar), GTK_STOCK_SAVE,
303 NULL, NULL, G_CALLBACK(save_cb), 669 _("Save"), NULL, G_CALLBACK(save_cb),
304 win, -1); 670 win, -1);
305 671
306 /* Clear button */ 672 /* Clear button */
307 gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar), GTK_STOCK_CLEAR, 673 gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar), GTK_STOCK_CLEAR,
308 NULL, NULL, G_CALLBACK(clear_cb), 674 _("Clear"), NULL, G_CALLBACK(clear_cb),
309 win, -1); 675 win, -1);
310 676
311 gtk_toolbar_insert_space(GTK_TOOLBAR(toolbar), -1); 677 gtk_toolbar_insert_space(GTK_TOOLBAR(toolbar), -1);
312 678
313 /* Pause */ 679 /* Pause */
314 image = gtk_image_new_from_stock(GAIM_STOCK_PAUSE, GTK_ICON_SIZE_MENU); 680 image = gtk_image_new_from_stock(GAIM_STOCK_PAUSE, GTK_ICON_SIZE_MENU);
315 button = gtk_toolbar_append_element(GTK_TOOLBAR(toolbar), 681 button = gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
316 GTK_TOOLBAR_CHILD_TOGGLEBUTTON, 682 GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
317 NULL, _("Pause"), NULL, 683 NULL, _("Pause"), _("Pause"),
318 NULL, image, 684 NULL, image,
319 G_CALLBACK(pause_cb), win); 685 G_CALLBACK(pause_cb), win);
320 686
321 /* Timestamps */ 687 /* Timestamps */
322 button = gtk_toolbar_append_element(GTK_TOOLBAR(toolbar), 688 button = gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
323 GTK_TOOLBAR_CHILD_TOGGLEBUTTON, 689 GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
324 NULL, _("Timestamps"), 690 NULL, _("Timestamps"),
325 NULL, NULL, NULL, 691 _("Timestamps"), NULL, NULL,
326 G_CALLBACK(timestamps_cb), 692 G_CALLBACK(timestamps_cb),
327 win); 693 win);
328 694
329 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 695 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
330 gaim_prefs_get_bool("/core/debug/timestamps")); 696 gaim_prefs_get_bool("/core/debug/timestamps"));
331 697
332 gaim_prefs_connect_callback(gaim_gtk_debug_get_handle(), 698 gaim_prefs_connect_callback(handle, "/core/debug/timestamps",
333 "/core/debug/timestamps",
334 timestamps_pref_cb, button); 699 timestamps_pref_cb, button);
335 700
336 button = gtk_check_button_new_with_label(_("Filter")); 701 #ifdef HAVE_REGEX_H
337 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(filter_cb), win); 702 /* regex stuff */
338 button = gtk_toolbar_append_element(GTK_TOOLBAR(toolbar), 703 gtk_toolbar_insert_space(GTK_TOOLBAR(toolbar), -1);
339 GTK_TOOLBAR_CHILD_WIDGET, 704
340 button, NULL, NULL, NULL, 705 /* regex toggle button */
341 NULL, NULL, NULL); 706 win->filter =
342 707 gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
343 button = gtk_scrolled_window_new(NULL, NULL); 708 GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
344 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(button), 709 NULL, _("Filter"), _("Filter"),
345 GTK_POLICY_NEVER, 710 NULL, NULL,
346 GTK_POLICY_AUTOMATIC); 711 G_CALLBACK(regex_filter_toggled_cb),
347 712 win);
348 liststore = gtk_list_store_new(1, G_TYPE_STRING); 713 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->filter),
349 win->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(liststore)); 714 gaim_prefs_get_bool("/gaim/gtk/debug/filter"));
350 renderer = gtk_cell_renderer_text_new(); 715 gaim_prefs_connect_callback(handle, "/gaim/gtk/debug/filter",
351 column = gtk_tree_view_column_new_with_attributes(_("Filter"), renderer, "text", 0, NULL); 716 regex_pref_filter_cb, win);
352 717
353 gtk_tree_view_append_column(GTK_TREE_VIEW(win->treeview), column); 718 /* regex entry */
354 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(win->treeview), FALSE); 719 win->expression = gtk_entry_new();
355 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(liststore), 0, GTK_SORT_ASCENDING); 720 gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
356 721 GTK_TOOLBAR_CHILD_WIDGET, win->expression,
357 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(win->treeview)); 722 NULL, _("Right click for more options."),
358 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); 723 NULL, NULL, NULL, NULL);
359 724 /* this needs to be before the text is set from the pref if we want it
360 g_hash_table_foreach(debug_categories, (GHFunc)debug_liststore_append, &liststore); 725 * to colorize a stored expression.
361 726 */
362 gtk_container_add(GTK_CONTAINER(button), win->treeview); 727 g_signal_connect(G_OBJECT(win->expression), "changed",
363 button = gtk_toolbar_append_element(GTK_TOOLBAR(toolbar), 728 G_CALLBACK(regex_changed_cb), win);
364 GTK_TOOLBAR_CHILD_WIDGET, 729 gtk_entry_set_text(GTK_ENTRY(win->expression),
365 button, NULL, NULL, 730 gaim_prefs_get_string("/gaim/gtk/debug/regex"));
366 NULL, NULL, NULL, NULL); 731 g_signal_connect(G_OBJECT(win->expression), "populate-popup",
732 G_CALLBACK(regex_popup_cb), win);
733 gaim_prefs_connect_callback(handle, "/gaim/gtk/debug/regex",
734 regex_pref_expression_cb, win);
735
736 /* connect the rest of our pref callbacks */
737 win->invert = gaim_prefs_get_bool("/gaim/gtk/debug/invert");
738 gaim_prefs_connect_callback(handle, "/gaim/gtk/debug/invert",
739 regex_pref_invert_cb, win);
740
741 win->highlight = gaim_prefs_get_bool("/gaim/gtk/debug/highlight");
742 gaim_prefs_connect_callback(handle, "/gaim/gtk/debug/highlight",
743 regex_pref_highlight_cb, win);
744
745 #endif /* HAVE_REGEX_H */
367 } 746 }
368 747
369 /* Add the gtkimhtml */ 748 /* Add the gtkimhtml */
370 frame = gaim_gtk_create_imhtml(FALSE, &win->text, NULL); 749 frame = gaim_gtk_create_imhtml(FALSE, &win->text, NULL);
371 gtk_imhtml_set_format_functions(GTK_IMHTML(win->text), 750 gtk_imhtml_set_format_functions(GTK_IMHTML(win->text),
372 GTK_IMHTML_ALL ^ GTK_IMHTML_SMILEY ^ GTK_IMHTML_IMAGE); 751 GTK_IMHTML_ALL ^ GTK_IMHTML_SMILEY ^ GTK_IMHTML_IMAGE);
373 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); 752 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
374 gtk_widget_show(frame); 753 gtk_widget_show(frame);
754
755 #ifdef HAVE_REGEX_H
756 /* add the tag for regex highlighting */
757 gtk_text_buffer_create_tag(GTK_IMHTML(win->text)->text_buffer, "regex",
758 "background", "#FFAFAF",
759 "weight", "bold",
760 NULL);
761 #endif /* HAVE_REGEX_H */
375 762
376 gtk_widget_show_all(win->window); 763 gtk_widget_show_all(win->window);
377 764
378 return win; 765 return win;
379 } 766 }
442 #endif 829 #endif
443 830
444 void 831 void
445 gaim_gtk_debug_init(void) 832 gaim_gtk_debug_init(void)
446 { 833 {
447 gaim_debug_register_category("gtkdebug");
448
449 /* Debug window preferences. */ 834 /* Debug window preferences. */
450 /* 835 /*
451 * NOTE: This must be set before prefs are loaded, and the callbacks 836 * NOTE: This must be set before prefs are loaded, and the callbacks
452 * set after they are loaded, since prefs sets the enabled 837 * set after they are loaded, since prefs sets the enabled
453 * preference here and that loads the window, which calls the 838 * preference here and that loads the window, which calls the
460 gaim_prefs_add_bool("/gaim/gtk/debug/enabled", FALSE); 845 gaim_prefs_add_bool("/gaim/gtk/debug/enabled", FALSE);
461 846
462 gaim_prefs_add_bool("/gaim/gtk/debug/toolbar", TRUE); 847 gaim_prefs_add_bool("/gaim/gtk/debug/toolbar", TRUE);
463 gaim_prefs_add_int("/gaim/gtk/debug/width", 450); 848 gaim_prefs_add_int("/gaim/gtk/debug/width", 450);
464 gaim_prefs_add_int("/gaim/gtk/debug/height", 250); 849 gaim_prefs_add_int("/gaim/gtk/debug/height", 250);
850
851 #ifdef HAVE_REGEX_H
852 gaim_prefs_add_string("/gaim/gtk/debug/regex", "");
853 gaim_prefs_add_bool("/gaim/gtk/debug/filter", FALSE);
854 gaim_prefs_add_bool("/gaim/gtk/debug/invert", FALSE);
855 gaim_prefs_add_bool("/gaim/gtk/debug/case_insensitive", FALSE);
856 gaim_prefs_add_bool("/gaim/gtk/debug/highlight", FALSE);
857 #endif /* HAVE_REGEX_H */
465 858
466 gaim_prefs_connect_callback(NULL, "/gaim/gtk/debug/enabled", 859 gaim_prefs_connect_callback(NULL, "/gaim/gtk/debug/enabled",
467 debug_enabled_cb, NULL); 860 debug_enabled_cb, NULL);
468 861
469 #define REGISTER_G_LOG_HANDLER(name) \ 862 #define REGISTER_G_LOG_HANDLER(name) \
488 } 881 }
489 882
490 void 883 void
491 gaim_gtk_debug_uninit(void) 884 gaim_gtk_debug_uninit(void)
492 { 885 {
493 gaim_debug_unregister_category("gtkdebug");
494
495 gaim_debug_set_ui_ops(NULL); 886 gaim_debug_set_ui_ops(NULL);
496 } 887 }
497 888
498 void 889 void
499 gaim_gtk_debug_window_show(void) 890 gaim_gtk_debug_window_show(void)
514 debug_window_destroy(NULL, NULL, NULL); 905 debug_window_destroy(NULL, NULL, NULL);
515 } 906 }
516 } 907 }
517 908
518 static void 909 static void
519 create_debug_selected_categories(GtkTreeModel *model, GtkTreePath *path,
520 GtkTreeIter *iter, gpointer data)
521 {
522 GHashTable **hashtable = (GHashTable **)data;
523 char *text = NULL;
524
525 gtk_tree_model_get(model, iter, 0, &text, -1);
526
527 g_hash_table_insert(*hashtable, text, NULL);
528 }
529
530 static gboolean
531 debug_is_filtered_out(const char *category)
532 {
533 GtkTreeSelection *selection = NULL;
534 GHashTable *hashtable = NULL;
535 gboolean found = FALSE;
536
537 if (category == NULL)
538 return FALSE;
539
540 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(debug_win->treeview));
541 hashtable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
542 NULL);
543
544 gtk_tree_selection_selected_foreach(selection,
545 create_debug_selected_categories,
546 &hashtable);
547
548 if (filter_enabled) {
549 if (g_hash_table_lookup_extended(hashtable, category, NULL, NULL))
550 found = FALSE;
551 else
552 found = TRUE;
553 }
554
555 g_hash_table_destroy(hashtable);
556 return found;
557 }
558
559 static void
560 gaim_gtk_debug_print(GaimDebugLevel level, const char *category, 910 gaim_gtk_debug_print(GaimDebugLevel level, const char *category,
561 const char *format, va_list args) 911 const char *format, va_list args)
562 { 912 {
913 GtkTreeIter iter;
563 gboolean timestamps; 914 gboolean timestamps;
564 gchar *arg_s, *ts_s; 915 gchar *arg_s, *ts_s;
565 gchar *esc_s, *cat_s, *tmp, *s; 916 gchar *esc_s, *cat_s, *tmp, *s;
566 917
567 if (!gaim_prefs_get_bool("/gaim/gtk/debug/enabled") || 918 if (!gaim_prefs_get_bool("/gaim/gtk/debug/enabled") ||
568 (debug_win == NULL) || debug_win->paused || 919 (debug_win == NULL))
569 debug_is_filtered_out(category)) { 920 {
570 return; 921 return;
571 } 922 }
572 923
573 timestamps = gaim_prefs_get_bool("/core/debug/timestamps"); 924 timestamps = gaim_prefs_get_bool("/core/debug/timestamps");
574 925
612 tmp = g_strdup_printf("<b>%s</b>", s); 963 tmp = g_strdup_printf("<b>%s</b>", s);
613 g_free(s); 964 g_free(s);
614 s = tmp; 965 s = tmp;
615 } 966 }
616 967
617 gtk_imhtml_append_text(GTK_IMHTML(debug_win->text), s, 0); 968 #ifdef HAVE_REGEX_H
969 /* add the text to the list store */
970 gtk_list_store_append(debug_win->store, &iter);
971 gtk_list_store_set(debug_win->store, &iter, 0, s, -1);
972 #else /* HAVE_REGEX_H */
973 if(!debug_win->paused)
974 gtk_imhtml_append_text(GTK_IMHTML(debug_win->text), s, 0);
975 #endif /* !HAVE_REGEX_H */
618 976
619 g_free(s); 977 g_free(s);
620 } 978 }
621 979
622 static void
623 gaim_gtk_debug_register_category(const char *category)
624 {
625 /* XXX I'd like to be able to put this creation in _init, but that
626 * would require that this be init:ed before anything that wants to
627 * register a category, and I'm not sure I can count on this coming
628 * first */
629 if (debug_categories == NULL)
630 debug_categories = g_hash_table_new_full(g_str_hash,
631 g_str_equal,
632 g_free, NULL);
633
634 if (!g_hash_table_lookup_extended(debug_categories, category, NULL, NULL)) {
635 g_hash_table_insert(debug_categories, g_strdup(category), NULL);
636
637 if (debug_win != NULL && debug_win->treeview != NULL) {
638 GtkTreeModel *model = NULL;
639 GtkTreeIter iter;
640
641 model = gtk_tree_view_get_model(GTK_TREE_VIEW(debug_win->treeview));
642
643 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
644 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0,
645 category, -1);
646 }
647 }
648 }
649
650 static gboolean
651 find_and_remove_category(GtkTreeModel *model, GtkTreePath *path,
652 GtkTreeIter *iter, gpointer data)
653 {
654 GValue value = {0};
655
656 gtk_tree_model_get_value(model, iter, 0, &value);
657
658 if (strcmp(g_value_get_string(&value), data) == 0) {
659 gtk_list_store_remove(GTK_LIST_STORE(model), iter);
660
661 return TRUE;
662 }
663
664 return FALSE;
665 }
666
667 static void
668 gaim_gtk_debug_unregister_category(const char *category)
669 {
670 GtkTreeModel *model = NULL;
671
672 if (debug_win == NULL)
673 return;
674
675 model = gtk_tree_view_get_model(GTK_TREE_VIEW(debug_win->treeview));
676
677 gtk_tree_model_foreach(model,
678 (GtkTreeModelForeachFunc)find_and_remove_category,
679 (char *)category);
680
681 g_hash_table_remove(debug_categories, category);
682 }
683
684 static GaimDebugUiOps ops = 980 static GaimDebugUiOps ops =
685 { 981 {
686 gaim_gtk_debug_print, 982 gaim_gtk_debug_print,
687 gaim_gtk_debug_register_category,
688 gaim_gtk_debug_unregister_category
689 }; 983 };
690 984
691 GaimDebugUiOps * 985 GaimDebugUiOps *
692 gaim_gtk_debug_get_ui_ops(void) 986 gaim_gtk_debug_get_ui_ops(void)
693 { 987 {