123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- /**
- * \file V_call.c
- *
- * \brief Interactive call functions.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * \note Modified by Jacques Bouchard and Markus Neteler 6/99 to make
- * cursor keys working. Exit now with ESC-CR.
- *
- * \author GRASS GIS Development Team
- *
- * \date 2007-11-01
- */
- /*
- ALGORITHM:
- | Zero out screen answer locations
- | Initial curses screens
- | Display text, constants, and answer fields
- | Write text (results from V_line() calls) to curses window
- | Write constants (results from V_const() calls) to curses window
- | Write answers (results from V_ques() calls) to curses window
- | Take commands from the keyboard
- | switch on commands:
- | case CR: case NL: case UP: case ESC:
- | switch on answer type
- | case string
- | remove trailing non-alphanumeric characters
- | copy answer to target denoted in V_ques() call
- | blank out screen line
- | copy target to curses window
- | case integer
- | run atoi on answer, putting results in target
- | denoted in V_ques() call
- | blank out screen line
- | printf target to curses window
- | case long
- | run atol on answer, putting results in target
- | denoted in V_ques() call
- | blank out screen line
- | printf target to curses window
- | case float, double
- | run sscanf on answer, putting results in target
- | denoted in V_ques() call
- | blank out screen line
- | printf target to curses window
- | default:
- | do nothing
- | if ESC+CR return from V_call()
- | if UP shift to previous question
- | if CR or NL shift to next question
- | case BS: Move cursor back one column in current question
- | case FS: Move cursor forward one column in current
- | question
- | case RPLT: Replot the current screen image
- | case DUMP: Dump (append) the current window to the user's
- | home dir.
- | default: If an alphanumeric, put that char on the screen
- | and in the current answer field
- | call V_exit (erase screen and exit curses)
- ********************************************************************** */
- #include <grass/config.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <grass/vask.h>
- static void centered(const char *);
- static void fmt(char *, int, double);
- /* define the V__ struct defined in vask.h */
- struct V__ V__;
- #define DUMP 001
- #define BS 010
- #define FS 014
- #define NL 012
- #define UP 013
- #define CR 015
- #define RPLT 022
- #define ESC 033
- #define CTRLC 003
- #define TARGET V__.usr_answ[at_answer].targetptr
- #define ROW V__.usr_answ[at_answer].row
- #define COL V__.usr_answ[at_answer].col
- #define LENGTH V__.usr_answ[at_answer].length
- #define TYPE V__.usr_answ[at_answer].var_type
- #define ANSWER scr_answ[at_answer].position
- #define RELINE do { \
- move(ROW, COL) ; \
- for (incr2=0;incr2<LENGTH; incr2++) \
- addch('_') ; \
- move(ROW, COL) ; \
- } while (0)
- /* flag ctrl-c is to be allowed */
- static int interrupts_ok = 0; /* mod shapiro */
- /**
- * \fn int V_call (void)
- *
- * \brief Interact with the user.
- *
- * Interactively allow the user to enter answers into all available
- * fields (as previously defined). Answer fields have been created with
- * calls to <i>V_ques()</i>. Information fields have been created using
- * <i>V_const()</i>. General text has been created with calls to
- * <i>V_line()</i>.
- *
- * <i>V_call()</i> clears the screen and writes the text and data values
- * specified by <i>V_line()</i>, <i>V_ques()</i> and <i>V_const()</i> to
- * the screen. It interfaces with the user, collecting user responses in
- * the <i>V_ques()</i> fields until the user is satisfied. A message is
- * automatically supplied on line number 23, explaining to the user to
- * enter an ESC when all inputs have been supplied as desired.
- * <i>V_call()</i> ends when the user hits ESC and returns a value of 1
- * (but see <i>V_intrpt_ok()</i> below). No error checking is done by
- * <i>V_call()</i>. Instead, all variables used in <i>V_ques()</i>
- * calls must be checked upon return from <i>V_call()</i>. If the user
- * has supplied inappropriate information, the user can be informed, and
- * the input prompted for again by further calls to <i>V_call()</i>.
- *
- * \return 1 user entered ESC to continue
- * \return 0 user entered CTRL-C to cancel
- * \return -1 no match
- */
- int V_call(void)
- {
- int incr;
- int incr2;
- int num_answers;
- int at_answer;
- int at_constant;
- int ans_col = 0;
- int newchar;
- int lastchar = 0;
- int new_answer = 0;
- struct
- {
- char position[80];
- } scr_answ[MAX_ANSW];
- int y, x; /* shapiro */
- char temp[100];
- int done;
- /* Zero out screen answer locations */
- for (incr = 0; incr < MAX_ANSW; incr++)
- for (incr2 = 0; incr2 < 80; incr2++)
- scr_answ[incr].position[incr2] = 000;
- /* Initialize the curses windows */
- V_init();
- /* Display text */
- for (incr = 0; incr < MAX_LINE; incr++) {
- move(incr, 0);
- addstr(V__.page.line[incr]);
- }
- /* Display constants */
- for (at_constant = 0; at_constant < V__.NUM_CONST; at_constant++) {
- move(V__.constant[at_constant].row, V__.constant[at_constant].col);
- switch (V__.constant[at_constant].var_type) {
- case 's':
- addstr(V__.constant[at_constant].targetptr.c);
- break;
- case 'i':
- sprintf(temp, "%d", *V__.constant[at_constant].targetptr.i);
- addstr(temp);
- break;
- case 'l':
- sprintf(temp, "%ld", *V__.constant[at_constant].targetptr.l);
- addstr(temp);
- break;
- case 'f':
- fmt(temp, V__.constant[at_constant].decimal_places,
- (double)*V__.constant[at_constant].targetptr.f);
- addstr(temp);
- break;
- case 'd':
- fmt(temp, V__.constant[at_constant].decimal_places,
- (double)*V__.constant[at_constant].targetptr.d);
- addstr(temp);
- break;
- default:
- break;
- }
- }
- /* Display answer locations */
- for (at_answer = 0; at_answer < V__.NUM_ANSW; at_answer++) {
- /* clear ANSWER */
- for (incr = 0; incr < 80; incr++)
- scr_answ[at_answer].position[incr] = 000;
- switch (TYPE) {
- case 's':
- strcpy(ANSWER, TARGET.c);
- RELINE;
- for (incr = 0; incr < LENGTH; incr++) {
- if (*(TARGET.c + incr) == '\000')
- while (incr++ < LENGTH)
- addch('_');
- else
- addch(*(TARGET.c + incr));
- }
- break;
- case 'i':
- sprintf(ANSWER, "%d", *TARGET.i);
- RELINE;
- addstr(ANSWER);
- break;
- case 'l':
- sprintf(ANSWER, "%ld", *TARGET.l);
- RELINE;
- addstr(ANSWER);
- break;
- case 'f':
- fmt(ANSWER, V__.usr_answ[at_answer].decimal_places,
- (double)*TARGET.f);
- RELINE;
- addstr(ANSWER);
- break;
- case 'd':
- fmt(ANSWER, V__.usr_answ[at_answer].decimal_places,
- (double)*TARGET.d);
- RELINE;
- addstr(ANSWER);
- break;
- default:
- break;
- }
- }
- num_answers = at_answer;
- if (interrupts_ok)
- move(22, 0);
- else
- move(23, 0);
- centered("AFTER COMPLETING ALL ANSWERS, HIT <ESC><ENTER> TO CONTINUE");
- if (interrupts_ok) {
- sprintf(temp, "(OR <Ctrl-C> TO %s)", V__.interrupt_msg);
- centered(temp);
- }
- /* Begin taking commands/answers from terminal */
- at_answer = 0;
- move(ROW, COL);
- refresh();
- for (done = 0; !done;) {
- getyx(stdscr, y, x);
- newchar = getch();
- switch (newchar) {
- case ERR:
- break;
- case ESC:
- if (V__.NUM_ANSW <= 0)
- done = 1;
- break;
- case CTRLC:
- if (interrupts_ok || V__.NUM_ANSW <= 0)
- done = 1;
- break;
- #ifdef KEY_UP
- case KEY_UP:
- #endif
- case UP:
- new_answer = (at_answer + num_answers - 1) % num_answers;
- ans_col = 0;
- break;
- #ifdef KEY_DOWN
- case KEY_DOWN:
- #endif
- case CR:
- case NL:
- new_answer = (at_answer + 1) % num_answers;
- ans_col = 0;
- if (lastchar == ESC && (newchar == CR || newchar == NL))
- done = 1;
- break;
- #ifdef KEY_LEFT
- case KEY_LEFT:
- ans_col = (ans_col - 1 >= 0) ? ans_col - 1 : 0;
- break;
- #endif
- #ifdef KEY_BACKSPACE
- case KEY_BACKSPACE:
- #endif
- case BS:
- ans_col = (ans_col - 1 >= 0) ? ans_col - 1 : 0;
- ANSWER[ans_col] = ' ';
- move(ROW, COL + ans_col);
- addch(' ');
- break;
- #ifdef KEY_RIGHT
- case KEY_RIGHT:
- #endif
- case FS:
- ans_col = (ans_col + 1 < LENGTH &&
- ANSWER[ans_col]) ? ans_col + 1 : ans_col;
- break;
- #ifdef KEY_HOME
- case KEY_HOME:
- ans_col = 0;
- break;
- #endif
- #ifdef KEY_END
- case KEY_END:
- for (ans_col = 0; ans_col < LENGTH && ANSWER[ans_col];
- ans_col++) ;
- break;
- #endif
- #ifdef KEY_REFRESH
- case KEY_REFRESH:
- #endif
- case RPLT:
- wrefresh(curscr);
- break;
- #ifdef KEY_PRINT
- case KEY_PRINT:
- #endif
- case DUMP:
- V__dump_window();
- break;
- case '\177':
- break;
- default:
- if (ans_col < LENGTH && newchar >= 040 && newchar <= 0377) {
- addch(newchar);
- ANSWER[ans_col] = newchar;
- ans_col++;
- }
- break;
- }
- if (new_answer != at_answer || done) {
- V__remove_trail(LENGTH, ANSWER);
- switch (TYPE) {
- case 's':
- strcpy(TARGET.c, ANSWER);
- RELINE;
- addstr(TARGET.c);
- break;
- case 'i':
- *TARGET.i = atoi(ANSWER);
- RELINE;
- sprintf(temp, "%d", *TARGET.i);
- addstr(temp);
- break;
- case 'l':
- *TARGET.l = atol(ANSWER);
- RELINE;
- sprintf(temp, "%ld", *TARGET.l);
- addstr(temp);
- break;
- case 'f':
- sscanf(ANSWER, "%f", TARGET.f);
- RELINE;
- fmt(ANSWER, V__.usr_answ[at_answer].decimal_places,
- (double)*TARGET.f);
- sscanf(ANSWER, "%f", TARGET.f);
- addstr(ANSWER);
- break;
- case 'd':
- sscanf(ANSWER, "%lf", TARGET.d);
- RELINE;
- fmt(ANSWER, V__.usr_answ[at_answer].decimal_places,
- (double)*TARGET.d);
- sscanf(ANSWER, "%lf", TARGET.d);
- addstr(ANSWER);
- break;
- default:
- break;
- }
- at_answer = new_answer;
- }
- lastchar = newchar;
- move(ROW, COL + ans_col);
- refresh();
- if (done) {
- interrupts_ok = 0;
- V_exit();
- return (newchar != CTRLC);
- }
- }
- return -1;
- }
- /**
- * \fn int V_intrpt_ok (void)
- *
- * \brief Allow CTRL-C.
- *
- * <i>V_call()</i> normally only allows the ESC character to end the
- * interactive input session. Sometimes, it is desirable to allow the
- * user to cancel the session. To provide this alternate means of exit,
- * the programmer can call <i>V_intrpt_ok()</i> before <i>V_call()</i>.
- * This allows the user to enter CTRL-C, which causes <i>V_call()</i> to
- * return a value of 0 instead of 1.<br>
- * A message is automatically supplied to the user on line 23 saying to
- * use CTRL-C to cancel the input session. The normal message
- * accompanying <i>V_call()</i> is moved up to line 22.<br>
- * <b>Note:</b> When <i>V_intrpt_ok()</i> is called, the programmer must
- * limit the use of <i>V_line()</i>, <i>V_ques()</i>, and
- * <i>V_const()</i> to lines 0-21.
- *
- * \return always returns 0
- */
- void V_intrpt_ok(void)
- {
- interrupts_ok = 1; /* will be set false when V_call() exists */
- }
- /**
- * \fn int V_intrpt_msg (const char *msg)
- *
- * \brief Change CTRL-C message.
- *
- * A call to <i>V_intrpt_msg()</i> changes the default
- * <i>V_intrpt_ok()</i> message from (OR <CTRL-C> TO CANCEL) to (OR
- * <CTRL-C> TO <i>msg</i>). The message is (re)set to the default by
- * <i>V_clear()</i>.
- *
- * \param[in] msg
- * \return always returns 0
- */
- void V_intrpt_msg(const char *msg)
- {
- strcpy(V__.interrupt_msg, msg);
- }
- static void fmt(char *s, int n, double x)
- {
- char buf[20];
- if (n >= 0)
- sprintf(buf, "%%.%dlf", n);
- else
- strcpy(buf, "%.5lf");
- /* I had to use .5lf instead of just lf since a number like 65.8 got
- * sprintf'ed as 65.800003 this is a hack - I admit it.
- */
- sprintf(s, buf, x);
- if (n < 0)
- V__trim_decimal(s);
- }
- static void centered(const char *msg)
- {
- int indent;
- indent = (80 - strlen(msg)) / 2;
- while (indent-- > 0)
- addstr(" ");
- addstr(msg);
- addstr("\n");
- }
|