V_call.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. /**
  2. * \file V_call.c
  3. *
  4. * \brief Interactive call functions.
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or (at
  9. * your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19. *
  20. * \note Modified by Jacques Bouchard and Markus Neteler 6/99 to make
  21. * cursor keys working. Exit now with ESC-CR.
  22. *
  23. * \author GRASS GIS Development Team
  24. *
  25. * \date 2007-11-01
  26. */
  27. /*
  28. ALGORITHM:
  29. | Zero out screen answer locations
  30. | Initial curses screens
  31. | Display text, constants, and answer fields
  32. | Write text (results from V_line() calls) to curses window
  33. | Write constants (results from V_const() calls) to curses window
  34. | Write answers (results from V_ques() calls) to curses window
  35. | Take commands from the keyboard
  36. | switch on commands:
  37. | case CR: case NL: case UP: case ESC:
  38. | switch on answer type
  39. | case string
  40. | remove trailing non-alphanumeric characters
  41. | copy answer to target denoted in V_ques() call
  42. | blank out screen line
  43. | copy target to curses window
  44. | case integer
  45. | run atoi on answer, putting results in target
  46. | denoted in V_ques() call
  47. | blank out screen line
  48. | printf target to curses window
  49. | case long
  50. | run atol on answer, putting results in target
  51. | denoted in V_ques() call
  52. | blank out screen line
  53. | printf target to curses window
  54. | case float, double
  55. | run sscanf on answer, putting results in target
  56. | denoted in V_ques() call
  57. | blank out screen line
  58. | printf target to curses window
  59. | default:
  60. | do nothing
  61. | if ESC+CR return from V_call()
  62. | if UP shift to previous question
  63. | if CR or NL shift to next question
  64. | case BS: Move cursor back one column in current question
  65. | case FS: Move cursor forward one column in current
  66. | question
  67. | case RPLT: Replot the current screen image
  68. | case DUMP: Dump (append) the current window to the user's
  69. | home dir.
  70. | default: If an alphanumeric, put that char on the screen
  71. | and in the current answer field
  72. | call V_exit (erase screen and exit curses)
  73. ********************************************************************** */
  74. #include <grass/config.h>
  75. #include <stdio.h>
  76. #include <stdlib.h>
  77. #include <string.h>
  78. #include <grass/vask.h>
  79. static void centered(const char *);
  80. static void fmt(char *, int, double);
  81. /* define the V__ struct defined in vask.h */
  82. struct V__ V__;
  83. #define DUMP 001
  84. #define BS 010
  85. #define FS 014
  86. #define NL 012
  87. #define UP 013
  88. #define CR 015
  89. #define RPLT 022
  90. #define ESC 033
  91. #define CTRLC 003
  92. #define TARGET V__.usr_answ[at_answer].targetptr
  93. #define ROW V__.usr_answ[at_answer].row
  94. #define COL V__.usr_answ[at_answer].col
  95. #define LENGTH V__.usr_answ[at_answer].length
  96. #define TYPE V__.usr_answ[at_answer].var_type
  97. #define ANSWER scr_answ[at_answer].position
  98. #define RELINE do { \
  99. move(ROW, COL) ; \
  100. for (incr2=0;incr2<LENGTH; incr2++) \
  101. addch('_') ; \
  102. move(ROW, COL) ; \
  103. } while (0)
  104. /* flag ctrl-c is to be allowed */
  105. static int interrupts_ok = 0; /* mod shapiro */
  106. /**
  107. * \fn int V_call (void)
  108. *
  109. * \brief Interact with the user.
  110. *
  111. * Interactively allow the user to enter answers into all available
  112. * fields (as previously defined). Answer fields have been created with
  113. * calls to <i>V_ques()</i>. Information fields have been created using
  114. * <i>V_const()</i>. General text has been created with calls to
  115. * <i>V_line()</i>.
  116. *
  117. * <i>V_call()</i> clears the screen and writes the text and data values
  118. * specified by <i>V_line()</i>, <i>V_ques()</i> and <i>V_const()</i> to
  119. * the screen. It interfaces with the user, collecting user responses in
  120. * the <i>V_ques()</i> fields until the user is satisfied. A message is
  121. * automatically supplied on line number 23, explaining to the user to
  122. * enter an ESC when all inputs have been supplied as desired.
  123. * <i>V_call()</i> ends when the user hits ESC and returns a value of 1
  124. * (but see <i>V_intrpt_ok()</i> below). No error checking is done by
  125. * <i>V_call()</i>. Instead, all variables used in <i>V_ques()</i>
  126. * calls must be checked upon return from <i>V_call()</i>. If the user
  127. * has supplied inappropriate information, the user can be informed, and
  128. * the input prompted for again by further calls to <i>V_call()</i>.
  129. *
  130. * \return 1 user entered ESC to continue
  131. * \return 0 user entered CTRL-C to cancel
  132. * \return -1 no match
  133. */
  134. int V_call(void)
  135. {
  136. int incr;
  137. int incr2;
  138. int num_answers;
  139. int at_answer;
  140. int at_constant;
  141. int ans_col = 0;
  142. int newchar;
  143. int lastchar = 0;
  144. int new_answer = 0;
  145. struct
  146. {
  147. char position[80];
  148. } scr_answ[MAX_ANSW];
  149. int y, x; /* shapiro */
  150. char temp[100];
  151. int done;
  152. /* Zero out screen answer locations */
  153. for (incr = 0; incr < MAX_ANSW; incr++)
  154. for (incr2 = 0; incr2 < 80; incr2++)
  155. scr_answ[incr].position[incr2] = 000;
  156. /* Initialize the curses windows */
  157. V_init();
  158. /* Display text */
  159. for (incr = 0; incr < MAX_LINE; incr++) {
  160. move(incr, 0);
  161. addstr(V__.page.line[incr]);
  162. }
  163. /* Display constants */
  164. for (at_constant = 0; at_constant < V__.NUM_CONST; at_constant++) {
  165. move(V__.constant[at_constant].row, V__.constant[at_constant].col);
  166. switch (V__.constant[at_constant].var_type) {
  167. case 's':
  168. addstr(V__.constant[at_constant].targetptr.c);
  169. break;
  170. case 'i':
  171. sprintf(temp, "%d", *V__.constant[at_constant].targetptr.i);
  172. addstr(temp);
  173. break;
  174. case 'l':
  175. sprintf(temp, "%ld", *V__.constant[at_constant].targetptr.l);
  176. addstr(temp);
  177. break;
  178. case 'f':
  179. fmt(temp, V__.constant[at_constant].decimal_places,
  180. (double)*V__.constant[at_constant].targetptr.f);
  181. addstr(temp);
  182. break;
  183. case 'd':
  184. fmt(temp, V__.constant[at_constant].decimal_places,
  185. (double)*V__.constant[at_constant].targetptr.d);
  186. addstr(temp);
  187. break;
  188. default:
  189. break;
  190. }
  191. }
  192. /* Display answer locations */
  193. for (at_answer = 0; at_answer < V__.NUM_ANSW; at_answer++) {
  194. /* clear ANSWER */
  195. for (incr = 0; incr < 80; incr++)
  196. scr_answ[at_answer].position[incr] = 000;
  197. switch (TYPE) {
  198. case 's':
  199. strcpy(ANSWER, TARGET.c);
  200. RELINE;
  201. for (incr = 0; incr < LENGTH; incr++) {
  202. if (*(TARGET.c + incr) == '\000')
  203. while (incr++ < LENGTH)
  204. addch('_');
  205. else
  206. addch(*(TARGET.c + incr));
  207. }
  208. break;
  209. case 'i':
  210. sprintf(ANSWER, "%d", *TARGET.i);
  211. RELINE;
  212. addstr(ANSWER);
  213. break;
  214. case 'l':
  215. sprintf(ANSWER, "%ld", *TARGET.l);
  216. RELINE;
  217. addstr(ANSWER);
  218. break;
  219. case 'f':
  220. fmt(ANSWER, V__.usr_answ[at_answer].decimal_places,
  221. (double)*TARGET.f);
  222. RELINE;
  223. addstr(ANSWER);
  224. break;
  225. case 'd':
  226. fmt(ANSWER, V__.usr_answ[at_answer].decimal_places,
  227. (double)*TARGET.d);
  228. RELINE;
  229. addstr(ANSWER);
  230. break;
  231. default:
  232. break;
  233. }
  234. }
  235. num_answers = at_answer;
  236. if (interrupts_ok)
  237. move(22, 0);
  238. else
  239. move(23, 0);
  240. centered("AFTER COMPLETING ALL ANSWERS, HIT <ESC><ENTER> TO CONTINUE");
  241. if (interrupts_ok) {
  242. sprintf(temp, "(OR <Ctrl-C> TO %s)", V__.interrupt_msg);
  243. centered(temp);
  244. }
  245. /* Begin taking commands/answers from terminal */
  246. at_answer = 0;
  247. move(ROW, COL);
  248. refresh();
  249. for (done = 0; !done;) {
  250. getyx(stdscr, y, x);
  251. newchar = getch();
  252. switch (newchar) {
  253. case ERR:
  254. break;
  255. case ESC:
  256. if (V__.NUM_ANSW <= 0)
  257. done = 1;
  258. break;
  259. case CTRLC:
  260. if (interrupts_ok || V__.NUM_ANSW <= 0)
  261. done = 1;
  262. break;
  263. #ifdef KEY_UP
  264. case KEY_UP:
  265. #endif
  266. case UP:
  267. new_answer = (at_answer + num_answers - 1) % num_answers;
  268. ans_col = 0;
  269. break;
  270. #ifdef KEY_DOWN
  271. case KEY_DOWN:
  272. #endif
  273. case CR:
  274. case NL:
  275. new_answer = (at_answer + 1) % num_answers;
  276. ans_col = 0;
  277. if (lastchar == ESC && (newchar == CR || newchar == NL))
  278. done = 1;
  279. break;
  280. #ifdef KEY_LEFT
  281. case KEY_LEFT:
  282. ans_col = (ans_col - 1 >= 0) ? ans_col - 1 : 0;
  283. break;
  284. #endif
  285. #ifdef KEY_BACKSPACE
  286. case KEY_BACKSPACE:
  287. #endif
  288. case BS:
  289. ans_col = (ans_col - 1 >= 0) ? ans_col - 1 : 0;
  290. ANSWER[ans_col] = ' ';
  291. move(ROW, COL + ans_col);
  292. addch(' ');
  293. break;
  294. #ifdef KEY_RIGHT
  295. case KEY_RIGHT:
  296. #endif
  297. case FS:
  298. ans_col = (ans_col + 1 < LENGTH &&
  299. ANSWER[ans_col]) ? ans_col + 1 : ans_col;
  300. break;
  301. #ifdef KEY_HOME
  302. case KEY_HOME:
  303. ans_col = 0;
  304. break;
  305. #endif
  306. #ifdef KEY_END
  307. case KEY_END:
  308. for (ans_col = 0; ans_col < LENGTH && ANSWER[ans_col];
  309. ans_col++) ;
  310. break;
  311. #endif
  312. #ifdef KEY_REFRESH
  313. case KEY_REFRESH:
  314. #endif
  315. case RPLT:
  316. wrefresh(curscr);
  317. break;
  318. #ifdef KEY_PRINT
  319. case KEY_PRINT:
  320. #endif
  321. case DUMP:
  322. V__dump_window();
  323. break;
  324. case '\177':
  325. break;
  326. default:
  327. if (ans_col < LENGTH && newchar >= 040 && newchar <= 0377) {
  328. addch(newchar);
  329. ANSWER[ans_col] = newchar;
  330. ans_col++;
  331. }
  332. break;
  333. }
  334. if (new_answer != at_answer || done) {
  335. V__remove_trail(LENGTH, ANSWER);
  336. switch (TYPE) {
  337. case 's':
  338. strcpy(TARGET.c, ANSWER);
  339. RELINE;
  340. addstr(TARGET.c);
  341. break;
  342. case 'i':
  343. *TARGET.i = atoi(ANSWER);
  344. RELINE;
  345. sprintf(temp, "%d", *TARGET.i);
  346. addstr(temp);
  347. break;
  348. case 'l':
  349. *TARGET.l = atol(ANSWER);
  350. RELINE;
  351. sprintf(temp, "%ld", *TARGET.l);
  352. addstr(temp);
  353. break;
  354. case 'f':
  355. sscanf(ANSWER, "%f", TARGET.f);
  356. RELINE;
  357. fmt(ANSWER, V__.usr_answ[at_answer].decimal_places,
  358. (double)*TARGET.f);
  359. sscanf(ANSWER, "%f", TARGET.f);
  360. addstr(ANSWER);
  361. break;
  362. case 'd':
  363. sscanf(ANSWER, "%lf", TARGET.d);
  364. RELINE;
  365. fmt(ANSWER, V__.usr_answ[at_answer].decimal_places,
  366. (double)*TARGET.d);
  367. sscanf(ANSWER, "%lf", TARGET.d);
  368. addstr(ANSWER);
  369. break;
  370. default:
  371. break;
  372. }
  373. at_answer = new_answer;
  374. }
  375. lastchar = newchar;
  376. move(ROW, COL + ans_col);
  377. refresh();
  378. if (done) {
  379. interrupts_ok = 0;
  380. V_exit();
  381. return (newchar != CTRLC);
  382. }
  383. }
  384. return -1;
  385. }
  386. /**
  387. * \fn int V_intrpt_ok (void)
  388. *
  389. * \brief Allow CTRL-C.
  390. *
  391. * <i>V_call()</i> normally only allows the ESC character to end the
  392. * interactive input session. Sometimes, it is desirable to allow the
  393. * user to cancel the session. To provide this alternate means of exit,
  394. * the programmer can call <i>V_intrpt_ok()</i> before <i>V_call()</i>.
  395. * This allows the user to enter CTRL-C, which causes <i>V_call()</i> to
  396. * return a value of 0 instead of 1.<br>
  397. * A message is automatically supplied to the user on line 23 saying to
  398. * use CTRL-C to cancel the input session. The normal message
  399. * accompanying <i>V_call()</i> is moved up to line 22.<br>
  400. * <b>Note:</b> When <i>V_intrpt_ok()</i> is called, the programmer must
  401. * limit the use of <i>V_line()</i>, <i>V_ques()</i>, and
  402. * <i>V_const()</i> to lines 0-21.
  403. *
  404. * \return always returns 0
  405. */
  406. void V_intrpt_ok(void)
  407. {
  408. interrupts_ok = 1; /* will be set false when V_call() exists */
  409. }
  410. /**
  411. * \fn int V_intrpt_msg (const char *msg)
  412. *
  413. * \brief Change CTRL-C message.
  414. *
  415. * A call to <i>V_intrpt_msg()</i> changes the default
  416. * <i>V_intrpt_ok()</i> message from (OR <CTRL-C> TO CANCEL) to (OR
  417. * <CTRL-C> TO <i>msg</i>). The message is (re)set to the default by
  418. * <i>V_clear()</i>.
  419. *
  420. * \param[in] msg
  421. * \return always returns 0
  422. */
  423. void V_intrpt_msg(const char *msg)
  424. {
  425. strcpy(V__.interrupt_msg, msg);
  426. }
  427. static void fmt(char *s, int n, double x)
  428. {
  429. char buf[20];
  430. if (n >= 0)
  431. sprintf(buf, "%%.%dlf", n);
  432. else
  433. strcpy(buf, "%.5lf");
  434. /* I had to use .5lf instead of just lf since a number like 65.8 got
  435. * sprintf'ed as 65.800003 this is a hack - I admit it.
  436. */
  437. sprintf(s, buf, x);
  438. if (n < 0)
  439. V__trim_decimal(s);
  440. }
  441. static void centered(const char *msg)
  442. {
  443. int indent;
  444. indent = (80 - strlen(msg)) / 2;
  445. while (indent-- > 0)
  446. addstr(" ");
  447. addstr(msg);
  448. addstr("\n");
  449. }