1/****************************************************************************
2 * Copyright (c) 2007,2008 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.33 2008/08/04 13:33:48 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
43typedef void (*InitPanel) (void);
44typedef void (*FillPanel) (PANEL *);
45
46static bool use_colors = FALSE;
47static bool unboxed = FALSE;
48static FILE *log_in;
49static FILE *log_out;
50
51static void
52close_input(void)
53{
54    if (log_in != 0) {
55	fclose(log_in);
56	log_in = 0;
57    }
58}
59
60static void
61close_output(void)
62{
63    if (log_out != 0) {
64	fclose(log_out);
65	log_out = 0;
66    }
67}
68
69static WINDOW *
70statusline(void)
71{
72    WINDOW *result = stdscr;
73
74    wmove(result, LINES - 1, 0);
75    wclrtoeol(result);
76    return result;
77}
78
79static void
80pflush(void)
81{
82    update_panels();
83    doupdate();
84}
85
86static void
87saywhat(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
97static void
98show_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
110static int
111get_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
203static PANEL *
204mkpanel(short color, int rows, int cols, int tly, int tlx)
205{
206    WINDOW *win;
207    PANEL *pan = 0;
208    char *userdata = typeMalloc(char, 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 = (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    sprintf(userdata, "p%d", color % 8);
227    set_panel_userptr(pan, (NCURSES_CONST void *) userdata);
228    return pan;
229}
230
231static void
232my_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
251static void
252my_create_panel(PANEL ** pans, int which, FillPanel myFill)
253{
254    PANEL *pan = 0;
255    int code;
256    short pair = (short) which;
257    short fg = (short) ((pair == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK);
258    short bg = pair;
259    int x0, y0, x1, y1;
260
261    init_pair(pair, fg, bg);
262
263    /* remove the old panel, if any */
264    my_remove_panel(pans, which);
265
266    /* get the position of one corner */
267    wmove(stdscr, getmaxy(stdscr) / 2, getmaxx(stdscr) / 2);
268    getyx(stdscr, y0, x0);
269    while ((code = get_position("First corner", "", which, &x0, &y0)) == 0) {
270	;
271    }
272
273    if (code > 0) {
274	char also[80];
275	sprintf(also, " (first %d,%d)", y0, x0);
276	/* get the position of the opposite corner */
277	while ((code = get_position("Opposite corner",
278				    also, which, &x1, &y1)) == 0) {
279	    ;
280	}
281
282	if (code > 0) {
283	    int tly = MIN(y0, y1);
284	    int tlx = MIN(x0, x1);
285	    pan = mkpanel(pair, ABS(y1 - y0) + 1, ABS(x1 - x0) + 1, tly, tlx);
286	    /* finish */
287	    myFill(pan);
288	    pans[which] = pan;
289	    pflush();
290	    wmove(stdscr, y1, x1);
291	}
292    }
293}
294
295static void
296my_move_panel(PANEL ** pans, int which, bool continuous)
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("Move panel", also, which, &x1, &y1)) == 0) {
309	    if (continuous) {
310		move_panel(pans[which], y1, x1);
311		pflush();
312	    }
313	}
314	if (code > 0) {
315	    move_panel(pans[which], y1, x1);
316	}
317    }
318}
319
320static void
321my_resize_panel(PANEL ** pans, int which, FillPanel myFill)
322{
323    if (pans[which] != 0) {
324	int code;
325	int y0, x0;
326	int y1, x1;
327	WINDOW *win = panel_window(pans[which]);
328	char also[80];
329
330	getbegyx(win, y0, x0);
331	sprintf(also, " (start %d,%d)", y0, x0);
332	wmove(stdscr, y0, x0);
333	while ((code = get_position("Resize panel",
334				    also, which, &x1, &y1)) == 0) {
335	    ;
336	}
337	if (code > 0) {
338	    WINDOW *next = newwin(ABS(y1 - y0) + 1,
339				  ABS(x1 - x0) + 1,
340				  MIN(y0, y1),
341				  MIN(x0, x1));
342	    if (next != 0) {
343		keypad(next, TRUE);
344		if (use_colors) {
345		    wbkgdset(next, (chtype) (COLOR_PAIR(which) | ' '));
346		} else if (!unboxed) {
347		    wbkgdset(next, A_BOLD | ' ');
348		}
349		replace_panel(pans[which], next);
350		myFill(pans[which]);
351		delwin(win);
352	    }
353	}
354    }
355}
356
357static void
358init_panel(void)
359{
360    register int y, x;
361
362    for (y = 0; y < LINES - 1; y++) {
363	for (x = 0; x < COLS; x++)
364	    wprintw(stdscr, "%d", (y + x) % 10);
365    }
366}
367
368static void
369fill_panel(PANEL * pan)
370{
371    WINDOW *win = panel_window(pan);
372    const char *userptr = (const char *) panel_userptr(pan);
373    int num = (userptr && *userptr) ? userptr[1] : '?';
374    int y, x;
375
376    wmove(win, 1, 1);
377    wprintw(win, "-pan%c-", num);
378    wclrtoeol(win);
379    box(win, 0, 0);
380    for (y = 2; y < getmaxy(win) - 1; y++) {
381	for (x = 1; x < getmaxx(win) - 1; x++) {
382	    wmove(win, y, x);
383	    waddch(win, UChar(num));
384	}
385    }
386}
387
388static void
389fill_unboxed(PANEL * pan)
390{
391    WINDOW *win = panel_window(pan);
392    const char *userptr = (const char *) panel_userptr(pan);
393    int num = (userptr && *userptr) ? userptr[1] : '?';
394    int y, x;
395
396    for (y = 0; y < getmaxy(win); y++) {
397	for (x = 0; x < getmaxx(win); x++) {
398	    wmove(win, y, x);
399	    waddch(win, UChar(num));
400	}
401    }
402}
403
404#if USE_WIDEC_SUPPORT
405static void
406make_fullwidth_digit(cchar_t *target, int digit)
407{
408    wchar_t source[2];
409
410    source[0] = digit + 0xff10;
411    source[1] = 0;
412    setcchar(target, source, A_NORMAL, 0, 0);
413}
414
415static void
416init_wide_panel(void)
417{
418    int digit;
419    cchar_t temp[10];
420
421    for (digit = 0; digit < 10; ++digit)
422	make_fullwidth_digit(&temp[digit], digit);
423
424    do {
425	int y, x;
426	getyx(stdscr, y, x);
427	digit = (y + x / 2) % 10;
428    } while (add_wch(&temp[digit]) != ERR);
429}
430
431static void
432fill_wide_panel(PANEL * pan)
433{
434    WINDOW *win = panel_window(pan);
435    int num = ((const char *) panel_userptr(pan))[1];
436    int y, x;
437
438    wmove(win, 1, 1);
439    wprintw(win, "-pan%c-", num);
440    wclrtoeol(win);
441    box(win, 0, 0);
442    for (y = 2; y < getmaxy(win) - 1; y++) {
443	for (x = 1; x < getmaxx(win) - 1; x++) {
444	    wmove(win, y, x);
445	    waddch(win, UChar(num));
446	}
447    }
448}
449#endif
450
451#define MAX_PANELS 5
452
453static int
454which_panel(PANEL * px[MAX_PANELS + 1], PANEL * pan)
455{
456    int result = 0;
457    int j;
458
459    for (j = 1; j <= MAX_PANELS; ++j) {
460	if (px[j] == pan) {
461	    result = j;
462	    break;
463	}
464    }
465    return result;
466}
467
468static void
469show_panels(PANEL * px[MAX_PANELS + 1])
470{
471    static const char *help[] =
472    {
473	"",
474	"Commands are letter/digit pairs.  Digits are the panel number.",
475	"",
476	"  b - put the panel on the bottom of the stack",
477	"  c - create the panel",
478	"  d - delete the panel",
479	"  h - hide the panel",
480	"  m - move the panel (M for continuous move)",
481	"  r - resize the panel",
482	"  s - show the panel",
483	"  b - put the panel on the top of the stack"
484    };
485
486    struct {
487	bool valid;
488	bool hidden;
489	PANEL *above;
490	PANEL *below;
491    } table[MAX_PANELS + 1];
492
493    WINDOW *win;
494    PANEL *pan;
495    int j;
496
497    for (j = 1; j <= MAX_PANELS; ++j) {
498	table[j].valid = (px[j] != 0);
499	if (table[j].valid) {
500	    table[j].hidden = panel_hidden(px[j]);
501	    table[j].above = panel_above(px[j]);
502	    table[j].below = panel_below(px[j]);
503	}
504    }
505
506    if ((win = newwin(LINES - 1, COLS, 0, 0)) != 0) {
507	keypad(win, TRUE);
508	if ((pan = new_panel(win)) != 0) {
509	    werase(win);
510	    mvwprintw(win, 0, 0, "Panels:\n");
511	    for (j = 1; j <= MAX_PANELS; ++j) {
512		if (table[j].valid) {
513		    wprintw(win, " %d:", j);
514		    if (table[j].hidden) {
515			waddstr(win, " hidden");
516		    } else {
517			if (table[j].above) {
518			    wprintw(win, " above %d",
519				    which_panel(px, table[j].above));
520			}
521			if (table[j].below) {
522			    wprintw(win, "%s below %d",
523				    table[j].above ? "," : "",
524				    which_panel(px, table[j].below));
525			}
526		    }
527		    waddch(win, '\n');
528		}
529	    }
530	    for (j = 0; j < (int) SIZEOF(help); ++j) {
531		if (wprintw(win, "%s\n", help[j]) == ERR)
532		    break;
533	    }
534	    wgetch(win);
535	    del_panel(pan);
536	    pflush();
537	}
538	delwin(win);
539    }
540}
541
542#define wrapper(func) \
543static int my_##func(PANEL *pan) \
544{ \
545    int code = ERR; \
546    if (pan != 0) { \
547	code = func(pan); \
548    } \
549    return code; \
550}
551/* *INDENT-OFF* */
552wrapper(bottom_panel)
553wrapper(hide_panel)
554wrapper(show_panel)
555wrapper(top_panel)
556/* *INDENT-ON* */
557
558static void
559do_panel(PANEL * px[MAX_PANELS + 1],
560	 NCURSES_CONST char *cmd,
561	 FillPanel myFill)
562{
563    int which = cmd[1] - '0';
564
565    if (which < 1 || which > MAX_PANELS) {
566	beep();
567	return;
568    }
569
570    if (log_in != 0) {
571	pflush();
572    }
573
574    saywhat(cmd);
575    switch (*cmd) {
576    case 'b':
577	my_bottom_panel(px[which]);
578	break;
579    case 'c':
580	my_create_panel(px, which, myFill);
581	break;
582    case 'd':
583	my_remove_panel(px, which);
584	break;
585    case 'h':
586	my_hide_panel(px[which]);
587	break;
588    case 'm':
589	my_move_panel(px, which, FALSE);
590	break;
591    case 'M':
592	my_move_panel(px, which, TRUE);
593	break;
594    case 'r':
595	my_resize_panel(px, which, myFill);
596	break;
597    case 's':
598	my_show_panel(px[which]);
599	break;
600    case 't':
601	my_top_panel(px[which]);
602	break;
603    }
604}
605
606static bool
607ok_letter(int ch)
608{
609    return isalpha(UChar(ch)) && strchr("bcdhmMrst", ch) != 0;
610}
611
612static bool
613ok_digit(int ch)
614{
615    return isdigit(UChar(ch)) && (ch >= '1') && (ch - '0' <= MAX_PANELS);
616}
617
618/*
619 * A command consists of one or more letter/digit pairs separated by a space.
620 * Digits are limited to 1..MAX_PANELS.
621 *
622 * End the command with a newline.  Reject other characters.
623 */
624static bool
625get_command(PANEL * px[MAX_PANELS + 1], char *buffer, int limit)
626{
627    int length = 0;
628    int y0, x0;
629    int c0, ch;
630    WINDOW *win;
631
632    getyx(stdscr, y0, x0);
633    win = statusline();
634    waddstr(win, "Command:");
635    buffer[length = 0] = '\0';
636
637    if (log_in != 0) {
638	if (fgets(buffer, limit - 3, log_in) != 0) {
639	    length = (int) strlen(buffer);
640	    while (length > 0 && isspace(UChar(buffer[length - 1])))
641		buffer[--length] = '\0';
642	    waddstr(win, buffer);
643	} else {
644	    close_input();
645	}
646	(void) wgetch(win);
647    } else {
648	c0 = 0;
649	for (;;) {
650	    ch = wgetch(win);
651	    if (ch == ERR || ch == QUIT || ch == ESCAPE) {
652		buffer[0] = '\0';
653		break;
654	    } else if (ch == CTRL('L')) {
655		wrefresh(curscr);
656	    } else if (ch == '\n' || ch == KEY_ENTER) {
657		break;
658	    } else if (ch == '?') {
659		show_panels(px);
660	    } else if (length + 3 < limit) {
661		if (ch >= KEY_MIN) {
662		    beep();
663		} else if (ok_letter(UChar(ch))) {
664		    if (isalpha(UChar(c0))) {
665			beep();
666		    } else if (isdigit(UChar(c0))) {
667			wprintw(win, " %c", ch);
668			buffer[length++] = ' ';
669			buffer[length++] = (char) (c0 = ch);
670		    } else {
671			wprintw(win, "%c", ch);
672			buffer[length++] = (char) (c0 = ch);
673		    }
674		} else if (ok_digit(ch)) {
675		    if (isalpha(UChar(c0))) {
676			wprintw(win, "%c", ch);
677			buffer[length++] = (char) (c0 = ch);
678		    } else {
679			beep();
680		    }
681		} else if (ch == ' ') {
682		    if (isdigit(UChar(c0))) {
683			wprintw(win, "%c", ch);
684			buffer[length++] = (char) (c0 = ch);
685		    } else {
686			beep();
687		    }
688		} else {
689		    beep();
690		}
691	    } else {
692		beep();
693	    }
694	}
695    }
696
697    wmove(stdscr, y0, x0);
698
699    buffer[length] = '\0';
700    if (log_out && length) {
701	fprintf(log_out, "%s\n", buffer);
702    }
703    return (length != 0);
704}
705
706static void
707demo_panels(InitPanel myInit, FillPanel myFill)
708{
709    int itmp;
710    PANEL *px[MAX_PANELS + 1];
711    char buffer[BUFSIZ];
712
713    scrollok(stdscr, FALSE);	/* we don't want stdscr to scroll! */
714    refresh();
715
716    myInit();
717    memset(px, 0, sizeof(px));
718
719    while (get_command(px, buffer, sizeof(buffer))) {
720	int limit = (int) strlen(buffer);
721	for (itmp = 0; itmp < limit; itmp += 3) {
722	    do_panel(px, buffer + itmp, myFill);
723	}
724	pflush();
725    }
726#if NO_LEAKS
727    for (itmp = 1; itmp <= MAX_PANELS; ++itmp) {
728	my_remove_panel(px, itmp);
729    }
730#endif
731}
732
733static void
734usage(void)
735{
736    static const char *const tbl[] =
737    {
738	"Usage: demo_panels [options]"
739	,""
740	,"Options:"
741	,"  -i file  read commands from file"
742	,"  -o file  record commands in file"
743	,"  -m       do not use colors"
744#if USE_WIDEC_SUPPORT
745	,"  -w       use wide-characters in panels and background"
746#endif
747	,"  -x       do not enclose panels in boxes"
748    };
749    size_t n;
750    for (n = 0; n < SIZEOF(tbl); n++)
751	fprintf(stderr, "%s\n", tbl[n]);
752    ExitProgram(EXIT_FAILURE);
753}
754
755int
756main(int argc, char *argv[])
757{
758    int c;
759    bool monochrome = FALSE;
760    InitPanel myInit = init_panel;
761    FillPanel myFill = fill_panel;
762
763    setlocale(LC_ALL, "");
764
765    while ((c = getopt(argc, argv, "i:o:mwx")) != -1) {
766	switch (c) {
767	case 'i':
768	    log_in = fopen(optarg, "r");
769	    break;
770	case 'o':
771	    log_out = fopen(optarg, "w");
772	    break;
773	case 'm':
774	    monochrome = TRUE;
775	    break;
776#if USE_WIDEC_SUPPORT
777	case 'w':
778	    myInit = init_wide_panel;
779	    myFill = fill_wide_panel;
780	    break;
781#endif
782	case 'x':
783	    unboxed = TRUE;
784	    break;
785	default:
786	    usage();
787	}
788    }
789    if (unboxed)
790	myFill = fill_unboxed;
791
792    initscr();
793    cbreak();
794    noecho();
795    keypad(stdscr, TRUE);
796
797    use_colors = monochrome ? FALSE : has_colors();
798    if (use_colors)
799	start_color();
800
801    demo_panels(myInit, myFill);
802    endwin();
803
804    close_input();
805    close_output();
806
807    ExitProgram(EXIT_SUCCESS);
808}
809#else
810int
811main(void)
812{
813    printf("This program requires the curses panel library\n");
814    ExitProgram(EXIT_FAILURE);
815}
816#endif
817