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.22 2008/04/12 22:01:41 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/* 37derwin 38mvderwin 39subwin 40mvwin 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 63typedef struct { 64 int y, x; 65} PAIR; 66 67typedef struct { 68 WINDOW *parent; /* need this since WINDOW->_parent is not portable */ 69 WINDOW *child; /* the actual value */ 70} FRAME; 71 72static void head_line(CONST_FMT char *fmt,...) GCC_PRINTFLIKE(1, 2); 73static void tail_line(CONST_FMT char *fmt,...) GCC_PRINTFLIKE(1, 2); 74 75static unsigned num_windows; 76static FRAME *all_windows; 77 78static void 79message(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 101static void 102head_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 111static void 112tail_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 */ 124static PAIR * 125selectcell(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 */ 186static bool 187getwindow(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 */ 218static void 219box_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 */ 246static void 247add_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 = typeRealloc(FRAME, need, all_windows); 255 } 256 all_windows[num_windows].parent = parent; 257 all_windows[num_windows].child = child; 258 num_windows++; 259} 260 261static int 262window2num(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 275static WINDOW * 276parent_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 285static void 286repaint_one(WINDOW *win) 287{ 288 touchwin(win); 289 wnoutrefresh(win); 290} 291 292static void 293refresh_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 307static WINDOW * 308next_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 321static WINDOW * 322prev_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 337static void 338recur_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 */ 356static bool 357move_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 */ 400static bool 401move_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 433static void 434fill_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 456static WINDOW * 457create_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 474static WINDOW * 475create_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 492static WINDOW * 493create_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 514static void 515show_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 554int 555main(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} 640