ncurses 5.5
[ncurses.git] / test / ins_wide.c
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
33 typedef enum {
34     oDefault = 0,
35     oMove = 1,
36     oWindow = 2,
37     oMoveWindow = 3
38 } Options;
39
40 static bool m_opt = FALSE;
41 static bool w_opt = FALSE;
42 static int n_opt = -1;
43
44 static void
45 legend(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
77 static int
78 ColOf(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
114 static int
115 ConvertCh(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
128 static int
129 MvWInsCh(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
142 static int
143 MvInsCh(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
156 static int
157 WInsCh(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
170 static int
171 InsCh(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)))
185 static void
186 test_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
441 static void
442 usage(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
459 int
460 main(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
492 int
493 main(void)
494 {
495     printf("This program requires the wide-ncurses library\n");
496     ExitProgram(EXIT_FAILURE);
497 }
498 #endif