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 }