2 * GNT - The GLib Ncurses Toolkit
4 * GNT is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "gntinternal.h"
24 #include "gntmarshal.h"
32 #define SEARCH_TIMEOUT_S 4 /* 4 secs */
33 #define SEARCHING(tree) (tree->priv->search && tree->priv->search->len > 0)
35 #define COLUMN_INVISIBLE(tree, index) (tree->columns[index].flags & GNT_TREE_COLUMN_INVISIBLE)
36 #define BINARY_DATA(tree, index) (tree->columns[index].flags & GNT_TREE_COLUMN_BINARY_DATA)
37 #define RIGHT_ALIGNED(tree, index) (tree->columns[index].flags & GNT_TREE_COLUMN_RIGHT_ALIGNED)
48 SIG_SELECTION_CHANGED
,
60 gboolean (*search_func
)(GntTree
*tree
, gpointer key
, const char *search
, const char *current
);
69 /* XXX: Make this one into a GObject?
74 void *data
; /* XXX: unused */
77 gboolean choice
; /* Is this a choice-box?
78 If choice is true, then child will be NULL */
80 GntTextFormatFlags flags
;
96 int span
; /* How many columns does it span? */
99 static void tree_selection_changed(GntTree
*, GntTreeRow
*, GntTreeRow
*);
100 static void _gnt_tree_init_internals(GntTree
*tree
, int col
);
102 static GntWidgetClass
*parent_class
= NULL
;
103 static guint signals
[SIGS
] = { 0 };
106 readjust_columns(GntTree
*tree
)
110 #define WIDTH(i) (tree->columns[i].width_ratio ? tree->columns[i].width_ratio : tree->columns[i].width)
111 gnt_widget_get_size(GNT_WIDGET(tree
), &width
, NULL
);
112 if (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree
), GNT_WIDGET_NO_BORDER
))
114 width
-= 1; /* Exclude the scrollbar from the calculation */
115 for (i
= 0, total
= 0; i
< tree
->ncol
; i
++) {
116 if (tree
->columns
[i
].flags
& GNT_TREE_COLUMN_INVISIBLE
)
118 if (tree
->columns
[i
].flags
& GNT_TREE_COLUMN_FIXED_SIZE
)
119 width
-= WIDTH(i
) + (tree
->priv
->lastvisible
!= i
);
121 total
+= WIDTH(i
) + (tree
->priv
->lastvisible
!= i
);
127 for (i
= 0; i
< tree
->ncol
; i
++) {
128 if (tree
->columns
[i
].flags
& GNT_TREE_COLUMN_INVISIBLE
)
130 if (tree
->columns
[i
].flags
& GNT_TREE_COLUMN_FIXED_SIZE
)
133 col
= (WIDTH(i
) * width
) / total
;
134 gnt_tree_set_col_width(GNT_TREE(tree
), i
, col
);
138 /* Move the item at position old to position new */
140 g_list_reposition_child(GList
*list
, int old
, int new)
142 gpointer item
= g_list_nth_data(list
, old
);
143 list
= g_list_remove(list
, item
);
145 new--; /* because the positions would have shifted after removing the item */
146 list
= g_list_insert(list
, item
, new);
151 _get_next(GntTreeRow
*row
, gboolean godeep
)
155 if (godeep
&& row
->child
)
159 return _get_next(row
->parent
, FALSE
);
163 row_matches_search(GntTreeRow
*row
)
165 GntTree
*t
= row
->tree
;
166 if (t
->priv
->search
&& t
->priv
->search
->len
> 0) {
167 GntTreeCol
*col
= (col
= g_list_nth_data(row
->columns
, t
->priv
->search_column
)) ? col
: row
->columns
->data
;
169 if (t
->priv
->search_func
)
170 return t
->priv
->search_func(t
, row
->key
, t
->priv
->search
->str
, col
->text
);
171 one
= g_utf8_casefold(col
->text
, -1);
172 two
= g_utf8_casefold(t
->priv
->search
->str
, -1);
173 z
= strstr(one
, two
);
183 get_next(GntTreeRow
*row
)
187 while ((row
= _get_next(row
, !row
->collapsed
)) != NULL
) {
188 if (row_matches_search(row
))
194 /* Returns the n-th next row. If it doesn't exist, returns NULL */
196 get_next_n(GntTreeRow
*row
, int n
)
203 /* Returns the n-th next row. If it doesn't exist, then the last non-NULL node */
205 get_next_n_opt(GntTreeRow
*row
, int n
, int *pos
)
207 GntTreeRow
*next
= row
;
230 get_last_child(GntTreeRow
*row
)
234 if (!row
->collapsed
&& row
->child
)
241 return get_last_child(row
);
245 get_prev(GntTreeRow
*row
)
251 row
= get_last_child(row
->prev
);
254 if (!row
|| row_matches_search(row
))
261 get_prev_n(GntTreeRow
*row
, int n
)
268 /* Distance of row from the root */
269 /* XXX: This is uber-inefficient */
271 get_root_distance(GntTreeRow
*row
)
275 return get_root_distance(get_prev(row
)) + 1;
278 /* Returns the distance between a and b.
279 * If a is 'above' b, then the distance is positive */
281 get_distance(GntTreeRow
*a
, GntTreeRow
*b
)
283 /* First get the distance from a to the root.
284 * Then the distance from b to the root.
286 * It's not that good, but it works. */
287 int ha
= get_root_distance(a
);
288 int hb
= get_root_distance(b
);
294 find_depth(GntTreeRow
*row
)
308 update_row_text(GntTree
*tree
, GntTreeRow
*row
)
310 GString
*string
= g_string_new(NULL
);
313 gboolean notfirst
= FALSE
;
315 for (i
= 0, iter
= row
->columns
; i
< tree
->ncol
&& iter
; i
++, iter
= iter
->next
)
317 GntTreeCol
*col
= iter
->data
;
321 gboolean cut
= FALSE
;
325 if (COLUMN_INVISIBLE(tree
, i
))
328 if (BINARY_DATA(tree
, i
))
333 len
= gnt_util_onscreen_width(display
, NULL
);
335 width
= tree
->columns
[i
].width
;
341 g_string_append_printf(string
, "[%c] ",
342 row
->isselected
? 'X' : ' ');
345 else if (find_depth(row
) < tree
->priv
->expander_level
&& row
->child
)
349 string
= g_string_append(string
, "+ ");
353 string
= g_string_append(string
, "- ");
359 fl
= TAB_SIZE
* find_depth(row
);
360 g_string_append_printf(string
, "%*s", fl
, "");
363 } else if (notfirst
&& tree
->show_separator
)
364 g_string_append_c(string
, '|');
366 g_string_append_c(string
, ' ');
371 len
= MAX(1, width
- 1);
375 if (RIGHT_ALIGNED(tree
, i
) && len
< tree
->columns
[i
].width
) {
376 g_string_append_printf(string
, "%*s", width
- len
- cut
, "");
379 text
= gnt_util_onscreen_width_to_pointer(display
, len
- fl
, NULL
);
380 string
= g_string_append_len(string
, display
, text
- display
);
381 if (cut
&& width
> 1) { /* ellipsis */
382 if (gnt_ascii_only())
383 g_string_append_c(string
, '~');
385 string
= g_string_append(string
, "\342\200\246");
389 if (!RIGHT_ALIGNED(tree
, i
) && len
< tree
->columns
[i
].width
&& iter
->next
)
390 g_string_append_printf(string
, "%*s", width
- len
, "");
392 return g_string_free(string
, FALSE
);
395 #define NEXT_X x += tree->columns[i].width + (i > 0 ? 1 : 0)
398 tree_mark_columns(GntTree
*tree
, int pos
, int y
, chtype type
)
400 GntWidget
*widget
= GNT_WIDGET(tree
);
403 gboolean notfirst
= FALSE
;
405 for (i
= 0; i
< tree
->ncol
- 1; i
++)
407 if (!COLUMN_INVISIBLE(tree
, i
)) {
411 if (!COLUMN_INVISIBLE(tree
, i
+1) && notfirst
)
412 mvwaddch(widget
->window
, y
, x
, type
);
417 redraw_tree(GntTree
*tree
)
420 GntWidget
*widget
= GNT_WIDGET(tree
);
422 int pos
, up
, down
= 0;
426 if (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree
), GNT_WIDGET_MAPPED
))
429 if (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_NO_BORDER
))
434 if (tree
->top
== NULL
)
435 tree
->top
= tree
->root
;
436 if (tree
->current
== NULL
&& tree
->root
!= NULL
) {
437 tree
->current
= tree
->root
;
438 tree_selection_changed(tree
, NULL
, tree
->current
);
441 wbkgd(widget
->window
, gnt_color_pair(GNT_COLOR_NORMAL
));
444 if (tree
->show_title
)
449 mvwhline(widget
->window
, pos
+ 1, pos
, ACS_HLINE
| gnt_color_pair(GNT_COLOR_NORMAL
),
450 widget
->priv
.width
- pos
- 1);
451 mvwhline(widget
->window
, pos
, pos
, ' ' | gnt_color_pair(GNT_COLOR_NORMAL
),
452 widget
->priv
.width
- pos
- 1);
454 for (i
= 0; i
< tree
->ncol
; i
++)
456 if (COLUMN_INVISIBLE(tree
, i
)) {
459 mvwaddnstr(widget
->window
, pos
, x
+ (x
!= pos
), tree
->columns
[i
].title
, tree
->columns
[i
].width
);
464 tree_mark_columns(tree
, pos
, 0,
465 (tree
->show_separator
? ACS_TTEE
: ACS_HLINE
) | gnt_color_pair(GNT_COLOR_NORMAL
));
466 tree_mark_columns(tree
, pos
, widget
->priv
.height
- pos
,
467 (tree
->show_separator
? ACS_BTEE
: ACS_HLINE
) | gnt_color_pair(GNT_COLOR_NORMAL
));
469 tree_mark_columns(tree
, pos
, pos
+ 1,
470 (tree
->show_separator
? ACS_PLUS
: ACS_HLINE
) | gnt_color_pair(GNT_COLOR_NORMAL
));
471 tree_mark_columns(tree
, pos
, pos
,
472 (tree
->show_separator
? ACS_VLINE
: ' ') | gnt_color_pair(GNT_COLOR_NORMAL
));
476 rows
= widget
->priv
.height
- pos
* 2 - start
- 1;
477 tree
->bottom
= get_next_n_opt(tree
->top
, rows
, &down
);
480 tree
->top
= get_prev_n(tree
->bottom
, rows
);
481 if (tree
->top
== NULL
)
482 tree
->top
= tree
->root
;
485 up
= get_distance(tree
->top
, tree
->current
);
487 tree
->top
= tree
->current
;
488 else if (up
>= widget
->priv
.height
- pos
)
489 tree
->top
= get_prev_n(tree
->current
, rows
);
491 if (tree
->top
&& !row_matches_search(tree
->top
))
492 tree
->top
= get_next(tree
->top
);
494 scrcol
= widget
->priv
.width
- 1 - 2 * pos
; /* exclude the borders and the scrollbar */
496 if (tree
->current
&& !row_matches_search(tree
->current
)) {
497 GntTreeRow
*old
= tree
->current
;
498 tree
->current
= tree
->top
;
499 tree_selection_changed(tree
, old
, tree
->current
);
502 for (i
= start
+ pos
; row
&& i
< widget
->priv
.height
- pos
;
503 i
++, row
= get_next(row
))
508 GntTextFormatFlags flags
= row
->flags
;
511 if (!row_matches_search(row
))
513 str
= update_row_text(tree
, row
);
515 if ((wr
= gnt_util_onscreen_width(str
, NULL
)) > scrcol
)
517 char *s
= (char*)gnt_util_onscreen_width_to_pointer(str
, scrcol
, &wr
);
521 if (flags
& GNT_TEXT_FLAG_BOLD
)
523 if (flags
& GNT_TEXT_FLAG_UNDERLINE
)
525 if (flags
& GNT_TEXT_FLAG_BLINK
)
528 if (row
== tree
->current
)
532 if (gnt_widget_has_focus(widget
))
533 attr
|= gnt_color_pair(GNT_COLOR_HIGHLIGHT
);
535 attr
|= gnt_color_pair(GNT_COLOR_HIGHLIGHT_D
);
539 if (flags
& GNT_TEXT_FLAG_DIM
)
541 attr
|= (A_DIM
| gnt_color_pair(row
->color
));
543 attr
|= (A_DIM
| gnt_color_pair(GNT_COLOR_DISABLED
));
544 else if (flags
& GNT_TEXT_FLAG_HIGHLIGHT
)
545 attr
|= (A_DIM
| gnt_color_pair(GNT_COLOR_HIGHLIGHT
));
547 attr
|= gnt_color_pair(row
->color
);
549 attr
|= gnt_color_pair(GNT_COLOR_NORMAL
);
552 wbkgdset(widget
->window
, '\0' | attr
);
553 mvwaddstr(widget
->window
, i
, pos
, C_(str
));
554 whline(widget
->window
, ' ', scrcol
- wr
);
557 tree_mark_columns(tree
, pos
, i
,
558 (tree
->show_separator
? ACS_VLINE
: ' ') | attr
);
561 wbkgdset(widget
->window
, '\0' | gnt_color_pair(GNT_COLOR_NORMAL
));
562 while (i
< widget
->priv
.height
- pos
)
564 mvwhline(widget
->window
, i
, pos
, ' ',
565 widget
->priv
.width
- pos
* 2 - 1);
566 tree_mark_columns(tree
, pos
, i
,
567 (tree
->show_separator
? ACS_VLINE
: ' '));
571 scrcol
= widget
->priv
.width
- pos
- 1; /* position of the scrollbar */
576 int showing
, position
;
578 get_next_n_opt(tree
->root
, g_list_length(tree
->list
), &total
);
579 showing
= rows
* rows
/ MAX(total
, 1) + 1;
580 showing
= MIN(rows
, showing
);
583 up
= get_distance(tree
->root
, tree
->top
);
586 position
= (rows
- showing
) * up
/ MAX(1, up
+ down
);
587 position
= MAX((tree
->top
!= tree
->root
), position
);
589 if (showing
+ position
> rows
)
590 position
= rows
- showing
;
592 if (showing
+ position
== rows
&& row
)
593 position
= MAX(0, rows
- 1 - showing
);
594 else if (showing
+ position
< rows
&& !row
)
595 position
= rows
- showing
;
597 position
+= pos
+ start
+ 1;
599 mvwvline(widget
->window
, pos
+ start
+ 1, scrcol
,
600 ' ' | gnt_color_pair(GNT_COLOR_NORMAL
), rows
);
601 mvwvline(widget
->window
, position
, scrcol
,
602 ACS_CKBOARD
| gnt_color_pair(GNT_COLOR_HIGHLIGHT_D
), showing
);
605 mvwaddch(widget
->window
, start
+ pos
, scrcol
,
606 ((tree
->top
!= tree
->root
) ? ACS_UARROW
: ' ') |
607 gnt_color_pair(GNT_COLOR_HIGHLIGHT_D
));
609 mvwaddch(widget
->window
, widget
->priv
.height
- pos
- 1, scrcol
,
610 (row
? ACS_DARROW
: ' ') | gnt_color_pair(GNT_COLOR_HIGHLIGHT_D
));
612 /* If there's a search-text, show it in the bottom of the tree */
613 if (tree
->priv
->search
&& tree
->priv
->search
->len
> 0) {
614 const char *str
= gnt_util_onscreen_width_to_pointer(tree
->priv
->search
->str
, scrcol
- 1, NULL
);
615 wbkgdset(widget
->window
, '\0' | gnt_color_pair(GNT_COLOR_HIGHLIGHT_D
));
616 mvwaddnstr(widget
->window
, widget
->priv
.height
- pos
- 1, pos
,
617 tree
->priv
->search
->str
, str
- tree
->priv
->search
->str
);
619 wmove(widget
->window
, current
, pos
);
621 gnt_widget_queue_update(widget
);
625 gnt_tree_draw(GntWidget
*widget
)
627 GntTree
*tree
= GNT_TREE(widget
);
635 gnt_tree_size_request(GntWidget
*widget
)
637 if (widget
->priv
.height
== 0)
638 widget
->priv
.height
= 10; /* XXX: Why?! */
639 if (widget
->priv
.width
== 0)
641 GntTree
*tree
= GNT_TREE(widget
);
643 width
= 1 + 2 * (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree
), GNT_WIDGET_NO_BORDER
));
644 for (i
= 0; i
< tree
->ncol
; i
++)
645 if (!COLUMN_INVISIBLE(tree
, i
)) {
646 width
= width
+ tree
->columns
[i
].width
;
647 if (tree
->priv
->lastvisible
!= i
)
650 widget
->priv
.width
= width
;
655 gnt_tree_map(GntWidget
*widget
)
657 GntTree
*tree
= GNT_TREE(widget
);
658 if (widget
->priv
.width
== 0 || widget
->priv
.height
== 0)
660 gnt_widget_size_request(widget
);
662 tree
->top
= tree
->root
;
663 tree
->current
= tree
->root
;
668 tree_selection_changed(GntTree
*tree
, GntTreeRow
*old
, GntTreeRow
*current
)
670 g_signal_emit(tree
, signals
[SIG_SELECTION_CHANGED
], 0, old
? old
->key
: NULL
,
671 current
? current
->key
: NULL
);
675 action_down(GntBindable
*bind
, GList
*null
)
678 GntTree
*tree
= GNT_TREE(bind
);
679 GntTreeRow
*old
= tree
->current
;
680 GntTreeRow
*row
= get_next(tree
->current
);
684 if ((dist
= get_distance(tree
->current
, tree
->bottom
)) < 0)
685 gnt_tree_scroll(tree
, -dist
);
688 if (old
!= tree
->current
)
689 tree_selection_changed(tree
, old
, tree
->current
);
694 action_move_parent(GntBindable
*bind
, GList
*null
)
696 GntTree
*tree
= GNT_TREE(bind
);
697 GntTreeRow
*row
= tree
->current
;
700 if (!row
|| !row
->parent
|| SEARCHING(tree
))
703 tree
->current
= row
->parent
;
704 if ((dist
= get_distance(tree
->current
, tree
->top
)) > 0)
705 gnt_tree_scroll(tree
, -dist
);
708 tree_selection_changed(tree
, row
, tree
->current
);
713 action_up(GntBindable
*bind
, GList
*list
)
716 GntTree
*tree
= GNT_TREE(bind
);
717 GntTreeRow
*old
= tree
->current
;
718 GntTreeRow
*row
= get_prev(tree
->current
);
722 if ((dist
= get_distance(tree
->current
, tree
->top
)) > 0)
723 gnt_tree_scroll(tree
, -dist
);
726 if (old
!= tree
->current
)
727 tree_selection_changed(tree
, old
, tree
->current
);
733 action_page_down(GntBindable
*bind
, GList
*null
)
735 GntTree
*tree
= GNT_TREE(bind
);
736 GntTreeRow
*old
= tree
->current
;
737 GntTreeRow
*row
= get_next(tree
->bottom
);
740 int dist
= get_distance(tree
->top
, tree
->current
);
741 tree
->top
= tree
->bottom
;
742 tree
->current
= get_next_n_opt(tree
->top
, dist
, NULL
);
745 else if (tree
->current
!= tree
->bottom
)
747 tree
->current
= tree
->bottom
;
751 if (old
!= tree
->current
)
752 tree_selection_changed(tree
, old
, tree
->current
);
757 action_page_up(GntBindable
*bind
, GList
*null
)
759 GntWidget
*widget
= GNT_WIDGET(bind
);
760 GntTree
*tree
= GNT_TREE(bind
);
762 GntTreeRow
*old
= tree
->current
;
764 if (tree
->top
!= tree
->root
)
766 int dist
= get_distance(tree
->top
, tree
->current
);
767 row
= get_prev_n(tree
->top
, widget
->priv
.height
- 1 -
768 tree
->show_title
* 2 - 2 * (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_NO_BORDER
) == 0));
772 tree
->current
= get_next_n_opt(tree
->top
, dist
, NULL
);
775 else if (tree
->current
!= tree
->top
)
777 tree
->current
= tree
->top
;
780 if (old
!= tree
->current
)
781 tree_selection_changed(tree
, old
, tree
->current
);
786 end_search(GntTree
*tree
)
788 if (tree
->priv
->search
) {
789 g_source_remove(tree
->priv
->search_timeout
);
790 g_string_free(tree
->priv
->search
, TRUE
);
791 tree
->priv
->search
= NULL
;
792 tree
->priv
->search_timeout
= 0;
793 GNT_WIDGET_UNSET_FLAGS(GNT_WIDGET(tree
), GNT_WIDGET_DISABLE_ACTIONS
);
798 search_timeout(gpointer data
)
800 GntTree
*tree
= data
;
809 gnt_tree_key_pressed(GntWidget
*widget
, const char *text
)
811 GntTree
*tree
= GNT_TREE(widget
);
812 GntTreeRow
*old
= tree
->current
;
814 if (text
[0] == '\r' || text
[0] == '\n') {
816 gnt_widget_activate(widget
);
817 } else if (tree
->priv
->search
) {
818 gboolean changed
= TRUE
;
819 if (g_unichar_isprint(*text
)) {
820 tree
->priv
->search
= g_string_append_c(tree
->priv
->search
, *text
);
821 } else if (g_utf8_collate(text
, GNT_KEY_BACKSPACE
) == 0) {
822 if (tree
->priv
->search
->len
)
823 tree
->priv
->search
->str
[--tree
->priv
->search
->len
] = '\0';
829 gnt_bindable_perform_action_key(GNT_BINDABLE(tree
), text
);
831 g_source_remove(tree
->priv
->search_timeout
);
832 tree
->priv
->search_timeout
= g_timeout_add_seconds(SEARCH_TIMEOUT_S
, search_timeout
, tree
);
834 } else if (text
[0] == ' ' && text
[1] == 0) {
836 GntTreeRow
*row
= tree
->current
;
837 if (row
&& row
->child
)
839 row
->collapsed
= !row
->collapsed
;
841 g_signal_emit(tree
, signals
[SIG_COLLAPSED
], 0, row
->key
, row
->collapsed
);
843 else if (row
&& row
->choice
)
845 row
->isselected
= !row
->isselected
;
846 g_signal_emit(tree
, signals
[SIG_TOGGLED
], 0, row
->key
);
853 if (old
!= tree
->current
)
855 tree_selection_changed(tree
, old
, tree
->current
);
862 gnt_tree_free_columns(GntTree
*tree
)
865 for (i
= 0; i
< tree
->ncol
; i
++) {
866 g_free(tree
->columns
[i
].title
);
868 g_free(tree
->columns
);
872 gnt_tree_destroy(GntWidget
*widget
)
874 GntTree
*tree
= GNT_TREE(widget
);
878 g_hash_table_destroy(tree
->hash
);
879 g_list_free(tree
->list
);
880 gnt_tree_free_columns(tree
);
885 gnt_tree_clicked(GntWidget
*widget
, GntMouseEvent event
, int x
, int y
)
887 GntTree
*tree
= GNT_TREE(widget
);
888 GntTreeRow
*old
= tree
->current
;
889 if (event
== GNT_MOUSE_SCROLL_UP
) {
890 action_up(GNT_BINDABLE(widget
), NULL
);
891 } else if (event
== GNT_MOUSE_SCROLL_DOWN
) {
892 action_down(GNT_BINDABLE(widget
), NULL
);
893 } else if (event
== GNT_LEFT_MOUSE_DOWN
) {
895 GntTree
*tree
= GNT_TREE(widget
);
897 if (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_NO_BORDER
))
899 if (tree
->show_title
)
901 pos
= y
- widget
->priv
.y
- pos
;
902 row
= get_next_n(tree
->top
, pos
);
903 if (row
&& tree
->current
!= row
) {
904 GntTreeRow
*old
= tree
->current
;
907 tree_selection_changed(tree
, old
, tree
->current
);
908 } else if (row
&& row
== tree
->current
) {
910 row
->isselected
= !row
->isselected
;
911 g_signal_emit(tree
, signals
[SIG_TOGGLED
], 0, row
->key
);
914 gnt_widget_activate(widget
);
920 if (old
!= tree
->current
) {
921 tree_selection_changed(tree
, old
, tree
->current
);
927 gnt_tree_size_changed(GntWidget
*widget
, int w
, int h
)
929 GntTree
*tree
= GNT_TREE(widget
);
930 if (widget
->priv
.width
<= 0)
933 readjust_columns(tree
);
937 start_search(GntBindable
*bindable
, GList
*list
)
939 GntTree
*tree
= GNT_TREE(bindable
);
940 if (tree
->priv
->search
)
942 GNT_WIDGET_SET_FLAGS(GNT_WIDGET(tree
), GNT_WIDGET_DISABLE_ACTIONS
);
943 tree
->priv
->search
= g_string_new(NULL
);
944 tree
->priv
->search_timeout
= g_timeout_add_seconds(SEARCH_TIMEOUT_S
, search_timeout
, tree
);
949 end_search_action(GntBindable
*bindable
, GList
*list
)
951 GntTree
*tree
= GNT_TREE(bindable
);
952 if (tree
->priv
->search
== NULL
)
954 GNT_WIDGET_UNSET_FLAGS(GNT_WIDGET(tree
), GNT_WIDGET_DISABLE_ACTIONS
);
961 move_first_action(GntBindable
*bind
, GList
*null
)
963 GntTree
*tree
= GNT_TREE(bind
);
964 GntTreeRow
*row
= tree
->root
;
965 GntTreeRow
*old
= tree
->current
;
966 if (row
&& !row_matches_search(row
))
971 if (old
!= tree
->current
)
972 tree_selection_changed(tree
, old
, tree
->current
);
979 move_last_action(GntBindable
*bind
, GList
*null
)
981 GntTree
*tree
= GNT_TREE(bind
);
982 GntTreeRow
*old
= tree
->current
;
983 GntTreeRow
*row
= tree
->bottom
;
986 while ((next
= get_next(row
)))
992 if (old
!= tree
->current
)
993 tree_selection_changed(tree
, old
, tree
->current
);
1000 gnt_tree_set_property(GObject
*obj
, guint prop_id
, const GValue
*value
,
1003 GntTree
*tree
= GNT_TREE(obj
);
1006 _gnt_tree_init_internals(tree
, g_value_get_int(value
));
1009 if (tree
->priv
->expander_level
== g_value_get_int(value
))
1011 tree
->priv
->expander_level
= g_value_get_int(value
);
1012 g_object_notify(obj
, "expander-level");
1019 gnt_tree_get_property(GObject
*obj
, guint prop_id
, GValue
*value
,
1022 GntTree
*tree
= GNT_TREE(obj
);
1025 g_value_set_int(value
, tree
->ncol
);
1028 g_value_set_int(value
, tree
->priv
->expander_level
);
1036 gnt_tree_class_init(GntTreeClass
*klass
)
1038 GntBindableClass
*bindable
= GNT_BINDABLE_CLASS(klass
);
1039 GObjectClass
*gclass
= G_OBJECT_CLASS(klass
);
1041 parent_class
= GNT_WIDGET_CLASS(klass
);
1042 parent_class
->destroy
= gnt_tree_destroy
;
1043 parent_class
->draw
= gnt_tree_draw
;
1044 parent_class
->map
= gnt_tree_map
;
1045 parent_class
->size_request
= gnt_tree_size_request
;
1046 parent_class
->key_pressed
= gnt_tree_key_pressed
;
1047 parent_class
->clicked
= gnt_tree_clicked
;
1048 parent_class
->size_changed
= gnt_tree_size_changed
;
1050 gclass
->set_property
= gnt_tree_set_property
;
1051 gclass
->get_property
= gnt_tree_get_property
;
1052 g_object_class_install_property(gclass
,
1054 g_param_spec_int("columns", "Columns",
1055 "Number of columns in the tree.",
1057 G_PARAM_READWRITE
|G_PARAM_STATIC_NAME
|G_PARAM_STATIC_NICK
|G_PARAM_STATIC_BLURB
1060 g_object_class_install_property(gclass
,
1062 g_param_spec_int("expander-level", "Expander level",
1063 "Number of levels to show expander in the tree.",
1065 G_PARAM_READWRITE
|G_PARAM_STATIC_NAME
|G_PARAM_STATIC_NICK
|G_PARAM_STATIC_BLURB
1069 signals
[SIG_SELECTION_CHANGED
] =
1070 g_signal_new("selection-changed",
1071 G_TYPE_FROM_CLASS(klass
),
1073 G_STRUCT_OFFSET(GntTreeClass
, selection_changed
),
1075 gnt_closure_marshal_VOID__POINTER_POINTER
,
1076 G_TYPE_NONE
, 2, G_TYPE_POINTER
, G_TYPE_POINTER
);
1077 signals
[SIG_SCROLLED
] =
1078 g_signal_new("scrolled",
1079 G_TYPE_FROM_CLASS(klass
),
1083 g_cclosure_marshal_VOID__INT
,
1084 G_TYPE_NONE
, 1, G_TYPE_INT
);
1085 signals
[SIG_TOGGLED
] =
1086 g_signal_new("toggled",
1087 G_TYPE_FROM_CLASS(klass
),
1089 G_STRUCT_OFFSET(GntTreeClass
, toggled
),
1091 g_cclosure_marshal_VOID__POINTER
,
1092 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
1093 signals
[SIG_COLLAPSED
] =
1094 g_signal_new("collapse-toggled",
1095 G_TYPE_FROM_CLASS(klass
),
1099 gnt_closure_marshal_VOID__POINTER_BOOLEAN
,
1100 G_TYPE_NONE
, 2, G_TYPE_POINTER
, G_TYPE_BOOLEAN
);
1102 gnt_bindable_class_register_action(bindable
, "move-up", action_up
,
1104 gnt_bindable_register_binding(bindable
, "move-up", GNT_KEY_CTRL_P
, NULL
);
1105 gnt_bindable_class_register_action(bindable
, "move-down", action_down
,
1106 GNT_KEY_DOWN
, NULL
);
1107 gnt_bindable_register_binding(bindable
, "move-down", GNT_KEY_CTRL_N
, NULL
);
1108 gnt_bindable_class_register_action(bindable
, "move-parent", action_move_parent
,
1109 GNT_KEY_BACKSPACE
, NULL
);
1110 gnt_bindable_class_register_action(bindable
, "page-up", action_page_up
,
1111 GNT_KEY_PGUP
, NULL
);
1112 gnt_bindable_class_register_action(bindable
, "page-down", action_page_down
,
1113 GNT_KEY_PGDOWN
, NULL
);
1114 gnt_bindable_class_register_action(bindable
, "start-search", start_search
,
1116 gnt_bindable_class_register_action(bindable
, "end-search", end_search_action
,
1118 gnt_bindable_class_register_action(bindable
, "move-first", move_first_action
,
1119 GNT_KEY_HOME
, NULL
);
1120 gnt_bindable_class_register_action(bindable
, "move-last", move_last_action
,
1123 gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass
), bindable
);
1128 gnt_tree_init(GTypeInstance
*instance
, gpointer
class)
1130 GntWidget
*widget
= GNT_WIDGET(instance
);
1131 GntTree
*tree
= GNT_TREE(widget
);
1132 tree
->show_separator
= TRUE
;
1133 tree
->priv
= g_new0(GntTreePriv
, 1);
1134 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_GROW_X
| GNT_WIDGET_GROW_Y
|
1135 GNT_WIDGET_CAN_TAKE_FOCUS
| GNT_WIDGET_NO_SHADOW
);
1136 gnt_widget_set_take_focus(widget
, TRUE
);
1137 widget
->priv
.minw
= 4;
1138 widget
->priv
.minh
= 1;
1142 /******************************************************************************
1144 *****************************************************************************/
1146 gnt_tree_get_gtype(void)
1148 static GType type
= 0;
1152 static const GTypeInfo info
= {
1153 sizeof(GntTreeClass
),
1154 NULL
, /* base_init */
1155 NULL
, /* base_finalize */
1156 (GClassInitFunc
)gnt_tree_class_init
,
1157 NULL
, /* class_finalize */
1158 NULL
, /* class_data */
1160 0, /* n_preallocs */
1161 gnt_tree_init
, /* instance_init */
1162 NULL
/* value_table */
1165 type
= g_type_register_static(GNT_TYPE_WIDGET
,
1174 free_tree_col(gpointer data
)
1176 GntTreeCol
*col
= data
;
1183 free_tree_row(gpointer data
)
1185 GntTreeRow
*row
= data
;
1190 g_list_foreach(row
->columns
, (GFunc
)free_tree_col
, NULL
);
1191 g_list_free(row
->columns
);
1195 GntWidget
*gnt_tree_new()
1197 return gnt_tree_new_with_columns(1);
1200 void gnt_tree_set_visible_rows(GntTree
*tree
, int rows
)
1202 GntWidget
*widget
= GNT_WIDGET(tree
);
1203 widget
->priv
.height
= rows
;
1204 if (!GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_NO_BORDER
))
1205 widget
->priv
.height
+= 2;
1208 int gnt_tree_get_visible_rows(GntTree
*tree
)
1210 GntWidget
*widget
= GNT_WIDGET(tree
);
1211 int ret
= widget
->priv
.height
;
1212 if (!GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_NO_BORDER
))
1217 GList
*gnt_tree_get_rows(GntTree
*tree
)
1222 void gnt_tree_scroll(GntTree
*tree
, int count
)
1228 if (get_root_distance(tree
->top
) == 0)
1230 row
= get_prev_n(tree
->top
, -count
);
1237 get_next_n_opt(tree
->bottom
, count
, &count
);
1238 tree
->top
= get_next_n(tree
->top
, count
);
1242 g_signal_emit(tree
, signals
[SIG_SCROLLED
], 0, count
);
1246 find_position(GntTree
*tree
, gpointer key
, gpointer parent
)
1250 if (tree
->priv
->compare
== NULL
)
1256 row
= g_hash_table_lookup(tree
->hash
, parent
);
1266 if (tree
->priv
->compare(key
, row
->key
) < 0)
1267 return (row
->prev
? row
->prev
->key
: NULL
);
1276 void gnt_tree_sort_row(GntTree
*tree
, gpointer key
)
1278 GntTreeRow
*row
, *q
, *s
;
1281 if (!tree
->priv
->compare
)
1284 row
= g_hash_table_lookup(tree
->hash
, key
);
1285 g_return_if_fail(row
!= NULL
);
1287 current
= g_list_index(tree
->list
, key
);
1290 s
= row
->parent
->child
;
1296 if (tree
->priv
->compare(row
->key
, s
->key
) < 0)
1302 /* Move row between q and s */
1303 if (row
== q
|| row
== s
)
1307 /* row becomes the first child of its parent */
1308 row
->prev
->next
= row
->next
; /* row->prev cannot be NULL at this point */
1310 row
->next
->prev
= row
->prev
;
1312 row
->parent
->child
= row
;
1316 s
->prev
= row
; /* s cannot be NULL */
1318 newp
= g_list_index(tree
->list
, s
) - 1;
1321 row
->prev
->next
= row
->next
;
1323 /* row was the first child of its parent */
1325 row
->parent
->child
= row
->next
;
1327 tree
->top
= row
->next
;
1331 row
->next
->prev
= row
->prev
;
1338 newp
= g_list_index(tree
->list
, q
) + 1;
1340 tree
->list
= g_list_reposition_child(tree
->list
, current
, newp
);
1345 GntTreeRow
*gnt_tree_add_row_after(GntTree
*tree
, void *key
, GntTreeRow
*row
, void *parent
, void *bigbro
)
1347 GntTreeRow
*pr
= NULL
;
1349 if (g_hash_table_lookup(tree
->hash
, key
)) {
1350 gnt_tree_remove(tree
, key
);
1356 g_hash_table_replace(tree
->hash
, key
, row
);
1358 if (bigbro
== NULL
&& tree
->priv
->compare
)
1360 bigbro
= find_position(tree
, key
, parent
);
1363 if (tree
->root
== NULL
)
1366 tree
->list
= g_list_prepend(tree
->list
, key
);
1374 pr
= g_hash_table_lookup(tree
->hash
, bigbro
);
1377 if (pr
->next
) pr
->next
->prev
= row
;
1378 row
->next
= pr
->next
;
1381 row
->parent
= pr
->parent
;
1383 position
= g_list_index(tree
->list
, bigbro
);
1387 if (pr
== NULL
&& parent
)
1389 pr
= g_hash_table_lookup(tree
->hash
, parent
);
1392 if (pr
->child
) pr
->child
->prev
= row
;
1393 row
->next
= pr
->child
;
1397 position
= g_list_index(tree
->list
, parent
);
1403 GntTreeRow
*r
= tree
->root
;
1405 if (r
) r
->prev
= row
;
1406 if (tree
->current
== tree
->root
)
1407 tree
->current
= row
;
1409 tree
->list
= g_list_prepend(tree
->list
, key
);
1413 tree
->list
= g_list_insert(tree
->list
, key
, position
+ 1);
1421 GntTreeRow
*gnt_tree_add_row_last(GntTree
*tree
, void *key
, GntTreeRow
*row
, void *parent
)
1423 GntTreeRow
*pr
= NULL
, *br
= NULL
;
1426 pr
= g_hash_table_lookup(tree
->hash
, parent
);
1439 return gnt_tree_add_row_after(tree
, key
, row
, parent
, br
? br
->key
: NULL
);
1442 gpointer
gnt_tree_get_selection_data(GntTree
*tree
)
1445 return tree
->current
->key
; /* XXX: perhaps we should just get rid of 'data' */
1449 char *gnt_tree_get_selection_text(GntTree
*tree
)
1452 return update_row_text(tree
, tree
->current
);
1456 GList
*gnt_tree_get_row_text_list(GntTree
*tree
, gpointer key
)
1458 GList
*list
= NULL
, *iter
;
1459 GntTreeRow
*row
= key
? g_hash_table_lookup(tree
->hash
, key
) : tree
->current
;
1465 for (i
= 0, iter
= row
->columns
; i
< tree
->ncol
&& iter
;
1466 i
++, iter
= iter
->next
)
1468 GntTreeCol
*col
= iter
->data
;
1469 list
= g_list_append(list
, BINARY_DATA(tree
, i
) ? col
->text
: g_strdup(col
->text
));
1475 GList
*gnt_tree_get_selection_text_list(GntTree
*tree
)
1477 return gnt_tree_get_row_text_list(tree
, NULL
);
1480 void gnt_tree_remove(GntTree
*tree
, gpointer key
)
1482 GntTreeRow
*row
= g_hash_table_lookup(tree
->hash
, key
);
1483 static int depth
= 0; /* Only redraw after all child nodes are removed */
1486 gboolean redraw
= FALSE
;
1490 while (row
->child
) {
1491 gnt_tree_remove(tree
, row
->child
->key
);
1496 if (get_distance(tree
->top
, row
) >= 0 && get_distance(row
, tree
->bottom
) >= 0)
1499 /* Update root/top/current/bottom if necessary */
1500 if (tree
->root
== row
)
1501 tree
->root
= get_next(row
);
1502 if (tree
->top
== row
)
1504 if (tree
->top
!= tree
->root
)
1505 tree
->top
= get_prev(row
);
1507 tree
->top
= get_next(row
);
1509 if (tree
->current
== row
)
1511 if (tree
->current
!= tree
->root
)
1512 tree
->current
= get_prev(row
);
1514 tree
->current
= get_next(row
);
1515 tree_selection_changed(tree
, row
, tree
->current
);
1517 if (tree
->bottom
== row
)
1519 tree
->bottom
= get_prev(row
);
1524 row
->next
->prev
= row
->prev
;
1525 if (row
->parent
&& row
->parent
->child
== row
)
1526 row
->parent
->child
= row
->next
;
1528 row
->prev
->next
= row
->next
;
1530 g_hash_table_remove(tree
->hash
, key
);
1531 tree
->list
= g_list_remove(tree
->list
, key
);
1533 if (redraw
&& depth
== 0)
1541 return_true(gpointer key
, gpointer data
, gpointer null
)
1546 void gnt_tree_remove_all(GntTree
*tree
)
1549 g_hash_table_foreach_remove(tree
->hash
, (GHRFunc
)return_true
, tree
);
1550 g_list_free(tree
->list
);
1552 tree
->current
= tree
->top
= tree
->bottom
= NULL
;
1555 int gnt_tree_get_selection_visible_line(GntTree
*tree
)
1557 return get_distance(tree
->top
, tree
->current
) +
1558 !!(GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree
), GNT_WIDGET_NO_BORDER
));
1561 void gnt_tree_change_text(GntTree
*tree
, gpointer key
, int colno
, const char *text
)
1566 g_return_if_fail(colno
< tree
->ncol
);
1568 row
= g_hash_table_lookup(tree
->hash
, key
);
1571 col
= g_list_nth_data(row
->columns
, colno
);
1572 if (BINARY_DATA(tree
, colno
)) {
1573 col
->text
= (gpointer
)text
;
1576 col
->text
= g_strdup(text
? text
: "");
1579 if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree
), GNT_WIDGET_MAPPED
) &&
1580 get_distance(tree
->top
, row
) >= 0 && get_distance(row
, tree
->bottom
) >= 0)
1585 GntTreeRow
*gnt_tree_add_choice(GntTree
*tree
, void *key
, GntTreeRow
*row
, void *parent
, void *bigbro
)
1588 r
= g_hash_table_lookup(tree
->hash
, key
);
1589 g_return_val_if_fail(!r
|| !r
->choice
, NULL
);
1591 if (bigbro
== NULL
) {
1592 if (tree
->priv
->compare
)
1593 bigbro
= find_position(tree
, key
, parent
);
1595 r
= g_hash_table_lookup(tree
->hash
, parent
);
1607 row
= gnt_tree_add_row_after(tree
, key
, row
, parent
, bigbro
);
1613 void gnt_tree_set_choice(GntTree
*tree
, void *key
, gboolean set
)
1615 GntTreeRow
*row
= g_hash_table_lookup(tree
->hash
, key
);
1619 g_return_if_fail(row
->choice
);
1621 row
->isselected
= set
;
1625 gboolean
gnt_tree_get_choice(GntTree
*tree
, void *key
)
1627 GntTreeRow
*row
= g_hash_table_lookup(tree
->hash
, key
);
1631 g_return_val_if_fail(row
->choice
, FALSE
);
1633 return row
->isselected
;
1636 void gnt_tree_set_row_flags(GntTree
*tree
, void *key
, GntTextFormatFlags flags
)
1638 GntTreeRow
*row
= g_hash_table_lookup(tree
->hash
, key
);
1639 if (!row
|| row
->flags
== flags
)
1643 redraw_tree(tree
); /* XXX: It shouldn't be necessary to redraw the whole darned tree */
1646 void gnt_tree_set_row_color(GntTree
*tree
, void *key
, int color
)
1648 GntTreeRow
*row
= g_hash_table_lookup(tree
->hash
, key
);
1649 if (!row
|| row
->color
== color
)
1656 void gnt_tree_set_selected(GntTree
*tree
, void *key
)
1659 GntTreeRow
*row
= g_hash_table_lookup(tree
->hash
, key
);
1660 if (!row
|| row
== tree
->current
)
1663 if (tree
->top
== NULL
)
1665 if (tree
->bottom
== NULL
)
1668 tree
->current
= row
;
1669 if ((dist
= get_distance(tree
->current
, tree
->bottom
)) < 0)
1670 gnt_tree_scroll(tree
, -dist
);
1671 else if ((dist
= get_distance(tree
->current
, tree
->top
)) > 0)
1672 gnt_tree_scroll(tree
, -dist
);
1675 tree_selection_changed(tree
, row
, tree
->current
);
1678 static void _gnt_tree_init_internals(GntTree
*tree
, int col
)
1680 gnt_tree_free_columns(tree
);
1683 tree
->hash
= g_hash_table_new_full(g_direct_hash
, g_direct_equal
, NULL
, free_tree_row
);
1684 tree
->columns
= g_new0(struct _GntTreeColInfo
, col
);
1685 tree
->priv
->lastvisible
= col
- 1;
1688 tree
->columns
[col
].width
= 15;
1691 tree
->show_title
= FALSE
;
1692 g_object_notify(G_OBJECT(tree
), "columns");
1695 GntWidget
*gnt_tree_new_with_columns(int col
)
1697 GntWidget
*widget
= g_object_new(GNT_TYPE_TREE
,
1699 "expander-level", 1,
1705 GntTreeRow
*gnt_tree_create_row_from_list(GntTree
*tree
, GList
*list
)
1709 GntTreeRow
*row
= g_new0(GntTreeRow
, 1);
1711 for (i
= 0, iter
= list
; i
< tree
->ncol
&& iter
; iter
= iter
->next
, i
++)
1713 GntTreeCol
*col
= g_new0(GntTreeCol
, 1);
1715 if (BINARY_DATA(tree
, i
)) {
1716 col
->text
= iter
->data
;
1717 col
->isbinary
= TRUE
;
1719 col
->text
= g_strdup(iter
->data
? iter
->data
: "");
1720 col
->isbinary
= FALSE
;
1723 row
->columns
= g_list_append(row
->columns
, col
);
1729 GntTreeRow
*gnt_tree_create_row(GntTree
*tree
, ...)
1736 va_start(args
, tree
);
1737 for (i
= 0; i
< tree
->ncol
; i
++)
1739 list
= g_list_append(list
, va_arg(args
, char *));
1743 row
= gnt_tree_create_row_from_list(tree
, list
);
1749 void gnt_tree_set_col_width(GntTree
*tree
, int col
, int width
)
1751 g_return_if_fail(col
< tree
->ncol
);
1753 tree
->columns
[col
].width
= width
;
1754 if (tree
->columns
[col
].width_ratio
== 0)
1755 tree
->columns
[col
].width_ratio
= width
;
1758 void gnt_tree_set_column_title(GntTree
*tree
, int index
, const char *title
)
1760 g_free(tree
->columns
[index
].title
);
1761 tree
->columns
[index
].title
= g_strdup(title
);
1764 void gnt_tree_set_column_titles(GntTree
*tree
, ...)
1769 va_start(args
, tree
);
1770 for (i
= 0; i
< tree
->ncol
; i
++)
1772 const char *title
= va_arg(args
, const char *);
1773 tree
->columns
[i
].title
= g_strdup(title
);
1778 void gnt_tree_set_show_title(GntTree
*tree
, gboolean set
)
1780 tree
->show_title
= set
;
1781 GNT_WIDGET(tree
)->priv
.minh
= (set
? 6 : 4);
1784 void gnt_tree_set_compare_func(GntTree
*tree
, GCompareFunc func
)
1786 tree
->priv
->compare
= func
;
1789 void gnt_tree_set_expanded(GntTree
*tree
, void *key
, gboolean expanded
)
1791 GntTreeRow
*row
= g_hash_table_lookup(tree
->hash
, key
);
1793 row
->collapsed
= !expanded
;
1794 if (GNT_WIDGET(tree
)->window
)
1795 gnt_widget_draw(GNT_WIDGET(tree
));
1796 g_signal_emit(tree
, signals
[SIG_COLLAPSED
], 0, key
, row
->collapsed
);
1800 void gnt_tree_set_show_separator(GntTree
*tree
, gboolean set
)
1802 tree
->show_separator
= set
;
1805 void gnt_tree_adjust_columns(GntTree
*tree
)
1807 GntTreeRow
*row
= tree
->root
;
1808 int *widths
, i
, twidth
;
1810 widths
= g_new0(int, tree
->ncol
);
1813 for (i
= 0, iter
= row
->columns
; iter
; iter
= iter
->next
, i
++) {
1814 GntTreeCol
*col
= iter
->data
;
1815 int w
= gnt_util_onscreen_width(col
->text
, NULL
);
1816 if (i
== 0 && row
->choice
)
1819 w
+= find_depth(row
) * TAB_SIZE
;
1824 row
= get_next(row
);
1827 twidth
= 1 + 2 * (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree
), GNT_WIDGET_NO_BORDER
));
1828 for (i
= 0; i
< tree
->ncol
; i
++) {
1829 if (tree
->columns
[i
].flags
& GNT_TREE_COLUMN_FIXED_SIZE
)
1830 widths
[i
] = tree
->columns
[i
].width
;
1831 gnt_tree_set_col_width(tree
, i
, widths
[i
]);
1832 if (!COLUMN_INVISIBLE(tree
, i
)) {
1833 twidth
= twidth
+ widths
[i
];
1834 if (tree
->priv
->lastvisible
!= i
)
1840 gnt_widget_set_size(GNT_WIDGET(tree
), twidth
, -1);
1843 void gnt_tree_set_hash_fns(GntTree
*tree
, gpointer hash
, gpointer eq
, gpointer kd
)
1845 g_hash_table_foreach_remove(tree
->hash
, return_true
, NULL
);
1846 g_hash_table_destroy(tree
->hash
);
1847 tree
->hash
= g_hash_table_new_full(hash
, eq
, kd
, free_tree_row
);
1851 set_column_flag(GntTree
*tree
, int col
, GntTreeColumnFlag flag
, gboolean set
)
1854 tree
->columns
[col
].flags
|= flag
;
1856 tree
->columns
[col
].flags
&= ~flag
;
1859 void gnt_tree_set_column_visible(GntTree
*tree
, int col
, gboolean vis
)
1861 g_return_if_fail(col
< tree
->ncol
);
1862 set_column_flag(tree
, col
, GNT_TREE_COLUMN_INVISIBLE
, !vis
);
1864 /* the column is visible */
1865 if (tree
->priv
->lastvisible
< col
)
1866 tree
->priv
->lastvisible
= col
;
1868 if (tree
->priv
->lastvisible
== col
)
1869 while (tree
->priv
->lastvisible
) {
1870 tree
->priv
->lastvisible
--;
1871 if (!COLUMN_INVISIBLE(tree
, tree
->priv
->lastvisible
))
1875 if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree
), GNT_WIDGET_MAPPED
))
1876 readjust_columns(tree
);
1879 void gnt_tree_set_column_resizable(GntTree
*tree
, int col
, gboolean res
)
1881 g_return_if_fail(col
< tree
->ncol
);
1882 set_column_flag(tree
, col
, GNT_TREE_COLUMN_FIXED_SIZE
, !res
);
1885 void gnt_tree_set_column_is_binary(GntTree
*tree
, int col
, gboolean bin
)
1887 g_return_if_fail(col
< tree
->ncol
);
1888 set_column_flag(tree
, col
, GNT_TREE_COLUMN_BINARY_DATA
, bin
);
1891 void gnt_tree_set_column_is_right_aligned(GntTree
*tree
, int col
, gboolean right
)
1893 g_return_if_fail(col
< tree
->ncol
);
1894 set_column_flag(tree
, col
, GNT_TREE_COLUMN_RIGHT_ALIGNED
, right
);
1897 void gnt_tree_set_column_width_ratio(GntTree
*tree
, int cols
[])
1900 for (i
= 0; i
< tree
->ncol
&& cols
[i
]; i
++) {
1901 tree
->columns
[i
].width_ratio
= cols
[i
];
1905 void gnt_tree_set_search_column(GntTree
*tree
, int col
)
1907 g_return_if_fail(col
< tree
->ncol
);
1908 g_return_if_fail(!BINARY_DATA(tree
, col
));
1909 tree
->priv
->search_column
= col
;
1912 gboolean
gnt_tree_is_searching(GntTree
*tree
)
1914 return (tree
->priv
->search
!= NULL
);
1917 void gnt_tree_set_search_function(GntTree
*tree
,
1918 gboolean (*func
)(GntTree
*tree
, gpointer key
, const char *search
, const char *current
))
1920 tree
->priv
->search_func
= func
;
1923 gpointer
gnt_tree_get_parent_key(GntTree
*tree
, gpointer key
)
1925 GntTreeRow
*row
= g_hash_table_lookup(tree
->hash
, key
);
1926 return (row
&& row
->parent
) ? row
->parent
->key
: NULL
;
1929 gpointer
gnt_tree_row_get_key(GntTree
*tree
, GntTreeRow
*row
)
1931 g_return_val_if_fail(row
&& row
->tree
== tree
, NULL
);
1935 GntTreeRow
* gnt_tree_row_get_next(GntTree
*tree
, GntTreeRow
*row
)
1937 g_return_val_if_fail(row
&& row
->tree
== tree
, NULL
);
1941 GntTreeRow
* gnt_tree_row_get_prev(GntTree
*tree
, GntTreeRow
*row
)
1943 g_return_val_if_fail(row
&& row
->tree
== tree
, NULL
);
1947 GntTreeRow
* gnt_tree_row_get_child(GntTree
*tree
, GntTreeRow
*row
)
1949 g_return_val_if_fail(row
&& row
->tree
== tree
, NULL
);
1953 GntTreeRow
* gnt_tree_row_get_parent(GntTree
*tree
, GntTreeRow
*row
)
1955 g_return_val_if_fail(row
&& row
->tree
== tree
, NULL
);