home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2957 < prev    next >
Encoding:
Internet Message Format  |  1991-03-04  |  46.1 KB

  1. From: lee@sq.sq.com (Liam R. E. Quin)
  2. Newsgroups: alt.sources
  3. Subject: lq-text Full Text Retrieval Database Part 09/13
  4. Message-ID: <1991Mar4.020855.16769@sq.sq.com>
  5. Date: 4 Mar 91 02:08:55 GMT
  6.  
  7. : cut here --- cut here --
  8. : To unbundle, sh this file
  9. #! /bin/sh
  10. : part 09
  11. echo x - lq-text/src/menu/menu.c 1>&2
  12. sed 's/^X//' >lq-text/src/menu/menu.c <<'@@@End of lq-text/src/menu/menu.c'
  13. X/* menu.c -- curses based windowing interface
  14. X * Liam Quin, July 1989
  15. X *
  16. X * $Id: menu.c,v 1.3 90/10/13 03:06:36 lee Rel1-10 $
  17. X *
  18. X * $Log:    menu.c,v $
  19. X * Revision 1.3  90/10/13  03:06:36  lee
  20. X * After Sabre.
  21. X * 
  22. X * Revision 1.2  90/10/04  16:28:19  lee
  23. X * SysV compat improved.
  24. X * 
  25. X * Revision 1.1  90/08/29  21:50:42  lee
  26. X * Initial revision
  27. X * 
  28. X * Revision 2.1  89/08/07  13:50:09  lee
  29. X * First fully working (V.3.2 only) release;
  30. X * this is the baseline for future development.
  31. X * 
  32. X * Revision 1.4  89/08/04  17:58:31  lee
  33. X * Fully working with Basic Functionality.
  34. X * Scrolling menubar, scrolling menus, moveable Info windows.
  35. X * 
  36. X * Revision 1.3  89/08/02  17:12:21  lee
  37. X * gernally working.
  38. X * You can scroll up/down in menus.
  39. X * 
  40. X * Revision 1.2  89/07/28  18:20:17  lee
  41. X * Pull-down menus now scroll if the items don't all fit on the screen.
  42. X * The info-boxes pop up nearer to the highlighted item.
  43. X * Help on menu-bar items.
  44. X * In infoshow(), arrow keys return, so you can move from one item to
  45. X * another and have the info boxes displayed foe each.
  46. X * 
  47. X * Revision 1.1  89/07/27  11:38:23  lee
  48. X * Initial revision
  49. X * 
  50. X * Revision 1.1  89/07/24  18:34:43  lee
  51. X * Initial revision
  52. X * 
  53. X *
  54. X */
  55. X
  56. X#ifndef lint
  57. Xstatic char rcsid[] = "@(#) $Id: menu.c,v 1.3 90/10/13 03:06:36 lee Rel1-10 $";
  58. X#endif
  59. X
  60. X#ifdef ultrix
  61. X# include <cursesX.h>
  62. X#else
  63. X# include <curses.h>
  64. X#endif
  65. X#include <malloc.h>
  66. X
  67. X#ifndef A_STANDOUT
  68. X# include "oldcurses.h"
  69. X#endif
  70. X
  71. X#include "menu.h"
  72. X#include "internal.h"
  73. X#include "error.h"
  74. X
  75. Xvoid SetStringBoxSize();
  76. X
  77. X/* Some Shorthand */
  78. X#define BL (MENUTOP - 1)
  79. X/* VSO is Vertical Scroll Offset... */
  80. X#define VSO (Menu->TopLineOnScreen)
  81. X
  82. X
  83. X/* Appearance of menus:
  84. X * Each menu is boxed with the terminal line drawing characters.  It will
  85. X * use '|' if you don't have VLINE; ACS_SSBS looks like the upper-case T
  86. X * that will join the HLINE of the Menu Line to the menu box itself.
  87. X * See /usr/include/curses.h for an explanation of these names.  On pre-
  88. X * System V, curses often uses + - and | to draw lines, which is ugly but
  89. X * doeas at least work.  It might be possible to define variables with
  90. X * the appropriate names (ACS_VLINE, etc), and to put appropriate things
  91. X * into termcap.  Uniplex used to do this, for example.
  92. X *
  93. X * If not compiled with -DSAVEMENULINE, to save a line on the screen,
  94. X * the first line of each menu over-writes the Menu line, so you get:
  95. X * File  Edit  Method  Position  Adults  Children  Animals
  96. X * -------------------------------------+ Daniel  +-----------------------
  97. X *                    | Simon   |
  98. X *                    | Lorella |
  99. X *                    | Carol   |
  100. X *                    +---------+
  101. X * Otherwise, you get this:
  102. X * File  Edit  Method  Position  Adults  Children  Animals
  103. X * -------------------------------------+---------+-----------------------
  104. X *                    | Daniel  |
  105. X *                    | Simon   |
  106. X *                    | Lorella |
  107. X *                    | Carol   |
  108. X *                    +---------+
  109. X * which might look neater, but uses an extra line on the screen.
  110. X * It saves the menu line on each redraw, hence SAVEMENULINE.
  111. X *
  112. X * Most of this is wired into the Show* routines -- ShowMenuBar() and
  113. X * ShowMenu().
  114. X */
  115. X
  116. Xstatic int DisplayedMenu = 0;
  117. Xstatic int NextMenuId = 1;
  118. X
  119. Xt_Menu *
  120. XNewMenu(Name)
  121. X    char *Name;
  122. X{
  123. X    static int MCount = 0;
  124. X
  125. X    t_Menu *M = (t_Menu *) malloc(sizeof(t_Menu));
  126. X
  127. X    if (!M) {
  128. X    error(ERR_FATAL, "Not enough memory for new menu");
  129. X    }
  130. X    M->MenuStyle = 0; /* default */
  131. X    M->TopLineOnScreen = 0;
  132. X    M->Width = -1; /*i.e. not calculated yet */
  133. X    M->NameLength = strlen(Name);
  134. X    M->Name = malloc(M->NameLength + 1);
  135. X    if (!M->Name) {
  136. X    error(ERR_FATAL, "Not enough memory for new menu name");
  137. X    }
  138. X    (void) strcpy(M->Name, Name);
  139. X    M->SelectedLine = 0;
  140. X    M->MenuId = MCount++;
  141. X
  142. X    return M;
  143. X}
  144. X
  145. XShowMenuBar(MenuBar)
  146. X    t_MenuBar *MenuBar;
  147. X{
  148. X    /* draw a menu bar... */
  149. X    int barpos = 0;
  150. X    int i;
  151. X    int SelectedLength;
  152. X    int EndOfMenu = 0;
  153. X
  154. X    /* Clear the top two lines. */
  155. X    (void) wmove(stdscr, 0, 0);
  156. X    (void) wclrtoeol(stdscr);
  157. X    (void) wmove(stdscr, 1, 0);
  158. X    (void) wclrtoeol(stdscr);
  159. X    /* draw the menu line */
  160. X    for (i = 0; i < COLS - 1; i++) {
  161. X    (void) mvwaddch(stdscr, 1, i, (chtype) ACS_HLINE);
  162. X    }
  163. X
  164. X    /* On really slow terminals it might be worth doing a refresh()
  165. X     * at this point, so the clear can happen at the same time as we
  166. X     * are working out the size of the menu bar, etc.
  167. X     */
  168. X
  169. X#define HSO (MenuBar->ScrollOffset) /* Horiz. scroll offset */
  170. X
  171. X    /** Horizontal scrolling of long menus... **/
  172. X
  173. X    /* Check for sanity! */
  174. X    if (MenuBar->ScrollOffset < 0) {
  175. X    MenuBar->ScrollOffset = 0;
  176. X    }
  177. X
  178. X    /* Find where to start displaying...
  179. X     * keep adding the menu widths until the selected menu is on screen
  180. X     */
  181. X
  182. X    /* First, work out the widths.  It might be worth pre-computing
  183. X     * these strlen()s....
  184. X     */
  185. X    for (i = 0; i < MenuBar->HowManyMenus; i++) {
  186. X    barpos = (EndOfMenu += 2);
  187. X    MenuBar->Menus[i]->PositionInBar = barpos;
  188. X    if (MenuBar->Menus[i]->NameLength <= 0) {
  189. X        MenuBar->Menus[i]->NameLength = strlen(MenuBar->Menus[i]->Name);
  190. X    }
  191. X    EndOfMenu += MenuBar->Menus[i]->NameLength;
  192. X    }
  193. X    /* Now, we can work out how to get SelectedMenu on screen! */
  194. X    /* First, check we're in range: */
  195. X    if (MenuBar->SelectedMenu == 0) {
  196. X    MenuBar->ScrollOffset = 0;
  197. X    }
  198. X    if (MenuBar->ScrollOffset > EndOfMenu) {
  199. X    MenuBar->ScrollOffset = EndOfMenu;
  200. X    }
  201. X    /* Now, check that SelectedMenu and scrolloffset both fit on the screen
  202. X     */
  203. X    barpos = MenuBar->Menus[MenuBar->SelectedMenu]->PositionInBar;
  204. X    if (MenuBar->ScrollOffset > barpos) {
  205. X    MenuBar->ScrollOffset = barpos - 2;
  206. X    }
  207. X    /* 2      9           20         28 ........... barpos = 28ish
  208. X     * File |<Implement   Severity  *Duration*  Location   |
  209. X     *|<--->|ScrollOffset ............. MenuBar->ScrollOffset = 5ish
  210. X     */
  211. X    /* The +6 is
  212. X     * 2 for the extreme left
  213. X     * 2 for the extreme righht
  214. X     * 2 spare, so you can see the menu
  215. X     */
  216. X    SelectedLength = MenuBar->Menus[MenuBar->SelectedMenu]->NameLength;
  217. X
  218. X    if (barpos - MenuBar->ScrollOffset >= COLS - SelectedLength) {
  219. X    /* it's off the right-hand edge of the screen.  The +2
  220. X     * is 1 for a space at the end of the name, leaving room for
  221. X     * a '>' if the bar continues, and one for a space to leave room
  222. X     * for the right edge of the menu bar if it gets pulled down. */
  223. X    MenuBar->ScrollOffset = barpos - (COLS - SelectedLength - 2);
  224. X    }
  225. X    /* Horizontal scrolling of long menus... */
  226. X    if (MenuBar->ScrollOffset < 0) {
  227. X    MenuBar->ScrollOffset = 0;
  228. X    }
  229. X
  230. X    /* Now the menu bar itself */
  231. X
  232. X    EndOfMenu = barpos = 0;
  233. X
  234. X    for (i = 0; i < MenuBar->HowManyMenus; i++) {
  235. X    register char *q;
  236. X    
  237. X    /* leave 2 spaces before each menu so the box lines up
  238. X     * when it's selected.
  239. X     */
  240. X    EndOfMenu += 2;
  241. X
  242. X    /* Draw the spaces -- EndOfMenu represents the end of the
  243. X     * menubar, in characters, ignoring HSO --- so, it can be
  244. X     * bigger than COLS.
  245. X     */
  246. X    while (barpos < EndOfMenu) {
  247. X        if (barpos >= HSO && barpos < HSO + COLS) {
  248. X        (void) mvwaddch(stdscr, 0, barpos - HSO, (chtype) ' ');
  249. X        }
  250. X        ++barpos;
  251. X    }
  252. X
  253. X    /* This is really for ShowMenu() */
  254. X    MenuBar->Menus[i]->PositionInBar = barpos - 2 - HSO;
  255. X
  256. X
  257. X    if (i == MenuBar->SelectedMenu) {
  258. X        (void) attrset(A_STANDOUT);
  259. X    }
  260. X
  261. X    for (q = MenuBar->Menus[i]->Name; *q; q++) {
  262. X        /* Only write characters onto the screen in the range
  263. X         * HSO ... HSO + COLS
  264. X         */
  265. X        if (barpos >= HSO && barpos < HSO + COLS) {
  266. X        (void) mvwaddch(stdscr,  0, barpos - HSO, (chtype) *q);
  267. X        }
  268. X        barpos++;
  269. X    }
  270. X
  271. X    if (i == MenuBar->SelectedMenu) {
  272. X        (void) attrset(0);
  273. X    }
  274. X
  275. X    if (barpos > COLS + HSO) {
  276. X        break; /* we can give up now! */
  277. X    }
  278. X
  279. X    EndOfMenu = barpos;
  280. X    }
  281. X
  282. X    if (barpos == 0) {
  283. X    error(ERR_FATAL, "UseMenuBar: The Menus must have names");
  284. X    }
  285. X
  286. X    if (MenuBar->ScrollOffset > 0) {
  287. X    (void) mvwaddch(stdscr, 0, 0, ACS_LARROW); /* left arrow */
  288. X    }
  289. X
  290. X    if (i < MenuBar->HowManyMenus) {
  291. X    /* not all the menuse were shown */
  292. X    (void) mvwaddch(stdscr, 0, COLS - 1, ACS_RARROW); /* right arrow */
  293. X    }
  294. X}
  295. X
  296. Xint
  297. XUseMenuBar(MenuBar)
  298. X    t_MenuBar *MenuBar;
  299. X{
  300. X    int SelectedChar;
  301. X    extern int MenuUsed;
  302. X    int InfoBoxOnScreen = 0;
  303. X
  304. X    MenuBar->ScrollOffset = 0;
  305. X    MenuBar->SelectedMenu = 0;
  306. X
  307. X    do {
  308. X    int mval;
  309. X    int InMenu;
  310. X
  311. X    InMenu = 0;
  312. X
  313. X    ShowMenuBar(MenuBar);
  314. X    (void) refresh();
  315. X    SelectedChar = getch();
  316. X
  317. X    switch (SelectedChar) {
  318. X    case 'q':
  319. X    case 'f':
  320. X    case 'Q':
  321. X    case 'F':
  322. X        return -2;
  323. Xinfo_key:
  324. X    case KEY_HELP:
  325. X    case '?':
  326. X    case 'x': /* "eXplain" */
  327. X    case 'X': /* "eXplain" */
  328. X    case 'i': /* "info" */
  329. X    case 'I': /* "Info" */
  330. X        {
  331. X        char *p = MenuBar->Menus[MenuBar->SelectedMenu]->Description;
  332. X        int tlx = MenuBar->Menus[MenuBar->SelectedMenu]->PositionInBar;
  333. X        tlx += 4; /* a little to the right.. */
  334. X        if (!p) {
  335. X            p = "No information available about this menu.";
  336. X        }
  337. X        SelectedChar = ShowInfo(p, (WINDOW *) 0, tlx, 2);
  338. X        (void) touchwin(stdscr);
  339. X        (void) wnoutrefresh(stdscr);
  340. X        InfoBoxOnScreen = (SelectedChar != ' ');
  341. X        if (SelectedChar == 0 || SelectedChar == ' ') {
  342. X            continue;
  343. X        }
  344. X        break;
  345. X        }
  346. X    }
  347. X    /* The character was changed if ShowInfo was called... 
  348. X     * The next switch() deals with selecting a menu, and also
  349. X     * canonicalises the input for the subsequent switch().
  350. X     */
  351. X    switch (SelectedChar) {
  352. X    case ' ':
  353. X        /* ShowInfo() returns ' 'to disable the info box. */
  354. X        if (InfoBoxOnScreen) {
  355. X        InfoBoxOnScreen = 0;
  356. X        break;
  357. X        }
  358. X    case '\r':
  359. X    case '\n':
  360. X    case 'j':
  361. X    case KEY_DOWN:
  362. Xfire_menu:
  363. X        if (InfoBoxOnScreen) {
  364. X        InfoBoxOnScreen = 0;
  365. X        }
  366. X        InMenu = 1;
  367. X        mval = UseMenu(MenuBar->Menus[MenuBar->SelectedMenu]);
  368. X        /* clear(); */
  369. X        if (mval > MenuBar->Menus[MenuBar->SelectedMenu]->HowManyItems ||
  370. X        mval < 0) {
  371. X        SelectedChar = mval;
  372. X        if (mval == -2) {
  373. X            SelectedChar = '\004';
  374. X            continue;
  375. X        }
  376. X        } else {
  377. X        /* If we're supposed to print the final selection on
  378. X         * exit, we need to know which menu was selected ---
  379. X         * see the end of the example main().
  380. X         */
  381. X        MenuUsed = MenuBar->SelectedMenu;
  382. X
  383. X        if (mval >= 0 && MenuBar->Menus[MenuUsed]->Items[mval].Function != 0) {
  384. X            return (* MenuBar->Menus[MenuUsed]->Items[mval].Function)(MenuBar->Menus[MenuUsed], mval);
  385. X        } else {
  386. X            /* No function to call */
  387. X            return mval;
  388. X        }
  389. X        }
  390. X        break;
  391. X    case 'h':
  392. X        SelectedChar = KEY_LEFT;
  393. X        break;
  394. X    case 'l': /* lower case ell */
  395. X        SelectedChar = KEY_RIGHT;
  396. X        break;
  397. X    }
  398. X    /* The submenu might have been left with KEY_LEFT (say), so
  399. X     * we check again here.
  400. X     */
  401. X    switch (SelectedChar) {
  402. X    case KEY_HOME:
  403. X        MenuBar->ScrollOffset = 0;
  404. X        MenuBar->SelectedMenu = 0;
  405. X        break;
  406. X    case '$':
  407. X        MenuBar->SelectedMenu = MenuBar->HowManyMenus - 1;
  408. X        break;
  409. X    case KEY_LEFT:
  410. X        if (MenuBar->SelectedMenu > 0) {
  411. X        (MenuBar->SelectedMenu)--;
  412. X        ShowMenuBar(MenuBar);
  413. X        } else {
  414. X        (void) beep();
  415. X        }
  416. X        break;
  417. X    case KEY_RIGHT:
  418. X        if (MenuBar->SelectedMenu + 1 < MenuBar->HowManyMenus) {
  419. X        MenuBar->SelectedMenu++;
  420. X        ShowMenuBar(MenuBar);
  421. X        } else {
  422. X        (void) beep();
  423. X        }
  424. X        break;
  425. X    case 'R' ^ 64: /* control-R */
  426. X        (void) clearok(stdscr, TRUE);
  427. X        (void) refresh();
  428. X        break;
  429. X    case ' ':
  430. X    case '\004': /* used internally */
  431. X        break;
  432. X    default:
  433. X        (void) beep();
  434. X    }
  435. X    if (InfoBoxOnScreen) {
  436. X        goto info_key; /* sorry */
  437. X    }
  438. X    if (InMenu) {
  439. X        /* clear(); */
  440. X        ShowMenuBar(MenuBar);
  441. X        (void) refresh();
  442. X        goto fire_menu;
  443. X    }
  444. X    } while (SelectedChar != EOF && SelectedChar != 'q');
  445. X    /*NOTREACHED*/
  446. X    error(ERR_FATAL|ERR_INTERNAL, "/*NOTREACHED*/ in UseMenuBar()");
  447. X    /*NOTREACHED*/
  448. X    return -1;
  449. X}
  450. X
  451. X/* UseMenu() -- display a menu, allow the user to select from it,
  452. X * then undisplay it and redraw the screen
  453. X */
  454. Xint
  455. XUseMenu(Menu)
  456. X    t_Menu *Menu;
  457. X{
  458. X    WINDOW *MenuWin;
  459. X    int tlx, bly; /* top left x/bot. right y of menu box */
  460. X    static int InfoBoxOnScreen = 0;
  461. X
  462. X    if (InfoBoxOnScreen) {
  463. X    InfoBoxOnScreen++; /* force the box to be redrawn */
  464. X    }
  465. X    /* Ensure that the menu has a MenuId --
  466. X     * This is the only module which knows about MenuId...
  467. X     */
  468. X    if (Menu->MenuId <= 0) { /* a new menu */
  469. X    Menu->MenuId = NextMenuId++;
  470. X    }
  471. X    /* If the menu is not currently displayed */
  472. X    if (DisplayedMenu <= 0) {
  473. X    DisplayedMenu = -1;
  474. X    }
  475. X    if (Menu->SelectedLine < 0) {
  476. X    Menu->SelectedLine = 0;
  477. X    Menu->TopLineOnScreen = 0;
  478. X    }
  479. X    tlx = Menu->PositionInBar;
  480. X    if ((bly = Menu->HowManyItems + 2) > LINES - 2) {
  481. X    /* -2 -- one for the menu bar, one because top line is 0 */
  482. X    bly = LINES - 2;
  483. X    }
  484. X
  485. X    /* Ensure that the width has been calculated */
  486. X    if (Menu->Width <= 0) {
  487. X    (void) FindMenuWidth(Menu);
  488. X    }
  489. X
  490. X    /* We leave 1 space either side, + 1 for the box */
  491. X    if ((MenuWin = newwin(bly, Menu->Width + 4, 1, tlx)) == (WINDOW *) 0) {
  492. X    (void) beep();
  493. X    error(ERR_FATAL|ERR_INTERNAL, "No more memory");
  494. X    }
  495. X
  496. X    /* spl6(); */
  497. X    DisplayedMenu = Menu->MenuId;
  498. X    /* spl0(); */
  499. X
  500. X    for (;;) {
  501. X    int ch;
  502. X
  503. X    ShowMenu(Menu, MenuWin);
  504. X    (void) wrefresh(MenuWin);
  505. X
  506. X    ch = (InfoBoxOnScreen > 1) ? KEY_HELP : getch();
  507. X
  508. X    if (InfoBoxOnScreen > 1) InfoBoxOnScreen = 1;
  509. X
  510. X    switch (ch) {
  511. X        char *p;
  512. X
  513. X    case 'q':
  514. X    case 'Q': /* return immediately */
  515. X    case 'f':
  516. X    case 'F': /* Finish */
  517. X        InfoBoxOnScreen = 0;
  518. X        (void) delwin(MenuWin);
  519. X        (void) touchwin(stdscr);
  520. X        return -2;
  521. X    case '?': /* "huhn?" */
  522. X    case 'x': /* "eXplain" */
  523. X    case 'X': /* "eXplain" */
  524. X    case 'i': /* "info" */
  525. X    case 'I': /* "info" */
  526. X    case KEY_HELP:
  527. Xkey_info:
  528. X        p = Menu->Items[Menu->SelectedLine].Description;
  529. X
  530. X        if (!p) {
  531. X        p = "No information available for this selection.";
  532. X        }
  533. X
  534. X        ch = ShowInfo(p, MenuWin, tlx + 4, Menu->SelectedLine + 3 - VSO);
  535. X
  536. X        (void) touchwin(stdscr);
  537. X        (void) wnoutrefresh(stdscr);
  538. X        (void) touchwin(MenuWin);
  539. X        (void) wrefresh(MenuWin);
  540. X        /* ShowInfo returns SPACE if the user wanted to get
  541. X         * rid of the box; otherwise, it returns the appropriate
  542. X         * key.
  543. X         * The box went away anyway, but we might put another
  544. X         * one there.
  545. X         */
  546. X        InfoBoxOnScreen = (ch != ' ');
  547. X
  548. X        if (ch == 0 || ch == ' ') continue;
  549. X        break;
  550. X    }
  551. X
  552. X    /* So either we are looking at the original character,
  553. X     * or the user pressed the Info key...
  554. X     */
  555. X
  556. X    switch (ch) {
  557. X    case ' ':
  558. X    case '\r':
  559. X    case '\n':
  560. X        (void) delwin(MenuWin);
  561. X        (void) touchwin(stdscr);
  562. X        return Menu->SelectedLine;
  563. X    case KEY_HOME:
  564. X        if (Menu->SelectedLine > 0) {
  565. X        Menu->SelectedLine = 0;
  566. X        if (InfoBoxOnScreen) {
  567. X            ShowMenu(Menu, MenuWin);
  568. X            (void) wrefresh(MenuWin);
  569. X            goto key_info; /* sorry */
  570. X        }
  571. X        } else {
  572. X        (void) delwin(MenuWin);
  573. X        (void) touchwin(stdscr);
  574. X        return KEY_HOME;
  575. X        }
  576. X    case 'k':
  577. X    case KEY_UP:
  578. X        if (Menu->SelectedLine > 0) {
  579. X        Menu->SelectedLine--;
  580. X        } else {
  581. X        InfoBoxOnScreen = 0;
  582. X        (void) delwin(MenuWin);
  583. X        (void) touchwin(stdscr);
  584. X        return -2;
  585. X        }
  586. X        if (InfoBoxOnScreen) {
  587. X        ShowMenu(Menu, MenuWin);
  588. X        (void) wrefresh(MenuWin);
  589. X        goto key_info; /* sorry */
  590. X        }
  591. X        break;
  592. X    case 'j':
  593. X    case KEY_DOWN:
  594. X        if (Menu->SelectedLine + 1 < Menu->HowManyItems) {
  595. X        Menu->SelectedLine++;
  596. X        if (Menu->SelectedLine - Menu->TopLineOnScreen >= COLS - 4) {
  597. X            Menu->TopLineOnScreen++;
  598. X        }
  599. X        } else {
  600. X        (void) beep();
  601. X        }
  602. X        if (InfoBoxOnScreen) {
  603. X        ShowMenu(Menu, MenuWin);
  604. X        (void) wrefresh(MenuWin);
  605. X        goto key_info; /* sorry */
  606. X        }
  607. X        break;
  608. X    case '$': /* last item */
  609. X        Menu->SelectedLine = Menu->HowManyItems - 1;
  610. X        break;
  611. X    case 'h':
  612. X    case KEY_LEFT:
  613. X        (void) delwin(MenuWin);
  614. X        (void) touchwin(stdscr);
  615. X        return KEY_LEFT;
  616. X    case 'l':
  617. X    case KEY_RIGHT:
  618. X        (void) delwin(MenuWin);
  619. X        (void) touchwin(stdscr);
  620. X        return KEY_RIGHT;
  621. X    default:
  622. X        (void) beep();
  623. X        break;
  624. X    }
  625. X    }
  626. X}
  627. X
  628. X/* FindMenuWidth() returns the length of the longest item in the menu.
  629. X */
  630. Xint
  631. XFindMenuWidth(Menu)
  632. X    t_Menu *Menu;
  633. X{
  634. X    int line;
  635. X    if (Menu->Width <= 0) {
  636. X    /* Include the width of the name of the menu.  I am not sure
  637. X     * whether this is right, but it looks a bit odd if the menu
  638. X     * box is narrower than the highlighted menu name on the Bar...
  639. X     */
  640. X    if (Menu->NameLength <= 0) Menu->NameLength = strlen(Menu->Name);
  641. X    Menu->Width = Menu->NameLength;
  642. X
  643. X    /* Now see if any of the items are wider... */
  644. X    for (line = 0; line < Menu->HowManyItems; line++) {
  645. X        /* Individual length entries... */
  646. X        if (Menu->Items[line].NameLength == 0) {
  647. X        Menu->Items[line].NameLength = strlen(Menu->Items[line].Name);
  648. X        }
  649. X
  650. X        /* Overall widest... */
  651. X        if (Menu->Items[line].NameLength > Menu->Width) {
  652. X        Menu->Width = Menu->Items[line].NameLength;
  653. X        }
  654. X    }
  655. X    }
  656. X    return Menu->Width;
  657. X}
  658. X
  659. XShowMenu(Menu, MenuWin)
  660. X    t_Menu *Menu;
  661. X    WINDOW *MenuWin;
  662. X{
  663. X    int line;
  664. X    int pos = 0;
  665. X    register int i;
  666. X
  667. X    /* It is possible to compile this code so that the menu bar-line is
  668. X     * broken by the top item in a selected menu.  See the comments at
  669. X     * the start of this file, and in menu.h
  670. X     */
  671. X#ifdef SAVEMENULINE
  672. X    /* draw the box by hand, because the top line is a little special...
  673. X     * SVR3V2 has a line drawing function mvwhline(), but it is very
  674. X     * V.3.2 specific.  Sigh.
  675. X     */
  676. X    for (i = 1; i <= Menu->Width + 2; i++) {
  677. X    (void) mvwaddch(MenuWin, 0, i, ACS_HLINE);
  678. X    }
  679. X
  680. X    /* NOTE:
  681. X     * the ACS_trbl characters are PC/vt100-style line characters,
  682. X     * where the t,r,b and l can either be B for Blank or S for single;
  683. X     * later versions of curses may also have D for double, etc.;
  684. X     * hence a character with single lines joining left and top would
  685. X     * be ACS_SBBS. with single lines.  If you have double lines, it
  686. X     * might be good to use them for the menu bar, so as to give
  687. X     *  Tools  Pain *Where* When  Whom
  688. X     * ============+=======+=================
  689. X     *           | Hands |
  690. X     *           | Feet  |
  691. X     *           | etc.  |
  692. X     *           +-------+
  693. X     */
  694. X
  695. X    /* T-pieces to join the menuline to the verticals of the menu */
  696. X    (void) mvwaddch(MenuWin, 0, 0, ACS_BSSS);
  697. X    /* The +3 in the next call to mvwaddch() comes from
  698. X     * +2 for the box and space on the left
  699. X     * +2 for the box and space on the right
  700. X     * -1 because things start at 0
  701. X     */
  702. X    (void) mvwaddch(MenuWin, 0, Menu->Width + 3, ACS_BSSS);
  703. X#else
  704. X    /* top line gets overwritten... just add corners! */
  705. X    (void) mvwaddch(MenuWin, 0, 0, ACS_BBSS);
  706. X    /* See above for the +3 */
  707. X    (void) mvwaddch(MenuWin, 0, Menu->Width + 3, ACS_BSSB);
  708. X#endif /*SAVEMENULINE*/
  709. X
  710. X    if (Menu->TopLineOnScreen > Menu->HowManyItems) {
  711. X    Menu->TopLineOnScreen = Menu->HowManyItems - (LINES - 3);
  712. X    (void) touchwin(stdscr);
  713. X    (void) wnoutrefresh(stdscr);
  714. X    }
  715. X
  716. X    if (Menu->SelectedLine < Menu->TopLineOnScreen) {
  717. X    Menu->TopLineOnScreen = Menu->SelectedLine;
  718. X    } else if (Menu->SelectedLine - Menu->TopLineOnScreen > LINES - 4) {
  719. X    Menu->TopLineOnScreen = Menu->SelectedLine - (LINES - 4);
  720. X    }
  721. X
  722. X    /* Final sanity check */
  723. X    if (Menu->TopLineOnScreen < 0) {
  724. X    Menu->TopLineOnScreen = 0;
  725. X    }
  726. X
  727. X    /* Now the lines of the menu, one at a time */
  728. X    for (line = 0; line < LINES; line++) {
  729. X    char *p;
  730. X    int w;
  731. X
  732. X    if (line + VSO >= Menu->HowManyItems) break;
  733. X
  734. X    p = ((Menu->Items)[line + VSO]).Name;
  735. X
  736. X    /* A neat horizontal line on the left */
  737. X    if (line+BL > 0) {
  738. X        (void) mvwaddch(MenuWin, line+BL, 0, ACS_VLINE);
  739. X    }
  740. X    (void) mvwaddch(MenuWin, line+BL, 1, (chtype) ' ');
  741. X
  742. X    /* line is {1..N}, Selected is {0..N-1}.  This simplifies
  743. X     * its use elsewhere, at the expense of making this routine
  744. X     * a little more complex.
  745. X     */
  746. X    if (line + VSO == Menu->SelectedLine) {
  747. X        (void) wattrset(MenuWin, A_STANDOUT);
  748. X    }
  749. X
  750. X    /* On terminals without standout mode, one could use stars:
  751. X     *   Tools  Pain *Where* When  Whom
  752. X     * =============+=======+=================
  753. X     *        | Hands |
  754. X     *        |*Feet**|
  755. X     *        | etc.  |
  756. X     *        +-------+
  757. X     * I wonder how I can tell if standout mode is working?
  758. X     */
  759. X
  760. X    /* Add the actual menu item */
  761. X    (void) mvwaddstr(MenuWin, line+BL, 2, p);
  762. X
  763. X    /* check how long it was */
  764. X    w = (Menu->Items)[line + VSO].NameLength;
  765. X
  766. X    /* Add spaces where to pad it out
  767. X     * These might be inverse video spaces.  Of course, not all
  768. X     * terminals make stand-out spaces different from
  769. X     * ordinary ones.  On really dumb ttys, could use '*' here
  770. X     * instead of ' '.
  771. X     */
  772. X
  773. X    for (i = w; i <= Menu->Width; i++) {
  774. X        (void) mvwaddch(MenuWin, line+BL, i + 2, (chtype)' ');
  775. X    }
  776. X
  777. X    /* Turn bold/inverse off */
  778. X    if (line + VSO == Menu->SelectedLine) {
  779. X        (void) wattrset(MenuWin, 0);
  780. X    }
  781. X
  782. X    /* The extra space is really defense against magic cookies.
  783. X     * See terminfo/termcap manuals...
  784. X     */
  785. X    (void) mvwaddch(MenuWin, line+BL, i + 1, (chtype) ' ');
  786. X
  787. X    /* And a neat vertical line on the right. */
  788. X    if (line+BL > 0) {
  789. X        (void) mvwaddch(MenuWin, line+BL, i + 2, (chtype) ACS_VLINE);
  790. X    }
  791. X    }
  792. X
  793. X    /* draw a line along the bottom only if it all fitted */
  794. X    if (Menu->HowManyItems - Menu->TopLineOnScreen <= LINES - 2) {
  795. X    for (pos = i + 1; pos > 0; pos--) {
  796. X        (void) mvwaddch(MenuWin, line+BL, pos, (chtype) ACS_HLINE);
  797. X    }
  798. X    /* Lower left and lower right corner pieces join up neatly;
  799. X     * Curses uses a '+' sign if it or the tty is too old...
  800. X     * But the vt100-style character looks much better.
  801. X     */
  802. X    (void) mvwaddch(MenuWin, line+BL, 0, ACS_LLCORNER);
  803. X    (void) mvwaddch(MenuWin, line+BL, i + 2, ACS_LRCORNER);
  804. X    }
  805. X}
  806. X
  807. Xint
  808. XShowInfo(String, MenuWin, Itlx, Itly)
  809. X    char *String;
  810. X    WINDOW *MenuWin;
  811. X    int Itlx;
  812. X    int Itly;
  813. X    /* Itlx and Itly control the place at which the pop-up window
  814. X     * pops up... the top left corner of the new window is at
  815. X     * (Itlx, Itly).  By default, this is as near to the selected
  816. X     * item as possible.
  817. X     * So, down a bit and to the right a little.
  818. X     */
  819. X{
  820. X    int RetVal = 0;
  821. X    int ch;
  822. X    t_StringBox *StringBox, *MakeStringBox();
  823. X
  824. X    /* If there is no information given, say so! */
  825. X    if (String == (char *) 0) {
  826. X    String = "No more information";
  827. X    }
  828. X
  829. X    /* Call a function to calculate the derived parameters, create the
  830. X     * window and put the text on it!
  831. X     */
  832. X    StringBox = MakeStringBox("Info", String, 0L);
  833. X    if (Itlx < 0) Itlx = 0;
  834. X    if (Itly < 0) Itly = 0;
  835. X    StringBox->tlx = Itlx;
  836. X    StringBox->tly = Itly;
  837. X
  838. X    /* Try to ensure that as much of the box is on screen as possible;
  839. X     * if the user wants to move it off-screen, that's up to it, but
  840. X     * we can at least ensure that it starts off OK!
  841. X     */
  842. X    if (StringBox->tlx + StringBox->Width >= COLS) {
  843. X    if ((StringBox->tlx = COLS - StringBox->Width) < 0) {
  844. X        StringBox->tlx = 0;
  845. X    }
  846. X    }
  847. X    if (StringBox->tly + StringBox->Height >= LINES - 3) {
  848. X    if ((StringBox->tly = LINES - StringBox->Height) < MENUTOP) {
  849. X        /* MENUTOP is defined in internal.h to be the first row
  850. X         * on the screen below the menu bar.
  851. X         */
  852. X        StringBox->tly = MENUTOP;
  853. X    }
  854. X    }
  855. X
  856. X    do {
  857. X    /* Add the text.  UseBold is set after the user asks for more help.
  858. X     * As there isn't any more, perhaps he/she hasn't noticed the info
  859. X     * box, so we highlight it.
  860. X     * (this feature seems to have been deleted -- Lee)
  861. X     */
  862. X    (void) touchwin(stdscr);
  863. X    (void) wnoutrefresh(stdscr);
  864. X    if (MenuWin) (void) touchwin(MenuWin);
  865. X    if (MenuWin) (void) wnoutrefresh(MenuWin);
  866. X    ShowStringBox(StringBox);
  867. X    (void) touchwin(StringBox->Window); /* now we're on top... */
  868. X    (void) wrefresh(StringBox->Window);
  869. X    ch = getch();
  870. X
  871. Xcase_place:    /* ComeFrom lower down, resize/move/scroll */
  872. X
  873. X    if (ch == '\r' || ch == '\n') ch = ' ';
  874. X
  875. X    switch (ch) {
  876. X    case '?': case 'x': case 'X': /* x for explain */
  877. X    case 'i': case 'I': /* i for info */
  878. X    case KEY_HELP:
  879. X        (void) mvwaddch(StringBox->Window, 0, 0, (chtype) 'X');
  880. X        (void) wnoutrefresh(StringBox->Window);
  881. X        {
  882. X        char *p = 
  883. X"Info window:\n\
  884. XPress SPACE or Q when you are done.\n\
  885. XYou can Resize the window with R,\n\
  886. XScroll the text inside it with S,\n\
  887. Xand Move it with M.\n\
  888. XHOME will first home the text, and\n\
  889. Xthen put the window near the top\n\
  890. Xleft hand corner of the screen.\n\
  891. X\n\
  892. X[press SPACE to continue]";
  893. X        ch = ShowInfo(p, StringBox->Window, StringBox->tly + 2,
  894. X                            StringBox->tlx + 2);
  895. X        if (ch == ' ') ch = 'x';
  896. X        else goto case_place; /* sigh */
  897. X        }
  898. X        break;
  899. X    case 'm': /* Move window */
  900. X    case 'M':
  901. X        ch = MoveStringBox(StringBox, MenuWin);
  902. X        if (ch == ' ') ch = 'x';
  903. X        else goto case_place;
  904. X        break;
  905. X    case 'r':
  906. X    case 'R':
  907. X        /* Resize */
  908. X        ch = ResizeStringBox(StringBox, MenuWin);
  909. X        if (ch == ' ') ch = 'x';
  910. X        else goto case_place;
  911. X        break;
  912. X    case 's':
  913. X    case 'S':
  914. X        /* Scroll */
  915. X        ch = ScrollStringBox(StringBox, MenuWin);
  916. X        if (ch == ' ') ch = 'x';
  917. X        else goto case_place;
  918. X        break;
  919. X    case KEY_LEFT:
  920. X    case 'h':
  921. X        RetVal = KEY_LEFT;
  922. X        ch = ' ';
  923. X        break;
  924. X    case 'H':
  925. X    case KEY_HOME:
  926. X        RetVal = KEY_HOME;
  927. X        ch = ' ';
  928. X        break;
  929. X    case KEY_RIGHT:
  930. X    case 'l':
  931. X        RetVal = KEY_RIGHT;
  932. X        ch = ' ';
  933. X        break;
  934. X    case 'k':
  935. X    case KEY_UP:
  936. X        RetVal = KEY_UP;
  937. X        ch = ' ';
  938. X        break;
  939. X    case 'j':
  940. X    case KEY_DOWN:
  941. X        RetVal = KEY_DOWN;
  942. X        ch = ' ';
  943. X        break;
  944. X    default:
  945. X        (void) beep();
  946. X        break;
  947. X    case 'q':
  948. X    case 'Q': /* quit */
  949. X    case 'f':
  950. X    case 'F': /* finish */
  951. X        ch = ' ';
  952. X    case ' ':
  953. X        RetVal = ' ';
  954. X        break;
  955. X    }
  956. X    } while (ch != EOF && ch != ' ');
  957. X    /* Clean Up */
  958. X    (void) delwin(StringBox->Window);
  959. X    (void) free((char *) StringBox);
  960. X    (void) touchwin(stdscr);
  961. X    return RetVal;
  962. X}
  963. X
  964. @@@End of lq-text/src/menu/menu.c
  965. echo x - lq-text/src/menu/stringbox.c 1>&2
  966. sed 's/^X//' >lq-text/src/menu/stringbox.c <<'@@@End of lq-text/src/menu/stringbox.c'
  967. X/* Functions to deal with StringBoxes...
  968. X *
  969. X * $Header: /usr/src/cmd/lq-text/src/menu/RCS/stringbox.c,v 1.2 90/10/04 16:28:22 lee Rel1-10 $
  970. X *
  971. X * $Log:    stringbox.c,v $
  972. X * Revision 1.2  90/10/04  16:28:22  lee
  973. X * SysV compat improved.
  974. X * 
  975. X * Revision 1.1  90/08/29  21:50:55  lee
  976. X * Initial revision
  977. X * 
  978. X * Revision 2.1  89/08/07  13:50:19  lee
  979. X * First fully working (V.3.2 only) release;
  980. X * this is the baseline for future development.
  981. X * 
  982. X *
  983. X */
  984. X
  985. X#ifdef ultrix
  986. X# include <cursesX.h>
  987. X#else
  988. X# include <curses.h>
  989. X#endif
  990. X
  991. X#ifndef A_STANDOUT
  992. X# include "oldcurses.h"
  993. X#endif
  994. X
  995. X#include <malloc.h>
  996. X#include "menu.h"
  997. X#include "error.h"
  998. X#include "internal.h"
  999. X
  1000. Xvoid SetStringBoxSize();
  1001. X
  1002. X/* SetStringSize() --
  1003. X * Set *Height and *Width to the minimum dimensions which will
  1004. X * hold the given String.
  1005. X * String can contain embedded newlines and tabs.
  1006. X */
  1007. Xvoid
  1008. XSetStringSize(String, Height, Width)
  1009. X    char *String;
  1010. X    short *Height, *Width;
  1011. X{
  1012. X    int ThisLineWidth = 0;
  1013. X    register char *p;
  1014. X
  1015. X    *Height = 0;
  1016. X    *Width = 0;
  1017. X
  1018. X    if (!String || !*String) return;
  1019. X
  1020. X    for (p = String; *p; p++) {
  1021. X    if (*p == '\n') {
  1022. X        if (ThisLineWidth > *Width) {
  1023. X        *Width = ThisLineWidth;
  1024. X        }
  1025. X        ThisLineWidth = 0;
  1026. X        ++*Height;
  1027. X    } else {
  1028. X        ThisLineWidth++; /* Tabs always cause motion */
  1029. X        if (*p == '\t') {
  1030. X        ThisLineWidth |= 7;
  1031. X        }
  1032. X    }
  1033. X    }
  1034. X    if (ThisLineWidth > 0) {
  1035. X    /* No trailing newline, so treat the partial line at the
  1036. X     * end as if it were complete.
  1037. X     */
  1038. X    if (ThisLineWidth > *Width) {
  1039. X        *Width = ThisLineWidth;
  1040. X    }
  1041. X    ++*Height;
  1042. X    }
  1043. X}
  1044. X
  1045. X/* PutStringInBox is a replacement for waddstr() that treats
  1046. X * newlines specially in order to facilitate scrolling.
  1047. X * The string
  1048. X *    "I am a\nhappy boy"
  1049. X * (assuming \n is actually a newline character)
  1050. X * gets put into the window as
  1051. X *    +-----------
  1052. X *    | I am a
  1053. X *    | happy boy
  1054. X * (assuming that the window is large enough).
  1055. X * If HScrollPos were 3, and VScrollPos 1, you would get
  1056. X *    +-----------
  1057. X *    | py boy
  1058. X */
  1059. X
  1060. X/* MakeStringBox() is called to create a StringBox. */
  1061. X
  1062. Xt_StringBox *
  1063. XMakeStringBox(Name, String, Flags)
  1064. X    unsigned long Flags;
  1065. X    char *Name;
  1066. X    char *String;
  1067. X{
  1068. X    t_StringBox *StringBox;
  1069. X
  1070. X    if ((StringBox = new(t_StringBox)) == (t_StringBox *) 0) {
  1071. X    error(ERR_FATAL|ERR_INTERNAL, "Not enough memory to create StringBox");
  1072. X    /*NOTREACHED*/
  1073. X    }
  1074. X    StringBox->Name = Name;
  1075. X    /** StringBox->Flags = Flags;**/
  1076. X    StringBox->Window = (WINDOW *) 0;
  1077. X    StringBox->tlx = StringBox->tly = 0;
  1078. X    StringBox->HScrollPos = StringBox->VScrollPos = 0;
  1079. X    StringBox->String = String;
  1080. X
  1081. X    SetStringBoxSize(StringBox);
  1082. X
  1083. X    return StringBox;
  1084. X}
  1085. X
  1086. X/* Put the string box on the screen */
  1087. XShowStringBox(StringBox)
  1088. X    t_StringBox *StringBox;
  1089. X{
  1090. X    register char *pp;
  1091. X    char *String = StringBox->String;
  1092. X    register int x = 1; /* start at (1, 1) */
  1093. X    int y = 1;
  1094. X    int AtStartOfLine = 1;
  1095. X
  1096. X    if (StringBox->Window == (WINDOW *) 0) {
  1097. X    if ((StringBox->Window =
  1098. X         newwin(StringBox->Height, StringBox->Width,
  1099. X            StringBox->tly, StringBox->tlx)) == (WINDOW *) 0) {
  1100. X        
  1101. X        error(ERR_FATAL|ERR_INTERNAL,
  1102. X        "Not enough memory for StringBox window %dx%d",
  1103. X                        StringBox->Height,
  1104. X                        StringBox->Width);
  1105. X    }
  1106. X    }
  1107. X    /* Now we have a window of the right size in the right place... */
  1108. X
  1109. X    /* If vertical scrolling is in use, check it's in range */
  1110. X    if (StringBox->VScrollPos < 0) {
  1111. X    StringBox->VScrollPos = 0;
  1112. X    } else if (StringBox->VScrollPos >= StringBox->HowManyLines) {
  1113. X    StringBox->VScrollPos = StringBox->HowManyLines - 1;
  1114. X    } 
  1115. X
  1116. X    /* Ignore the first VScrollPos lines: */
  1117. X    String = StringBox->String;
  1118. X    for (y = 0; y < StringBox->VScrollPos; y++) {
  1119. X    extern char *strchr();
  1120. X
  1121. X    String = strchr(String, '\n');
  1122. X    /*CANTHAPPEN*/
  1123. X    if (*++String == '\0') {
  1124. X        /* Not enough lines in the buffer.... */
  1125. X        return;
  1126. X    }
  1127. X    }
  1128. X
  1129. X    y = 1; /* start the text on row 1, after the box */
  1130. X    AtStartOfLine = 1;
  1131. X    for (pp = String; *pp; pp++) {
  1132. X    if (AtStartOfLine) {
  1133. X        int i;
  1134. X
  1135. X        /* skip the initial portion of each line */
  1136. X        for (i = 0; i < StringBox->HScrollPos; i++) {
  1137. X        if (!*pp) {
  1138. X            pp--;
  1139. X            break;
  1140. X        } else if (*pp == '\n') {
  1141. X            break;
  1142. X        }
  1143. X        pp++;
  1144. X        }
  1145. X    }
  1146. X    AtStartOfLine = (*pp == '\n');
  1147. X    if (*pp == '\n') { /* end of line.... */
  1148. X        (void) wmove(StringBox->Window, y, x);
  1149. X        (void) wclrtoeol(StringBox->Window);
  1150. X        y++;
  1151. X        x = 1;
  1152. X        (void) wmove(StringBox->Window, y, x);
  1153. X        (void) wclrtoeol(StringBox->Window);
  1154. X    } else { /* not a newline */
  1155. X        if (*pp == '\t') {
  1156. X        x |= 7;
  1157. X        x++;
  1158. X        /* tabs always move.  Also, we started
  1159. X         * at position 1, so increment after the x |= 7.
  1160. X         */
  1161. X        } else {
  1162. X        (void) mvwaddch(StringBox->Window, y, x, (chtype) *pp);
  1163. X        x++;
  1164. X        }
  1165. X    }
  1166. X    } /* end for */
  1167. X    (void) wclrtoeol(StringBox->Window);
  1168. X    (void) wattrset(StringBox->Window, 0);
  1169. X
  1170. X    /* do the box second, in case the text over-ran */
  1171. X    (void) box(StringBox->Window, 0, 0);
  1172. X    if (StringBox->HScrollPos > 0) {
  1173. X    /* Put a symbol like   | to show that the message can
  1174. X     * be scrolled to    --+ the right by pressing the right-
  1175. X     * arrow or "l".       |
  1176. X     */
  1177. X    (void) mvwaddch(StringBox->Window, 1, 0, ACS_SBSS);
  1178. X    }
  1179. X
  1180. X    if (StringBox->StringWidth - StringBox->HScrollPos > StringBox->Width - 2) {
  1181. X    /* Put a symbol like |   to show that the message can
  1182. X     * be scrolled to    +-- the left by pressing the left-
  1183. X     * arrow or "h".     |   Also, the HOME key will move the
  1184. X     * text to the top left of the InfoWin, and put the window
  1185. X     * near the top left of the screen.
  1186. X     */
  1187. X    /* (the -1 is because numbering starts form zero) */
  1188. X    (void) mvwaddch(StringBox->Window, 1, StringBox->Width - 1, ACS_SSSB);
  1189. X    }
  1190. X
  1191. X    /* If you can go up... */
  1192. X    if (StringBox->VScrollPos > 0) {
  1193. X    /* ACS_trbl */
  1194. X    (void) mvwaddch(StringBox->Window, 0, 1, ACS_SSBS);
  1195. X    }
  1196. X
  1197. X    /* And if you can go down (the -2 is for the box): */
  1198. X    if (StringBox->HowManyLines >
  1199. X            StringBox->VScrollPos + StringBox->Height - 2) {
  1200. X    (void) mvwaddch(StringBox->Window, StringBox->Height - 1, 1, ACS_BSSS);
  1201. X    
  1202. X    }
  1203. X}
  1204. X
  1205. XResizeStringBox(StringBox, MenuWin)
  1206. X    t_StringBox *StringBox;
  1207. X    WINDOW *MenuWin;
  1208. X{
  1209. X    int ch;
  1210. X    int Changed = 0;
  1211. X
  1212. X    do {
  1213. X    if (Changed) {
  1214. X        /* Recreate the window... */
  1215. X        (void) delwin(StringBox->Window);
  1216. X        StringBox->Window = (WINDOW *) 0;
  1217. X        ShowStringBox(StringBox);
  1218. X        (void) touchwin(stdscr);
  1219. X        (void) wnoutrefresh(stdscr);
  1220. X        if (MenuWin) (void) touchwin(MenuWin);
  1221. X        if (MenuWin) (void) wnoutrefresh(MenuWin);
  1222. X        (void) touchwin(StringBox->Window);
  1223. X    }
  1224. X
  1225. X    Changed = 0;
  1226. X
  1227. X    (void) mvwaddch(StringBox->Window, 0, 0, (chtype) 'R');
  1228. X    (void) wmove(StringBox->Window, StringBox->Height, StringBox->Width);
  1229. X    (void) wrefresh(StringBox->Window);
  1230. X
  1231. X    ch = getch();
  1232. X
  1233. X    switch(ch) {
  1234. X    case 'q': case 'Q':
  1235. X    case 'f': case 'F':
  1236. X        ch = ' ';
  1237. X    case ' ':
  1238. X        break;
  1239. X    case '?':
  1240. X    case 'x': case 'X':
  1241. X    case 'i': case 'I':
  1242. X    case KEY_HELP:
  1243. X        {
  1244. X        char *p = 
  1245. X"Resize window: use the arrow keys\n\
  1246. Xto move the window about.  You can\n\
  1247. Xuse HOME, H, or the d key to set\n\
  1248. Xthe window to the default size.\n\
  1249. XPress SPACE when you are done.";
  1250. X        ch = ShowInfo(p, StringBox->Window, StringBox->tly + 2,
  1251. X                            StringBox->tlx + 2);
  1252. X        if (ch == ' ') ch = 'x';
  1253. X        else continue;
  1254. X        }
  1255. X        break;
  1256. X    case KEY_LEFT:
  1257. X    case 'h':
  1258. X        if (StringBox->Width > 3) {
  1259. X        StringBox->Width--;
  1260. X        Changed = 1;
  1261. X        } else {
  1262. X        (void) beep();
  1263. X        }
  1264. X        break;
  1265. X    case KEY_RIGHT:
  1266. X    case 'l':
  1267. X        if (StringBox->Width + StringBox->tlx < COLS - 1) {
  1268. X        StringBox->Width++;
  1269. X        Changed = 1;
  1270. X        } else {
  1271. X        (void) beep();
  1272. X        }
  1273. X        break;
  1274. X    case KEY_DOWN:
  1275. X    case 'j':
  1276. X        if (StringBox->Height + StringBox->tly < LINES - 1) {
  1277. X        StringBox->Height++;
  1278. X        Changed = 1;
  1279. X        } else {
  1280. X        (void) beep();
  1281. X        }
  1282. X        break;
  1283. X    case KEY_UP:
  1284. X    case 'k':
  1285. X        if (StringBox->Height > 3) {
  1286. X        StringBox->Height--;
  1287. X        Changed = 1;
  1288. X        } else {
  1289. X        (void) beep();
  1290. X        }
  1291. X        break;
  1292. X    case KEY_HOME: case 'H':
  1293. X    case 'd': case 'D':
  1294. X        /* special case: restore to default size; may
  1295. X         * also move the box, if it wouldn't fit on the screen in
  1296. X         * the new size.
  1297. X         */
  1298. X        SetStringBoxSize(StringBox);
  1299. X        Changed = 1;
  1300. X        break;
  1301. X    case 's': case'S':
  1302. X    case 'm': case 'M': /* Sideways jump to other window functions... */
  1303. X        return ch;
  1304. X    default:
  1305. X        (void) beep();
  1306. X        break;
  1307. X    }
  1308. X    } while (ch != EOF && ch != ' ');
  1309. X    return ch;
  1310. X}
  1311. X
  1312. XMoveStringBox(StringBox, MenuWin)
  1313. X    t_StringBox *StringBox;
  1314. X    WINDOW *MenuWin;
  1315. X{
  1316. X    int ch;
  1317. X    int Changed = 0;
  1318. X
  1319. X    do {
  1320. X    if (Changed) {
  1321. X        /* Recreate the window... */
  1322. X        (void) delwin(StringBox->Window);
  1323. X        StringBox->Window = (WINDOW *) 0;
  1324. X        (void) touchwin(stdscr);
  1325. X        (void) wnoutrefresh(stdscr);
  1326. X        if (MenuWin) (void) touchwin(MenuWin);
  1327. X        if (MenuWin) (void) wnoutrefresh(MenuWin);
  1328. X        ShowStringBox(StringBox);
  1329. X    }
  1330. X
  1331. X    Changed = 0;
  1332. X
  1333. X    (void) mvwaddch(StringBox->Window, 0, 0, (chtype) 'M');
  1334. X    (void) wmove(StringBox->Window, StringBox->Height, StringBox->Width);
  1335. X    (void) wrefresh(StringBox->Window);
  1336. X
  1337. X    ch = getch();
  1338. X
  1339. X    switch(ch) {
  1340. X    case 'q': case 'Q':
  1341. X    case 'f': case 'F':
  1342. X        ch = ' ';
  1343. X    case ' ':
  1344. X        break;
  1345. X
  1346. X    case '?': case 'x': case 'X': /* x for explain */
  1347. X    case 'i': case 'I': /* i for info */
  1348. X    case KEY_HELP:
  1349. X        (void) mvwaddch(StringBox->Window, 0, 0, (chtype) 'X');
  1350. X        (void) wnoutrefresh(StringBox->Window);
  1351. X        {
  1352. X        char *p = 
  1353. X"Scrolling Text:\n\
  1354. XUse the arrow keys (or h j k and l)\n\
  1355. Xto move the window around.\n\
  1356. XYou can use the HOME key (or H) to\n\
  1357. Xmove it near the top left hand corner\n\
  1358. Xof the screen.\n\
  1359. XWhen you finish moving the window,\n\
  1360. Xor if you want to resize it,\n\
  1361. Xpress SPACE.\n\
  1362. XAfter you have finished, you can\n\
  1363. Xuse R to resize the window,\n\
  1364. Xand S to scroll the text in it.\n\
  1365. X\n\
  1366. X[press SPACE to continue]";
  1367. X        ch = ShowInfo(p, StringBox->Window, StringBox->tly + 2,
  1368. X                            StringBox->tlx + 2);
  1369. X        if (ch == ' ') ch = 'x';
  1370. X        else continue;
  1371. X        }
  1372. X        break;
  1373. X    case 'h':
  1374. X    case KEY_LEFT:
  1375. X        if (StringBox->tlx > 0) {
  1376. X        StringBox->tlx--;
  1377. X        (void) mvwin(StringBox->Window, StringBox->tly, StringBox->tlx);
  1378. X        Changed = 1;
  1379. X        } else {
  1380. X        (void) beep();
  1381. X        }
  1382. X        break;
  1383. X    case 'l':
  1384. X    case KEY_RIGHT:
  1385. X        if (StringBox->tlx + StringBox->Width < COLS) {
  1386. X        StringBox->tlx++;
  1387. X        (void) mvwin(StringBox->Window, StringBox->tly, StringBox->tlx);
  1388. X        Changed = 1;
  1389. X        } else {
  1390. X        (void) beep();
  1391. X        }
  1392. X        break;
  1393. X    case 'k':
  1394. X    case KEY_UP:
  1395. X        if (StringBox->tly > 2) {
  1396. X        --StringBox->tly;
  1397. X        (void) mvwin(StringBox->Window, StringBox->tly, StringBox->tlx);
  1398. X        Changed = 1;
  1399. X        } else {
  1400. X        (void) beep();
  1401. X        }
  1402. X        break;
  1403. X    case 'j':
  1404. X    case KEY_DOWN:
  1405. X        if (StringBox->tly + StringBox->Height < COLS - 1) {
  1406. X        ++StringBox->tly;
  1407. X        (void) mvwin(StringBox->Window, StringBox->tly, StringBox->tlx);
  1408. X        Changed = 1;
  1409. X        } else {
  1410. X        (void) beep();
  1411. X        }
  1412. X        break;
  1413. X    case 'H':
  1414. X    case KEY_HOME:
  1415. X        StringBox->tlx = 0;
  1416. X        StringBox->tly = 2;
  1417. X        (void) mvwin(StringBox->Window, StringBox->tly, StringBox->tlx);
  1418. X        Changed = 1;
  1419. X        break;
  1420. X    case 's': case'S':
  1421. X    case 'r': case 'R': /* Jump to other functions */
  1422. X        return ch;
  1423. X    default:
  1424. X        (void) beep();
  1425. X    }
  1426. X    } while (ch != EOF && ch != ' ');
  1427. X    return ch;
  1428. X}
  1429. X
  1430. XScrollStringBox(StringBox, MenuWin)
  1431. X    t_StringBox *StringBox;
  1432. X    WINDOW *MenuWin;
  1433. X{
  1434. X    int ch;
  1435. X    int Changed = 0;
  1436. X
  1437. X    do {
  1438. X    if (Changed) {
  1439. X        /* Recreate the window... */
  1440. X        (void) delwin(StringBox->Window);
  1441. X        StringBox->Window = (WINDOW *) 0;
  1442. X        (void) touchwin(stdscr);
  1443. X        (void) wnoutrefresh(stdscr);
  1444. X        if (MenuWin) (void) touchwin(MenuWin);
  1445. X        if (MenuWin) (void) wnoutrefresh(MenuWin);
  1446. X        ShowStringBox(StringBox);
  1447. X    }
  1448. X
  1449. X    Changed = 0;
  1450. X
  1451. X    (void) mvwaddch(StringBox->Window, 0, 0, (chtype) 'S');
  1452. X    (void) wmove(StringBox->Window, StringBox->Height, StringBox->Width);
  1453. X    (void) wrefresh(StringBox->Window);
  1454. X
  1455. X    ch = getch();
  1456. X
  1457. X    switch(ch) {
  1458. X    case 'q': case 'Q':
  1459. X    case 'f': case 'F':
  1460. X        ch = ' ';
  1461. X    case ' ':
  1462. X        break;
  1463. X
  1464. X    case '?': case 'x': case 'X': /* x for explain */
  1465. X    case 'i': case 'I': /* i for info */
  1466. X    case KEY_HELP:
  1467. X        (void) mvwaddch(StringBox->Window, 0, 0, (chtype) 'X');
  1468. X        (void) wnoutrefresh(StringBox->Window);
  1469. X        {
  1470. X        char *p = 
  1471. X"Scrolling Text:\n\
  1472. XUse the arrow keys (or h j k and l)\n\
  1473. Xto move the text within the window.\n\
  1474. XYou can use the HOME key (or H) to\n\
  1475. Xmove to the start of the text.\n\
  1476. XWhen you finish scrolling the text,\n\
  1477. Xor if you want to move or resize the\n\
  1478. Xwindow, press SPACE.\n\
  1479. X\n\
  1480. X[press SPACE to continue]";
  1481. X        ch = ShowInfo(p, StringBox->Window, StringBox->tly + 2,
  1482. X                            StringBox->tlx + 2);
  1483. X        if (ch == ' ') ch = 'x';
  1484. X        else continue;
  1485. X        }
  1486. X        break;
  1487. X    case 'h':
  1488. X    case KEY_LEFT:
  1489. X        /* Press |space to continue. |
  1490. X         * <-----------------------> DescLen
  1491. X         *       <-------------------> InfoWidth
  1492. X         * <------> StringBox->HScrollPos
  1493. X         * (Not allowed to go further left than this)
  1494. X         */
  1495. X        if (StringBox->HScrollPos > 0) {
  1496. X        StringBox->HScrollPos--;
  1497. X        Changed = 1;
  1498. X        } else {
  1499. X        (void) beep();
  1500. X        }
  1501. X        break;
  1502. X    case 'l':
  1503. X    case KEY_RIGHT:
  1504. X        if (StringBox->StringWidth - StringBox->HScrollPos >
  1505. X            StringBox->Width - 2) {
  1506. X        StringBox->HScrollPos++;
  1507. X        Changed = 1;
  1508. X        } else {
  1509. X        beep();
  1510. X        }
  1511. X        break;
  1512. X    case 'k':
  1513. X    case KEY_UP:
  1514. X        if (StringBox->VScrollPos) {
  1515. X        StringBox->VScrollPos--;
  1516. X        Changed = 1;
  1517. X        } else {
  1518. X        (void) beep();
  1519. X        }
  1520. X        break;
  1521. X    case 'j':
  1522. X    case KEY_DOWN:
  1523. X        /* forbid the last line of the screen so as to avoid scrolling/
  1524. X         * bottom-right-corner hassle, and also to avoid obscuring
  1525. X         * soft function labels if they're there.
  1526. X         */
  1527. X        if (StringBox->Height - 2 + StringBox->VScrollPos <
  1528. X                        StringBox->HowManyLines) {
  1529. X        StringBox->VScrollPos++;
  1530. X        Changed = 1;
  1531. X        } else {
  1532. X        (void) beep();
  1533. X        }
  1534. X        break;
  1535. X    case 'H':
  1536. X    case KEY_HOME:
  1537. X        /* Not testing for > 0 here means that if things get really
  1538. X         * screwed up and {H,V}ScrollPos gets -ve, HOME will set
  1539. X         * things right again.  Of course, the user won't know
  1540. X         * that, but every little helps...
  1541. X         */
  1542. X        if (StringBox->HScrollPos || StringBox->VScrollPos) {
  1543. X        StringBox->HScrollPos = 0;
  1544. X        StringBox->VScrollPos = 0;
  1545. X        Changed = 1;
  1546. X        }
  1547. X        break;
  1548. X    case 'r': case 'R':
  1549. X    case 'm': case 'M': /* Jump to other functions: */
  1550. X        return ch;
  1551. X    default:
  1552. X        (void) beep();
  1553. X    }
  1554. X    } while (ch != EOF && ch != ' ');
  1555. X    return ch;
  1556. X}
  1557. X
  1558. Xvoid
  1559. XSetStringBoxSize(StringBox)
  1560. X    t_StringBox *StringBox;
  1561. X{
  1562. X    /* Measure the text */
  1563. X    SetStringSize(StringBox->String, &(StringBox->Height), &(StringBox->Width));
  1564. X    StringBox->HowManyLines = StringBox->Height;
  1565. X    StringBox->StringWidth = StringBox->Width;
  1566. X
  1567. X    /* Leave room for a box */
  1568. X    StringBox->Height += 2;
  1569. X    StringBox->Width += 2;
  1570. X
  1571. X    /* Check it's not too wide */
  1572. X    if (StringBox->Width >= COLS) {
  1573. X    StringBox->Width = COLS - 4;
  1574. X    }
  1575. X
  1576. X    /* Check it's not too tall */
  1577. X    if (StringBox->Height >= LINES - 4) {
  1578. X    StringBox->Height = LINES - 4;
  1579. X    }
  1580. X
  1581. X    /* check it's not too thin */
  1582. X    if (StringBox->Width < 3) { /* tiny screen, or no text at all... */
  1583. X    StringBox->Width = 3;
  1584. X    }
  1585. X
  1586. X    /* Check it's not too short */
  1587. X    if (StringBox->Height < 3) { /* tiny screen, or no text at all... */
  1588. X    StringBox->Height = 3;
  1589. X    }
  1590. X
  1591. X    /* Check it's on the screen */
  1592. X    if (StringBox->tlx + StringBox->Width >= COLS) {
  1593. X    /* right adjust if it didn't fit */
  1594. X    StringBox->tlx = (COLS - StringBox->Width) - 1;
  1595. X    /* ASSERT: StringBox->tlx > 0 because StringBox->Width < COLS */
  1596. X    }
  1597. X    if (StringBox->tly + StringBox->Height >= LINES - 3) {
  1598. X    /* avoid the bottom line of the screen */
  1599. X    StringBox->tly = (LINES - 3) - StringBox->Height;
  1600. X    }
  1601. X}
  1602. X
  1603. Xchar *
  1604. XAskForString(Message, MaxLen, MenuWin, Itlx, Itly)
  1605. X    char *Message;
  1606. X    int MaxLen;
  1607. X    WINDOW *MenuWin;
  1608. X    int Itlx;
  1609. X    int Itly;
  1610. X{
  1611. X    t_StringBox *Question;
  1612. X    char *Answer;
  1613. X    char *p;
  1614. X    char *Text;
  1615. X    int i;
  1616. X    int ch;
  1617. X
  1618. X    /* ensure that we allow at least 1 char of input! */
  1619. X    if (MaxLen < 1) MaxLen = 1;
  1620. X
  1621. X    if ((Text = malloc(strlen(Message) + MaxLen + 5)) == (char *) 0) {
  1622. X    error(ERR_FATAL|ERR_MEMORY, "Not enough mem for question \"%s\"\n",
  1623. X                    Message);
  1624. X    }
  1625. X
  1626. X    (void) sprintf(Text, "%s\n : ", Message);
  1627. X    p = Answer = &Text[strlen(Text)]; /* i.e. pointing to the \0 */
  1628. X
  1629. X    /* Make a StringBox containing the question and a space for
  1630. X     * the answer
  1631. X     */
  1632. X    Question = MakeStringBox("Question", Text, (unsigned long) 0);
  1633. X
  1634. X    /* add space for the answer... */
  1635. X    if (Question->Width < 5) Question->Width = 5;
  1636. X
  1637. X    if (Itlx < 0) Itlx = 0;
  1638. X    if (Itly < 3) Itly = 3; /* avoid the menu bar */
  1639. X    
  1640. X    Question->tlx = Itlx;
  1641. X    Question->tly = Itly;
  1642. X    /* Itlx and Itly control the place at which the pop-up window
  1643. X     * pops up... the top left corner of the new window is at
  1644. X     * (Itlx, Itly).  By default, this is as near to the selected
  1645. X     * item as possible.
  1646. X     * So, down a bit and to the right a little.
  1647. X     */
  1648. X
  1649. X    /* display the StringBox and wait for an answer.  If necessary,
  1650. X     * extend and then scroll the StringBox to make the answer fit
  1651. X     */
  1652. X
  1653. X    do {
  1654. X    for (i = 0;
  1655. X        i - Question->HScrollPos < Question->Width - 2 && i < MaxLen;
  1656. X        i++) {
  1657. X        if (&Answer[i] >= p) {
  1658. X        Answer[i] = '_';
  1659. X        Answer[i + 1] = '\0';
  1660. X        }
  1661. X    }
  1662. X
  1663. X    (void) touchwin(stdscr);
  1664. X    (void) wnoutrefresh(stdscr);
  1665. X    if (MenuWin) {
  1666. X        (void) touchwin(MenuWin);
  1667. X        (void) wnoutrefresh(MenuWin);
  1668. X    }
  1669. X    /* SetStringBoxSize(Question); /* in case the text has changed */
  1670. X
  1671. X    ShowStringBox(Question);
  1672. X
  1673. X    (void) touchwin(Question->Window); /* now we're on top... */
  1674. X
  1675. X    /* the +7 is for the bar on the left of the window, the " : ",
  1676. X     * and an extra one to put the cursor where the *next* character
  1677. X     * will be, plus an unexplained fudge factor.
  1678. X     */
  1679. X    (void) move(Question->tly + Question->Height - 2,
  1680. X        Question->tlx + (p - Answer) - Question->HScrollPos + 4);
  1681. X    (void) wrefresh(Question->Window);
  1682. X    (void) move(Question->tly + Question->Height - 2,
  1683. X        Question->tlx + (p - Answer) - Question->HScrollPos + 4);
  1684. X    ch = getch();
  1685. X
  1686. X    if (ch == KEY_F(1) || ch == '\r' || ch == '\n') break;
  1687. X
  1688. X    /* Note: p always points to the point at which the next character
  1689. X     * to be typed would go, and *p is \0.
  1690. X     */
  1691. X
  1692. X    switch (ch) {
  1693. X    case KEY_UP:
  1694. X    case KEY_LEFT:
  1695. X    case KEY_DOWN:
  1696. X    case KEY_RIGHT:
  1697. X        (void) MoveStringBox(Question, MenuWin);
  1698. X        break;
  1699. X    case KEY_HOME:
  1700. X        /* Resize */
  1701. X        (void) ResizeStringBox(Question, MenuWin);
  1702. X        break;
  1703. X    case '\b':
  1704. X    case 127:
  1705. X    case 255:
  1706. X    case -1:
  1707. X        if (p > Answer) {
  1708. X        *p = '\0';
  1709. X        --p;
  1710. X        if (Question->HScrollPos > 0) --Question->HScrollPos;
  1711. X        } else {
  1712. X        beep();
  1713. X        }
  1714. X        break;
  1715. X    case 'W' ^ 64: /* copntrol-W -- delete word */
  1716. X        *p = '_';
  1717. X        while (p > Answer && (*p == ' ' || *p == '/')) {
  1718. X        p--;
  1719. X        if (Question->HScrollPos > 0) --Question->HScrollPos;
  1720. X        }
  1721. X        while (p > Answer && *p != ' ' && *p != '/') {
  1722. X        p--;
  1723. X        if (Question->HScrollPos > 0) --Question->HScrollPos;
  1724. X        }
  1725. X        if (*p == '/' || *p == ' ') p++;
  1726. X        *p = '\0';
  1727. X        break;
  1728. X    case 'U' ^ 64:
  1729. X    case 'X' ^ 64:
  1730. X        p = Answer;
  1731. X        *p = '\0';
  1732. X        Question->HScrollPos = 0;
  1733. X        break;
  1734. X    default:
  1735. X        if (p - Answer >= MaxLen) {
  1736. X        beep();
  1737. X        } else {
  1738. X        *p++ = ch;
  1739. X        /* The magic number on the next line works as follows:
  1740. X         * | : here is me typing stuff__ |
  1741. X         * ^^^^                ^^
  1742. X         * So there are 6 characters, plus one for the cursor;
  1743. X         * (p - Answer) gives one fewer than needed, so we arrive
  1744. X         * at 8. 
  1745. X         */
  1746. X        if (p - Answer + 7 - Question->HScrollPos >= Question->Width) {
  1747. X            Question->HScrollPos += 4;
  1748. X        }
  1749. X        }
  1750. X    }
  1751. X    } while (ch != EOF && ch != KEY_F(1));
  1752. X    /* Clean Up */
  1753. X    (void) delwin(Question->Window);
  1754. X    (void) touchwin(stdscr);
  1755. X
  1756. X    if (p > Answer) {
  1757. X    *p = '\0';
  1758. X    --p;
  1759. X    }
  1760. X
  1761. X    if (p == Answer) {
  1762. X    (void) free(Text);
  1763. X    return (char *) 0;
  1764. X    } else {
  1765. X    unsigned len = strlen(Answer);
  1766. X    (void) bcopy(Answer, Text, len + 1);
  1767. X    Text[len] = '\0';
  1768. X    return realloc(Text, len + 1);
  1769. X    }
  1770. X    /*NOTREACHED*/
  1771. X}
  1772. @@@End of lq-text/src/menu/stringbox.c
  1773. echo end of part 09
  1774. -- 
  1775. Liam R. E. Quin,  lee@sq.com, SoftQuad Inc., Toronto, +1 (416) 963-8337
  1776.