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