1 /*******************************************************************************
2  * Copyright (c) 2013-2021, Andrés Martinelli <andmarti@gmail.com>             *
3  * All rights reserved.                                                        *
4  *                                                                             *
5  * This file is a part of SC-IM                                                *
6  *                                                                             *
7  * SC-IM is a spreadsheet program that is based on SC. The original authors    *
8  * of SC are James Gosling and Mark Weiser, and mods were later added by       *
9  * Chuck Martin.                                                               *
10  *                                                                             *
11  * Redistribution and use in source and binary forms, with or without          *
12  * modification, are permitted provided that the following conditions are met: *
13  * 1. Redistributions of source code must retain the above copyright           *
14  *    notice, this list of conditions and the following disclaimer.            *
15  * 2. Redistributions in binary form must reproduce the above copyright        *
16  *    notice, this list of conditions and the following disclaimer in the      *
17  *    documentation and/or other materials provided with the distribution.     *
18  * 3. All advertising materials mentioning features or use of this software    *
19  *    must display the following acknowledgement:                              *
20  *    This product includes software developed by Andrés Martinelli            *
21  *    <andmarti@gmail.com>.                                                    *
22  * 4. Neither the name of the Andrés Martinelli nor the                        *
23  *   names of other contributors may be used to endorse or promote products    *
24  *   derived from this software without specific prior written permission.     *
25  *                                                                             *
26  * THIS SOFTWARE IS PROVIDED BY ANDRES MARTINELLI ''AS IS'' AND ANY            *
27  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED   *
28  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE      *
29  * DISCLAIMED. IN NO EVENT SHALL ANDRES MARTINELLI BE LIABLE FOR ANY           *
30  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  *
31  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;*
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
33  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  *
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE       *
35  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
36  *******************************************************************************/
37 
38 /**
39  * \file tui.c
40  * \author Andrés Martinelli <andmarti@gmail.com>
41  * \date 2017-07-18
42  * \brief  This is the ncurses implementation of sc-im user interface,
43  *
44  * \details This is the ncurses implementation of sc-im user interface,
45  * or called tui. It mainly consists on the following two windows:
46  * main_win: window that shows the grid
47  * input_win: status bar window (or called header) and stdin input
48  *
49  * these are the functions called outside tui.c:
50  * ui_query               // function to read text from stdin
51  * ui_query_opt           // function that shows a message and waits for confirmation between a couple of defined options
52  * yyerror                // error routine for yacc parser
53  * ui_bail                // function to print errors of lua scripts
54  * ui_winchg              // function that handles SIGWINCH
55  * ui_show_celldetails    // function that shows cell details in header bar
56  * ui_start_colors        // exclusive ui startup routine for colors
57  * ui_pause
58  * ui_resume
59  * ui_mv_bottom_bar
60  * ui_refresh_pad
61  *
62  * these are local functions that might not be needed to reimplement if writing another ui:
63  * ui_set_ucolor          // function called internally for setting a color
64  * ui_show_content
65  * ui_show_sc_col_headings
66  * ui_show_sc_row_headings
67  * ui_add_cell_detail     // Add details of an ent to a char * received as a parameter. used for input_win
68  *
69  * ANYONE WHO WANTS TO PORT THIS TO ANOTHER UI, WOULD JUST NEED TO REIMPLEMENT THIS FILE
70  * AND HELP() IN HELP.C
71  *
72  *
73  * if not working with ncurses, you should also have to define LINES and COLS macros in Xui.h as well.
74  * see ui example inside /examples/gui_example folder
75  */
76 
77 #include <string.h>
78 #include <ncurses.h>
79 #include <stdio.h>
80 #include <time.h>
81 #include <locale.h>
82 #include <stdlib.h>
83 #include <stdarg.h>
84 
85 #include "main.h"
86 #include "macros.h"
87 #include "conf.h"
88 #include "input.h"
89 #include "tui.h"
90 #include "range.h"
91 #include "sc.h"
92 #include "cmds.h"
93 #include "cmds_visual.h"
94 #include "cmds_command.h"
95 #include "conf.h"
96 #include "version.h"
97 #include "file.h"
98 #include "format.h"
99 #include "utils/string.h"
100 #include "digraphs.h"
101 
102 extern struct dictionary * d_colors_param;
103 extern int cmd_pending;
104 extern int cmd_multiplier;
105 extern char insert_edit_submode;
106 
107 WINDOW * main_win;
108 WINDOW * input_win;
109 WINDOW * input_pad;
110 SCREEN * sstderr;
111 SCREEN * sstdout;
112 
113 /* for the "show_cursor" option */
114 int status_line_empty;
115 int curwinrow, curwincol;
116 
117 /**
118  * \brief Called to start UI
119  *
120  * \return none
121  */
122 
ui_start_screen()123 void ui_start_screen() {
124     sstderr = newterm(NULL, stderr, stdin);
125     noecho();
126     sstdout = newterm(NULL, stdout, stdin);
127     set_term(sstdout);
128 
129     main_win = newwin(LINES - RESROW, COLS, get_conf_int("input_bar_bottom") ? 0 : RESROW, 0);
130     input_win = newwin(RESROW-1, COLS, get_conf_int("input_bar_bottom") ? LINES-RESROW : 0, 0);
131     input_pad = newpad(RESROW-1, MAX_IB_LEN);
132 
133     status_line_empty = 1;
134 
135     #ifdef USECOLORS
136     if (has_colors()) {
137         start_color();
138 
139         if (get_d_colors_param() == NULL) {
140             start_default_ucolors();
141 
142             // in case we decide to change colors
143             // Create a dictionary and save equivalences between macros and
144             // values defined in '.sc' files
145             set_colors_param_dict();
146         }
147         //wbkgd(main_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS) + ucolors[DEFAULT].bg + 2));
148         //wbkgd(input_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS) + ucolors[DEFAULT].bg + 2));
149         //wbkgd(input_pad, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS) + ucolors[DEFAULT].bg + 2));
150     }
151     #endif
152 
153     wtimeout(input_pad, TIMEOUT_CURSES);
154     noecho();
155     curs_set(0);
156 
157     // Mouse support
158     #ifdef MOUSE
159     mmask_t old;
160     mousemask (ALL_MOUSE_EVENTS, &old);
161     #endif
162 
163     #ifndef NETBSD
164     if ((char *) getenv ("ESCDELAY") == NULL) set_escdelay(ESC_DELAY);
165     #endif
166     cbreak();
167     keypad(input_pad, 1);
168 }
169 
170 /**
171  * \brief Called to stop UI
172  *
173  * \return none
174  */
175 
ui_stop_screen()176 void ui_stop_screen() {
177     #ifdef USECOLORS
178         //if (get_d_colors_param() != NULL)
179         free_colors_param_dict();
180     #endif
181     move(0, 0);
182     clrtobot();
183     refresh();
184 
185     set_term(sstdout);
186     endwin();
187     set_term(sstderr);
188     endwin();
189     return;
190 }
191 
192 /**
193  * \brief Asks the user input from stdin (non-blocking)
194  *
195  * \details This function asks the user for input from stdin. Should be
196  * non-blocking and should return -1 when no key was pressed; 0 when
197  * key was pressed. It receives wint_t as a parameter. When a valid
198  * key is pressed, its value is then updated in that win_t variable.
199  *
200  * \param[in] wd
201  *
202  * \return 0 on key press; -1 otherwise
203  */
204 
ui_getch(wint_t * wd)205 int ui_getch(wint_t * wd) {
206     return wget_wch(input_pad, wd);
207 }
208 
209 /**
210  * \brief Asks the user for input from stdin (blocking)
211  *
212  * \details This function asks the user for input from stdin. Should be
213  * blocking and should return -1 when ESC was pressed; 0 otherwise. It
214  * receives * wint_t as a parameter. When a valid key is pressed, its value
215  * is then updated in that wint_t variable.
216  *
217  * \param[in] wd
218  *
219  * \return -1 on ESC press; 0 otherwise
220  */
221 
ui_getch_b(wint_t * wd)222 int ui_getch_b(wint_t * wd) {
223     wint_t digraph = 0;
224     wtimeout(input_pad, -1);
225     move(0, inputline_pos + 1);
226     wget_wch(input_pad, wd);
227     if (*wd == ctl('k')) {
228         wget_wch(input_pad, wd);
229         if (*wd != OKEY_ESC) {
230             digraph = *wd;
231             wget_wch(input_pad, wd);
232             if (*wd != OKEY_ESC)
233                 *wd = get_digraph(digraph, *wd);
234         }
235     }
236     wtimeout(input_pad, TIMEOUT_CURSES);
237     if ( *wd != OKEY_ESC ) return 0;
238     return -1;
239 }
240 
241 /**
242  * \brief Used for sc_info, sc_error, and sc_debug macros
243  *
244  * \param[in] s
245  * \param[in] type
246  * \return none
247  */
248 
ui_sc_msg(char * s,int type,...)249 void ui_sc_msg(char * s, int type, ...) {
250     if (get_conf_int("quiet")) return;
251     if (type == DEBUG_MSG && ! get_conf_int("debug")) return;
252     char t[BUFFERSIZE];
253     va_list args;
254     va_start(args, type);
255     vsprintf (t, s, args);
256     if ( ! get_conf_int("nocurses")) {
257 #ifdef USECOLORS
258         if (type == ERROR_MSG)
259             ui_set_ucolor(input_pad, &ucolors[ERROR_MSG], DEFAULT_COLOR);
260         else
261             ui_set_ucolor(input_pad, &ucolors[INFO_MSG], DEFAULT_COLOR);
262 #endif
263         mvwprintw(input_pad, 0, 0, "%s", t);
264         wclrtoeol(input_pad);
265         wmove(input_pad, 0, 0);
266         status_line_empty = 0;
267 
268         ui_refresh_pad(0);
269 #ifdef USECOLORS
270         ui_set_ucolor(input_win, &ucolors[INPUT], DEFAULT_COLOR);
271 #endif
272         if (type == DEBUG_MSG || (loading && type == ERROR_MSG)) {
273             wtimeout(input_pad, -1);
274             wgetch(input_pad);
275             wtimeout(input_pad, TIMEOUT_CURSES);
276         }
277 
278     } else if (type == VALUE_MSG && get_conf_value("output") != NULL && fdoutput != NULL) {
279         fwprintf(fdoutput, L"%s\n", t);
280     } else if (type == VALUE_MSG) {
281         if (fwide(stdout, 0) >0)
282             wprintf(L"%s\n", t);
283         else
284             printf("%s\n", t);
285         fflush(stdout);
286     } else {
287         if (fwide(stderr, 0) >0)
288             fwprintf(stderr, L"%s\n", t);
289         else
290             fprintf(stderr, "%s\n", t);
291         fflush(stderr);
292     }
293     va_end(args);
294     return;
295 }
296 
297 /**
298  * \brief Welcome screen function used when starting sc-im without a file
299  * as a parameter
300  *
301  * \return none
302  */
ui_do_welcome()303 void ui_do_welcome() {
304     char * msg_title = "SC-IM - SpreadSheet Calculator Improvised";
305     char * msg_by = "An SC fork by Andrés Martinelli";
306     char * msg_version = rev;
307     char * msg_help  = "Press  :help<Enter>  to get help         ";
308     char * msg_help2 = "Press  <Enter>       to enter NORMAL mode";
309     int i;
310 
311     #ifdef USECOLORS
312     wbkgd(main_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS) + ucolors[DEFAULT].bg + 2));
313     wbkgd(input_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS) + ucolors[DEFAULT].bg + 2));
314     wbkgd(input_pad, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS) + ucolors[DEFAULT].bg + 2));
315     #endif
316 
317     // show headings
318     int nbcols = calc_mobile_cols(NULL);
319     int nbrows = calc_mobile_rows(NULL);
320     ui_show_sc_col_headings(main_win, nbcols);
321     ui_show_content(main_win, nbrows, nbcols);
322     ui_show_sc_row_headings(main_win, nbrows); // show_sc_row_headings must be after show_content
323 
324     #ifdef USECOLORS
325     ui_set_ucolor(main_win, &ucolors[WELCOME], DEFAULT_COLOR);
326     #endif
327 
328     // show message
329     mvwaddstr(main_win, LINES/2-3, COLS/2-strlen(msg_title)/2  , msg_title);
330     mvwaddstr(main_win, LINES/2-2, COLS/2-strlen(msg_by)/2     , msg_by);
331     mvwaddstr(main_win, LINES/2-1, COLS/2-strlen(msg_version)/2, msg_version);
332 
333     #ifdef USECOLORS
334     ui_set_ucolor(main_win, &ucolors[WELCOME], DEFAULT_COLOR);
335     #endif
336     for (i=0; msg_help[i] != '\0'; i++) {
337         if (msg_help[i] == '<') {
338             #ifdef USECOLORS
339             ui_set_ucolor(main_win, &ucolors[NUMB], DEFAULT_COLOR);
340             #endif
341         }
342         mvwaddstr(main_win, LINES/2, COLS/2-strlen(msg_help)/2+i, &msg_help[i]);
343         if (msg_help[i] == '>') {
344             #ifdef USECOLORS
345             ui_set_ucolor(main_win, &ucolors[WELCOME], DEFAULT_COLOR);
346             #endif
347         }
348     }
349     for (i=0; msg_help2[i] != '\0'; i++) {
350         if (msg_help2[i] == '<') {
351             #ifdef USECOLORS
352             ui_set_ucolor(main_win, &ucolors[NUMB], DEFAULT_COLOR);
353             #endif
354         }
355         mvwaddstr(main_win, LINES/2+1, COLS/2-strlen(msg_help2)/2+i, &msg_help2[i]);
356         if (msg_help2[i] == '>') {
357             #ifdef USECOLORS
358             ui_set_ucolor(main_win, &ucolors[WELCOME], DEFAULT_COLOR);
359             #endif
360         }
361     }
362     wrefresh(main_win);
363 
364     if (get_conf_int("show_cursor")) {
365         /* land cursor next to the help line */
366         curwinrow = LINES / 2;
367         curwincol = COLS / 2 - strlen(msg_help) / 2 - 1;
368         status_line_empty = 1;
369     }
370 
371     // refresh pad
372     ui_refresh_pad(0);
373     return;
374 }
375 
376 
377 /**
378  * \brief Refreshes screen grid
379  *
380  * \details This function is used to refresh the screen content. If
381  * the header flag is set, the first column of the screen gets
382  * refreshed.
383  *
384  * \param[in] header
385  *
386  * \return none
387  */
ui_update(int header)388 void ui_update(int header) {
389     if (loading) return;
390     if (cmd_multiplier > 1) return;
391     if (get_conf_int("nocurses")) return;
392 
393     if (header) {
394     #ifdef USECOLORS
395     wbkgd(main_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS) + ucolors[DEFAULT].bg + 2));
396     // comment this to prevent bold to be reset
397     wbkgd(input_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS) + ucolors[DEFAULT].bg + 2));
398     wbkgd(input_pad, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS) + ucolors[DEFAULT].bg + 2));
399     #endif
400 
401         // Clean from top to bottom
402         wmove(main_win, 0, 0);
403         wclrtobot(main_win);
404         // comment this to prevent info message to be erased
405         //wmove(input_win, 0, 0);
406         //wclrtobot(input_win);
407         ui_show_celldetails(); // always before ui_print_mode
408         ui_print_mode();
409         wrefresh(input_win);
410         ui_refresh_pad(0);
411     }
412 
413     /* You can't hide the last row or col */
414     while (row_hidden[currow])
415         currow++;
416     while (col_hidden[curcol])
417         curcol++;
418 
419     /*
420      * Calculate offscreen rows and columns
421      *
422      * offscr_sc_cols are the number of columns left at the left of start
423      * of grid. For instance, if offscr_sc_cols is 4. the first visible
424      * column in grid would be column E.
425      *
426      * Mobile rows and columns are not frozen. They can move in and out of
427      * the display screen. Here nb_mobile_cols and nb_mobile_rows contain
428      * the number of visible mobile columns and rows. The more there are
429      * frozen rows/cols, the fewer mobile rows/cols.
430      */
431     int nb_mobile_cols = calc_mobile_cols(NULL);
432     int nb_mobile_rows = calc_mobile_rows(NULL);
433 
434     // Show sc_col headings: A, B, C, D..
435     ui_show_sc_col_headings(main_win, nb_mobile_cols);
436 
437     // Show the content of the cells
438     // Numeric values, strings.
439     ui_show_content(main_win, nb_mobile_rows, nb_mobile_cols);
440 
441     // Show sc_row headings: 0, 1, 2, 3..
442     ui_show_sc_row_headings(main_win, nb_mobile_rows); // schow_sc_row_headings must be after show_content
443 
444     if (status_line_empty && get_conf_int("show_cursor")) {
445         // Leave cursor on selected cell when no status message
446         wmove(main_win, curwinrow, curwincol);
447     }
448 
449     // Refresh curses windows
450     wrefresh(main_win);
451 
452     return;
453 }
454 
455 /**
456  * \brief Enable cursor and echo depending on the current screen mode
457  *
458  * \return none
459  */
ui_handle_cursor()460 void ui_handle_cursor() {
461     switch (curmode) {
462         case COMMAND_MODE:
463             noecho();
464             curs_set(1);
465             break;
466         case INSERT_MODE:
467         case EDIT_MODE:
468             noecho();
469             curs_set(1); // changed for NETBSD compatibility
470             break;
471         default:
472             noecho();
473             curs_set(0);
474     }
475     return;
476 }
477 
478 
479 /**
480  * \brief Print string with alignment
481  *
482  * \details Internal function to print a string with alignment.
483  *
484  * JUSTIF: 0 left shift
485  *
486  * JUSTIF: 1 right shift
487  *
488  * \param[in] win
489  * \param[in] word
490  * \param[in] row
491  * \param[in] justify
492  *
493  * \return none
494  */
ui_write_j(WINDOW * win,const char * word,const unsigned int row,const unsigned int justif)495 void ui_write_j(WINDOW * win, const char * word, const unsigned int row, const unsigned int justif) {
496     (justif == 0) ? (wmove(win, row, 0) && wclrtoeol(win)) : wmove(win, row, COLS - strlen(word));
497     wprintw(win, "%s", word);
498     return;
499 }
500 
501 
502 /**
503  * \brief Print multiplier and pending operator to the status bar
504  *
505  * \return none
506  */
ui_print_mult_pend()507 void ui_print_mult_pend() {
508     if (curmode != NORMAL_MODE && curmode != VISUAL_MODE && curmode != EDIT_MODE) return;
509 
510     int row_orig, col_orig;
511     getyx(input_pad, row_orig, col_orig);
512 
513     #ifdef USECOLORS
514     ui_set_ucolor(input_win, &ucolors[MODE], DEFAULT_COLOR);
515     #endif
516     // Show multiplier and pending operator
517     char strm[COLS];
518     strm[0]='\0';
519     if (cmd_multiplier > 0) sprintf(strm, "%d", cmd_multiplier);
520     if (cmd_pending) {
521         strcat(strm, "?");
522     }
523 
524     char field[rescol+1];
525     field[0]='\0';
526     sprintf(field, "%0*d", rescol - (int) strlen(strm), 0);
527     subst(field, '0', ' ');
528     strcat(strm, field);
529 
530     mvwprintw(input_win, 0, COLS - rescol - 14, "%s", strm);
531     wrefresh(input_win);
532 
533     // Return cursor to previous position
534     wmove(input_pad, row_orig, col_orig);
535     ui_refresh_pad(0);
536 
537     if (status_line_empty && curmode != EDIT_MODE && get_conf_int("show_cursor")) {
538         // Leave cursor on selected cell when no status message
539         wmove(main_win, curwinrow, curwincol);
540         wrefresh(main_win);
541     }
542 }
543 
544 
545 /**
546  * \brief Show first and second row (header). Handle cursor position.
547  *
548  * \return none
549  */
ui_show_header()550 void ui_show_header() {
551     //ui_clr_header(0);
552     ui_clr_header(1);
553 
554     // print multiplier
555     ui_print_mult_pend();
556 
557     // Show current mode
558     ui_print_mode();
559 
560     // Print input text over the input pad
561     switch (curmode) {
562         case COMMAND_MODE:
563             mvwprintw(input_pad, 0, 0, ":%ls", inputline);
564             wmove(input_pad, 0, inputline_pos + 1);
565             break;
566         case INSERT_MODE:
567             mvwprintw(input_pad, 0, 1, "%ls", inputline);
568             wmove(input_pad, 0, inputline_pos + 1);
569             break;
570         case EDIT_MODE:
571             mvwprintw(input_pad, 0, 1, "%ls", inputline);
572             wmove(input_pad, 0, inputline_pos + 1);
573     }
574     int scroll = 0;
575     if (inputline_pos > COLS - 14) scroll += inputline_pos - COLS + 14;
576     wrefresh(input_win);
577     ui_refresh_pad(scroll);
578     return;
579 }
580 
581 
582 /**
583  * \brief ui_clr_header
584  * \details clr a line
585  * \param[in] i (row)
586  *
587  * \return none
588  */
ui_clr_header(int i)589 void ui_clr_header(int i) {
590     int row_orig, col_orig;
591     getyx(i == 0 ? input_win : input_pad, row_orig, col_orig);
592     if (col_orig > COLS) col_orig = COLS - 1;
593 
594     if (i == 0) {
595         wbkgd(input_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS) + ucolors[DEFAULT].bg + 2));
596         wmove(input_win, 0, 0);
597         wclrtoeol(input_win);
598     } else {
599         wbkgd(input_pad, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS) + ucolors[DEFAULT].bg + 2));
600         getyx(input_pad, row_orig, col_orig);
601         wmove(input_pad, 0, 0);
602         wclrtoeol(input_pad);
603         status_line_empty = 1;
604     }
605     // Return cursor to previous position
606     wmove(i == 0 ? input_win : input_pad, row_orig, col_orig);
607     if (! i) ui_refresh_pad(0);
608 }
609 
610 
611 /**
612  * \brief Print current mode in the first row
613  *
614  * \details Print the current mode in the first row. Print ':' (colon)
615  * or submode indicator.
616  *
617  * \return none
618  */
ui_print_mode()619 void ui_print_mode() {
620     unsigned int row = 0; // Print mode in first row
621     char strm[PATHLEN+22] = "";
622 
623     #ifdef USECOLORS
624     ui_set_ucolor(input_win, &ucolors[MODE], DEFAULT_COLOR);
625     #endif
626 
627     strm[0] = '\0';
628     if (get_conf_int("filename_with_mode") && curfile[0]) {
629         sprintf(strm, "%s ", curfile);
630     }
631 
632     if (curmode == NORMAL_MODE) {
633         strcat(strm, " -- NORMAL --");
634         ui_write_j(input_win, strm, row, RIGHT);
635 
636     } else if (curmode == INSERT_MODE) {
637         strcat(strm, " -- INSERT --");
638         ui_write_j(input_win, strm, row, RIGHT);
639 
640         #ifdef USECOLORS
641         ui_set_ucolor(input_win, &ucolors[INPUT], DEFAULT_COLOR);
642         #endif
643         // Show submode (INSERT)
644         mvwprintw(input_pad, 0, 0, "%c", insert_edit_submode);
645 
646     } else if (curmode == EDIT_MODE) {
647         strcat(strm, "   -- EDIT --");
648         ui_write_j(input_win, strm, row, RIGHT);
649         // Show ^ in 0,0 position of pad
650         mvwprintw(input_pad, 0, 0, "^");
651 
652     } else if (curmode == VISUAL_MODE) {
653         strcat(strm, " -- VISUAL --");
654         if (visual_submode != '0')
655             strcpy(strm, " << VISUAL >>");
656         ui_write_j(input_win, strm, row, RIGHT);
657 
658     } else if (curmode == COMMAND_MODE) {
659         strcat(strm, "-- COMMAND --");
660 
661         ui_write_j(input_win, strm, row, RIGHT);
662         #ifdef USECOLORS
663         ui_set_ucolor(input_win, &ucolors[INPUT], DEFAULT_COLOR);
664         #endif
665         // show ':'
666         mvwprintw(input_pad, 0, 0, ":");
667         wmove(input_pad, 0, 1);
668     }
669 
670     return;
671 }
672 
673 
674 /**
675  * \brief Show sc_row headings: 0, 1, 2...
676  *
677  * \param[in] win
678  * \param[in] row_boundary
679  *
680  * \return none
681  */
ui_show_sc_row_headings(WINDOW * win,int nb_mobile_rows)682 void ui_show_sc_row_headings(WINDOW * win, int nb_mobile_rows) {
683     #ifdef USECOLORS
684     if (has_colors()) ui_set_ucolor(win, &ucolors[HEADINGS], DEFAULT_COLOR);
685     #endif
686 
687     int i, j;
688     srange * s = get_selected_range();
689 
690     int winrow = RESCOLHEADER;
691     int mobile_rows = nb_mobile_rows;
692     int total_rows = nb_mobile_rows + nb_frozen_rows;
693 
694     for (i = 0; i < maxrows && total_rows > 0; i++) {
695         if (row_hidden[i])
696             continue;
697         if (! row_frozen[i]) {
698             if (i < offscr_sc_rows)
699                 continue;
700             if (--mobile_rows < 0)
701                 continue;
702         }
703         --total_rows;
704 
705         if ( (s != NULL && i >= s->tlrow && i <= s->brrow) || i == currow ) {
706             #ifdef USECOLORS
707             if (has_colors()) ui_set_ucolor(win, &ucolors[CELL_SELECTION], DEFAULT_COLOR);
708             #else
709             wattron(win, A_REVERSE);
710             #endif
711         }
712         mvwprintw (win, winrow, 0, "%*d ", rescol-1, i);
713         if (row_frozen[i]) mvwprintw (win, winrow, rescol-1, "!");
714 
715         for (j = 1; j < row_format[i]; j++)
716             mvwprintw (win, winrow+j, 0, "%*c ", rescol-1, ' ');
717 
718         #ifdef USECOLORS
719         if (has_colors()) ui_set_ucolor(win, &ucolors[HEADINGS], DEFAULT_COLOR);
720         #else
721         wattroff(win, A_REVERSE);
722         #endif
723 
724         winrow += row_format[i];
725     }
726 }
727 
728 
729 /**
730  * \brief Show sc_col headings: A, B, C...
731  *
732  * \param[in] win
733  * \param[in] col_boundary
734  *
735  * \return none
736  */
ui_show_sc_col_headings(WINDOW * win,int nb_mobile_cols)737 void ui_show_sc_col_headings(WINDOW * win, int nb_mobile_cols) {
738     int i;
739     srange * s = get_selected_range();
740 
741     wmove(win, 0, 0);
742     wclrtoeol(win);
743 
744     int wincol = rescol;
745     int mobile_cols = nb_mobile_cols;
746     int total_cols = nb_mobile_cols + nb_frozen_cols;
747 
748     for (i = 0; i < maxcols && total_cols > 0; i++) {
749         if (col_hidden[i])
750             continue;
751         if (! col_frozen[i]) {
752             if (i < offscr_sc_cols)
753                 continue;
754             if (--mobile_cols < 0)
755                 continue;
756         }
757         --total_cols;
758 
759         #ifdef USECOLORS
760         if (has_colors() && i % 2 == 0)
761             ui_set_ucolor(win, &ucolors[HEADINGS], DEFAULT_COLOR);
762         else if (i % 2 == 1)
763             ui_set_ucolor(win, &ucolors[HEADINGS_ODD], DEFAULT_COLOR);
764         #endif
765 
766         if ( (s != NULL && i >= s->tlcol && i <= s->brcol) || i == curcol ) {
767             #ifdef USECOLORS
768             if (has_colors()) ui_set_ucolor(win, &ucolors[CELL_SELECTION], DEFAULT_COLOR);
769             #else
770             wattron(win, A_REVERSE);
771             #endif
772         }
773 
774         // we want ! after column name:
775         int k = (fwidth[i] - 1) / 2;
776         mvwprintw(win, 0, wincol, "%*s%s%s%*s", k, "", coltoa(i),
777         col_frozen[i] ? "!" : "", fwidth[i] - k - (col_frozen[i] ? strlen(coltoa(i))+1 : strlen(coltoa(i))), "");
778 
779         #ifdef USECOLORS
780         if (has_colors() && i % 2 == 0)
781             ui_set_ucolor(win, &ucolors[HEADINGS], DEFAULT_COLOR);
782         else if (i % 2 == 1)
783             ui_set_ucolor(win, &ucolors[HEADINGS_ODD], DEFAULT_COLOR);
784         #else
785         wattroff(win, A_REVERSE);
786         #endif
787 
788         wincol += fwidth[i];
789     }
790 }
791 
792 
793 /**
794  * \brief Show the content of the cell
795  *
796  * \param[in] win
797  * \param[in] row_boundary
798  * \param[in] col_boundary
799  *
800  * \return none
801  */
ui_show_content(WINDOW * win,int nb_mobile_rows,int nb_mobile_cols)802 void ui_show_content(WINDOW * win, int nb_mobile_rows, int nb_mobile_cols) {
803     int row, col;
804 
805     srange * s = get_selected_range();
806     int conf_underline_grid = get_conf_int("underline_grid");
807     int conf_truncate = get_conf_int("truncate");
808     int conf_overlap = get_conf_int("overlap");
809     int conf_autowrap = get_conf_int("autowrap");
810 
811     int winrow = RESCOLHEADER;
812     int mobile_rows = nb_mobile_rows;
813     int total_rows = nb_mobile_rows + nb_frozen_rows;
814 
815     for (row = 0; row < maxrows && total_rows > 0; row++) {
816         if (row_hidden[row])
817             continue;
818         if (! row_frozen[row]) {
819             if (row < offscr_sc_rows)
820                 continue;
821             if (--mobile_rows < 0)
822                 continue;
823         }
824         --total_rows;
825 
826         int wincol = rescol;
827         int mobile_cols = nb_mobile_cols;
828         int total_cols = nb_mobile_cols + nb_frozen_cols;
829 
830         for (col = 0; col < maxcols && total_cols > 0; col++) {
831             if (col_hidden[col])
832                 continue;
833             if (! col_frozen[col]) {
834                 if (col < offscr_sc_cols)
835                     continue;
836                 if (--mobile_cols < 0)
837                     continue;
838             }
839             --total_cols;
840 
841             struct ent ** p = ATBL(tbl, row, col);
842             int fieldlen = fwidth[col];
843 
844             // Clean format
845 #ifdef USECOLORS
846             if ((*p) && (*p)->cellerror) {                                  // cellerror
847                 ui_set_ucolor(win, &ucolors[CELL_ERROR], ucolors[CELL_ERROR].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_EVEN].bg : ucolors[GRID_ODD].bg);
848             } else if ((*p) && (*p)->v < 0) {                               // cell negative
849                 ui_set_ucolor(win, &ucolors[CELL_NEGATIVE], ucolors[CELL_NEGATIVE].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_EVEN].bg : ucolors[GRID_ODD].bg);
850             } else if ((*p) && (*p)->expr) {
851                 ui_set_ucolor(win, &ucolors[EXPRESSION], ucolors[EXPRESSION].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_EVEN].bg : ucolors[GRID_ODD].bg);
852             } else if ((*p) && (*p)->label) {                               // string
853                 ui_set_ucolor(win, &ucolors[STRG], ucolors[STRG].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_EVEN].bg : ucolors[GRID_ODD].bg);
854             } else if ((*p) && (*p)->flags & is_valid && ! (*p)->format) {  // numeric value
855                 ui_set_ucolor(win, &ucolors[NUMB], ucolors[NUMB].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_EVEN].bg : ucolors[GRID_ODD].bg);
856             } else if ((*p) && (*p)->format && (*p)->format[0] == 'd') {    // date format
857                 ui_set_ucolor(win, &ucolors[DATEF], ucolors[DATEF].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_EVEN].bg : ucolors[GRID_ODD].bg);
858             } else {
859                 ui_set_ucolor(win, &ucolors[NORMAL], ucolors[NORMAL].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_EVEN].bg : ucolors[GRID_ODD].bg);
860             }
861 #endif
862 
863             // Cell color!
864             if ((*p) && (*p)->ucolor != NULL) {
865                 ui_set_ucolor(win, (*p)->ucolor, (*p)->ucolor->bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_EVEN].bg : ucolors[GRID_ODD].bg);
866             }
867 
868             // setup color for selected cell
869             if ((currow == row) && (curcol == col)) {
870 #ifdef USECOLORS
871                     if (has_colors()) ui_set_ucolor(win, &ucolors[CELL_SELECTION_SC], ucolors[CELL_SELECTION_SC].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_EVEN].bg : ucolors[GRID_ODD].bg);
872 #else
873                     wattron(win, A_REVERSE);
874 #endif
875             }
876 
877             // setup color for selected range
878             int in_range = 0; // this is for coloring empty cells within a range
879             if (s != NULL && row >= s->tlrow && row <= s->brrow && col >= s->tlcol && col <= s->brcol ) {
880 #ifdef USECOLORS
881                     if (has_colors()) ui_set_ucolor(win, &ucolors[CELL_SELECTION_SC], ucolors[CELL_SELECTION_SC].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_EVEN].bg : ucolors[GRID_ODD].bg);
882 #else
883                     wattron(win, A_REVERSE);
884 #endif
885                 in_range = 1; // local variable. this is for coloring empty cells within a range
886             }
887 
888             /* Color empty cells inside a range */
889             if ( in_range && row >= ranges->tlrow && row <= ranges->brrow &&
890                  col >= ranges->tlcol && col <= ranges->brcol
891                ) {
892 #ifdef USECOLORS
893                     if (has_colors()) ui_set_ucolor(win, &ucolors[CELL_SELECTION_SC], ucolors[CELL_SELECTION_SC].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_EVEN].bg : ucolors[GRID_ODD].bg);
894 #else
895                     wattron(win, A_REVERSE);
896 #endif
897             }
898 
899             char num [FBUFLEN] = "";
900             char text[FBUFLEN] = "";
901             wchar_t out [FBUFLEN] = L"";
902             char formated_s[FBUFLEN] = "";
903             int res = -1;
904             int align = 1;
905 
906             // If a numeric value exists
907             if ( (*p) && (*p)->flags & is_valid) {
908                 res = ui_get_formated_value(p, col, formated_s);
909                 // res = 0, indicates that in num we store a date
910                 // res = 1, indicates a format is applied in num
911                 if (res == 0 || res == 1) {
912                     strcpy(num, formated_s);
913                 } else if (res == -1) {
914                     sprintf(num, "%.*f", precision[col], (*p)->v);
915                 }
916             }
917 
918             // If a string exists
919             if ((*p) && (*p)->label) {
920                 strcpy(text, (*p)->label);
921                 align = 1;                               // right alignment
922                 if ((*p)->flags & is_label) {            // center alignment
923                     align = 0;
924                 } else if ((*p)->flags & is_leftflush) { // left alignment
925                     align = -1;
926                 } else if (res == 0) {                   // res must ¿NOT? be zero for label to be printed // TODO CHECK!
927                     text[0] = '\0';
928                 }
929             }
930 
931             if ((*p) && (*p)->cellerror == CELLERROR) {
932                (void) mvwprintw(win, winrow, wincol, "%*.*s", fieldlen, fieldlen, "ERROR");
933                align = 0;
934                strcpy(text, "ERROR");
935                num[0]='\0';
936             }
937             if ((*p) && (*p)->cellerror == CELLREF) {
938                (void) mvwprintw(win, winrow, wincol, "%*.*s", fieldlen, fieldlen, "REF");
939                align = 0;
940                strcpy(text, "REF");
941                num[0]='\0';
942             }
943 
944             // repaint a blank cell or range
945             if ( (currow == row && curcol == col) ||
946                ( in_range && row >= ranges->tlrow && row <= ranges->brrow &&
947                  col >= ranges->tlcol && col <= ranges->brcol ) ) {
948 #ifdef USECOLORS
949                 if (has_colors()) ui_set_ucolor(win, &ucolors[CELL_SELECTION_SC], ucolors[CELL_SELECTION_SC].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_EVEN].bg : ucolors[GRID_ODD].bg);
950 #else
951                 wattron(win, A_REVERSE);
952 #endif
953             }
954 
955 #ifdef USECOLORS
956             if (has_colors() && conf_underline_grid) {
957                 attr_t attr;
958                 short color;
959                 wattr_get(win, &attr, &color, NULL);
960                 wattr_set(win, attr | A_UNDERLINE, color, NULL);
961             }
962 #endif
963 
964             // new implementation for wide char support
965             cchar_t cht[fieldlen];
966             wchar_t w;
967             int i, j, k;
968 
969             for (k=0; k < row_format[row]; k++) {
970                 for (i = 0; i < fieldlen; ) {
971                     w = L' ';
972                     j = mvwin_wchnstr (win, winrow+k, wincol+i, cht, 1);
973                     if (j == OK && cht[0].chars[0] != L'\0')
974                         w = cht[0].chars[0];
975                     //mvwprintw(win, winrow+k, wincol+i, "%lc", L'*');//w
976                     mvwprintw(win, winrow+k, wincol+i, "%lc", w);
977                     i += wcwidth(w);
978                 }
979             }
980 
981             // we print text and number
982             if ( (*p) && ( ((*p)->flags & is_valid) || (*p)->label ) ) {
983                 // hide text or number from num & text combined cells
984                 // if ((strlen(text) && strlen(num)) {
985                 //    if get_conf_int("hide_text_from_combined"))
986                 //        text[0]='\0';
987                 //    else if get_conf_int("hide_number_from_combined"))
988                 //        num[0]='\0';
989                 // }
990                 pad_and_align(text, num, fieldlen, align, (*p)->pad, out, row_format[row]);
991 
992                 // auto wrap
993                 if (!conf_truncate && !conf_overlap && conf_autowrap) {
994                     int newheight = (wcslen(out) + fwidth[col] - 1) / fwidth[col];
995                     if (row_format[row] < newheight) row_format[row] = newheight;
996                 }
997 
998 #ifdef USECOLORS
999                 if (has_colors() && conf_underline_grid) {
1000                     attr_t attr;
1001                     short color;
1002                     wattr_get(win, &attr, &color, NULL);
1003                     wattr_set(win, attr | A_UNDERLINE, color, NULL);
1004                 }
1005 #endif
1006                 // 'out' may contain the output to fill multiple rows. not just one.
1007                 int count_row = 0;
1008                 wchar_t new[wcslen(out)+1];
1009                 for (count_row = 0; count_row < row_format[row]; count_row++) {
1010                     int cw = count_width_widestring(out, fieldlen);
1011                     wcscpy(new, out);
1012                     if (conf_truncate || !conf_overlap) new[cw] = L'\0';
1013 
1014                     int whites = fieldlen - cw;
1015                     while (whites-- > 0) add_wchar(new, L' ', wcslen(new));
1016                     if (wcslen(new)) mvwprintw(win, winrow+count_row, wincol, "%ls", new);
1017                     //wclrtoeol(win);
1018                     if (cw) del_range_wchars(out, 0, cw-1);
1019                     if (conf_overlap) break;
1020                 }
1021             }
1022 
1023             if (currow == row && curcol == col) {
1024                 curwinrow = winrow;
1025                 curwincol = wincol;
1026                 if (out[0] == L'\0') {
1027                     // center the cursor on empty cells
1028                     curwincol += (fieldlen - 1)/2;;
1029                 } else if (out[0] == L' ') {
1030                     // cursor on last space but not beyond the middle
1031                     int i;
1032                     for (i = 0; i < (fieldlen - 1)/2 && out[i+1] == L' '; i++);
1033                     curwincol += i;
1034                 }
1035             }
1036 
1037             // clean format
1038 #ifndef USECOLORS
1039             wattroff(win, A_REVERSE);
1040 #endif
1041 
1042             wincol += fieldlen;
1043         }
1044         winrow += row_format[row];
1045     }
1046 }
1047 
1048 
1049 /**
1050  * \brief Add details of an ent to a char*
1051  *
1052  * \details Add details of an ent to a char * received as a parameter.
1053  * Used for 'input_win'
1054  *
1055  * \param[in] d
1056  * \param[in] p1
1057  *
1058  * \return none
1059  */
ui_add_cell_detail(char * d,struct ent * p1)1060 void ui_add_cell_detail(char * d, struct ent * p1) {
1061     if ( ! p1 ) return;
1062 
1063     /* string expressions
1064     if (p1->expr && (p1->flags & is_strexpr)) {
1065         if (p1->flags & is_label)
1066             strcat(d, "|{");
1067         else
1068             strcat(d, (p1->flags & is_leftflush) ? "<{" : ">{");
1069         strcat(d, "??? } ");        // and this '}' is for vi %
1070 
1071     } else*/
1072 
1073     if (p1->label) {
1074         /* has constant label only */
1075         if (p1->flags & is_label)
1076             strcat(d, "|\"");
1077         else
1078             strcat(d, (p1->flags & is_leftflush) ? "<\"" : ">\"");
1079         strcat(d, p1->label);
1080         strcat(d, "\" ");
1081     }
1082 
1083     /* Display if cell is locked */
1084     if (p1 && p1->flags & is_locked)
1085          strcat(d, "[locked] ");
1086 
1087     // value part of cell:
1088     if (p1->flags & is_valid) {
1089         /* has value or num expr */
1090         if ( ( ! (p1->expr) ) || ( p1->flags & is_strexpr ) ) {
1091             sprintf(d + strlen(d), "%c", '[');
1092             (void) sprintf(d + strlen(d), "%.15g", p1->v);
1093             sprintf(d + strlen(d), "%c", ']');
1094         }
1095     }
1096 }
1097 
1098 
1099 /**
1100  * \brief Draw cell content detail in header
1101  * \return none
1102  */
ui_show_celldetails()1103 void ui_show_celldetails() {
1104     char head[FBUFLEN];
1105     int il_pos = 0;
1106 
1107     // show cell in header
1108     #ifdef USECOLORS
1109         ui_set_ucolor(input_win, &ucolors[CELL_ID], DEFAULT_COLOR);
1110     #endif
1111     sprintf(head, "%s%d ", coltoa(curcol), currow);
1112     mvwprintw(input_win, 0, 0, "%s", head);
1113     il_pos += strlen(head);
1114 
1115     // show the current cell's format
1116     #ifdef USECOLORS
1117         ui_set_ucolor(input_win, &ucolors[CELL_FORMAT], DEFAULT_COLOR);
1118     #endif
1119 
1120     register struct ent *p1 = *ATBL(tbl, currow, curcol);
1121 
1122     // show padding
1123     if (p1 != NULL && p1->pad)
1124         sprintf(head, "(%d) ", p1->pad);
1125     else
1126         head[0]='\0';
1127 
1128     // show format
1129     if ((p1) && p1->format)
1130         sprintf(head + strlen(head), "(%s) ", p1->format);
1131     else
1132         sprintf(head + strlen(head), "(%d %d %d) ", fwidth[curcol], precision[curcol], realfmt[curcol]);
1133     mvwprintw(input_win, 0, il_pos, "%s", head);
1134     il_pos += strlen(head);
1135 
1136     // show expr
1137     #ifdef USECOLORS
1138         ui_set_ucolor(input_win, &ucolors[CELL_CONTENT], DEFAULT_COLOR);
1139     #endif
1140     if (p1 && p1->expr) {
1141         linelim = 0;
1142         editexp(currow, curcol);  /* set line to expr */
1143         linelim = -1;
1144         sprintf(head, "[%.*s] ", FBUFLEN-4, line);
1145         mvwprintw(input_win, 0, il_pos, "%s", head);
1146         il_pos += strlen(head);
1147     }
1148     // add cell content to head string
1149     head[0] = '\0';
1150     ui_add_cell_detail(head, p1);
1151 
1152     // cut string if its too large!
1153     if (strlen(head) > COLS - il_pos - 1) {
1154         head[COLS - il_pos - 1 - 15]='>';
1155         head[COLS - il_pos - 1 - 14]='>';
1156         head[COLS - il_pos - 1 - 13]='>';
1157         head[COLS - il_pos - 1 - 12]='\0';
1158     }
1159 
1160     mvwprintw(input_win, 0, il_pos, "%s", head);
1161     wclrtoeol(input_win);
1162     wrefresh(input_win);
1163 }
1164 
1165 
1166 /**
1167  * \brief Error routine for yacc (gram.y)
1168  *
1169  * \param[in] err
1170 
1171  * \return none
1172  */
1173 
yyerror(char * err)1174 void yyerror(char * err) {
1175     mvwprintw(input_pad, 0, 0, "%s: %.*s<=%s", err, linelim, line, line + linelim);
1176     ui_refresh_pad(0);
1177     return;
1178 }
1179 
1180 
1181 /**
1182  * \brief Create a string that represents the formatted value of the cell
1183  *
1184  * \details This function creates a string (value) that represents
1185  * the formatted value of the cell.
1186  *
1187  * \param[in] err
1188 
1189  * \return 0 datetime format - number in p->v represents a date - format "d"
1190  * \return 1 if format of number - (numbers with format) - puede harber label.
1191  * \return -1 if there is no format in the cell
1192  */
ui_get_formated_value(struct ent ** p,int col,char * value)1193 int ui_get_formated_value(struct ent ** p, int col, char * value) {
1194     //char * cfmt = (*p)->format ? (*p)->format : NULL;
1195     char * cfmt = (*p)->format ? (*p)->format : (realfmt[col] >= 0 && realfmt[col] < COLFORMATS && colformat[realfmt[col]] != NULL) ? colformat[realfmt[col]] : NULL;
1196 
1197     if (cfmt) {
1198         if (*cfmt == 'd') {
1199             time_t v = (time_t) ((*p)->v);
1200             strftime(value, sizeof(char) * FBUFLEN, cfmt + 1, localtime(&v));
1201             return 0;
1202         } else {
1203             format(cfmt, precision[col], (*p)->v, value, sizeof(char) * FBUFLEN);
1204             return 1;
1205         }
1206     } else { // there is no format
1207         // FIXME: error with number and text in same cell and no overlap
1208         engformat(realfmt[col], fwidth[col], precision[col], (*p)->v, value, sizeof(char) * FBUFLEN);
1209         ltrim(value, ' ');
1210         return 1;
1211     }
1212 }
1213 
1214 
1215 /**
1216  * \brief Shows text in child process
1217  *
1218  * \details Shows text in child process. Used for set, version,
1219  * showmaps, print_graph, showfilters, hiddenrows, and hiddencols commands.
1220  *
1221  * \param[in] val
1222  */
ui_show_text(char * val)1223 void ui_show_text(char * val) {
1224     int pid;
1225     char px[MAXCMD];
1226     char * pager;
1227 
1228     (void) strcpy(px, "| ");
1229     if ( !(pager = getenv("PAGER")) )
1230         pager = DFLT_PAGER;
1231     (void) strcat(px, pager);
1232     FILE * f = openfile(px, &pid, NULL);
1233     if ( !f ) {
1234         sc_error("Can't open pipe to %s", pager);
1235         return;
1236     }
1237     def_prog_mode();
1238     endwin();
1239     fprintf(f, "%s\n", val);
1240     fprintf(f, "Press 'q' and then ENTER to return.");
1241     closefile(f, pid, 0);
1242     getchar();
1243     reset_prog_mode();
1244     refresh();
1245     ui_update(TRUE);
1246     wmove(input_pad, 0, 0);
1247     wclrtoeol(input_pad);
1248     ui_refresh_pad(0);
1249 }
1250 
1251 
1252 /**
1253  * \brief
1254  * UI function thats called after SIGWINCH signal.
1255  * \return none
1256  */
sig_winchg()1257 void sig_winchg() {
1258     if (isendwin()) return;
1259     endwin();
1260     refresh();
1261     clear();
1262     set_term(sstdout);
1263 
1264     wresize(main_win, LINES - RESROW, COLS);
1265     wresize(input_win, RESROW, COLS);
1266     ui_mv_bottom_bar();
1267     ui_update(TRUE);
1268     flushinp();
1269 
1270     return;
1271 }
1272 
1273 
1274 #ifdef XLUA
1275 /**
1276  * \brief Print error of lua scripts
1277  *
1278  * \param[in] L
1279  * \param[in] msg
1280  *
1281  * \return none
1282  */
ui_bail(lua_State * L,char * msg)1283 void ui_bail(lua_State *L, char * msg) {
1284     extern char stderr_buffer[1024];
1285     fprintf(stderr,"FATAL ERROR: %s: %s\n", msg, lua_tostring(L, -1));
1286     move(0, 0);
1287     clrtobot();
1288     wrefresh(stdscr);
1289     set_term(sstderr);
1290     move(0, 0);
1291     clrtobot();
1292     status_line_empty = 1;
1293     clearok(stdscr, TRUE);
1294     mvprintw(0, 0, "%s", stderr_buffer);
1295     stderr_buffer[0]='\0';
1296     fseek(stderr, 0, SEEK_END);
1297     refresh();
1298     getch();
1299     set_term(sstdout);
1300     clearok(stdscr, TRUE);
1301     ui_show_header();
1302     refresh();
1303     ui_update(TRUE);
1304 }
1305 #endif
1306 
1307 
1308 /**
1309  * \brief Show a message in the screen
1310  *
1311  * \details This function shows a message on the screen and waits for user
1312  * confirmation between a couple of options defined on valid (wchar *)
1313  * parameter.
1314  * \return wchar_t indicating user answer
1315  */
ui_query_opt(wchar_t * initial_msg,wchar_t * valid)1316 wchar_t ui_query_opt(wchar_t * initial_msg, wchar_t * valid) {
1317     if (! wcslen(initial_msg)) return L'\0';
1318     sc_info("%ls", initial_msg);
1319     wint_t wd = -1;
1320     wchar_t wdc[2] = L"";
1321     int res;
1322 
1323     curs_set(1);
1324     wtimeout(input_pad, -1);
1325     move(0, wcslen(initial_msg) + 1);
1326     while ((res = wstr_in_wstr(valid, wdc)) == -1) {
1327         wget_wch(input_pad, &wd);
1328         swprintf(wdc, FBUFLEN, L"%lc", wd);
1329     }
1330     wtimeout(input_pad, TIMEOUT_CURSES);
1331     curs_set(0);
1332     return wdc[0];
1333 }
1334 
1335 
1336 /**
1337  * \brief Read text from stdin
1338  * \param[in] initial_msg
1339  * \return user input
1340  */
ui_query(char * initial_msg)1341 char * ui_query(char * initial_msg) {
1342     char * hline = (char *) malloc(sizeof(char) * BUFFERSIZE);
1343     hline[0]='\0';
1344 
1345     // curses is not enabled
1346     if ( get_conf_int("nocurses")) {
1347         if (strlen(initial_msg)) wprintf(L"%s", initial_msg);
1348 
1349         if (fgets(hline, BUFFERSIZE-1, stdin) == NULL)
1350             hline[0]='\0';
1351 
1352         clean_carrier(hline);
1353         return hline;
1354     }
1355 
1356     // curses is enabled
1357     int loading_o;
1358     if (loading) {
1359         loading_o=loading;
1360         loading=0;
1361         ui_update(0);
1362         loading=loading_o;
1363     }
1364     curs_set(1);
1365 
1366     // show initial message
1367     if (strlen(initial_msg)) sc_info(initial_msg);
1368 
1369     // ask for input
1370     wtimeout(input_pad, -1);
1371     notimeout(input_pad, TRUE);
1372     //wmove(input_pad, 0, 0);
1373     wmove(input_pad, 0, strlen(initial_msg));
1374     //wclrtoeol(input_pad);
1375     ui_refresh_pad(0);
1376 
1377     int d = wgetch(input_pad);
1378 
1379     while (d != OKEY_ENTER && d != OKEY_ESC) {
1380         if (d == OKEY_BS || d == OKEY_BS2) {
1381             del_char(hline, strlen(hline) - 1);
1382         } else {
1383             sprintf(hline + strlen(hline), "%c", d);
1384         }
1385 
1386         mvwprintw(input_pad, 0, 0, "%s", hline);
1387         wclrtoeol(input_pad);
1388         ui_refresh_pad(0);
1389         d = wgetch(input_pad);
1390     }
1391     if (d == OKEY_ESC) hline[0]='\0';
1392 
1393     // go back to spreadsheet
1394     noecho();
1395     curs_set(0);
1396     wtimeout(input_pad, TIMEOUT_CURSES);
1397     wmove(input_win, 0,0);
1398     wclrtoeol(input_win);
1399     wmove(input_pad, 0,0);
1400     wclrtoeol(input_pad);
1401     status_line_empty = 1;
1402     ui_refresh_pad(0);
1403     return hline;
1404 }
1405 
1406 
1407 /**
1408  * \brief Set a color
1409  * if bg_override != -1 (DEFAULT_COLOR) set this instead of uc->bg
1410  *
1411  * \param[in] w
1412  * \param[in] uc
1413  * \param[in} bg_override
1414  * \return none
1415  */
ui_set_ucolor(WINDOW * w,struct ucolor * uc,int bg_override)1416 void ui_set_ucolor(WINDOW * w, struct ucolor * uc, int bg_override) {
1417     short color;
1418     long attr = A_NORMAL;
1419     if (uc->bold)      attr |= A_BOLD;
1420 
1421 #ifdef A_ITALIC
1422     if (uc->italic)    attr |= A_ITALIC;
1423 #endif
1424     if (uc->dim)       attr |= A_DIM;
1425     if (uc->reverse)   attr |= A_REVERSE;
1426     if (uc->standout)  attr |= A_STANDOUT;
1427     if (uc->blink)     attr |= A_BLINK;
1428     if (uc->underline) attr |= A_UNDERLINE;
1429 
1430     // see in ui_start_colors() the explanation of this
1431     int def = 9;
1432     if (COLORS > 8) def = 33;
1433     //
1434 
1435     if (uc->bg == NONE_COLOR || uc->fg == NONE_COLOR) {
1436         // leave colors intact
1437         // just apply other ncurses attributes
1438         attr_t a;
1439         wattr_get(w, &a, &color, NULL);
1440     } else if (bg_override == DEFAULT_COLOR) {
1441         color = (uc->fg+1)*def + uc->bg + 2;
1442     } else {
1443         color = (uc->fg+1)*def + bg_override + 2;
1444     }
1445     wattr_set (w, attr | COLOR_PAIR(color), color, NULL);
1446 }
1447 
1448 
1449 /**
1450  * \brief ui_start_colors()
1451  *
1452  * \return none
1453  */
ui_start_colors()1454 void ui_start_colors() {
1455     if (! has_colors()) return;
1456     int i, j;
1457 
1458     // Initialize all possible 81 init pairs
1459     use_default_colors();
1460 
1461     /* ncurses has 8 colors, but we need to keep 1 slot more for
1462      * default terminal background and foreground.
1463      * bg can be other than black.
1464      * we also have 24 custom colors.
1465      * that makes 33 x 33 combinations
1466      */
1467     int def = 9;
1468     if (COLORS > 8) def = 33;
1469     for (i=0; i < def; i++) {     // fg
1470         for (j=0; j < def; j++) { // bg
1471             /*
1472              * NOTE: calling init_pair with -1 sets it with default
1473              * terminal foreground and background colors
1474              */
1475 #if defined(NCURSES_VERSION_MAJOR) && (( NCURSES_VERSION_MAJOR > 5 && defined(NCURSES_VERSION_MINOR) && NCURSES_VERSION_MINOR > 0) || NCURSES_VERSION_MAJOR > 6)
1476             init_extended_pair( i*def+j+1, i-1, j-1); // i is fg and j is bg
1477 #else
1478             init_pair(i*def+j+1, i-1, j-1); // i is fg and j is bg
1479 #endif
1480         }
1481     }
1482 }
1483 
1484 
1485 /**
1486  * \brief
1487  * UI function thats called after SIGTSTP signal.
1488  * \return none
1489  */
ui_pause()1490 void ui_pause() {
1491     def_prog_mode();
1492     set_term(sstderr);
1493     endwin();
1494     return;
1495 }
1496 
1497 
1498 /**
1499  * \brief
1500  * UI function thats called after SIGCONT signal.
1501  * \return none
1502  */
ui_resume()1503 void ui_resume() {
1504     set_term(sstdout);
1505     reset_prog_mode();
1506     clearok(stdscr, TRUE);
1507     sig_winchg();
1508 
1509     return;
1510 }
1511 
1512 
1513 /**
1514  * \brief
1515  * UI function thats moves input bar to bottom or back to the top
1516  * \return none
1517  */
ui_mv_bottom_bar()1518 void ui_mv_bottom_bar() {
1519     mvwin(main_win, get_conf_int("input_bar_bottom") ? 0 : RESROW, 0);
1520     mvwin(input_win, get_conf_int("input_bar_bottom") ? LINES-RESROW : 0, 0);
1521     return;
1522 }
1523 
1524 
1525 /**
1526  * \brief
1527  * UI function thats refresh input_pad
1528  * \return none
1529  */
ui_refresh_pad(int scroll)1530 void ui_refresh_pad(int scroll) {
1531     prefresh(input_pad, 0, scroll, get_conf_int("input_bar_bottom") ? LINES-RESROW+1: RESROW-1, 0,
1532              get_conf_int("input_bar_bottom") ? LINES-RESROW+1: RESROW-1, COLS-1);
1533 }
1534 
1535 
1536 /**
1537  * \brief
1538  * function thats handles mouse movements
1539  * \return none
1540  */
1541 #ifdef MOUSE
ui_handle_mouse(MEVENT event)1542 void ui_handle_mouse(MEVENT event) {
1543     if (isendwin()) return;
1544 
1545     // if out of range return
1546     int i, j, r = 0, c = 0;
1547     if ( event.x < RESCOL || ( get_conf_int("input_bar_bottom") && (event.y == 0 || event.y >= LINES - RESROW)) ||
1548        ( !get_conf_int("input_bar_bottom") && (event.y <= RESROW))) return;
1549 
1550     // if mode is not handled return
1551     if (curmode != NORMAL_MODE && curmode != INSERT_MODE && curmode != COMMAND_MODE) return;
1552 
1553     // scroll in normal mode
1554 #ifdef BUTTON5_PRESSED
1555     if (curmode == NORMAL_MODE && (event.bstate & BUTTON4_PRESSED || // scroll up
1556         event.bstate & BUTTON5_PRESSED)) { // scroll down
1557             int n = calc_mobile_rows(NULL);
1558             if (get_conf_int("half_page_scroll")) n = n / 2;
1559             lastcol = curcol;
1560             lastrow = currow;
1561             currow = event.bstate & BUTTON5_PRESSED ? forw_row(n)->row : back_row(n)->row;
1562             if (event.bstate & BUTTON5_PRESSED) scroll_down(n);
1563             else scroll_up(n);
1564             unselect_ranges();
1565             ui_update(TRUE);
1566         return;
1567     }
1568 #else
1569     sc_error("Cannot handle mouse scroll. Please update your ncurses library");
1570     return;
1571 #endif
1572 
1573     // return if not a single click
1574     if (! (event.bstate & BUTTON1_CLICKED)) return;
1575 
1576     c = event.x - RESCOL;
1577     r = event.y - RESROW + (get_conf_int("input_bar_bottom") ? 1 : - 1);
1578 
1579     int mobile_cols = calc_mobile_cols(NULL);
1580     int mobile_rows = calc_mobile_rows(NULL);
1581     int scr_col = 0;
1582     int scr_row = 0;
1583 
1584     for (i = 0; i < maxcols; i++) {
1585         if (col_hidden[i])
1586             continue;
1587         if (! col_frozen[i]) {
1588             if (i < offscr_sc_cols)
1589                 continue;
1590             if (--mobile_cols < 0)
1591                 continue;
1592         }
1593 
1594         scr_col += fwidth[i];
1595         if (scr_col >= c + 1) break;
1596     }
1597 
1598     // same for rows
1599     for (j = 0; j < maxrows; j++) {
1600         if (row_hidden[j])
1601             continue;
1602         if (! row_frozen[j]) {
1603             if (j < offscr_sc_rows)
1604                 continue;
1605             if (--mobile_rows < 0)
1606                 continue;
1607         }
1608 
1609         scr_row += row_format[j];
1610         if (scr_row >= r + 1) break;
1611     }
1612 
1613     if (i >= maxcols || j >= maxrows) return;
1614 
1615     // if in normal mode, change currow and curcol
1616     if (curmode == NORMAL_MODE) {
1617         curcol = i;
1618         currow = j;
1619         unselect_ranges();
1620 
1621     // if in insert or command mode, we add the selected cell to inputbar
1622     } else if (curmode == COMMAND_MODE || curmode == INSERT_MODE) {
1623         wchar_t cline [BUFFERSIZE];
1624         int count;
1625         swprintf(cline, BUFFERSIZE, L"%s%d", coltoa(i), j);
1626         for(count = 0; count < wcslen(cline); count++) ins_in_line(cline[count]);
1627         ui_show_header();
1628         return;
1629     }
1630 
1631     ui_update(TRUE);
1632 }
1633 #endif
1634