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