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