ncurses 5.9 - patch 20130209
[ncurses.git] / test / movewindow.c
1 /****************************************************************************
2  * Copyright (c) 2006-2010,2012 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.38 2012/12/15 18:36:40 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
50 #ifdef HAVE_XCURSES
51 #undef derwin
52 #endif
53
54 #ifdef NCURSES_VERSION
55 #define CONST_FMT const
56 #else
57 #define CONST_FMT               /* nothing */
58 #endif
59
60 #undef LINE_MAX
61
62 #define LINE_MIN        2
63 #define LINE_MAX        (LINES - 2)
64 #define COL_MIN         2
65 #define COL_MAX         (COLS - 2)
66
67 typedef struct {
68     int y, x;
69 } PAIR;
70
71 typedef struct {
72     WINDOW *parent;             /* need this since WINDOW->_parent is not portable */
73     WINDOW *child;              /* the actual value */
74 } FRAME;
75
76 static void head_line(CONST_FMT char *fmt,...) GCC_PRINTFLIKE(1, 2);
77 static void tail_line(CONST_FMT char *fmt,...) GCC_PRINTFLIKE(1, 2);
78
79 static unsigned num_windows;
80 static FRAME *all_windows;
81
82 static void
83 failed(const char *s)
84 {
85     perror(s);
86     endwin();
87     ExitProgram(EXIT_FAILURE);
88 }
89
90 static void
91 message(int lineno, CONST_FMT char *fmt, va_list argp)
92 {
93     int y, x;
94
95     getyx(stdscr, y, x);
96     move(lineno, 0);
97     clrtoeol();
98
99 #ifdef HAVE_XCURSES
100     {
101         char buffer[1024];
102         vsprintf(buffer, fmt, argp);
103         addstr(buffer);
104     }
105 #else
106     vwprintw(stdscr, fmt, argp);
107 #endif
108
109     move(y, x);
110     refresh();
111 }
112
113 static void
114 head_line(CONST_FMT char *fmt,...)
115 {
116     va_list argp;
117
118     va_start(argp, fmt);
119     message(0, fmt, argp);
120     va_end(argp);
121 }
122
123 static void
124 tail_line(CONST_FMT char *fmt,...)
125 {
126     va_list argp;
127
128     va_start(argp, fmt);
129     message(LINES - 1, fmt, argp);
130     va_end(argp);
131 }
132
133 /*
134  * Arrow keys move cursor, return location at current on non-arrow key.
135  */
136 static PAIR *
137 selectcell(WINDOW *parent,
138            WINDOW *child,
139            int uli, int ulj,
140            int lri, int lrj,
141            bool relative,
142            bool * more)
143 {
144     static PAIR res;            /* result cell */
145     int si = lri - uli + 1;     /* depth of the select area */
146     int sj = lrj - ulj + 1;     /* width of the select area */
147     int i = 0, j = 0;           /* offsets into the select area */
148
149     res.y = uli;
150     res.x = ulj;
151
152     if (child != 0) {
153         if (relative) {
154             getparyx(child, i, j);
155         } else {
156             getbegyx(child, i, j);
157             i -= uli + getbegy(parent);
158             j -= ulj + getbegx(parent);
159         }
160     }
161
162     if (more)
163         *more = FALSE;
164
165     for (;;) {
166         bool moved = FALSE;
167
168         tail_line("Upper left [%2d,%2d] Lower right [%2d,%2d] -> %d,%d -> %d,%d",
169                   uli, ulj,
170                   lri, lrj,
171                   i, j,
172                   uli + i, ulj + j);
173         wmove(parent, uli + i, ulj + j);
174
175         switch (wgetch(parent)) {
176         case KEY_UP:
177             i += si - 1;
178             moved = TRUE;
179             break;
180         case KEY_DOWN:
181             i++;
182             moved = TRUE;
183             break;
184         case KEY_LEFT:
185             j += sj - 1;
186             moved = TRUE;
187             break;
188         case KEY_RIGHT:
189             j++;
190             moved = TRUE;
191             break;
192         case QUIT:
193         case ESCAPE:
194             return ((PAIR *) 0);
195 #ifdef NCURSES_MOUSE_VERSION
196         case KEY_MOUSE:
197             {
198                 MEVENT event;
199
200                 getmouse(&event);
201                 if (event.y > uli && event.x > ulj) {
202                     if (parent != stdscr) {
203                         i = event.y - getbegy(parent) - uli;
204                         j = event.x - getbegx(parent) - ulj;
205                     } else {
206                         i = event.y - uli;
207                         j = event.x - ulj;
208                     }
209                 } else {
210                     beep();
211                     break;
212                 }
213             }
214             /* FALLTHRU */
215 #endif
216         default:
217             res.y = uli + i;
218             res.x = ulj + j;
219             return (&res);
220         }
221
222         if (si <= 0)
223             i = 0;
224         else
225             i %= si;
226
227         if (sj <= 0)
228             j = 0;
229         else
230             j %= sj;
231
232         /*
233          * If the caller can handle continuous movement, return the result.
234          */
235         if (moved && more) {
236             *more = TRUE;
237             res.y = uli + i;
238             res.x = ulj + j;
239             return (&res);
240         }
241     }
242 }
243
244 /*
245  * Ask user for a window definition.
246  */
247 static bool
248 getwindow(WINDOW *parent, PAIR * ul, PAIR * lr)
249 {
250     int min_col = (parent == stdscr) ? COL_MIN : 0;
251     int max_col = (parent == stdscr) ? COL_MAX : getmaxx(parent);
252     int min_line = (parent == stdscr) ? LINE_MIN : 0;
253     int max_line = (parent == stdscr) ? LINE_MAX : getmaxy(parent);
254     PAIR *tmp;
255     bool result = FALSE;
256
257     head_line("Use arrows to move cursor, anything else to mark corner 1");
258     if ((tmp = selectcell(parent, 0,
259                           min_line, min_col,
260                           max_line, max_col,
261                           FALSE,
262                           (bool *) 0)) != 0) {
263         *ul = *tmp;
264         MvWAddCh(parent, ul->y, ul->x, '*');
265
266         head_line("Use arrows to move cursor, anything else to mark corner 2");
267         if ((tmp = selectcell(parent, 0,
268                               ul->y, ul->x,
269                               max_line, max_col,
270                               FALSE,
271                               (bool *) 0)) != 0) {
272             *lr = *tmp;
273             MvWAddCh(parent, lr->y, lr->x, '*');
274             wmove(parent, lr->y, lr->x);
275             wsyncdown(parent);
276             wrefresh(parent);
277             result = (lr->y != ul->y && lr->x != ul->x);
278         }
279     }
280     head_line("done");
281     return result;
282 }
283
284 /*
285  * Draw a box inside the given window.
286  */
287 static void
288 box_inside(WINDOW *win)
289 {
290     int y0, x0;
291     int y1, x1;
292
293     getyx(win, y0, x0);
294     getmaxyx(win, y1, x1);
295
296     MvWHLine(win, 0, 0, ACS_HLINE, x1);
297     MvWHLine(win, y1 - 1, 0, ACS_HLINE, x1);
298
299     MvWVLine(win, 0, 0, ACS_VLINE, y1);
300     MvWVLine(win, 0, x1 - 1, ACS_VLINE, y1);
301
302     MvWAddCh(win, 0, 0, ACS_ULCORNER);
303     MvWAddCh(win, y1 - 1, 0, ACS_LLCORNER);
304     MvWAddCh(win, 0, x1 - 1, ACS_URCORNER);
305     MvWAddCh(win, y1 - 1, x1 - 1, ACS_LRCORNER);
306
307     wsyncdown(win);
308     wmove(win, y0, x0);
309     wrefresh(win);
310 }
311
312 /*
313  * Add a window to our list.
314  */
315 static void
316 add_window(WINDOW *parent, WINDOW *child)
317 {
318     static unsigned have = 0;
319     unsigned need = ((num_windows + 1) | 31) + 1;
320
321     keypad(child, TRUE);
322     if (need > have) {
323         all_windows = typeRealloc(FRAME, need, all_windows);
324         if (!all_windows)
325             failed("add_window");
326     }
327     all_windows[num_windows].parent = parent;
328     all_windows[num_windows].child = child;
329     num_windows++;
330 }
331
332 static int
333 window2num(WINDOW *win)
334 {
335     int n;
336     int result = -1;
337     for (n = 0; n < (int) num_windows; ++n) {
338         if (win == all_windows[n].child) {
339             result = n;
340             break;
341         }
342     }
343     return result;
344 }
345
346 static WINDOW *
347 parent_of(WINDOW *win)
348 {
349     WINDOW *result = 0;
350     int n = window2num(win);
351     if (n >= 0)
352         result = all_windows[n].parent;
353     return result;
354 }
355
356 static void
357 repaint_one(WINDOW *win)
358 {
359     touchwin(win);
360     wnoutrefresh(win);
361 }
362
363 static void
364 refresh_all(WINDOW *win)
365 {
366     unsigned n;
367
368     for (n = 0; n < num_windows; ++n) {
369         if (all_windows[n].child != win) {
370             repaint_one(all_windows[n].child);
371         }
372     }
373
374     repaint_one(win);
375     doupdate();
376 }
377
378 static WINDOW *
379 next_window(WINDOW *win)
380 {
381     WINDOW *result = win;
382     int n = window2num(win);
383
384     if (n++ >= 0) {
385         result = all_windows[(unsigned) n % num_windows].child;
386         wmove(result, 0, 0);
387         wrefresh(result);
388     }
389     return result;
390 }
391
392 static WINDOW *
393 prev_window(WINDOW *win)
394 {
395     WINDOW *result = win;
396     int n = window2num(win);
397
398     if (n-- >= 0) {
399         if (n < 0)
400             n = (int) (num_windows - 1);
401         result = all_windows[(unsigned) n % num_windows].child;
402         wmove(result, 0, 0);
403         wrefresh(result);
404     }
405     return result;
406 }
407
408 static void
409 recur_move_window(WINDOW *parent, int dy, int dx)
410 {
411     unsigned n;
412
413     for (n = 0; n < num_windows; ++n) {
414         if (all_windows[n].parent == parent) {
415             mvwin(all_windows[n].child, dy, dx);
416             recur_move_window(all_windows[n].child, dy, dx);
417         }
418     }
419 }
420
421 /*
422  * test mvwin().
423  */
424 static bool
425 move_window(WINDOW *win, bool recur)
426 {
427     WINDOW *parent = parent_of(win);
428     bool result = FALSE;
429
430     if (parent != 0) {
431         bool top = (parent == stdscr);
432         int min_col = top ? COL_MIN : 0;
433         int max_col = top ? COL_MAX : getmaxx(parent);
434         int min_line = top ? LINE_MIN : 0;
435         int max_line = top ? LINE_MAX : getmaxy(parent);
436         PAIR *tmp;
437         bool more;
438
439         head_line("Select new position for %swindow", top ? "" : "sub");
440
441         while ((tmp = selectcell(parent,
442                                  win,
443                                  min_line, min_col,
444                                  max_line, max_col,
445                                  FALSE,
446                                  &more)) != 0) {
447             int y0, x0;
448             getbegyx(parent, y0, x0);
449             /*
450              * Moving a subwindow has the effect of moving a viewport around
451              * the screen.  The parent window retains the contents of the
452              * subwindow in the original location, but the viewport will show
453              * the contents (again) at the new location.  So it will look odd
454              * when testing.
455              */
456             if (mvwin(win, y0 + tmp->y, x0 + tmp->x) != ERR) {
457                 if (recur) {
458                     recur_move_window(win, tmp->y, tmp->x);
459                 }
460                 refresh_all(win);
461                 doupdate();
462                 result = TRUE;
463             } else {
464                 result = FALSE;
465             }
466             if (!more)
467                 break;
468         }
469     }
470     head_line("done");
471     return result;
472 }
473
474 static void
475 show_derwin(WINDOW *win)
476 {
477     int pary, parx, maxy, maxx;
478
479     getmaxyx(win, maxy, maxx);
480     getparyx(win, pary, parx);
481
482     head_line("Select new position for derived window at %d,%d (%d,%d)",
483               pary, parx, maxy, maxx);
484 }
485
486 /*
487  * test mvderwin().
488  */
489 static bool
490 move_derwin(WINDOW *win)
491 {
492     WINDOW *parent = parent_of(win);
493     bool result = FALSE;
494
495     if (parent != 0) {
496         bool top = (parent == stdscr);
497         if (!top) {
498             int min_col = top ? COL_MIN : 0;
499             int max_col = top ? COL_MAX : getmaxx(parent);
500             int min_line = top ? LINE_MIN : 0;
501             int max_line = top ? LINE_MAX : getmaxy(parent);
502             PAIR *tmp;
503             bool more;
504
505             show_derwin(win);
506             while ((tmp = selectcell(parent,
507                                      win,
508                                      min_line, min_col,
509                                      max_line, max_col,
510                                      TRUE,
511                                      &more)) != 0) {
512                 if (mvderwin(win, tmp->y, tmp->x) != ERR) {
513                     refresh_all(win);
514                     doupdate();
515                     repaint_one(win);
516                     doupdate();
517                     result = TRUE;
518                     show_derwin(win);
519                 } else {
520                     flash();
521                 }
522                 if (!more)
523                     break;
524             }
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         { '?',          "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     WINDOW *mywin = newwin(LINES, COLS, 0, 0);
660     int row;
661
662     for (row = 0; row < LINES - 2 && row < (int) SIZEOF(help); ++row) {
663         wmove(mywin, row + 1, 1);
664         wprintw(mywin, "%s", keyname(help[row].key));
665         wmove(mywin, row + 1, 20);
666         wprintw(mywin, "%s", help[row].msg);
667     }
668     box_inside(mywin);
669     wmove(mywin, 1, 1);
670     wgetch(mywin);
671     delwin(mywin);
672     refresh_all(current);
673 }
674
675 int
676 main(int argc GCC_UNUSED, char *argv[]GCC_UNUSED)
677 {
678     WINDOW *current_win;
679     int ch;
680     bool done = FALSE;
681
682     initscr();
683     cbreak();
684     noecho();
685     nonl();
686     intrflush(stdscr, FALSE);
687
688     add_window(0, current_win = stdscr);
689
690 #ifdef NCURSES_MOUSE_VERSION
691     (void) mousemask(BUTTON1_CLICKED, (mmask_t *) NULL);
692 #endif /* NCURSES_MOUSE_VERSION */
693
694     while (!done && (ch = wgetch(current_win)) != ERR) {
695         int y, x;
696
697         getyx(current_win, y, x);
698
699         switch (ch) {
700         case '?':
701             show_help(current_win);
702             break;
703         case 'b':
704             box_inside(current_win);
705             break;
706         case 'c':
707             current_win = create_my_window(current_win);
708             break;
709         case 'd':
710             current_win = create_my_derwin(current_win);
711             break;
712         case 'D':
713             if (!move_derwin(current_win)) {
714                 tail_line("error");
715                 continue;
716             }
717             break;
718         case 'f':
719             fill_window(current_win, (chtype) wgetch(current_win));
720             break;
721         case 'F':
722             fill_with_pattern(current_win);
723             break;
724         case 'm':
725         case 'M':
726             if (!move_window(current_win, (ch == 'M'))) {
727                 tail_line("error");
728                 continue;
729             }
730             break;
731         case 'q':
732             done = TRUE;
733             break;
734         case 's':
735             current_win = create_my_subwin(current_win);
736             break;
737         case CTRL('L'):
738             refresh_all(current_win);
739             break;
740         case CTRL('N'):
741             current_win = next_window(current_win);
742             break;
743         case CTRL('P'):
744             current_win = prev_window(current_win);
745             break;
746 #if 0
747             /* want to allow cursor to move around the current window too */
748             /* want to test the resizing of windows and subwindows too */
749             /* want to allow deleting a window also */
750 #endif
751         default:
752             wmove(current_win, y, x);
753             tail_line("unrecognized key (use '?' for help)");
754             beep();
755             continue;
756         }
757         tail_line("size [%d,%d] begin [%d,%d] parent [%d,%d]",
758                   getmaxy(current_win),
759                   getmaxx(current_win),
760                   getbegy(current_win),
761                   getbegx(current_win),
762                   getpary(current_win),
763                   getparx(current_win));
764         wmove(current_win, 0, 0);
765     }
766     endwin();
767     ExitProgram(EXIT_SUCCESS);
768 }