Mercurial > pidgin
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 { |
