ncurses 6.2 - patch 20210306
[ncurses.git] / test / demo_panels.c
1 /****************************************************************************
2  * Copyright 2018-2019,2020 Thomas E. Dickey                                *
3  * Copyright 2003-2016,2017 Free Software Foundation, Inc.                  *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29 /*
30  * $Id: demo_panels.c,v 1.45 2020/02/02 23:34:34 tom Exp $
31  *
32  * Demonstrate a variety of functions from the panel library.
33  */
34
35 #include <test.priv.h>
36
37 #if USE_LIBPANEL
38
39 #include <panel.h>
40
41 #define LAST_POS '@'
42 #define TEMP_POS '>'
43
44 typedef void (*InitPanel) (void);
45 typedef void (*FillPanel) (PANEL *);
46
47 static bool use_colors = FALSE;
48 static bool unboxed = FALSE;
49 static FILE *log_in;
50 static FILE *log_out;
51
52 static void
53 close_input(void)
54 {
55     if (log_in != 0) {
56         fclose(log_in);
57         log_in = 0;
58     }
59 }
60
61 static void
62 close_output(void)
63 {
64     if (log_out != 0) {
65         fclose(log_out);
66         log_out = 0;
67     }
68 }
69
70 static WINDOW *
71 statusline(void)
72 {
73     WINDOW *result = stdscr;
74
75     wmove(result, LINES - 1, 0);
76     wclrtoeol(result);
77     return result;
78 }
79
80 static void
81 pflush(void)
82 {
83     update_panels();
84     doupdate();
85 }
86
87 static void
88 saywhat(NCURSES_CONST char *text)
89 {
90     WINDOW *win = statusline();
91     if (text != 0 && *text != '\0') {
92         waddstr(win, text);
93         waddstr(win, "; ");
94     }
95     waddstr(win, "press any key to continue");
96 }
97
98 static void
99 show_position(NCURSES_CONST char *text,
100               NCURSES_CONST char *also,
101               int which,
102               int ypos,
103               int xpos)
104 {
105     WINDOW *win = statusline();
106
107     wprintw(win, "%s for panel %d now %d,%d%s", text, which, ypos, xpos, also);
108     wmove(stdscr, ypos, xpos);
109 }
110
111 static int
112 get_position(NCURSES_CONST char *text,
113              NCURSES_CONST char *also,
114              int which,
115              int *xpos,
116              int *ypos)
117 {
118     int result = 0;
119     int x1, y1;
120     char cmd;
121
122     getyx(stdscr, y1, x1);
123     (void) statusline();
124
125     show_position(text, also, which, y1, x1);
126
127     if (log_in != 0) {
128         if (fscanf(log_in, "%c%d,%d\n", &cmd, &y1, &x1) == 3) {
129             switch (cmd) {
130             case LAST_POS:
131                 result = 1;
132                 (void) wgetch(stdscr);
133                 break;
134             case TEMP_POS:
135                 result = 0;
136                 wrefresh(stdscr);
137                 napms(100);
138                 break;
139             default:
140                 result = -1;
141                 break;
142             }
143         } else {
144             result = -1;
145         }
146     } else {
147
148         switch (wgetch(stdscr)) {
149         case QUIT:
150         case ESCAPE:
151         case ERR:
152             result = -1;
153             break;
154         case ' ':
155             result = 1;
156             break;
157         case KEY_UP:
158             if (y1 > 0) {
159                 --y1;
160             } else {
161                 beep();
162             }
163             break;
164         case KEY_DOWN:
165             if (y1 < getmaxy(stdscr)) {
166                 ++y1;
167             } else {
168                 beep();
169             }
170             break;
171         case KEY_LEFT:
172             if (x1 > 0) {
173                 --x1;
174             } else {
175                 beep();
176             }
177             break;
178         case KEY_RIGHT:
179             if (x1 < getmaxx(stdscr)) {
180                 ++x1;
181             } else {
182                 beep();
183             }
184             break;
185         }
186     }
187
188     wmove(stdscr, y1, x1);
189     *ypos = y1;
190     *xpos = x1;
191
192     if (result >= 0) {
193         if (log_out)
194             fprintf(log_out, "%c%d,%d\n",
195                     ((result > 0)
196                      ? LAST_POS
197                      : TEMP_POS),
198                     y1, x1);
199     }
200     return result;
201 }
202
203 static PANEL *
204 mkpanel(short color, int rows, int cols, int tly, int tlx)
205 {
206     WINDOW *win;
207     PANEL *pan = 0;
208     char *userdata = typeMalloc(char, 6);
209
210     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
211         keypad(win, TRUE);
212         if ((pan = new_panel(win)) == 0) {
213             delwin(win);
214         } else if (use_colors) {
215             short fg = (short) ((color == COLOR_BLUE)
216                                 ? COLOR_WHITE
217                                 : COLOR_BLACK);
218             short bg = color;
219
220             init_pair(color, fg, bg);
221             wbkgdset(win, (chtype) (COLOR_PAIR(color) | ' '));
222         } else if (!unboxed) {
223             wbkgdset(win, A_BOLD | ' ');
224         }
225     }
226     _nc_SPRINTF(userdata, _nc_SLIMIT(4) "p%d", color % 8);
227     set_panel_userptr(pan, (NCURSES_CONST void *) userdata);
228     return pan;
229 }
230
231 static void
232 my_remove_panel(PANEL **pans, int which)
233 {
234     if (pans[which] != 0) {
235         PANEL *pan = pans[which];
236         WINDOW *win = panel_window(pan);
237         char *user = (char *) panel_userptr(pan);
238
239         free(user);
240         del_panel(pan);
241         delwin(win);
242
243         pans[which] = 0;
244     }
245 }
246
247 #undef MIN
248 #define MIN(a,b) ((a) < (b) ? (a) : (b))
249 #define ABS(a)   ((a) < 0 ? -(a) : (a))
250
251 static void
252 my_create_panel(PANEL **pans, int which, FillPanel myFill)
253 {
254     int code;
255     short pair = (short) which;
256     short fg = (short) ((pair == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK);
257     short bg = pair;
258     int x0, y0, x1, y1;
259
260     init_pair(pair, fg, bg);
261
262     /* remove the old panel, if any */
263     my_remove_panel(pans, which);
264
265     /* get the position of one corner */
266     wmove(stdscr, getmaxy(stdscr) / 2, getmaxx(stdscr) / 2);
267     getyx(stdscr, y0, x0);
268     while ((code = get_position("First corner", "", which, &x0, &y0)) == 0) {
269         ;
270     }
271
272     if (code > 0) {
273         char also[80];
274         _nc_SPRINTF(also, _nc_SLIMIT(sizeof(also)) " (first %d,%d)", y0, x0);
275         /* get the position of the opposite corner */
276         while ((code = get_position("Opposite corner",
277                                     also, which, &x1, &y1)) == 0) {
278             ;
279         }
280
281         if (code > 0) {
282             int tly = MIN(y0, y1);
283             int tlx = MIN(x0, x1);
284             PANEL *pan = mkpanel(pair,
285                                  ABS(y1 - y0) + 1,
286                                  ABS(x1 - x0) + 1,
287                                  tly, tlx);
288             /* finish */
289             myFill(pan);
290             pans[which] = pan;
291             pflush();
292             wmove(stdscr, y1, x1);
293         }
294     }
295 }
296
297 static void
298 my_move_panel(PANEL **pans, int which, bool continuous)
299 {
300     if (pans[which] != 0) {
301         int code;
302         int y0, x0;
303         int y1, x1;
304         WINDOW *win = panel_window(pans[which]);
305         char also[80];
306
307         getbegyx(win, y0, x0);
308         _nc_SPRINTF(also, _nc_SLIMIT(sizeof(also)) " (start %d,%d)", y0, x0);
309         wmove(stdscr, y0, x0);
310         while ((code = get_position("Move panel", also, which, &x1, &y1)) == 0) {
311             if (continuous) {
312                 move_panel(pans[which], y1, x1);
313                 pflush();
314             }
315         }
316         if (code > 0) {
317             move_panel(pans[which], y1, x1);
318         }
319     }
320 }
321
322 static void
323 my_resize_panel(PANEL **pans, int which, FillPanel myFill)
324 {
325     if (pans[which] != 0) {
326         int code;
327         int y0, x0;
328         int y1, x1;
329         WINDOW *win = panel_window(pans[which]);
330         char also[80];
331
332         getbegyx(win, y0, x0);
333         _nc_SPRINTF(also, _nc_SLIMIT(sizeof(also)) " (start %d,%d)", y0, x0);
334         wmove(stdscr, y0, x0);
335         while ((code = get_position("Resize panel",
336                                     also, which, &x1, &y1)) == 0) {
337             ;
338         }
339         if (code > 0) {
340             WINDOW *next = newwin(ABS(y1 - y0) + 1,
341                                   ABS(x1 - x0) + 1,
342                                   MIN(y0, y1),
343                                   MIN(x0, x1));
344             if (next != 0) {
345                 keypad(next, TRUE);
346                 if (use_colors) {
347                     wbkgdset(next, (chtype) (COLOR_PAIR(which) | ' '));
348                 } else if (!unboxed) {
349                     wbkgdset(next, A_BOLD | ' ');
350                 }
351                 replace_panel(pans[which], next);
352                 myFill(pans[which]);
353                 delwin(win);
354             }
355         }
356     }
357 }
358
359 static void
360 init_panel(void)
361 {
362     register int y, x;
363
364     for (y = 0; y < LINES - 1; y++) {
365         for (x = 0; x < COLS; x++)
366             wprintw(stdscr, "%d", (y + x) % 10);
367     }
368 }
369
370 static void
371 fill_panel(PANEL *pan)
372 {
373     WINDOW *win = panel_window(pan);
374     const char *userptr = (const char *) panel_userptr(pan);
375     int num = (userptr && *userptr) ? userptr[1] : '?';
376     int y, x;
377
378     wmove(win, 1, 1);
379     wprintw(win, "-pan%c-", num);
380     wclrtoeol(win);
381     box(win, 0, 0);
382     for (y = 2; y < getmaxy(win) - 1; y++) {
383         for (x = 1; x < getmaxx(win) - 1; x++) {
384             wmove(win, y, x);
385             waddch(win, UChar(num));
386         }
387     }
388 }
389
390 static void
391 fill_unboxed(PANEL *pan)
392 {
393     WINDOW *win = panel_window(pan);
394     const char *userptr = (const char *) panel_userptr(pan);
395     int num = (userptr && *userptr) ? userptr[1] : '?';
396     int y, x;
397
398     for (y = 0; y < getmaxy(win); y++) {
399         for (x = 0; x < getmaxx(win); x++) {
400             wmove(win, y, x);
401             waddch(win, UChar(num));
402         }
403     }
404 }
405
406 #if USE_WIDEC_SUPPORT
407 static void
408 make_fullwidth_digit(cchar_t *target, int digit)
409 {
410     wchar_t source[2];
411
412     source[0] = (wchar_t) (digit + 0xff10);
413     source[1] = 0;
414     setcchar(target, source, A_NORMAL, 0, 0);
415 }
416
417 static void
418 init_wide_panel(void)
419 {
420     int digit;
421     cchar_t temp[10];
422
423     for (digit = 0; digit < 10; ++digit)
424         make_fullwidth_digit(&temp[digit], digit);
425
426     do {
427         int y, x;
428         getyx(stdscr, y, x);
429         digit = (y + x / 2) % 10;
430     } while (add_wch(&temp[digit]) != ERR);
431 }
432
433 static void
434 fill_wide_panel(PANEL *pan)
435 {
436     WINDOW *win = panel_window(pan);
437     int num = ((const char *) panel_userptr(pan))[1];
438     int y, x;
439
440     wmove(win, 1, 1);
441     wprintw(win, "-pan%c-", num);
442     wclrtoeol(win);
443     box(win, 0, 0);
444     for (y = 2; y < getmaxy(win) - 1; y++) {
445         for (x = 1; x < getmaxx(win) - 1; x++) {
446             wmove(win, y, x);
447             waddch(win, UChar(num));
448         }
449     }
450 }
451 #endif
452
453 #define MAX_PANELS 5
454
455 static int
456 which_panel(PANEL *px[MAX_PANELS + 1], PANEL *pan)
457 {
458     int result = 0;
459     int j;
460
461     for (j = 1; j <= MAX_PANELS; ++j) {
462         if (px[j] == pan) {
463             result = j;
464             break;
465         }
466     }
467     return result;
468 }
469
470 static void
471 show_help(WINDOW *win)
472 {
473     static const char *help[] =
474     {
475         "",
476         "Commands are letter/digit pairs.  Digits are the panel number.",
477         "",
478         "  b - put the panel on the bottom of the stack",
479         "  c - create the panel",
480         "  d - delete the panel",
481         "  h - hide the panel",
482         "  m - move the panel (M for continuous move)",
483         "  r - resize the panel",
484         "  s - show the panel",
485         "  t - put the panel on the top of the stack"
486     };
487     int j;
488
489     for (j = 0; j < (int) SIZEOF(help); ++j) {
490         if (wprintw(win, "%s\n", help[j]) == ERR)
491             break;
492     }
493 }
494
495 static void
496 show_panels(PANEL *px[MAX_PANELS + 1])
497 {
498     struct {
499         bool valid;
500         bool hidden;
501         PANEL *above;
502         PANEL *below;
503     } table[MAX_PANELS + 1];
504
505     WINDOW *win;
506     int j;
507
508     memset(table, 0, sizeof(table));
509     for (j = 1; j <= MAX_PANELS; ++j) {
510         table[j].valid = (px[j] != 0);
511         if (table[j].valid) {
512             table[j].hidden = panel_hidden(px[j]);
513             table[j].above = panel_above(px[j]);
514             table[j].below = panel_below(px[j]);
515         }
516     }
517
518     if ((win = newwin(LINES - 1, COLS, 0, 0)) != 0) {
519         PANEL *pan;
520
521         keypad(win, TRUE);
522         if ((pan = new_panel(win)) != 0) {
523             werase(win);
524             MvWPrintw(win, 0, 0, "Panels:\n");
525             for (j = 1; j <= MAX_PANELS; ++j) {
526                 if (table[j].valid) {
527                     wprintw(win, " %d:", j);
528                     if (table[j].hidden) {
529                         waddstr(win, " hidden");
530                     } else {
531                         if (table[j].above) {
532                             wprintw(win, " above %d",
533                                     which_panel(px, table[j].above));
534                         }
535                         if (table[j].below) {
536                             wprintw(win, "%s below %d",
537                                     table[j].above ? "," : "",
538                                     which_panel(px, table[j].below));
539                         }
540                     }
541                     waddch(win, '\n');
542                 }
543             }
544             show_help(win);
545             wgetch(win);
546             del_panel(pan);
547             pflush();
548         }
549         delwin(win);
550     }
551 }
552
553 #define wrapper(func) \
554 static int my_##func(PANEL *pan) \
555 { \
556     int code = ERR; \
557     if (pan != 0) { \
558         code = func(pan); \
559     } \
560     return code; \
561 }
562 /* *INDENT-OFF* */
563 wrapper(bottom_panel)
564 wrapper(hide_panel)
565 wrapper(show_panel)
566 wrapper(top_panel)
567 /* *INDENT-ON* */
568
569 static void
570 do_panel(PANEL *px[MAX_PANELS + 1],
571          NCURSES_CONST char *cmd,
572          FillPanel myFill)
573 {
574     int which = cmd[1] - '0';
575
576     if (which < 1 || which > MAX_PANELS) {
577         beep();
578         return;
579     }
580
581     if (log_in != 0) {
582         pflush();
583     }
584
585     saywhat(cmd);
586     switch (*cmd) {
587     case 'b':
588         my_bottom_panel(px[which]);
589         break;
590     case 'c':
591         my_create_panel(px, which, myFill);
592         break;
593     case 'd':
594         my_remove_panel(px, which);
595         break;
596     case 'h':
597         my_hide_panel(px[which]);
598         break;
599     case 'm':
600         my_move_panel(px, which, FALSE);
601         break;
602     case 'M':
603         my_move_panel(px, which, TRUE);
604         break;
605     case 'r':
606         my_resize_panel(px, which, myFill);
607         break;
608     case 's':
609         my_show_panel(px[which]);
610         break;
611     case 't':
612         my_top_panel(px[which]);
613         break;
614     }
615 }
616
617 static bool
618 ok_letter(int ch)
619 {
620     return isalpha(UChar(ch)) && strchr("bcdhmMrst", ch) != 0;
621 }
622
623 static bool
624 ok_digit(int ch)
625 {
626     return isdigit(UChar(ch)) && (ch >= '1') && (ch - '0' <= MAX_PANELS);
627 }
628
629 /*
630  * A command consists of one or more letter/digit pairs separated by a space.
631  * Digits are limited to 1..MAX_PANELS.
632  *
633  * End the command with a newline.  Reject other characters.
634  */
635 static bool
636 get_command(PANEL *px[MAX_PANELS + 1], char *buffer, int limit)
637 {
638     int length = 0;
639     int y0, x0;
640     WINDOW *win;
641
642     getyx(stdscr, y0, x0);
643     win = statusline();
644     waddstr(win, "Command:");
645     buffer[length = 0] = '\0';
646
647     if (log_in != 0) {
648         if (fgets(buffer, limit - 3, log_in) != 0) {
649             length = (int) strlen(buffer);
650             while (length > 0 && isspace(UChar(buffer[length - 1])))
651                 buffer[--length] = '\0';
652             waddstr(win, buffer);
653         } else {
654             close_input();
655         }
656         (void) wgetch(win);
657     } else {
658         int c0 = 0;
659         for (;;) {
660             int ch = wgetch(win);
661             if (ch == ERR || ch == QUIT || ch == ESCAPE) {
662                 buffer[0] = '\0';
663                 break;
664             } else if (ch == CTRL('L')) {
665                 wrefresh(curscr);
666             } else if (ch == '\n' || ch == KEY_ENTER) {
667                 break;
668             } else if (ch == HELP_KEY_1) {
669                 show_panels(px);
670             } else if (length + 3 < limit) {
671                 if (ch >= KEY_MIN) {
672                     beep();
673                 } else if (ok_letter(UChar(ch))) {
674                     if (isalpha(UChar(c0))) {
675                         beep();
676                     } else if (isdigit(UChar(c0))) {
677                         wprintw(win, " %c", ch);
678                         buffer[length++] = ' ';
679                         buffer[length++] = (char) (c0 = ch);
680                     } else {
681                         wprintw(win, "%c", ch);
682                         buffer[length++] = (char) (c0 = ch);
683                     }
684                 } else if (ok_digit(ch)) {
685                     if (isalpha(UChar(c0))) {
686                         wprintw(win, "%c", ch);
687                         buffer[length++] = (char) (c0 = ch);
688                     } else {
689                         beep();
690                     }
691                 } else if (ch == ' ') {
692                     if (isdigit(UChar(c0))) {
693                         wprintw(win, "%c", ch);
694                         buffer[length++] = (char) (c0 = ch);
695                     } else {
696                         beep();
697                     }
698                 } else {
699                     beep();
700                 }
701             } else {
702                 beep();
703             }
704         }
705     }
706
707     wmove(stdscr, y0, x0);
708
709     buffer[length] = '\0';
710     if (log_out && length) {
711         fprintf(log_out, "%s\n", buffer);
712     }
713     return (length != 0);
714 }
715
716 static void
717 demo_panels(InitPanel myInit, FillPanel myFill)
718 {
719     int itmp;
720     PANEL *px[MAX_PANELS + 1];
721     char buffer[BUFSIZ];
722
723     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
724     refresh();
725
726     myInit();
727     memset(px, 0, sizeof(px));
728
729     while (get_command(px, buffer, sizeof(buffer))) {
730         int limit = (int) strlen(buffer);
731         for (itmp = 0; itmp < limit; itmp += 3) {
732             do_panel(px, buffer + itmp, myFill);
733         }
734         pflush();
735     }
736 #if NO_LEAKS
737     for (itmp = 1; itmp <= MAX_PANELS; ++itmp) {
738         my_remove_panel(px, itmp);
739     }
740 #endif
741 }
742
743 static void
744 usage(void)
745 {
746     static const char *const tbl[] =
747     {
748         "Usage: demo_panels [options]"
749         ,""
750         ,"Options:"
751         ,"  -i file  read commands from file"
752         ,"  -o file  record commands in file"
753         ,"  -m       do not use colors"
754 #if USE_WIDEC_SUPPORT
755         ,"  -w       use wide-characters in panels and background"
756 #endif
757         ,"  -x       do not enclose panels in boxes"
758     };
759     size_t n;
760     for (n = 0; n < SIZEOF(tbl); n++)
761         fprintf(stderr, "%s\n", tbl[n]);
762     ExitProgram(EXIT_FAILURE);
763 }
764
765 int
766 main(int argc, char *argv[])
767 {
768     int c;
769     bool monochrome = FALSE;
770     InitPanel myInit = init_panel;
771     FillPanel myFill = fill_panel;
772
773     setlocale(LC_ALL, "");
774
775     while ((c = getopt(argc, argv, "i:o:mwx")) != -1) {
776         switch (c) {
777         case 'i':
778             log_in = fopen(optarg, "r");
779             break;
780         case 'o':
781             log_out = fopen(optarg, "w");
782             break;
783         case 'm':
784             monochrome = TRUE;
785             break;
786 #if USE_WIDEC_SUPPORT
787         case 'w':
788             myInit = init_wide_panel;
789             myFill = fill_wide_panel;
790             break;
791 #endif
792         case 'x':
793             unboxed = TRUE;
794             break;
795         default:
796             usage();
797         }
798     }
799     if (unboxed)
800         myFill = fill_unboxed;
801
802     initscr();
803     cbreak();
804     noecho();
805     keypad(stdscr, TRUE);
806
807     use_colors = monochrome ? FALSE : has_colors();
808     if (use_colors)
809         start_color();
810
811     demo_panels(myInit, myFill);
812     endwin();
813
814     close_input();
815     close_output();
816
817     ExitProgram(EXIT_SUCCESS);
818 }
819 #else
820 int
821 main(void)
822 {
823     printf("This program requires the curses panel library\n");
824     ExitProgram(EXIT_FAILURE);
825 }
826 #endif