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