1 /*
2 * ui_draw.c -- drawing of a nodes and trees using curses
3 *
4 * Copyright (C) 2001-2003 �yvind Kol�s <pippin@users.sourceforge.net>
5 *
6 * This program is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2, or (at your option) any later
9 * version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc., 59
18 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <assert.h>
25 #include "tree.h"
26 #include "tree_todo.h"
27 #include <string.h>
28 #include <unistd.h>
29 #include "curses.h"
30 #include "stdio.h"
31 #include "prefs.h"
32 #include "ui_overlay.h"
33 #define UI_C
34 #include "ui.h"
35 #include "ui_draw.h"
36 #include "cli.h"
37 #include <stdlib.h>
38 #include <ctype.h>
39
40 #define KEEPLINES 5
41
42 static int nodes_above;
43 static int active_line;
44 static int nodes_below;
45
up(Node * sel,Node * node)46 static Node *up (Node *sel, Node *node)
47 {
48 if (node_up (node) && node_getflag( node_up (node), F_expanded)) {
49 node = node_up (node);
50 while (node_right (node) && node_getflag(node,F_expanded)) {
51 node = node_right (node);
52 node = node_bottom (node);
53 }
54 return (node);
55 } else {
56 if (node_up (node))
57 return (node_up (node));
58 else
59 return (node_left (node));
60 }
61 return node_left (node);
62 }
63
down(Node * sel,Node * node)64 static Node *down (Node *sel, Node *node)
65 {
66 if (node_getflag(node,F_expanded)) {
67 return node_recurse (node);
68 } else {
69 if (node_down (node)) {
70 return (node_down (node));
71 } else {
72 while (node != 0) {
73 node = node_left (node);
74 if (node_down (node))
75 return (node_down (node));
76 }
77 }
78 }
79 return NULL;
80 }
81
82 int startlevel = 0;
83
84 int hnb_edit_posup = 0; /*contains the cursor pos for up/down */
85 int hnb_edit_posdown = 0; /*from here when in editing mode */
86
87 enum {
88 drawmode_test = 0,
89 drawmode_normal,
90 drawmode_selected,
91 drawmode_edit,
92 drawmode_completion
93 };
94
95 /* draws the actual node data with word wrapping, should be reengineered into a general
96 * linewrapping function.
97 *
98 *
99 */
draw_textblock(int line_start,int col_start,int width,int cursor_pos,Node * node,int drawmode)100 static int draw_textblock (int line_start, int col_start, int width,
101 int cursor_pos, Node *node, int drawmode)
102 {
103 int col_end = col_start + width;
104 unsigned char word[200]; /* current word being rendered */
105 int wpos = 0; /* position in current word */
106 int dpos = 0; /* position in data */
107 int col; /* current column */
108
109 int lines_used = 1;
110
111 int cursor_state = 0;
112 int cx = 0, cy = 0; /* coordinates to draw cursor at */
113
114 unsigned char *data =
115 (unsigned char *) fixnullstring (node_get (node, TEXT));
116
117 col = col_start;
118
119 word[0] = 0;
120 if (drawmode == drawmode_edit) {
121 hnb_edit_posup = 0;
122 hnb_edit_posdown = strlen ((char *) data);
123 }
124
125 switch (drawmode) {
126 case drawmode_test:
127 break;
128 case drawmode_completion:
129 if (node_right (node)) {
130 ui_style (ui_style_parentnode);
131 } else {
132 ui_style (ui_style_node);
133 }
134 break;
135 case drawmode_selected:
136 if (node_right (node)) {
137 ui_style (ui_style_parentselected);
138 } else {
139 ui_style (ui_style_selected);
140 }
141 break;
142 case drawmode_normal:
143 case drawmode_edit:
144 default:
145 if (node_right (node)) {
146 ui_style (ui_style_parentnode);
147 } else {
148 ui_style (ui_style_node);
149 }
150 break;
151 }
152
153 while ((dpos == 0) || data[dpos - 1]) { /* loop through data + \0 */
154 switch (data[dpos]) {
155 case '\0': /* \0 as well,.. to print last word */
156 case ' ':
157 case '\t':
158 case '\n':
159 case '\r': /* all whitespace is treated as spaces */
160 if (col + wpos + 1 >= col_end) { /* reached margin */
161 if (drawmode == drawmode_edit) {
162 if (cursor_state == 0)
163 hnb_edit_posup = cursor_pos - (col - col_start);
164 if (cursor_state == 1) {
165 hnb_edit_posdown = cursor_pos + (col - col_start);
166 cursor_state = 2;
167 }
168 }
169
170 col = col_start;
171 lines_used++;
172 if (lines_used + line_start >= LINES)
173 return lines_used;
174 }
175 if (drawmode != drawmode_test) {
176 if (line_start + lines_used - 1 >= 0) {
177 move (line_start + lines_used - 1, col);
178
179 /* must break the word in two due to differnt text formatting */
180 if (drawmode == drawmode_completion
181 && cursor_state == 0 && dpos >= cursor_pos) {
182 int i;
183
184 for (i = 0; i < wpos - (dpos - cursor_pos); i++)
185 addch (word[i]);
186 if (node_right (node)) {
187 ui_style (ui_style_parentselected);
188 } else {
189 ui_style (ui_style_selected);
190 }
191 for (i = wpos - (dpos - cursor_pos); i < wpos;
192 i++)
193 addch (word[i]);
194 } else {
195
196 addstr ((char *) word);
197
198 }
199 if (data[dpos])
200 addch (' ');
201 }
202 }
203
204 switch (drawmode) {
205 case drawmode_edit:
206 if (cursor_state == 0 && dpos >= cursor_pos) {
207 cy = line_start + lines_used - 1;
208 cx = col - (dpos - cursor_pos) + wpos;
209 cursor_state = 1;
210 }
211 break;
212 case drawmode_completion:
213 if (cursor_state == 0 && dpos >= cursor_pos) {
214 if (node_right (node)) {
215 ui_style (ui_style_parentselected);
216 } else {
217 ui_style (ui_style_selected);
218 }
219 cursor_state = 1;
220 }
221 default:
222 break;
223 }
224
225 col += wpos + 1;
226 word[wpos = 0] = 0;
227 break;
228 default:
229 if (wpos < 198) {
230 word[wpos++] = data[dpos];
231 word[wpos] = 0;
232 }
233 break;
234 }
235 dpos++;
236 }
237 /* draw the cursor */
238 if (drawmode == drawmode_edit) {
239 move (cy, cx);
240 if (node_right (node)) {
241 ui_style (ui_style_parentselected);
242 } else {
243 ui_style (ui_style_selected);
244 }
245 addch (data[cursor_pos]);
246 if (node_right (node)) {
247 ui_style (ui_style_parentnode);
248 } else {
249 ui_style (ui_style_node);
250 }
251 }
252 return lines_used;
253 }
254
255
256
draw_dummy(int line,int col,int width,Node * node,int drawmode)257 static int draw_dummy (int line, int col, int width, Node *node, int drawmode)
258 {
259 if (width == 0)
260 width = 1;
261 if (drawmode != drawmode_test) {
262 int j;
263
264 move (line, col);
265 ui_style (ui_style_bullet);
266 for (j = 0; j < width; j++) {
267 addch ('X');
268 }
269 }
270 return width;
271 }
272
draw_spacing(int line,int col,int width,Node * node,int drawmode)273 static int draw_spacing (int line, int col, int width, Node *node,
274 int drawmode)
275 {
276 if (width == 0)
277 width = 1;
278 if (drawmode != drawmode_test) {
279 int j;
280
281 move (line, col);
282 ui_style (ui_style_background);
283 for (j = 0; j < width; j++) {
284 addch (' ');
285 }
286 }
287 return width;
288 }
289
290 static char bullet_leaf[4] = " �";
291 static char bullet_parent[4] = " +";
292 static char bullet_parent_expanded[4] = " -";
293
294
295
draw_bullet(int line,int col,int width,Node * node,int drawmode)296 static int draw_bullet (int line, int col, int width, Node *node,
297 int drawmode)
298 {
299 int asize;
300 int perc;
301
302 /* if(width==0)*/
303 width = 3;
304
305 perc = calc_percentage_size (node, &asize);
306 {
307 ui_style (ui_style_bullet);
308
309 move (line, col);
310 switch (perc) {
311 case -1:
312 if (drawmode != drawmode_test)
313 addstr ((node_right (node)) ? node_getflag(node,F_expanded)
314 ? bullet_parent_expanded : bullet_parent
315 : bullet_leaf);
316 break;
317 case 0:
318 if (drawmode != drawmode_test)
319 addstr ("[ ]");
320 break;
321 case 2000:
322 if (drawmode != drawmode_test)
323 addstr ("[X]");
324 break;
325 default:{
326 char str[100];
327
328 snprintf (str, 4, "%2i%%", perc);
329 if (drawmode != drawmode_test)
330 addstr (str);
331 }
332 }
333 }
334
335 return width;
336 }
337
338
node2no_path(Node * node)339 static char *node2no_path (Node *node)
340 {
341 static char path[512];
342 int pos = 0;
343 int levels = nodes_left (node);
344 int cnt;
345
346 path[0] = 0;
347
348 for (cnt = levels; cnt >= 0; cnt--) {
349 int cnt2;
350 Node *tnode = node;
351
352 for (cnt2 = 0; cnt2 < cnt; cnt2++)
353 tnode = node_left (tnode);
354
355 sprintf (&path[pos], "%i", nodes_up (tnode) + 1);
356 pos = strlen (path);
357 path[pos] = '.';
358 path[++pos] = 0;
359 }
360
361 path[--pos] = 0;
362
363 return (path);
364 }
365
draw_node_no(int line,int col,int width,Node * node,int drawmode)366 static int draw_node_no (int line, int col, int width, Node *node,
367 int drawmode)
368 {
369 char str[100] = "";
370
371 if (width == 0)
372 width = 4;
373
374 ui_style (ui_style_bullet);
375 move (line, col);
376 snprintf (str, 5, "%4i", node_no (node));
377 if (drawmode != drawmode_test) {
378 addstr (str);
379 }
380
381 return width;
382 }
383
384
draw_nr(int line,int col,int width,Node * node,int drawmode)385 static int draw_nr (int line, int col, int width, Node *node, int drawmode)
386 {
387 char str[100] = "";
388
389 if (width == 0)
390 width = 3;
391
392 ui_style (ui_style_bullet);
393 move (line, col);
394 snprintf (str, 5, "%3i", nodes_up (node) + 1);
395 if (drawmode != drawmode_test) {
396 addstr (str);
397 }
398
399 return width;
400 }
401
402
403
draw_anr(int line,int col,int width,Node * node,int drawmode)404 static int draw_anr (int line, int col, int width, Node *node, int drawmode)
405 {
406 char str[100] = "";
407 char fstr[20];
408
409 if (width == 0)
410 width = 8;
411
412 ui_style (ui_style_bullet);
413 move (line, col);
414 snprintf (fstr, 8, "%%%is", width);
415 snprintf (str, width + 2, fstr, node2no_path (node));
416 if (drawmode != drawmode_test) {
417 addstr (str);
418 }
419
420 return width;
421 }
422
node_getval(Node * node,char * name)423 static int node_getval(Node *node, char *name){
424 char *got=node_get(node,name);
425 if(!got)return -1;
426 return(atoi(got));
427 }
428
429
draw_debug(int line,int col,int width,Node * node,int drawmode)430 static int draw_debug (int line, int col, int width, Node *node, int drawmode)
431 {
432 int asize;
433 int size = node_getval (node,"size");
434 int perc;
435
436 width = 40;
437
438 if (drawmode != drawmode_test) {
439 ui_style (ui_style_background);
440 move (line, col);
441 perc = calc_percentage_size (node, &asize);
442
443 {
444 char str[64];
445
446 sprintf (str, "(%i/%i) ",
447 (perc == 2000 ? 100 : perc * asize) / 100, asize);
448 if (drawmode != drawmode_test)
449 addstr (str);
450 }
451
452 {
453 perc = calc_percentage_size (node, &asize);
454 attrset (A_NORMAL);
455 {
456 char str[256];
457
458 sprintf (str, "size:%i a_size:%i %i%% ", node_getval (node,"size"),
459 size, perc);
460 addstr (str);
461 }
462 }
463
464 if (node_calc_size (node) != -1) {
465 char str[10];
466
467 sprintf (str, "%4.1f ", (float) node_calc_size (node) / 10.0);
468 addstr (str);
469 }
470 }
471
472 return width;
473 }
474
475 #define MAX_COLUMNS 20
476
draw_indent(int line,int col,int width,Node * node,int drawmode)477 static int draw_indent (int line, int col, int width, Node *node,
478 int drawmode)
479 {
480 if (width == 0)
481 width = 4;
482
483 return width * nodes_left (node);
484 }
485
486
487 enum {
488 col_spacing = 0,
489 col_indent,
490 col_nr,
491 col_anr,
492 col_bullet,
493 col_data,
494 col_debug,
495 col_percentage,
496 col_node_no,
497 col_dummy,
498 col_terminate
499 };
500
501
502 static int (*col_fun[col_terminate + 1]) (int line, int col, int width,
503 Node *node, int drawmode) = {
504 draw_spacing, draw_indent, draw_nr, draw_anr, draw_bullet, draw_spacing,
505 draw_debug, draw_spacing, draw_node_no, draw_dummy, draw_dummy};
506
507 static struct {
508 int type;
509 int width;
510 } col_def[MAX_COLUMNS] = {
511 {
512 col_indent, 4}, {
513 col_spacing, 1}, {
514 col_bullet, 3}, {
515 col_spacing, 1}, {
516 col_data, 0}, {
517 col_spacing, 1}, {
518 col_dummy, 10}, {
519 col_spacing, 2}, {
520 col_dummy, 10}, {
521 col_spacing, 1}, {
522 col_terminate, 0}
523 };
524
525
526 /* FIXME: make backup?,.. and make sure data is present,.., make possiblity to write back? */
527
display_format_cmd(int argc,char ** argv,void * data)528 uint64_t display_format_cmd (int argc, char **argv, void *data)
529 {
530 char *p = argv[1];
531 int width;
532 int type;
533 int col_no = 0;
534
535 if(argc<2){
536 return PTR_TO_UINT64(data);
537 }
538
539 do {
540 width = 0;
541 type = col_spacing;
542 switch (*p) {
543 case 'i':
544 type = col_indent;
545 if (isdigit (*(p + 1))) {
546 width = atoi (p + 1);
547 while (isdigit ((unsigned char)*(p + 1)))
548 p++;
549 }
550 break;
551 case 'd':
552 type = col_data;
553 if (isdigit (*(p + 1))) {
554 width = atoi (p + 1);
555 while (isdigit ((unsigned char)*(p + 1)))
556 p++;
557 }
558 break;
559 case 'D':
560 type = col_debug;
561 if (isdigit (*(p + 1))) {
562 width = atoi (p + 1);
563 while (isdigit ((unsigned char)*(p + 1)))
564 p++;
565 }
566 break;
567 case 'x':
568 type = col_dummy;
569 if (isdigit (*(p + 1))) {
570 width = atoi (p + 1);
571 while (isdigit ((unsigned char)*(p + 1)))
572 p++;
573 }
574 break;
575 case '1':
576 type = col_nr;
577 if (*(p + 1) == '.') {
578 type = col_anr;
579 p++;
580 }
581 if (isdigit (*(p + 1))) {
582 width = atoi (p + 1);
583 while (isdigit ((unsigned char)*(p + 1)))
584 p++;
585 }
586 break;
587 case '-':
588 type = col_bullet;
589 if (isdigit (*(p + 1))) {
590 width = atoi (p + 1);
591 while (isdigit ((unsigned char)*(p + 1)))
592 p++;
593 }
594 break;
595 case '#':
596 type = col_node_no;
597 if (isdigit (*(p + 1))) {
598 width = atoi (p + 1);
599 while (isdigit ((unsigned char)*(p + 1)))
600 p++;
601 }
602 break;
603 case ' ':
604 type = col_spacing;
605 while (' ' == ((unsigned char)*(p + 1))) {
606 p++;
607 width++;
608 }
609 break;
610 default:
611 cli_outfunf ("td not_parsed(%c)", *p);
612 break;
613 }
614 col_def[col_no].type = type;
615 col_def[col_no].width = width;
616 col_no++;
617 } while (*(++p));
618
619 col_def[col_no].type = col_terminate;
620
621 return PTR_TO_UINT64(data);
622 }
623
624
625
626
627
628 /*
629 * @param line_start which line on the display the first line of the draw node is on
630 * @param level the indentation level of this item
631 * @param node the node to draw
632 * @param cursor_pos different meanings in different modes, testmode: none
633 * highlightmode: none, edit_mode: the position in the data
634 * that should be highlighted,
635 * completion: the number of matched chars in data
636 *
637 * @param draw_mode 1=draw, 0=test
638 *
639 * @return number of lines needed to draw item
640 **/
draw_item(int line_start,int cursor_pos,Node * node,int drawmode)641 static int draw_item (int line_start, int cursor_pos, Node *node,
642 int drawmode)
643 {
644 int col_no = 0;
645 int lines_used = 1;
646
647 int col_start = 0;
648 int col_end = COLS;
649
650 col_start = 0;
651 /* draw columns before col_data */
652
653 while (col_def[col_no].type != col_data
654 && col_def[col_no].type != col_terminate) {
655 col_start +=
656 col_fun[col_def[col_no].type] (line_start, col_start,
657 col_def[col_no].width, node,
658 drawmode);
659 col_no++;
660 }
661
662 /* fastforward to end of col_def */
663 while (col_def[col_no].type != col_terminate)
664 col_no++;
665
666 col_no--;
667
668 /* draw columns after col_data */
669 while (col_no && col_def[col_no].type != col_data) {
670 int width =
671 col_fun[col_def[col_no].type] (line_start,
672 col_end - col_def[col_no].width,
673 col_def[col_no].width, node,
674 drawmode_test);
675 col_end -=
676 col_fun[col_def[col_no].type] (line_start, col_end - width, width,
677 node, drawmode);
678 col_no--;
679 }
680
681 lines_used =
682 draw_textblock (line_start, col_start, (col_end - col_start),
683 cursor_pos, node, drawmode);
684
685 return lines_used;
686 }
687
688 extern int hnb_nodes_up;
689 extern int hnb_nodes_down;
690
691 #define MAXLINES 512
692 static int line_nodeno[MAXLINES] = { 0 };
693
ui_draw(Node * node,char * input,int edit_mode)694 void ui_draw (Node *node, char *input, int edit_mode)
695 {
696 int lines;
697
698 static struct {
699 int self;
700 int prev;
701 } node_numb = {
702 1, 1};
703
704 if (!prefs.fixedfocus) {
705
706 node_numb.prev = node_numb.self;
707 node_numb.self = node_no (node);
708
709 if (node_numb.self > node_numb.prev) {
710 active_line++;
711 } else if (node_numb.self < node_numb.prev) {
712 active_line--;
713 }
714
715 {
716 int i;
717
718 for (i = 0; i < ((LINES < MAXLINES) ? LINES : MAXLINES); i++)
719 if (line_nodeno[i] == node_numb.self) {
720 active_line = i;
721 break;
722 }
723 }
724
725 if (node_numb.self == 1) { /* jumped to root, always bring nodes to top of screen */
726 active_line = 1;
727 }
728
729 {
730 int i;
731
732 for (i = 0; i < ((LINES < MAXLINES) ? LINES : MAXLINES); i++)
733 line_nodeno[i] = 0;
734 }
735
736
737 {
738 int maxline = LINES - KEEPLINES;
739
740 if (active_line > maxline) /*if we overlap with help,.. move up */
741 active_line = maxline;
742 if (active_line < KEEPLINES)
743 active_line = KEEPLINES;
744 }
745 };
746
747 nodes_above = active_line;
748 nodes_below = LINES - active_line;
749
750 {
751 hnb_nodes_up = 0;
752 hnb_nodes_down = 0;
753
754 erase ();
755 /* draw nodes above selected node */
756 {
757 Node *prev_down = node; /* to aid pgup/pgdn */
758 int line = active_line;
759 Node *tnode = up (node, node);
760
761 while (tnode) {
762 draw_item (line -=
763 draw_item (0, 0, tnode, drawmode_test), 0, tnode,
764 drawmode_normal);
765
766 line_nodeno[line] = node_no (tnode);
767
768 if (node_down (tnode) == prev_down) {
769 hnb_nodes_up++;
770 prev_down = tnode;
771 }
772
773 tnode = up (node, tnode);
774 if (active_line - nodes_above >= line)
775 tnode = 0;
776 }
777 }
778 /* draw the currently selected item */
779
780 line_nodeno[active_line] = node_no (node);
781
782 if (edit_mode) {
783 lines = draw_item (active_line, (int) input, node, drawmode_edit);
784 } else {
785 lines =
786 draw_item (active_line, strlen (input), node,
787 drawmode_completion);
788 }
789
790 /* draw items below current item */
791 {
792 Node *prev_up = node; /* to aid pgup/pgdn */
793 Node *tnode = down (node, node);
794
795 lines += active_line;
796 if (lines >= LINES)
797 tnode = 0;
798 while (tnode) {
799 line_nodeno[lines] = node_no (tnode);
800 lines += draw_item (lines, 0, tnode, drawmode_normal);
801
802 if (node_up (tnode) == prev_up) {
803 hnb_nodes_down++;
804 prev_up = tnode;
805 }
806
807 tnode = down (node, tnode);
808 if (lines >= LINES)
809 tnode = 0;
810
811 }
812 }
813 }
814
815 help_draw (ui_current_scope);
816
817 move (LINES - 1, COLS - 1);
818
819 /* refresh ();*/
820
821 hnb_nodes_up++;
822 hnb_nodes_down++;
823 }
824
825 /*
826 !init_ui_draw();
827 */
init_ui_draw()828 void init_ui_draw ()
829 {
830 cli_add_command ("display_format", display_format_cmd, "<format string>");
831 cli_add_help ("display_format", "\
832 defines how each node is displayed, the display string syntax is \
833 interpreted as follows: \
834 spaces turn into real spaces, i means indentation, - means bullet, \
835 d means the real data of the node, x is a temporary placeholder for \
836 upcoming columntypes,. (for debugging only) \
837 i and x can take an argument specifying how many characters wide \
838 the field should be");
839 cli_add_string ("bullet_leaf", bullet_leaf, "");
840 cli_add_string ("bullet_parent", bullet_parent, "");
841 cli_add_string ("bullet_parent_expanded", bullet_parent_expanded, "");
842
843 }
844