comparison console/libgnt/gnttree.c @ 13964:0a0d2a1fd2bc

[gaim-migrate @ 16520] Add multi-column support for GntTree. Use it for email-notifications. Restore colors before exiting. committer: Tailor Script <tailor@pidgin.im>
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Wed, 19 Jul 2006 07:12:59 +0000
parents 841a5ffbfee4
children df8183b7fa2c
comparison
equal deleted inserted replaced
13963:f7cfaee79982 13964:0a0d2a1fd2bc
16 /* XXX: Make this one into a GObject? 16 /* XXX: Make this one into a GObject?
17 * ... Probably not */ 17 * ... Probably not */
18 struct _GnTreeRow 18 struct _GnTreeRow
19 { 19 {
20 void *key; 20 void *key;
21 char *text;
22 void *data; /* XXX: unused */ 21 void *data; /* XXX: unused */
23 22
24 gboolean collapsed; 23 gboolean collapsed;
25 gboolean choice; /* Is this a choice-box? 24 gboolean choice; /* Is this a choice-box?
26 If choice is true, then child will be NULL */ 25 If choice is true, then child will be NULL */
29 28
30 GntTreeRow *parent; 29 GntTreeRow *parent;
31 GntTreeRow *child; 30 GntTreeRow *child;
32 GntTreeRow *next; 31 GntTreeRow *next;
33 GntTreeRow *prev; 32 GntTreeRow *prev;
33
34 GList *columns;
35 };
36
37 struct _GnTreeCol
38 {
39 char *text;
40 int span; /* How many columns does it span? */
34 }; 41 };
35 42
36 static GntWidgetClass *parent_class = NULL; 43 static GntWidgetClass *parent_class = NULL;
37 static guint signals[SIGS] = { 0 }; 44 static guint signals[SIGS] = { 0 };
38 45
149 int hb = get_root_distance(b); 156 int hb = get_root_distance(b);
150 157
151 return (hb - ha); 158 return (hb - ha);
152 } 159 }
153 160
161 static int
162 find_depth(GntTreeRow *row)
163 {
164 int dep = -1;
165
166 while (row)
167 {
168 dep++;
169 row = row->parent;
170 }
171
172 return dep;
173 }
174
175 static char *
176 update_row_text(GntTree *tree, GntTreeRow *row)
177 {
178 GString *string = g_string_new(NULL);
179 GList *iter;
180 int i;
181
182 for (i = 0, iter = row->columns; i < tree->ncol && iter; i++, iter = iter->next)
183 {
184 GntTreeCol *col = iter->data;
185 char *text;
186 int len = g_utf8_strlen(col->text, -1);
187 int fl = 0;
188 gboolean ell = FALSE;
189
190 if (i == 0)
191 {
192 if (row->choice)
193 {
194 g_string_append_printf(string, "[%c] ",
195 row->isselected ? 'X' : ' ');
196 fl = 4;
197 }
198 else if (row->parent == NULL && row->child)
199 {
200 if (row->collapsed)
201 {
202 string = g_string_append(string, "+ ");
203 }
204 else
205 {
206 string = g_string_append(string, "- ");
207 }
208 fl = 2;
209 }
210 else
211 {
212 fl = TAB_SIZE * find_depth(row);
213 g_string_append_printf(string, "%*s", fl, "");
214 }
215 len += fl;
216 }
217 else
218 g_string_append_c(string, '|');
219
220 if (len > tree->columns[i].width)
221 {
222 len = tree->columns[i].width;
223 ell = TRUE;
224 }
225
226 text = g_utf8_offset_to_pointer(col->text, len - fl - ell);
227 string = g_string_append_len(string, col->text, text - col->text);
228 if (len < tree->columns[i].width)
229 g_string_append_printf(string, "%*s", tree->columns[i].width - len, "");
230 else if (ell)
231 {
232 g_string_append_unichar(string, (gunichar)2026);
233 }
234 }
235 return g_string_free(string, FALSE);
236 }
237
154 static void 238 static void
155 redraw_tree(GntTree *tree) 239 redraw_tree(GntTree *tree)
156 { 240 {
157 int start; 241 int start;
158 GntWidget *widget = GNT_WIDGET(tree); 242 GntWidget *widget = GNT_WIDGET(tree);
159 GntTreeRow *row; 243 GntTreeRow *row;
160 int pos; 244 int pos;
161 gboolean deep;
162 245
163 if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) 246 if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER))
164 pos = 0; 247 pos = 0;
165 else 248 else
166 pos = 1; 249 pos = 1;
170 if (tree->current == NULL) 253 if (tree->current == NULL)
171 tree->current = tree->root; 254 tree->current = tree->root;
172 255
173 wbkgd(widget->window, COLOR_PAIR(GNT_COLOR_NORMAL)); 256 wbkgd(widget->window, COLOR_PAIR(GNT_COLOR_NORMAL));
174 257
175 deep = TRUE;
176 row = tree->top; 258 row = tree->top;
177 for (start = pos; row && start < widget->priv.height - pos; 259 for (start = pos; row && start < widget->priv.height - pos;
178 start++, row = get_next(row)) 260 start++, row = get_next(row))
179 { 261 {
180 char str[2048]; 262 char *str;
181 int wr; 263 int wr;
182 char format[16] = "";
183 264
184 GntTextFormatFlags flags = row->flags; 265 GntTextFormatFlags flags = row->flags;
185 int attr = 0; 266 int attr = 0;
186 267
187 deep = TRUE; 268 str = update_row_text(tree, row);
188
189 if (row->parent == NULL && row->child)
190 {
191 if (row->collapsed)
192 {
193 strcpy(format, "+ ");
194 deep = FALSE;
195 }
196 else
197 strcpy(format, "- ");
198 }
199 else if (row->choice)
200 {
201 g_snprintf(format, sizeof(format) - 1, "[%c] ", row->isselected ? 'X' : ' ');
202 }
203
204 g_snprintf(str, sizeof(str) - 1, "%s%s", format, row->text);
205 269
206 if ((wr = g_utf8_strlen(str, -1)) >= widget->priv.width - 1 - pos) 270 if ((wr = g_utf8_strlen(str, -1)) >= widget->priv.width - 1 - pos)
207 { 271 {
208 /* XXX: ellipsize */ 272 /* XXX: ellipsize */
209 char *s = g_utf8_offset_to_pointer(str, widget->priv.width - 1 - pos); 273 char *s = g_utf8_offset_to_pointer(str, widget->priv.width - 1 - pos);
236 300
237 wbkgdset(widget->window, '\0' | attr); 301 wbkgdset(widget->window, '\0' | attr);
238 mvwprintw(widget->window, start, pos, str); 302 mvwprintw(widget->window, start, pos, str);
239 whline(widget->window, ' ', widget->priv.width - pos * 2 - g_utf8_strlen(str, -1)); 303 whline(widget->window, ' ', widget->priv.width - pos * 2 - g_utf8_strlen(str, -1));
240 tree->bottom = row; 304 tree->bottom = row;
305 g_free(str);
241 } 306 }
242 307
243 wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL)); 308 wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL));
244 while (start < widget->priv.height - pos) 309 while (start < widget->priv.height - pos)
245 { 310 {
265 gnt_tree_size_request(GntWidget *widget) 330 gnt_tree_size_request(GntWidget *widget)
266 { 331 {
267 if (widget->priv.height == 0) 332 if (widget->priv.height == 0)
268 widget->priv.height = 10; /* XXX: Why?! */ 333 widget->priv.height = 10; /* XXX: Why?! */
269 if (widget->priv.width == 0) 334 if (widget->priv.width == 0)
270 widget->priv.width = 20; /* YYY: 'cuz ... */ 335 {
336 GntTree *tree = GNT_TREE(widget);
337 int i, width = 0;
338 for (i = 0; i < tree->ncol; i++)
339 width += tree->columns[i].width;
340 widget->priv.width = width + i;
341 }
271 } 342 }
272 343
273 static void 344 static void
274 gnt_tree_map(GntWidget *widget) 345 gnt_tree_map(GntWidget *widget)
275 { 346 {
435 506
436 return type; 507 return type;
437 } 508 }
438 509
439 static void 510 static void
511 free_tree_col(gpointer data)
512 {
513 GntTreeCol *col = data;
514
515 g_free(col->text);
516 g_free(col);
517 }
518
519 static void
440 free_tree_row(gpointer data) 520 free_tree_row(gpointer data)
441 { 521 {
442 GntTreeRow *row = data; 522 GntTreeRow *row = data;
443 523
444 if (!row) 524 if (!row)
445 return; 525 return;
446 526
447 g_free(row->text); 527 g_list_foreach(row->columns, (GFunc)free_tree_col, NULL);
528 g_list_free(row->columns);
448 g_free(row); 529 g_free(row);
449 } 530 }
450 531
451 GntWidget *gnt_tree_new() 532 GntWidget *gnt_tree_new()
452 { 533 {
453 GntWidget *widget = g_object_new(GNT_TYPE_TREE, NULL); 534 return gnt_tree_new_with_columns(1);
454 GntTree *tree = GNT_TREE(widget);
455
456 tree->hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_tree_row);
457 GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_SHADOW);
458 gnt_widget_set_take_focus(widget, TRUE);
459
460 return widget;
461 } 535 }
462 536
463 void gnt_tree_set_visible_rows(GntTree *tree, int rows) 537 void gnt_tree_set_visible_rows(GntTree *tree, int rows)
464 { 538 {
465 GntWidget *widget = GNT_WIDGET(tree); 539 GntWidget *widget = GNT_WIDGET(tree);
498 572
499 redraw_tree(tree); 573 redraw_tree(tree);
500 g_signal_emit(tree, signals[SIG_SCROLLED], 0, count); 574 g_signal_emit(tree, signals[SIG_SCROLLED], 0, count);
501 } 575 }
502 576
503 static int 577 GntTreeRow *gnt_tree_add_row_after(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro)
504 find_depth(GntTreeRow *row) 578 {
505 { 579 GntTreeRow *pr = NULL;
506 int dep = -1;
507
508 while (row)
509 {
510 dep++;
511 row = row->parent;
512 }
513
514 return dep;
515 }
516
517 GntTreeRow *gnt_tree_add_row_after(GntTree *tree, void *key, const char *text, void *parent, void *bigbro)
518 {
519 GntTreeRow *row = g_new0(GntTreeRow, 1), *pr = NULL;
520 580
521 g_hash_table_replace(tree->hash, key, row); 581 g_hash_table_replace(tree->hash, key, row);
522 582
523 if (tree->root == NULL) 583 if (tree->root == NULL)
524 { 584 {
573 tree->list = g_list_insert(tree->list, key, position + 1); 633 tree->list = g_list_insert(tree->list, key, position + 1);
574 } 634 }
575 } 635 }
576 636
577 row->key = key; 637 row->key = key;
578 row->text = g_strdup_printf("%*s%s", TAB_SIZE * find_depth(row), "", text);
579 row->data = NULL; 638 row->data = NULL;
580 639
581 if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_MAPPED)) 640 if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_MAPPED))
582 redraw_tree(tree); 641 redraw_tree(tree);
583 642
589 if (tree->current) 648 if (tree->current)
590 return tree->current->key; /* XXX: perhaps we should just get rid of 'data' */ 649 return tree->current->key; /* XXX: perhaps we should just get rid of 'data' */
591 return NULL; 650 return NULL;
592 } 651 }
593 652
594 const char *gnt_tree_get_selection_text(GntTree *tree) 653 char *gnt_tree_get_selection_text(GntTree *tree)
595 { 654 {
596 if (tree->current) 655 if (tree->current)
597 return tree->current->text; 656 update_row_text(tree, tree->current);
598 return NULL; 657 return NULL;
599 } 658 }
600 659
601 /* XXX: Should this also remove all the children of the row being removed? */ 660 /* XXX: Should this also remove all the children of the row being removed? */
602 void gnt_tree_remove(GntTree *tree, gpointer key) 661 void gnt_tree_remove(GntTree *tree, gpointer key)
655 { 714 {
656 return get_distance(tree->top, tree->current) + 715 return get_distance(tree->top, tree->current) +
657 !!(GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_NO_BORDER)); 716 !!(GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_NO_BORDER));
658 } 717 }
659 718
660 void gnt_tree_change_text(GntTree *tree, gpointer key, const char *text) 719 void gnt_tree_change_text(GntTree *tree, gpointer key, int colno, const char *text)
661 { 720 {
662 GntTreeRow *row = g_hash_table_lookup(tree->hash, key); 721 GntTreeRow *row;
722 GntTreeCol *col;
723
724 g_return_if_fail(colno < tree->ncol);
725
726 row = g_hash_table_lookup(tree->hash, key);
663 if (row) 727 if (row)
664 { 728 {
665 g_free(row->text); 729 col = g_list_nth_data(row->columns, colno);
666 row->text = g_strdup_printf("%*s%s", TAB_SIZE * find_depth(row), "", text); 730 g_free(col->text);
731 col->text = g_strdup(text);
667 732
668 if (get_distance(tree->top, row) >= 0 && get_distance(row, tree->bottom) > 0) 733 if (get_distance(tree->top, row) >= 0 && get_distance(row, tree->bottom) > 0)
669 redraw_tree(tree); 734 redraw_tree(tree);
670 } 735 }
671 } 736 }
672 737
673 GntTreeRow *gnt_tree_add_choice(GntTree *tree, void *key, const char *text, void *parent, void *bigbro) 738 GntTreeRow *gnt_tree_add_choice(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro)
674 { 739 {
675 GntTreeRow *row; 740 GntTreeRow *r;
676 741 r = g_hash_table_lookup(tree->hash, key);
677 row = g_hash_table_lookup(tree->hash, key); 742 g_return_val_if_fail(!r || !r->choice, NULL);
678 g_return_val_if_fail(!row || !row->choice, NULL);
679 743
680 row = gnt_tree_add_row_after(tree, key, text, parent, bigbro); 744 row = gnt_tree_add_row_after(tree, key, row, parent, bigbro);
681 row->choice = TRUE; 745 row->choice = TRUE;
682 746
683 return row; 747 return row;
684 } 748 }
685 749
735 gnt_tree_scroll(tree, -dist); 799 gnt_tree_scroll(tree, -dist);
736 else 800 else
737 redraw_tree(tree); 801 redraw_tree(tree);
738 } 802 }
739 803
804 GntWidget *gnt_tree_new_with_columns(int col)
805 {
806 GntWidget *widget = g_object_new(GNT_TYPE_TREE, NULL);
807 GntTree *tree = GNT_TREE(widget);
808
809 tree->hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_tree_row);
810 tree->ncol = col;
811 tree->columns = g_new0(struct _GntTreeColInfo, col);
812 while (col--)
813 {
814 tree->columns[col].width = 15;
815 }
816
817 GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_SHADOW);
818 gnt_widget_set_take_focus(widget, TRUE);
819
820 return widget;
821 }
822
823 GntTreeRow *gnt_tree_create_row(GntTree *tree, ...)
824 {
825 GntTreeRow *row = g_new0(GntTreeRow, 1);
826 int i;
827 va_list args;
828
829 va_start(args, tree);
830
831 for (i = 0; i < tree->ncol; i++)
832 {
833 GntTreeCol *col = g_new0(GntTreeCol, 1);
834 col->span = 1;
835 col->text = g_strdup(va_arg(args, const char *));
836
837 row->columns = g_list_append(row->columns, col);
838 }
839
840 return row;
841 }
842
843 void gnt_tree_set_col_width(GntTree *tree, int col, int width)
844 {
845 g_return_if_fail(col < tree->ncol);
846
847 tree->columns[col].width = width;
848 }
849