2 * tree.c -- implements the 'tree' interface element for libdialog
4 * Author: Anatoly A. Orehovsky (tolik@mpeks.tomsk.su)
6 * Copyright (c) 1997, Anatoly A. Orehovsky
7 * 09/28/98 - patched by Anatoly A. Orehovsky (smart_tree())
12 static const char rcsid[] =
20 #include "dialog.priv.h"
23 /* static utils for make tree */
25 unsigned char *name; /* name of leaf */
26 unsigned char *branches; /* branches that going by leaf */
27 unsigned char slip; /* slip of leaf*/
28 int shift; /* shift relative root of tree */
31 static int mk_slip(struct leaf array[], int arr_size,
32 int number, int shift);
34 /* make tree from file
36 * filename - name of file with like find(1) output
37 * p_names - pointer to array of strings
38 * p_size - pointer to size of array
39 * FS - fields separator
40 * p_array - pointer to array of leafs
43 * 0 - ok and names by p_names, size by p_size, array by p_array set
44 * -1 - memory allocation error (errno set)
47 static int mk_ftree(char *filename,
48 unsigned char ***p_names, int *p_size, unsigned char FS,
49 struct leaf **p_array);
51 /* make tree from array
53 * names - array of strings
54 * size - size of array
55 * FS - fields separator
56 * p_array - pointer to array of leafs
59 * 0 - ok and array by p_array set
60 * -1 - memory allocation error (errno set)
63 static int mk_tree(unsigned char **names, int size, unsigned char FS,
64 struct leaf **p_array);
66 /* free memory from tree (leafs)
72 static void free_leafs(struct leaf *array, int size);
74 /* free memory from source data for tree (names)
77 * if 0 <= choice <= size - pointer to name from names,
78 * and memory for name not released (must be freed later)
79 * else - NULL (recomended choice -1 for it)
82 static unsigned char *free_names(unsigned char **names,
83 int size, int choice);
85 /* end of static utils for make tree */
87 /* static utils for ftree */
89 /* control struct for queue */
91 int size; /* size of queue */
92 struct m_queue *first; /* begin of queue */
93 struct m_queue *last; /* end of queue */
98 void *pointer; /* queue member */
99 struct m_queue *next; /* next queue member */
102 /* init struct queue by zeros */
103 static void init_queue(struct queue *queue);
105 /* add pointer to queue */
106 /* return - pointer or NULL if error */
107 static void *p2_queue(struct queue *queue, void *pointer);
109 /* get first from queue */
110 /* return - pointer or NULL if queue is empty */
111 static void *first_queue(struct queue *queue);
113 /* make zero terminated array from queue */
114 /* return - pointer to array or NULL if error */
115 static void **q2arr(struct queue *queue, int depth);
117 /* smart_tree (for like find(1) with -d flag output compliance) */
118 /* return - not NULL or NULL if malloc error */
119 static unsigned char *smart_tree(struct queue *queue, unsigned char FS,
120 unsigned char *current,
121 unsigned char *prev);
123 /* end of static utils for ftree */
125 /* static utils for saved_tree */
127 /* saved values for unique tree */
129 unsigned char **names; /* names + */
130 int size; /* size + */
131 unsigned char FS; /* FS + */
132 int height; /* height + */
133 int width; /* width + */
134 int menu_height; /* menu_height - unique for treebox ? */
135 int ch; /* saved ch - choice */
136 int sc; /* saved sc - scroll */
139 /* search saved tree within queue */
140 /* return - struct saved_tree * or NULL if not found */
141 static struct saved_tree *search_saved_tree(struct queue *queue,
142 unsigned char **names,
149 /* end of static utils for saved_tree */
151 static void print_item(WINDOW *win, struct leaf item, int choice, int selected);
153 static void print_position(WINDOW *win, int x, int y,
154 int cur_pos, int size);
156 static int menu_width, item_x;
158 static int dialog_treemenu(unsigned char *title, unsigned char *prompt,
159 int height, int width, int menu_height,
160 int item_no, struct leaf items[],
165 * Display a menu for choosing among a number of options
168 int dialog_treemenu(unsigned char *title, unsigned char *prompt,
169 int height, int width, int menu_height,
170 int item_no, struct leaf items[],
174 int i, j, x, y, cur_x, cur_y, box_x, box_y, key = 0, button = 0, choice = 0,
175 l, scroll = 0, max_choice, redraw_menu = FALSE;
176 WINDOW *dialog, *menu;
178 if (ch) /* restore menu item info */
183 max_choice = MIN(menu_height, item_no);
186 /* Find length of longest item in order to center menu */
187 for (i = 0; i < item_no; i++) {
188 l = strlen(items[i].name) + strlen(items[i].branches) * 4 + 4;
189 item_x = MAX(item_x, l);
193 height = strheight(prompt)+menu_height+4+2;
195 i = strwidth(prompt);
196 j = ((title != NULL) ? strwidth(title) : 0);
198 width = MAX(width,item_x+4)+4;
200 width = MAX(width,24);
206 /* center dialog box on screen */
207 x = (COLS - width)/2;
208 y = (LINES - height)/2;
212 draw_shadow(stdscr, y, x, height, width);
214 dialog = newwin(height, width, y, x);
215 if (dialog == NULL) {
217 fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height,width,y,x);
220 keypad(dialog, TRUE);
222 draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
223 wattrset(dialog, border_attr);
224 wmove(dialog, height-3, 0);
225 waddch(dialog, ACS_LTEE);
226 for (i = 0; i < width-2; i++)
227 waddch(dialog, ACS_HLINE);
228 wattrset(dialog, dialog_attr);
229 waddch(dialog, ACS_RTEE);
230 wmove(dialog, height-2, 1);
231 for (i = 0; i < width-2; i++)
235 wattrset(dialog, title_attr);
236 wmove(dialog, 0, (width - strlen(title))/2 - 1);
238 waddstr(dialog, title);
241 wattrset(dialog, dialog_attr);
243 print_autowrap(dialog, prompt, height-1, width-2, width, 1, 2, TRUE, FALSE);
245 menu_width = width-6;
246 getyx(dialog, cur_y, cur_x);
248 box_x = (width - menu_width)/2 - 1;
250 /* create new window for the menu */
251 menu = subwin(dialog, menu_height, menu_width, y + box_y + 1, x + box_x + 1);
254 fprintf(stderr, "\nsubwin(dialog,%d,%d,%d,%d) failed, maybe wrong dims\n", menu_height,menu_width,y+box_y+1,x+box_x+1);
259 /* draw a box around the menu items */
260 draw_box(dialog, box_y, box_x, menu_height+2, menu_width+2, menubox_border_attr, menubox_attr);
265 for (i = 0; i < max_choice; i++)
266 print_item(menu, items[(scroll+i)], i, i == choice);
268 print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, item_x, cur_x, cur_y);
269 print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no);
271 display_helpline(dialog, height-1, width);
275 print_button(dialog, "Cancel", y, x+14, FALSE);
276 print_button(dialog, " OK ", y, x, TRUE);
281 key = wgetch(dialog);
282 /* Check if key pressed matches first character of any item tag in menu */
284 if (key == KEY_UP || key == KEY_DOWN || key == '-' || key == '+') {
285 if (key == KEY_UP || key == '-') {
289 /* wscrl() in ncurses 1.8.1 seems to be broken, causing a segmentation
290 violation when scrolling windows of height = 4, so scrolling is not
293 getyx(dialog, cur_y, cur_x); /* Save cursor position */
294 /* Reprint menu to scroll down */
295 for (i = 0; i < max_choice; i++)
296 print_item(menu, items[(scroll+i)], i, i == choice);
300 /* Scroll menu down */
301 getyx(dialog, cur_y, cur_x); /* Save cursor position */
302 if (menu_height > 1) {
303 /* De-highlight current first item before scrolling down */
304 print_item(menu, items[scroll], 0, FALSE);
305 scrollok(menu, TRUE);
307 scrollok(menu, FALSE);
310 print_item(menu, items[scroll], 0, TRUE);
313 print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, item_x, cur_x, cur_y);
314 print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no);
315 wmove(dialog, cur_y, cur_x); /* Restore cursor to previous position */
318 continue; /* wait for another key press */
323 else if (key == KEY_DOWN || key == '+') {
324 if (choice == max_choice - 1) {
325 if (scroll+choice < item_no-1) {
327 /* wscrl() in ncurses 1.8.1 seems to be broken, causing a segmentation
328 violation when scrolling windows of height = 4, so scrolling is not
331 getyx(dialog, cur_y, cur_x); /* Save cursor position */
332 /* Reprint menu to scroll up */
333 for (i = 0; i < max_choice; i++)
334 print_item(menu, items[(scroll+i)], i, i == choice);
339 getyx(dialog, cur_y, cur_x); /* Save cursor position */
340 if (menu_height > 1) {
341 /* De-highlight current last item before scrolling up */
342 print_item(menu, items[(scroll+max_choice-1)], max_choice-1, FALSE);
343 scrollok(menu, TRUE);
345 scrollok(menu, FALSE);
348 print_item(menu, items[(scroll+max_choice-1)], max_choice-1, TRUE);
351 print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, item_x, cur_x, cur_y);
352 print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no);
353 wmove(dialog, cur_y, cur_x); /* Restore cursor to previous position */
356 continue; /* wait for another key press */
363 /* De-highlight current item */
364 getyx(dialog, cur_y, cur_x); /* Save cursor position */
365 print_item(menu, items[(scroll+choice)], choice, FALSE);
367 /* Highlight new item */
369 print_item(menu, items[(scroll+choice)], choice, TRUE);
371 print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no);
372 wmove(dialog, cur_y, cur_x); /* Restore cursor to previous position */
375 continue; /* wait for another key press */
378 /* save info about menu item position */
388 if (scroll > menu_height) { /* can we go up? */
389 scroll -= (menu_height);
398 if (scroll + menu_height >= item_no-1 - menu_height) { /* can we go down a full page? */
399 scroll = item_no - menu_height;
400 if (scroll < 0) scroll = 0;
402 scroll += menu_height;
414 scroll = item_no - menu_height;
415 if (scroll < 0) scroll = 0;
416 choice = max_choice - 1;
422 *result = scroll+choice;
433 button = 1; /* Indicates "Cancel" button is selected */
434 print_button(dialog, " OK ", y, x, FALSE);
435 print_button(dialog, "Cancel", y, x+14, TRUE);
438 button = 0; /* Indicates "OK" button is selected */
439 print_button(dialog, "Cancel", y, x+14, FALSE);
440 print_button(dialog, " OK ", y, x, TRUE);
449 *result = scroll+choice;
459 for (i = 0; i < max_choice; i++) {
460 print_item(menu, items[(scroll+i)],
464 getyx(dialog, cur_y, cur_x); /* Save cursor position */
465 print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, item_x, cur_x, cur_y);
466 print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no);
467 wmove(dialog, cur_y, cur_x); /* Restore cursor to previous position */
474 return -1; /* ESC pressed */
476 /* End of dialog_treemenu() */
482 static void print_item(WINDOW *win, struct leaf item, int choice, int selected)
484 int i, j = menu_width - 2;
485 char *branches = item.branches;
487 /* Clear 'residue' of last item */
488 wattrset(win, menubox_attr);
489 wmove(win, choice, 0);
490 for (i = 0; i < menu_width; i++)
492 wmove(win, choice, item_x);
494 while(*branches && j)
496 switch (*branches++) {
497 case ' ' : waddch(win, ' ');
499 case '|' : waddch(win, ACS_VLINE);
514 case '+' : waddch(win, ACS_LTEE);
516 case '`' : waddch(win, ACS_LLCORNER);
524 waddch(win, ACS_HLINE);
528 wattrset(win, selected ? item_selected_attr : item_attr);
530 waddnstr(win, item.name, j);
532 /* End of print_item() */
535 * Print current position
537 static void print_position(WINDOW *win, int x, int y,
538 int cur_pos, int size)
542 wattrset(win, position_indicator_attr);
543 percent = cur_pos == size - 1 ? 100 : (cur_pos * 100)/(size - 1);
544 wmove(win, y + 1, x - 6);
545 wprintw(win, "(%3d%%)", percent);
547 /* End of print_position() */
550 * Display a tree menu from file
552 * filename - file with like find(1) output
553 * FS - fields separator
554 * title - title of dialog box
555 * prompt - prompt text into dialog box
556 * height - height of dialog box
557 * width - width of dialog box
558 * menu_height - height of menu box
559 * result - pointer to char array
563 * 0 - Ok, result set (must be freed later)
567 int dialog_ftree(unsigned char *filename, unsigned char FS,
568 unsigned char *title, unsigned char *prompt,
569 int height, int width, int menu_height,
570 unsigned char **result)
572 int retcode, choice, size;
574 unsigned char **names;
576 if (mk_ftree(filename, &names, &size, FS, &items))
578 perror("dialog_ftree");
585 fprintf(stderr, "\ndialog_ftree: file %s is empty\n", filename);
590 retcode = dialog_treemenu(title, prompt, height, width, menu_height,
591 size, items, &choice, NULL, NULL);
593 free_leafs(items, size);
596 *result = free_names(names, size, choice);
598 (void)free_names(names, size, -1);
602 /* End of dialog_ftree() */
605 * Display a tree menu from array
607 * names - array with like find(1) output
608 * size - size of array
609 * FS - fields separator
610 * title - title of dialog box
611 * prompt - prompt text into dialog box
612 * height - height of dialog box
613 * width - width of dialog box
614 * menu_height - height of menu box
615 * result - pointer to char array
623 int dialog_tree(unsigned char **names, int size, unsigned char FS,
624 unsigned char *title, unsigned char *prompt,
625 int height, int width, int menu_height,
626 unsigned char **result)
630 struct saved_tree *st;
631 static struct queue *q_saved_tree = NULL;
635 fprintf(stderr, "\ndialog_tree: source array is empty\n");
640 if (mk_tree(names, size, FS, &items))
642 perror("dialog_tree");
647 /* is tree saved ? */
648 if (!(st = search_saved_tree(q_saved_tree, names,
650 height, width, menu_height))) {
653 calloc(sizeof (struct queue), 1))) {
654 perror("dialog_tree");
660 if (!(st = calloc(sizeof (struct saved_tree), 1))) {
661 perror("dialog_tree");
671 st->menu_height = menu_height;
673 if (!p2_queue(q_saved_tree, st)) {
674 perror("dialog_tree");
680 retcode = dialog_treemenu(title, prompt, height, width, menu_height,
681 size, items, &choice,
682 &(st->ch), &(st->sc));
684 free_leafs(items, size);
687 *result = names[choice];
691 /* End of dialog_tree() */
693 /* utils for ftree */
695 /* init struct queue by zeros */
697 init_queue(struct queue *queue)
699 bzero((void *)queue, sizeof(struct queue));
702 /* add pointer to queue */
703 /* return - pointer or NULL if error */
705 p2_queue(struct queue *queue, void *pointer)
712 if (!(queue->first = queue->last =
713 calloc(1, sizeof(struct m_queue))))
719 if (!(queue->last->next =
720 calloc(1, sizeof(struct m_queue))))
723 queue->last = queue->last->next;
727 return queue->last->pointer = pointer;
730 /* get first from queue */
731 /* return - pointer or NULL if queue is empty */
733 first_queue(struct queue *queue)
736 struct m_queue *new_first;
743 retval = queue->first->pointer;
744 new_first = queue->first->next;
746 queue->first = new_first;
753 /* make zero terminated array from queue */
754 /* return - pointer to array or NULL if error */
756 q2arr(struct queue *queue, int depth)
765 /* memory allocation for array */
766 if (!(mono = end = malloc(depth * sizeof(void *) + 1)))
771 if (!(*end++ = first_queue(queue)))
782 * smart_tree (for like find(1) with -d flag output compliance)
785 * NULL - malloc error
791 smart_tree(struct queue *queue,
793 unsigned char *current,
794 unsigned char *prev) {
795 unsigned char *pcurrent = current, *pprev = prev, *toqueue;
796 register char break_flag = 0;
798 while(*pcurrent && *pprev) {
799 if (*pcurrent == *pprev) {
809 if (!*pprev || break_flag) {
810 if (*pcurrent == FS) {
813 if ((!*prev) && (*pcurrent)) {
814 unsigned char tchar = *pcurrent;
817 if (!(toqueue = strdup(current))) {
821 if (!p2_queue(queue, toqueue)) {
830 if (*pcurrent == FS) {
832 if (!(toqueue = strdup(current))) {
836 if (!p2_queue(queue, toqueue)) {
844 if (!p2_queue(queue, current))
850 /* end of utils for ftree */
852 /* utils for make tree */
854 /* if error - return -1 */
857 mk_slip(struct leaf array[], int arr_size, int number, int shift)
862 if (number > arr_size - 1)
867 if (!(array[number].branches = calloc(1, t_shift + 1)))
870 (void)memset(array[number].branches, ' ', t_shift);
874 while (array[number].shift < array[t_number + 1].shift)
876 t_number = mk_slip(array, arr_size, t_number + 1, t_shift + 1);
879 if (t_number == arr_size - 1)
883 if (array[number].shift == array[t_number + 1].shift)
884 array[number].slip = '+';
886 if ((array[number].shift > array[t_number + 1].shift) ||
887 t_number == arr_size - 1)
888 array[number].slip = '`';
894 /* make tree from file
896 * filename - name of file with like find(1) output
897 * p_names - pointer to array of strings
898 * p_size - pointer to size of array
899 * FS - fields separator
900 * p_array - pointer to array of leafs
903 * 0 - ok and names by p_names, size by p_size, array by p_array set
904 * -1 - memory allocation error (errno set)
909 mk_ftree(char *filename,
910 unsigned char ***p_names, int *p_size, unsigned char FS,
911 struct leaf **p_array)
913 int NR; /* number of input records */
915 unsigned char *string, *sstring = "";
916 unsigned char **names;
920 if (!(input_file = fopen(filename, "r")))
925 if (!(string = malloc(BUFSIZ)))
928 /* read input file into queue */
929 while(fgets(string, BUFSIZ, input_file))
931 if (strchr(string, '\n'))
932 *strchr(string, '\n') = '\0';
934 if (!(string = realloc(string, strlen(string) + 1)))
937 if (!smart_tree(&queue, FS, string, sstring))
941 if (!(string = malloc(BUFSIZ)))
943 } /* read input file into queue */
945 if (fclose(input_file) == EOF)
948 if (!(NR = queue.size))
954 /* make array from queue */
955 if (!(names = (unsigned char **)q2arr(&queue, NR)))
961 /* make tree from array */
962 return mk_tree(names, NR, FS, p_array);
966 /* make tree from array
968 * names - array of strings
969 * size - size of array
970 * FS - fields separator
971 * p_array - pointer to array of leafs
974 * 0 - ok and array by p_array set
975 * -1 - memory allocation error (errno set)
980 mk_tree(unsigned char **names, int size, unsigned char FS,
981 struct leaf **p_array)
986 /* make array of leafs */
987 if (!(array = calloc(size, sizeof(struct leaf))))
991 for (i = 0; i < size; i++)
993 unsigned char *in_string, *name;
996 in_string = name = names[i];
999 if (*in_string == FS) {
1000 if (!i && !*(in_string + 1))
1005 name = in_string + 1;
1010 array[i].name = name;
1011 array[i].shift = shift;
1012 array[i].slip = '\0';
1013 array[i].branches = NULL;
1017 for (i = 0;i < size; i++)
1019 i = mk_slip(array, size, i, 0);
1025 for (i = 1;i < size; i++)
1027 unsigned char *src = array[i - 1].branches;
1028 unsigned char *dst = array[i].branches;
1034 switch (array[i - 1].slip) {
1035 case '+' : *dst = '|';
1037 case '`' : *dst = ' ';
1039 } /* make branches */
1046 /* free memory from tree (leafs)
1054 free_leafs(struct leaf *array, int size)
1056 struct leaf *p_array = array;
1059 free(array++->branches);
1062 } /* free_leafs() */
1064 /* free memory from source data for tree (names)
1067 * if 0 <= choice <= size - pointer to name from names,
1068 * and memory for name not released (must be freed later)
1069 * else - NULL (recomended choice -1 for it)
1074 free_names(unsigned char **names, int size, int choice)
1076 unsigned char *retval = NULL;
1077 unsigned char **p_names = names;
1088 } /* free_names() */
1090 /* end of utils for make tree */
1092 /* static utils for saved_tree */
1094 /* search saved tree within queue */
1095 /* return - struct *saved_tree or NULL if not found */
1098 search_saved_tree(struct queue *queue, unsigned char **names, int size,
1100 int height, int width,
1103 struct m_queue *member;
1104 struct saved_tree *retval;
1106 if (!queue || !names || !FS ||
1107 !height || !width || !menu_height)
1110 if (!(member = queue->first))
1113 while (member->next) {
1114 retval = member->pointer;
1115 if ((names == retval->names) &&
1116 (size == retval->size) &&
1117 (FS == retval->FS) &&
1118 (height == retval->height) &&
1119 (width == retval->width) &&
1120 (menu_height == retval->menu_height))
1122 member = member->next;
1124 retval = member->pointer;
1125 if ((names == retval->names) &&
1126 (size == retval->size) &&
1127 (FS == retval->FS) &&
1128 (height == retval->height) &&
1129 (width == retval->width) &&
1130 (menu_height == retval->menu_height))
1135 /* end of static utils for saved_tree */