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