Mercurial > pidgin
comparison console/libgnt/gnttree.c @ 15160:c6b79e535eb8
[gaim-migrate @ 17946]
Experimental search-as-you-type in GntTree. Press "/" to enter the search-mode,
and when you're done, press escape to cancel. The search-mode remains active
for 4 seconds before returning to normal.
Someone should update the manual, and possibly enhance the behaviour.
committer: Tailor Script <tailor@pidgin.im>
author | Sadrul Habib Chowdhury <imadil@gmail.com> |
---|---|
date | Sun, 10 Dec 2006 11:26:17 +0000 |
parents | 68385f5bbd61 |
children | 16cd6fd20e6f |
comparison
equal
deleted
inserted
replaced
15159:9ca8095b6243 | 15160:c6b79e535eb8 |
---|---|
3 #include "gnttree.h" | 3 #include "gnttree.h" |
4 #include "gntutils.h" | 4 #include "gntutils.h" |
5 | 5 |
6 #include <string.h> | 6 #include <string.h> |
7 #include <ctype.h> | 7 #include <ctype.h> |
8 | |
9 #define SEARCH_TIMEOUT 4000 /* 4 secs */ | |
8 | 10 |
9 enum | 11 enum |
10 { | 12 { |
11 SIG_SELECTION_CHANGED, | 13 SIG_SELECTION_CHANGED, |
12 SIG_SCROLLED, | 14 SIG_SCROLLED, |
33 GntTreeRow *child; | 35 GntTreeRow *child; |
34 GntTreeRow *next; | 36 GntTreeRow *next; |
35 GntTreeRow *prev; | 37 GntTreeRow *prev; |
36 | 38 |
37 GList *columns; | 39 GList *columns; |
40 GntTree *tree; | |
38 }; | 41 }; |
39 | 42 |
40 struct _GnTreeCol | 43 struct _GnTreeCol |
41 { | 44 { |
42 char *text; | 45 char *text; |
68 if (row->next) | 71 if (row->next) |
69 return row->next; | 72 return row->next; |
70 return _get_next(row->parent, FALSE); | 73 return _get_next(row->parent, FALSE); |
71 } | 74 } |
72 | 75 |
76 static gboolean | |
77 row_matches_search(GntTreeRow *row) | |
78 { | |
79 GntTree *t = row->tree; | |
80 if (t->search && t->search->len > 0) { | |
81 char *one = g_utf8_casefold(((GntTreeCol*)row->columns->data)->text, -1); | |
82 char *two = g_utf8_casefold(t->search->str, -1); | |
83 char *z = strstr(one, two); | |
84 g_free(one); | |
85 g_free(two); | |
86 if (z == NULL) | |
87 return FALSE; | |
88 } | |
89 return TRUE; | |
90 } | |
91 | |
73 static GntTreeRow * | 92 static GntTreeRow * |
74 get_next(GntTreeRow *row) | 93 get_next(GntTreeRow *row) |
75 { | 94 { |
76 if (row == NULL) | 95 if (row == NULL) |
77 return NULL; | 96 return NULL; |
78 return _get_next(row, !row->collapsed); | 97 while ((row = _get_next(row, !row->collapsed)) != NULL) { |
98 if (row_matches_search(row)) | |
99 break; | |
100 } | |
101 return row; | |
79 } | 102 } |
80 | 103 |
81 /* Returns the n-th next row. If it doesn't exist, returns NULL */ | 104 /* Returns the n-th next row. If it doesn't exist, returns NULL */ |
82 static GntTreeRow * | 105 static GntTreeRow * |
83 get_next_n(GntTreeRow *row, int n) | 106 get_next_n(GntTreeRow *row, int n) |
133 static GntTreeRow * | 156 static GntTreeRow * |
134 get_prev(GntTreeRow *row) | 157 get_prev(GntTreeRow *row) |
135 { | 158 { |
136 if (row == NULL) | 159 if (row == NULL) |
137 return NULL; | 160 return NULL; |
138 if (row->prev) | 161 while (row) { |
139 return get_last_child(row->prev); | 162 if (row->prev) |
140 return row->parent; | 163 row = get_last_child(row->prev); |
164 else | |
165 row = row->parent; | |
166 if (!row || row_matches_search(row)) | |
167 break; | |
168 } | |
169 return row; | |
141 } | 170 } |
142 | 171 |
143 static GntTreeRow * | 172 static GntTreeRow * |
144 get_prev_n(GntTreeRow *row, int n) | 173 get_prev_n(GntTreeRow *row, int n) |
145 { | 174 { |
330 if (up < 0) | 359 if (up < 0) |
331 tree->top = tree->current; | 360 tree->top = tree->current; |
332 else if (up >= widget->priv.height - pos) | 361 else if (up >= widget->priv.height - pos) |
333 tree->top = get_prev_n(tree->current, rows); | 362 tree->top = get_prev_n(tree->current, rows); |
334 | 363 |
364 if (tree->top && !row_matches_search(tree->top)) | |
365 tree->top = get_next(tree->top); | |
335 row = tree->top; | 366 row = tree->top; |
336 scrcol = widget->priv.width - 1 - 2 * pos; /* exclude the borders and the scrollbar */ | 367 scrcol = widget->priv.width - 1 - 2 * pos; /* exclude the borders and the scrollbar */ |
337 for (i = start + pos; row && i < widget->priv.height - pos; | 368 for (i = start + pos; row && i < widget->priv.height - pos; |
338 i++, row = get_next(row)) | 369 i++, row = get_next(row)) |
339 { | 370 { |
341 int wr; | 372 int wr; |
342 | 373 |
343 GntTextFormatFlags flags = row->flags; | 374 GntTextFormatFlags flags = row->flags; |
344 int attr = 0; | 375 int attr = 0; |
345 | 376 |
377 if (!row_matches_search(row)) | |
378 continue; | |
346 str = update_row_text(tree, row); | 379 str = update_row_text(tree, row); |
347 | 380 |
348 if ((wr = gnt_util_onscreen_width(str, NULL)) > scrcol) | 381 if ((wr = gnt_util_onscreen_width(str, NULL)) > scrcol) |
349 { | 382 { |
350 char *s = (char*)gnt_util_onscreen_width_to_pointer(str, scrcol, &wr); | 383 char *s = (char*)gnt_util_onscreen_width_to_pointer(str, scrcol, &wr); |
572 if (old != tree->current) | 605 if (old != tree->current) |
573 tree_selection_changed(tree, old, tree->current); | 606 tree_selection_changed(tree, old, tree->current); |
574 return TRUE; | 607 return TRUE; |
575 } | 608 } |
576 | 609 |
610 static void | |
611 end_search(GntTree *tree) | |
612 { | |
613 if (tree->search) { | |
614 g_source_remove(tree->search_timeout); | |
615 g_string_free(tree->search, TRUE); | |
616 tree->search = NULL; | |
617 tree->search_timeout = 0; | |
618 } | |
619 } | |
620 | |
621 static gboolean | |
622 search_timeout(gpointer data) | |
623 { | |
624 GntTree *tree = data; | |
625 | |
626 end_search(tree); | |
627 redraw_tree(tree); | |
628 | |
629 return FALSE; | |
630 } | |
631 | |
577 static gboolean | 632 static gboolean |
578 gnt_tree_key_pressed(GntWidget *widget, const char *text) | 633 gnt_tree_key_pressed(GntWidget *widget, const char *text) |
579 { | 634 { |
580 GntTree *tree = GNT_TREE(widget); | 635 GntTree *tree = GNT_TREE(widget); |
581 GntTreeRow *old = tree->current; | 636 GntTreeRow *old = tree->current; |
582 | 637 |
583 if (text[0] == '\r') | 638 if (text[0] == '\r') { |
584 { | 639 end_search(tree); |
585 gnt_widget_activate(widget); | 640 gnt_widget_activate(widget); |
586 } | 641 } else if (tree->search) { |
587 else if (text[0] == ' ' && text[1] == 0) | 642 if (isalnum(*text)) { |
588 { | 643 tree->search = g_string_append_c(tree->search, *text); |
644 redraw_tree(tree); | |
645 g_source_remove(tree->search_timeout); | |
646 tree->search_timeout = g_timeout_add(SEARCH_TIMEOUT, search_timeout, tree); | |
647 } | |
648 return TRUE; | |
649 } else if (text[0] == ' ' && text[1] == 0) { | |
589 /* Space pressed */ | 650 /* Space pressed */ |
590 GntTreeRow *row = tree->current; | 651 GntTreeRow *row = tree->current; |
591 if (row && row->child) | 652 if (row && row->child) |
592 { | 653 { |
593 row->collapsed = !row->collapsed; | 654 row->collapsed = !row->collapsed; |
614 gnt_tree_destroy(GntWidget *widget) | 675 gnt_tree_destroy(GntWidget *widget) |
615 { | 676 { |
616 GntTree *tree = GNT_TREE(widget); | 677 GntTree *tree = GNT_TREE(widget); |
617 int i; | 678 int i; |
618 | 679 |
680 end_search(tree); | |
619 g_hash_table_destroy(tree->hash); | 681 g_hash_table_destroy(tree->hash); |
620 g_list_free(tree->list); | 682 g_list_free(tree->list); |
621 | 683 |
622 for (i = 0; i < tree->ncol; i++) | 684 for (i = 0; i < tree->ncol; i++) |
623 { | 685 { |
682 tree->columns[tree->ncol - 1].width += widget->priv.width - n - 1 * tree->ncol; | 744 tree->columns[tree->ncol - 1].width += widget->priv.width - n - 1 * tree->ncol; |
683 else | 745 else |
684 tree->columns[tree->ncol - 1].width += widget->priv.width - n - 2 - 1 * tree->ncol; | 746 tree->columns[tree->ncol - 1].width += widget->priv.width - n - 2 - 1 * tree->ncol; |
685 } | 747 } |
686 | 748 |
749 static gboolean | |
750 start_search(GntBindable *bindable, GList *list) | |
751 { | |
752 GntTree *tree = GNT_TREE(bindable); | |
753 if (tree->search) | |
754 return FALSE; | |
755 tree->search = g_string_new(NULL); | |
756 tree->search_timeout = g_timeout_add(SEARCH_TIMEOUT, search_timeout, tree); | |
757 return TRUE; | |
758 } | |
759 | |
760 static gboolean | |
761 end_search_action(GntBindable *bindable, GList *list) | |
762 { | |
763 GntTree *tree = GNT_TREE(bindable); | |
764 if (tree->search == NULL) | |
765 return FALSE; | |
766 end_search(tree); | |
767 redraw_tree(tree); | |
768 return TRUE; | |
769 } | |
770 | |
687 static void | 771 static void |
688 gnt_tree_class_init(GntTreeClass *klass) | 772 gnt_tree_class_init(GntTreeClass *klass) |
689 { | 773 { |
690 GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass); | 774 GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass); |
691 parent_class = GNT_WIDGET_CLASS(klass); | 775 parent_class = GNT_WIDGET_CLASS(klass); |
730 gnt_bindable_register_binding(bindable, "move-down", GNT_KEY_CTRL_N, NULL); | 814 gnt_bindable_register_binding(bindable, "move-down", GNT_KEY_CTRL_N, NULL); |
731 gnt_bindable_class_register_action(bindable, "page-up", action_page_up, | 815 gnt_bindable_class_register_action(bindable, "page-up", action_page_up, |
732 GNT_KEY_PGUP, NULL); | 816 GNT_KEY_PGUP, NULL); |
733 gnt_bindable_class_register_action(bindable, "page-down", action_page_down, | 817 gnt_bindable_class_register_action(bindable, "page-down", action_page_down, |
734 GNT_KEY_PGDOWN, NULL); | 818 GNT_KEY_PGDOWN, NULL); |
819 gnt_bindable_class_register_action(bindable, "start-search", start_search, | |
820 "/", NULL); | |
821 gnt_bindable_class_register_action(bindable, "end-search", end_search_action, | |
822 "\033", NULL); | |
735 | 823 |
736 gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), bindable); | 824 gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), bindable); |
737 GNTDEBUG; | 825 GNTDEBUG; |
738 } | 826 } |
739 | 827 |
954 GntTreeRow *gnt_tree_add_row_after(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro) | 1042 GntTreeRow *gnt_tree_add_row_after(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro) |
955 { | 1043 { |
956 GntTreeRow *pr = NULL; | 1044 GntTreeRow *pr = NULL; |
957 | 1045 |
958 g_hash_table_replace(tree->hash, key, row); | 1046 g_hash_table_replace(tree->hash, key, row); |
1047 row->tree = tree; | |
959 | 1048 |
960 if (bigbro == NULL && tree->compare) | 1049 if (bigbro == NULL && tree->compare) |
961 { | 1050 { |
962 bigbro = find_position(tree, key, parent); | 1051 bigbro = find_position(tree, key, parent); |
963 } | 1052 } |