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