1/*
2 * $Id: ins_wide.c,v 1.6 2005/04/16 17:45:17 tom Exp $
3 *
4 * Demonstrate the wins_wstr() and wins_wch functions.
5 * Thomas Dickey - 2002/11/23
6 *
7 * Note: to provide inputs for *ins_wch(), we use setcchar().  A quirk of the
8 * X/Open definition for that function is that the string contains no
9 * characters with negative width.  Any control character (such as tab) falls
10 * into that category.  So it follows that *ins_wch() cannot render a tab
11 * character because there is no legal way to construct a cchar_t containing
12 * one.  X/Open does not document this, and it would be logical to assume that
13 * *ins_wstr() has the same limitation, but it uses a wchar_t string directly,
14 * and does not document how tabs are handled.
15 */
16
17#include <test.priv.h>
18
19#if USE_WIDEC_SUPPORT
20
21/* definitions to make it simpler to compare with inserts.c */
22#define InsNStr    ins_nwstr
23#define InsStr     ins_wstr
24#define MvInsNStr  mvins_nwstr
25#define MvInsStr   mvins_wstr
26#define MvWInsNStr mvwins_nwstr
27#define MvWInsStr  mvwins_wstr
28#define WInsNStr   wins_nwstr
29#define WInsStr    wins_wstr
30
31#define TABSIZE 8
32
33typedef enum {
34    oDefault = 0,
35    oMove = 1,
36    oWindow = 2,
37    oMoveWindow = 3
38} Options;
39
40static bool m_opt = FALSE;
41static bool w_opt = FALSE;
42static int n_opt = -1;
43
44static void
45legend(WINDOW *win, int level, Options state, wchar_t *buffer, int length)
46{
47    NCURSES_CONST char *showstate;
48
49    switch (state) {
50    default:
51    case oDefault:
52	showstate = "";
53	break;
54    case oMove:
55	showstate = " (mvXXX)";
56	break;
57    case oWindow:
58	showstate = " (winXXX)";
59	break;
60    case oMoveWindow:
61	showstate = " (mvwinXXX)";
62	break;
63    }
64
65    wmove(win, 0, 0);
66    wprintw(win,
67	    "The Strings/Chars displays should match.  Enter any characters, except:\n");
68    wprintw(win,
69	    "down-arrow or ^N to repeat on next line, 'w' for inner window, 'q' to exit.\n");
70    wclrtoeol(win);
71    wprintw(win, "Level %d,%s inserted %d characters <", level,
72	    showstate, length);
73    waddwstr(win, buffer);
74    waddstr(win, ">");
75}
76
77static int
78ColOf(wchar_t *buffer, int length, int margin)
79{
80    int n;
81    int result;
82
83    for (n = 0, result = margin + 1; n < length; ++n) {
84	int ch = buffer[n];
85	switch (ch) {
86	case '\n':
87	    /* actually newline should clear the remainder of the line
88	     * and move to the next line - but that seems a little awkward
89	     * in this example.
90	     */
91	case '\r':
92	    result = 0;
93	    break;
94	case '\b':
95	    if (result > 0)
96		--result;
97	    break;
98	case '\t':
99	    result += (TABSIZE - (result % TABSIZE));
100	    break;
101	case '\177':
102	    result += 2;
103	    break;
104	default:
105	    result += wcwidth(ch);
106	    if (ch < 32)
107		++result;
108	    break;
109	}
110    }
111    return result;
112}
113
114static int
115ConvertCh(chtype source, cchar_t *target)
116{
117    wchar_t tmp_wchar[2];
118
119    tmp_wchar[0] = source;
120    tmp_wchar[1] = 0;
121    if (setcchar(target, tmp_wchar, A_NORMAL, 0, (void *) 0) == ERR) {
122	beep();
123	return FALSE;
124    }
125    return TRUE;
126}
127
128static int
129MvWInsCh(WINDOW *win, int y, int x, chtype ch)
130{
131    int code;
132    cchar_t tmp_cchar;
133
134    if (ConvertCh(ch, &tmp_cchar)) {
135	code = mvwins_wch(win, y, x, &tmp_cchar);
136    } else {
137	code = mvwinsch(win, y, x, ch);
138    }
139    return code;
140}
141
142static int
143MvInsCh(int y, int x, chtype ch)
144{
145    int code;
146    cchar_t tmp_cchar;
147
148    if (ConvertCh(ch, &tmp_cchar)) {
149	code = mvins_wch(y, x, &tmp_cchar);
150    } else {
151	code = mvinsch(y, x, ch);
152    }
153    return code;
154}
155
156static int
157WInsCh(WINDOW *win, chtype ch)
158{
159    int code;
160    cchar_t tmp_cchar;
161
162    if (ConvertCh(ch, &tmp_cchar)) {
163	code = wins_wch(win, &tmp_cchar);
164    } else {
165	code = winsch(win, ch);
166    }
167    return code;
168}
169
170static int
171InsCh(chtype ch)
172{
173    int code;
174    cchar_t tmp_cchar;
175
176    if (ConvertCh(ch, &tmp_cchar)) {
177	code = ins_wch(&tmp_cchar);
178    } else {
179	code = insch(ch);
180    }
181    return code;
182}
183
184#define LEN(n) ((length - (n) > n_opt) ? n_opt : (length - (n)))
185static void
186test_inserts(int level)
187{
188    static bool first = TRUE;
189
190    wint_t ch;
191    int code;
192    int limit;
193    int row = 1;
194    int col;
195    int row2, col2;
196    int length;
197    wchar_t buffer[BUFSIZ];
198    WINDOW *look = 0;
199    WINDOW *work = 0;
200    WINDOW *show = 0;
201    int margin = (2 * TABSIZE) - 1;
202    Options option = ((m_opt ? oMove : oDefault)
203		      | ((w_opt || (level > 0)) ? oWindow : oDefault));
204
205    if (first) {
206	static char cmd[80];
207	setlocale(LC_ALL, "");
208
209	putenv(strcpy(cmd, "TABSIZE=8"));
210
211	initscr();
212	(void) cbreak();	/* take input chars one at a time, no wait for \n */
213	(void) noecho();	/* don't echo input */
214	keypad(stdscr, TRUE);
215    }
216
217    limit = LINES - 5;
218    if (level > 0) {
219	look = newwin(limit, COLS - (2 * (level - 1)), 0, level - 1);
220	work = newwin(limit - 2, COLS - (2 * level), 1, level);
221	show = newwin(4, COLS, limit + 1, 0);
222	box(look, 0, 0);
223	wnoutrefresh(look);
224	limit -= 2;
225    } else {
226	work = stdscr;
227	show = derwin(stdscr, 4, COLS, limit + 1, 0);
228    }
229    keypad(work, TRUE);
230
231    for (col = margin + 1; col < COLS; col += TABSIZE)
232	mvwvline(work, row, col, '.', limit - 2);
233
234    mvwvline(work, row, margin, ACS_VLINE, limit - 2);
235    mvwvline(work, row, margin + 1, ACS_VLINE, limit - 2);
236    limit /= 2;
237
238    mvwaddstr(work, 1, 2, "String");
239    mvwaddstr(work, limit + 1, 2, "Chars");
240    wnoutrefresh(work);
241
242    buffer[length = 0] = '\0';
243    legend(show, level, option, buffer, length);
244    wnoutrefresh(show);
245
246    doupdate();
247
248    /*
249     * Show the characters inserted in color, to distinguish from those that
250     * are shifted.
251     */
252    if (has_colors()) {
253	start_color();
254	init_pair(1, COLOR_WHITE, COLOR_BLUE);
255	wbkgdset(work, COLOR_PAIR(1) | ' ');
256    }
257
258    while ((code = wget_wch(work, &ch)) != ERR) {
259
260	if (code == KEY_CODE_YES) {
261	    switch (ch) {
262	    case KEY_DOWN:
263		ch = CTRL('N');
264		break;
265	    case KEY_BACKSPACE:
266		ch = '\b';
267		break;
268	    default:
269		beep();
270		continue;
271	    }
272	} else if (code == ERR) {
273	    beep();
274	    break;
275	}
276	if (ch == 'q')
277	    break;
278
279	wmove(work, row, margin + 1);
280	switch (ch) {
281	case 'w':
282	    test_inserts(level + 1);
283
284	    touchwin(look);
285	    touchwin(work);
286	    touchwin(show);
287
288	    wnoutrefresh(look);
289	    wnoutrefresh(work);
290	    wnoutrefresh(show);
291
292	    doupdate();
293	    break;
294	case CTRL('N'):
295	    if (row < limit) {
296		++row;
297		/* put the whole string in, all at once */
298		col2 = margin + 1;
299		switch (option) {
300		case oDefault:
301		    if (n_opt > 1) {
302			for (col = 0; col < length; col += n_opt) {
303			    col2 = ColOf(buffer, col, margin);
304			    if (move(row, col2) != ERR) {
305				InsNStr(buffer + col, LEN(col));
306			    }
307			}
308		    } else {
309			if (move(row, col2) != ERR) {
310			    InsStr(buffer);
311			}
312		    }
313		    break;
314		case oMove:
315		    if (n_opt > 1) {
316			for (col = 0; col < length; col += n_opt) {
317			    col2 = ColOf(buffer, col, margin);
318			    MvInsNStr(row, col2, buffer + col, LEN(col));
319			}
320		    } else {
321			MvInsStr(row, col2, buffer);
322		    }
323		    break;
324		case oWindow:
325		    if (n_opt > 1) {
326			for (col = 0; col < length; col += n_opt) {
327			    col2 = ColOf(buffer, col, margin);
328			    if (wmove(work, row, col2) != ERR) {
329				WInsNStr(work, buffer + col, LEN(col));
330			    }
331			}
332		    } else {
333			if (wmove(work, row, col2) != ERR) {
334			    WInsStr(work, buffer);
335			}
336		    }
337		    break;
338		case oMoveWindow:
339		    if (n_opt > 1) {
340			for (col = 0; col < length; col += n_opt) {
341			    col2 = ColOf(buffer, col, margin);
342			    MvWInsNStr(work, row, col2, buffer + col, LEN(col));
343			}
344		    } else {
345			MvWInsStr(work, row, col2, buffer);
346		    }
347		    break;
348		}
349
350		/* do the corresponding single-character insertion */
351		row2 = limit + row;
352		for (col = 0; col < length; ++col) {
353		    col2 = ColOf(buffer, col, margin);
354		    switch (option) {
355		    case oDefault:
356			if (move(row2, col2) != ERR) {
357			    InsCh((chtype) buffer[col]);
358			}
359			break;
360		    case oMove:
361			MvInsCh(row2, col2, (chtype) buffer[col]);
362			break;
363		    case oWindow:
364			if (wmove(work, row2, col2) != ERR) {
365			    WInsCh(work, (chtype) buffer[col]);
366			}
367			break;
368		    case oMoveWindow:
369			MvWInsCh(work, row2, col2, (chtype) buffer[col]);
370			break;
371		    }
372		}
373	    } else {
374		beep();
375	    }
376	    break;
377	case KEY_BACKSPACE:
378	    ch = '\b';
379	    /* FALLTHRU */
380	default:
381	    buffer[length++] = ch;
382	    buffer[length] = '\0';
383
384	    /* put the string in, one character at a time */
385	    col = ColOf(buffer, length - 1, margin);
386	    switch (option) {
387	    case oDefault:
388		if (move(row, col) != ERR) {
389		    InsStr(buffer + length - 1);
390		}
391		break;
392	    case oMove:
393		MvInsStr(row, col, buffer + length - 1);
394		break;
395	    case oWindow:
396		if (wmove(work, row, col) != ERR) {
397		    WInsStr(work, buffer + length - 1);
398		}
399		break;
400	    case oMoveWindow:
401		MvWInsStr(work, row, col, buffer + length - 1);
402		break;
403	    }
404
405	    /* do the corresponding single-character insertion */
406	    switch (option) {
407	    case oDefault:
408		if (move(limit + row, col) != ERR) {
409		    InsCh(ch);
410		}
411		break;
412	    case oMove:
413		MvInsCh(limit + row, col, ch);
414		break;
415	    case oWindow:
416		if (wmove(work, limit + row, col) != ERR) {
417		    WInsCh(work, ch);
418		}
419		break;
420	    case oMoveWindow:
421		MvWInsCh(work, limit + row, col, ch);
422		break;
423	    }
424
425	    wnoutrefresh(work);
426
427	    legend(show, level, option, buffer, length);
428	    wnoutrefresh(show);
429
430	    doupdate();
431	    break;
432	}
433    }
434    if (level > 0) {
435	delwin(show);
436	delwin(work);
437	delwin(look);
438    }
439}
440
441static void
442usage(void)
443{
444    static const char *tbl[] =
445    {
446	"Usage: inserts [options]"
447	,""
448	,"Options:"
449	,"  -n NUM  limit string-inserts to NUM bytes on ^N replay"
450	,"  -m      perform wmove/move separately from insert-functions"
451	,"  -w      use window-parameter even when stdscr would be implied"
452    };
453    unsigned n;
454    for (n = 0; n < SIZEOF(tbl); ++n)
455	fprintf(stderr, "%s\n", tbl[n]);
456    ExitProgram(EXIT_FAILURE);
457}
458
459int
460main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
461{
462    int ch;
463
464    setlocale(LC_ALL, "");
465
466    while ((ch = getopt(argc, argv, "mn:w")) != EOF) {
467	switch (ch) {
468	case 'm':
469	    m_opt = TRUE;
470	    break;
471	case 'n':
472	    n_opt = atoi(optarg);
473	    if (n_opt == 0)
474		n_opt = -1;
475	    break;
476	case 'w':
477	    w_opt = TRUE;
478	    break;
479	default:
480	    usage();
481	    break;
482	}
483    }
484    if (optind < argc)
485	usage();
486
487    test_inserts(0);
488    endwin();
489    ExitProgram(EXIT_SUCCESS);
490}
491#else
492int
493main(void)
494{
495    printf("This program requires the wide-ncurses library\n");
496    ExitProgram(EXIT_FAILURE);
497}
498#endif
499