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