ncurses 6.0 - patch 20170729
[ncurses.git] / test / movewindow.c
1 /****************************************************************************
2  * Copyright (c) 2006-2013,2017 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: movewindow.c,v 1.43 2017/06/24 20:48:46 tom Exp $
30  *
31  * Demonstrate move functions for windows and derived windows from the curses
32  * library.
33  *
34  * Author: Thomas E. Dickey
35  */
36 /*
37 derwin
38 mvderwin
39 subwin
40 mvwin
41
42 TODO:
43     add command to reset subwindow's origin to coincide with parent.
44     add command to delete subwindow (check if it has subwindows though)
45  */
46
47 #include <test.priv.h>
48 #include <stdarg.h>
49 #include <popup_msg.h>
50
51 #ifdef HAVE_XCURSES
52 #undef derwin
53 #endif
54
55 #ifdef NCURSES_VERSION
56 #define CONST_FMT const
57 #else
58 #define CONST_FMT               /* nothing */
59 #endif
60
61 #undef LINE_MAX
62
63 #define LINE_MIN        2
64 #define LINE_MAX        (LINES - 2)
65 #define COL_MIN         2
66 #define COL_MAX         (COLS - 2)
67
68 typedef struct {
69     int y, x;
70 } PAIR;
71
72 typedef struct {
73     WINDOW *parent;             /* need this since WINDOW->_parent is not portable */
74     WINDOW *child;              /* the actual value */
75 } FRAME;
76
77 static void head_line(CONST_FMT char *fmt,...) GCC_PRINTFLIKE(1, 2);
78 static void tail_line(CONST_FMT char *fmt,...) GCC_PRINTFLIKE(1, 2);
79
80 static unsigned num_windows;
81 static FRAME *all_windows;
82
83 static void
84 failed(const char *s)
85 {
86     perror(s);
87     endwin();
88     ExitProgram(EXIT_FAILURE);
89 }
90
91 static void
92 message(int lineno, CONST_FMT char *fmt, va_list argp)
93 {
94     int y, x;
95
96     getyx(stdscr, y, x);
97     move(lineno, 0);
98     clrtoeol();
99
100 #ifdef HAVE_XCURSES
101     {
102         char buffer[1024];
103         vsprintf(buffer, fmt, argp);
104         addstr(buffer);
105     }
106 #else
107     vwprintw(stdscr, fmt, argp);
108 #endif
109
110     move(y, x);
111     refresh();
112 }
113
114 static void
115 head_line(CONST_FMT char *fmt,...)
116 {
117     va_list argp;
118
119     va_start(argp, fmt);
120     message(0, fmt, argp);
121     va_end(argp);
122 }
123
124 static void
125 tail_line(CONST_FMT char *fmt,...)
126 {
127     va_list argp;
128
129     va_start(argp, fmt);
130     message(LINES - 1, fmt, argp);
131     va_end(argp);
132 }
133
134 /*
135  * Arrow keys move cursor, return location at current on non-arrow key.
136  */
137 static PAIR *
138 selectcell(WINDOW *parent,
139            WINDOW *child,
140            int uli, int ulj,
141            int lri, int lrj,
142            bool relative,
143            bool * more)
144 {
145     static PAIR res;            /* result cell */
146     int si = lri - uli + 1;     /* depth of the select area */
147     int sj = lrj - ulj + 1;     /* width of the select area */
148     int i = 0, j = 0;           /* offsets into the select area */
149
150     res.y = uli;
151     res.x = ulj;
152
153     if (child != 0) {
154         if (relative) {
155             getparyx(child, i, j);
156         } else {
157             getbegyx(child, i, j);
158             i -= uli + getbegy(parent);
159             j -= ulj + getbegx(parent);
160         }
161     }
162
163     if (more)
164         *more = FALSE;
165
166     for (;;) {
167         bool moved = FALSE;
168
169         tail_line("Upper left [%2d,%2d] Lower right [%2d,%2d] -> %d,%d -> %d,%d",
170                   uli, ulj,
171                   lri, lrj,
172                   i, j,
173                   uli + i, ulj + j);
174         wmove(parent, uli + i, ulj + j);
175
176         switch (wgetch(parent)) {
177         case KEY_UP:
178             i += si - 1;
179             moved = TRUE;
180             break;
181         case KEY_DOWN:
182             i++;
183             moved = TRUE;
184             break;
185         case KEY_LEFT:
186             j += sj - 1;
187             moved = TRUE;
188             break;
189         case KEY_RIGHT:
190             j++;
191             moved = TRUE;
192             break;
193         case QUIT:
194             /* FALLTHRU */
195         case ESCAPE:
196             return ((PAIR *) 0);
197 #ifdef NCURSES_MOUSE_VERSION
198         case KEY_MOUSE:
199             {
200                 MEVENT event;
201
202                 getmouse(&event);
203                 if (event.y > uli && event.x > ulj) {
204                     if (parent != stdscr) {
205                         i = event.y - getbegy(parent) - uli;
206                         j = event.x - getbegx(parent) - ulj;
207                     } else {
208                         i = event.y - uli;
209                         j = event.x - ulj;
210                     }
211                 } else {
212                     beep();
213                     break;
214                 }
215             }
216 #endif
217             /* FALLTHRU */
218         default:
219             res.y = uli + i;
220             res.x = ulj + j;
221             return (&res);
222         }
223
224         if (si <= 0)
225             i = 0;
226         else
227             i %= si;
228
229         if (sj <= 0)
230             j = 0;
231         else
232             j %= sj;
233
234         /*
235          * If the caller can handle continuous movement, return the result.
236          */
237         if (moved && more) {
238             *more = TRUE;
239             res.y = uli + i;
240             res.x = ulj + j;
241             return (&res);
242         }
243     }
244 }
245
246 /*
247  * Ask user for a window definition.
248  */
249 static bool
250 getwindow(WINDOW *parent, PAIR * ul, PAIR * lr)
251 {
252     int min_col = (parent == stdscr) ? COL_MIN : 0;
253     int max_col = (parent == stdscr) ? COL_MAX : getmaxx(parent);
254     int min_line = (parent == stdscr) ? LINE_MIN : 0;
255     int max_line = (parent == stdscr) ? LINE_MAX : getmaxy(parent);
256     PAIR *tmp;
257     bool result = FALSE;
258
259     head_line("Use arrows to move cursor, anything else to mark corner 1");
260     if ((tmp = selectcell(parent, 0,
261                           min_line, min_col,
262                           max_line, max_col,
263                           FALSE,
264                           (bool *) 0)) != 0) {
265         *ul = *tmp;
266         MvWAddCh(parent, ul->y, ul->x, '*');
267
268         head_line("Use arrows to move cursor, anything else to mark corner 2");
269         if ((tmp = selectcell(parent, 0,
270                               ul->y, ul->x,
271                               max_line, max_col,
272                               FALSE,
273                               (bool *) 0)) != 0) {
274             *lr = *tmp;
275             MvWAddCh(parent, lr->y, lr->x, '*');
276             wmove(parent, lr->y, lr->x);
277             wsyncdown(parent);
278             wrefresh(parent);
279             result = (lr->y != ul->y && lr->x != ul->x);
280         }
281     }
282     head_line("done");
283     return result;
284 }
285
286 /*
287  * Draw a box inside the given window.
288  */
289 static void
290 box_inside(WINDOW *win)
291 {
292     int y0, x0;
293     int y1, x1;
294
295     getyx(win, y0, x0);
296     getmaxyx(win, y1, x1);
297
298     MvWHLine(win, 0, 0, ACS_HLINE, x1);
299     MvWHLine(win, y1 - 1, 0, ACS_HLINE, x1);
300
301     MvWVLine(win, 0, 0, ACS_VLINE, y1);
302     MvWVLine(win, 0, x1 - 1, ACS_VLINE, y1);
303
304     MvWAddCh(win, 0, 0, ACS_ULCORNER);
305     MvWAddCh(win, y1 - 1, 0, ACS_LLCORNER);
306     MvWAddCh(win, 0, x1 - 1, ACS_URCORNER);
307     MvWAddCh(win, y1 - 1, x1 - 1, ACS_LRCORNER);
308
309     wsyncdown(win);
310     wmove(win, y0, x0);
311     wrefresh(win);
312 }
313
314 /*
315  * Add a window to our list.
316  */
317 static void
318 add_window(WINDOW *parent, WINDOW *child)
319 {
320     static unsigned have = 0;
321     unsigned need = ((num_windows + 1) | 31) + 1;
322
323     keypad(child, TRUE);
324     if (need > have) {
325         all_windows = typeRealloc(FRAME, need, all_windows);
326         if (!all_windows)
327             failed("add_window");
328     }
329     all_windows[num_windows].parent = parent;
330     all_windows[num_windows].child = child;
331     num_windows++;
332 }
333
334 static int
335 window2num(WINDOW *win)
336 {
337     int n;
338     int result = -1;
339     for (n = 0; n < (int) num_windows; ++n) {
340         if (win == all_windows[n].child) {
341             result = n;
342             break;
343         }
344     }
345     return result;
346 }
347
348 static WINDOW *
349 parent_of(WINDOW *win)
350 {
351     WINDOW *result = 0;
352     int n = window2num(win);
353     if (n >= 0)
354         result = all_windows[n].parent;
355     return result;
356 }
357
358 static void
359 repaint_one(WINDOW *win)
360 {
361     touchwin(win);
362     wnoutrefresh(win);
363 }
364
365 static void
366 refresh_all(WINDOW *win)
367 {
368     unsigned n;
369
370     for (n = 0; n < num_windows; ++n) {
371         if (all_windows[n].child != win) {
372             repaint_one(all_windows[n].child);
373         }
374     }
375
376     repaint_one(win);
377     doupdate();
378 }
379
380 static WINDOW *
381 next_window(WINDOW *win)
382 {
383     WINDOW *result = win;
384     int n = window2num(win);
385
386     if (n++ >= 0) {
387         result = all_windows[(unsigned) n % num_windows].child;
388         wmove(result, 0, 0);
389         wrefresh(result);
390     }
391     return result;
392 }
393
394 static WINDOW *
395 prev_window(WINDOW *win)
396 {
397     WINDOW *result = win;
398     int n = window2num(win);
399
400     if (n-- >= 0) {
401         if (n < 0)
402             n = (int) (num_windows - 1);
403         result = all_windows[(unsigned) n % num_windows].child;
404         wmove(result, 0, 0);
405         wrefresh(result);
406     }
407     return result;
408 }
409
410 static void
411 recur_move_window(WINDOW *parent, int dy, int dx)
412 {
413     unsigned n;
414
415     for (n = 0; n < num_windows; ++n) {
416         if (all_windows[n].parent == parent) {
417             mvwin(all_windows[n].child, dy, dx);
418             recur_move_window(all_windows[n].child, dy, dx);
419         }
420     }
421 }
422
423 /*
424  * test mvwin().
425  */
426 static bool
427 move_window(WINDOW *win, bool recur)
428 {
429     WINDOW *parent = parent_of(win);
430     bool result = FALSE;
431
432     if (parent != 0) {
433         bool top = (parent == stdscr);
434         int min_col = top ? COL_MIN : 0;
435         int max_col = top ? COL_MAX : getmaxx(parent);
436         int min_line = top ? LINE_MIN : 0;
437         int max_line = top ? LINE_MAX : getmaxy(parent);
438         PAIR *tmp;
439         bool more;
440
441         head_line("Select new position for %swindow", top ? "" : "sub");
442
443         while ((tmp = selectcell(parent,
444                                  win,
445                                  min_line, min_col,
446                                  max_line, max_col,
447                                  FALSE,
448                                  &more)) != 0) {
449             int y0, x0;
450             getbegyx(parent, y0, x0);
451             /*
452              * Moving a subwindow has the effect of moving a viewport around
453              * the screen.  The parent window retains the contents of the
454              * subwindow in the original location, but the viewport will show
455              * the contents (again) at the new location.  So it will look odd
456              * when testing.
457              */
458             if (mvwin(win, y0 + tmp->y, x0 + tmp->x) != ERR) {
459                 if (recur) {
460                     recur_move_window(win, tmp->y, tmp->x);
461                 }
462                 refresh_all(win);
463                 doupdate();
464                 result = TRUE;
465             } else {
466                 result = FALSE;
467             }
468             if (!more)
469                 break;
470         }
471     }
472     head_line("done");
473     return result;
474 }
475
476 static void
477 show_derwin(WINDOW *win)
478 {
479     int pary, parx, maxy, maxx;
480
481     getmaxyx(win, maxy, maxx);
482     getparyx(win, pary, parx);
483
484     head_line("Select new position for derived window at %d,%d (%d,%d)",
485               pary, parx, maxy, maxx);
486 }
487
488 /*
489  * test mvderwin().
490  */
491 static bool
492 move_derwin(WINDOW *win)
493 {
494     WINDOW *parent = parent_of(win);
495     bool result = FALSE;
496
497     if (parent != 0) {
498         bool top = (parent == stdscr);
499         int min_col = top ? COL_MIN : 0;
500         int max_col = top ? COL_MAX : getmaxx(parent);
501         int min_line = top ? LINE_MIN : 0;
502         int max_line = top ? LINE_MAX : getmaxy(parent);
503         PAIR *tmp;
504         bool more;
505
506         show_derwin(win);
507         while ((tmp = selectcell(parent,
508                                  win,
509                                  min_line, min_col,
510                                  max_line, max_col,
511                                  TRUE,
512                                  &more)) != 0) {
513             if (mvderwin(win, tmp->y, tmp->x) != ERR) {
514                 refresh_all(win);
515                 doupdate();
516                 repaint_one(win);
517                 doupdate();
518                 result = TRUE;
519                 show_derwin(win);
520             } else {
521                 flash();
522             }
523             if (!more)
524                 break;
525         }
526     }
527     head_line("done");
528     return result;
529 }
530
531 static void
532 fill_window(WINDOW *win, chtype ch)
533 {
534     int y, x;
535     int y0, x0;
536     int y1, x1;
537
538     getyx(win, y0, x0);
539     getmaxyx(win, y1, x1);
540     for (y = 0; y < y1; ++y) {
541         for (x = 0; x < x1; ++x) {
542             MvWAddCh(win, y, x, ch);
543         }
544     }
545     wsyncdown(win);
546     wmove(win, y0, x0);
547     wrefresh(win);
548 }
549
550 static void
551 fill_with_pattern(WINDOW *win)
552 {
553     int y, x;
554     int y0, x0;
555     int y1, x1;
556     int ch = 'a';
557
558     getyx(win, y0, x0);
559     getmaxyx(win, y1, x1);
560     for (y = 0; y < y1; ++y) {
561         for (x = 0; x < x1; ++x) {
562             MvWAddCh(win, y, x, (chtype) ch);
563             if (++ch > 'z')
564                 ch = 'a';
565         }
566     }
567     wsyncdown(win);
568     wmove(win, y0, x0);
569     wrefresh(win);
570 }
571
572 #define lines_of(ul,lr) (lr.y - ul.y + 1)
573 #define cols_of(ul,lr)  (lr.x - ul.x + 1)
574 #define pair_of(ul)     ul.y, ul.x
575
576 static WINDOW *
577 create_my_window(WINDOW *current)
578 {
579     PAIR ul, lr;
580     WINDOW *result = 0;
581
582     if (getwindow(stdscr, &ul, &lr)) {
583         result = newwin(lines_of(ul, lr), cols_of(ul, lr), pair_of(ul));
584         if (result != 0) {
585             fill_window(result, 'c');
586             add_window(stdscr, result);
587         }
588     }
589     if (result == 0)
590         result = current;
591     return result;
592 }
593
594 static WINDOW *
595 create_my_derwin(WINDOW *parent)
596 {
597     PAIR ul, lr;
598     WINDOW *result = 0;
599
600     if (getwindow(parent, &ul, &lr)) {
601         result = derwin(parent, lines_of(ul, lr), cols_of(ul, lr), pair_of(ul));
602         if (result != 0) {
603             fill_window(result, 'd');
604             add_window(parent, result);
605         }
606     }
607     if (result == 0)
608         result = parent;
609     return result;
610 }
611
612 static WINDOW *
613 create_my_subwin(WINDOW *parent)
614 {
615     PAIR ul, lr;
616     WINDOW *result = 0;
617
618     if (getwindow(parent, &ul, &lr)) {
619         result = subwin(parent,
620                         lines_of(ul, lr),
621                         cols_of(ul, lr),
622                         ul.y + getbegy(parent),
623                         ul.x + getbegx(parent));
624         if (result != 0) {
625             fill_window(result, 's');
626             add_window(parent, result);
627         }
628     }
629     if (result == 0)
630         result = parent;
631     return result;
632 }
633
634 static void
635 show_help(WINDOW *current)
636 {
637     /* *INDENT-OFF* */
638     static struct {
639         int     key;
640         CONST_FMT char * msg;
641     } help[] = {
642         { HELP_KEY_1,   "Show this screen" },
643         { 'b',          "Draw a box inside the current window" },
644         { 'c',          "Create a new window" },
645         { 'd',          "Create a new derived window" },
646         { 'D',          "Move derived window (moves viewport)" },
647         { 'f',          "Fill the current window with the next character" },
648         { 'F',          "Fill the current window with a pattern" },
649         { 'm',          "Move the current window" },
650         { 'M',          "Move the current window (and its children)" },
651         { 'q',          "Quit" },
652         { 's',          "Create a new subwindow" },
653         { CTRL('L'),    "Repaint all windows, doing current one last" },
654         { CTRL('N'),    "Cursor to next window" },
655         { CTRL('P'),    "Cursor to previous window" },
656     };
657     /* *INDENT-ON* */
658
659     char **msgs = typeCalloc(char *, SIZEOF(help) + 1);
660     size_t n;
661
662     for (n = 0; n < SIZEOF(help); ++n) {
663         msgs[n] = typeMalloc(char, 21 + strlen(help[n].msg));
664         sprintf(msgs[n], "%-20s%s", keyname(help[n].key), help[n].msg);
665     }
666     popup_msg2(current, msgs);
667     for (n = 0; n < SIZEOF(help); ++n) {
668         free(msgs[n]);
669     }
670     free(msgs);
671 }
672
673 int
674 main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
675 {
676     WINDOW *current_win;
677     int ch;
678     bool done = FALSE;
679
680     initscr();
681     cbreak();
682     noecho();
683     nonl();
684     intrflush(stdscr, FALSE);
685
686     add_window(0, current_win = stdscr);
687
688 #ifdef NCURSES_MOUSE_VERSION
689     (void) mousemask(BUTTON1_CLICKED, (mmask_t *) NULL);
690 #endif /* NCURSES_MOUSE_VERSION */
691
692     while (!done && (ch = wgetch(current_win)) != ERR) {
693         int y, x;
694
695         getyx(current_win, y, x);
696
697         switch (ch) {
698         case HELP_KEY_1:
699             show_help(current_win);
700             break;
701         case 'b':
702             box_inside(current_win);
703             break;
704         case 'c':
705             current_win = create_my_window(current_win);
706             break;
707         case 'd':
708             current_win = create_my_derwin(current_win);
709             break;
710         case 'D':
711             if (!move_derwin(current_win)) {
712                 tail_line("error");
713                 continue;
714             }
715             break;
716         case 'f':
717             fill_window(current_win, (chtype) wgetch(current_win));
718             break;
719         case 'F':
720             fill_with_pattern(current_win);
721             break;
722         case 'm':
723         case 'M':
724             if (!move_window(current_win, (ch == 'M'))) {
725                 tail_line("error");
726                 continue;
727             }
728             break;
729         case 'q':
730             done = TRUE;
731             break;
732         case 's':
733             current_win = create_my_subwin(current_win);
734             break;
735         case CTRL('L'):
736             refresh_all(current_win);
737             break;
738         case CTRL('N'):
739             current_win = next_window(current_win);
740             break;
741         case CTRL('P'):
742             current_win = prev_window(current_win);
743             break;
744 #if 0
745             /* want to allow cursor to move around the current window too */
746             /* want to test the resizing of windows and subwindows too */
747             /* want to allow deleting a window also */
748 #endif
749         default:
750             wmove(current_win, y, x);
751             tail_line("unrecognized key (use '?' for help)");
752             beep();
753             continue;
754         }
755         tail_line("size [%d,%d] begin [%d,%d] parent [%d,%d]",
756                   getmaxy(current_win),
757                   getmaxx(current_win),
758                   getbegy(current_win),
759                   getbegx(current_win),
760                   getpary(current_win),
761                   getparx(current_win));
762         wmove(current_win, 0, 0);
763     }
764     endwin();
765 #if NO_LEAKS
766     free(all_windows);
767 #endif
768     ExitProgram(EXIT_SUCCESS);
769 }