text: /*******************************************************************************
   * Copyright (c) 2013-2017, Andrés Martinelli <andmarti@gmail.com              *
   * All rights reserved.                                                        *
   *                                                                             *
   * This file is a part of SC-IM                                                *
   *                                                                             *
   * SC-IM is a spreadsheet program that is based on SC. The original authors    *
   * of SC are James Gosling and Mark Weiser, and mods were later added by       *
   * Chuck Martin.                                                               *
   *                                                                             *
   * Redistribution and use in source and binary forms, with or without          *
   * modification, are permitted provided that the following conditions are met: *
   * 1. Redistributions of source code must retain the above copyright           *
   *    notice, this list of conditions and the following disclaimer.            *
   * 2. Redistributions in binary form must reproduce the above copyright        *
   *    notice, this list of conditions and the following disclaimer in the      *
   *    documentation and/or other materials provided with the distribution.     *
   * 3. All advertising materials mentioning features or use of this software    *
   *    must display the following acknowledgement:                              *
   *    This product includes software developed by Andrés Martinelli            *
   *    <andmarti@gmail.com>.                                                    *
   * 4. Neither the name of the Andrés Martinelli nor the                        *
   *   names of other contributors may be used to endorse or promote products    *
   *   derived from this software without specific prior written permission.     *
   *                                                                             *
   * THIS SOFTWARE IS PROVIDED BY ANDRES MARTINELLI ''AS IS'' AND ANY            *
   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED   *
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE      *
   * DISCLAIMED. IN NO EVENT SHALL ANDRES MARTINELLI BE LIABLE FOR ANY           *
   * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  *
   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;*
   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  *
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE       *
   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
   *******************************************************************************/
  
  /**
   * \file tui.c
   * \author Andrés Martinelli <andmarti@gmail.com>
   * \date 2017-07-18
   * \brief  This is the ncurses implementation of sc-im user interface,
   *
   * \details This is the ncurses implementation of sc-im user interface,
   * or called tui. It mainly consists on the following two windows:
   * main_win: window that shows the grid
   * input_win: status bar window (or called header) and stdin input
   *
   * these are the functions called outside tui.c:
   * ui_query               // function to read text from stdin
   * ui_query_opt           // function that shows a message and waits for confirmation between a couple of defined options
   * yyerror                // error routine for yacc parser
   * ui_bail                // function to print errors of lua scripts
   * ui_winchg              // function that handles SIGWINCH
   * ui_show_celldetails    // function that shows cell details in header bar
   * ui_start_colors        // exclusive ui startup routine for colors
   * ui_pause
   * ui_resume
   *
   * these are local functions that might not be needed to reimplement if writing another ui:
   * ui_set_ucolor          // function called internally for setting a color
   * ui_show_content
   * ui_show_sc_col_headings
   * ui_add_cell_detail     // Add details of an ent to a char * received as a parameter. used for input_win
   *
   * ANYONE WHO WANTS TO PORT THIS TO ANOTHER UI, WOULD JUST NEED TO REIMPLEMENT THIS FILE
   * AND HELP() IN HELP.C
   *
   *
   * if not working with ncurses, you should also have to define LINES and COLS macros in Xui.h as well.
   * see ui example inside /files folder
   */
  
  #include <string.h>
  #include <ncurses.h>
  #include <stdio.h>
  #include <time.h>
  #include <locale.h>
  #include <stdlib.h>
  #include <stdarg.h>
  
  #include "main.h"
  #include "conf.h"
  #include "input.h"
  #include "tui.h"
  #include "range.h"
  #include "sc.h"
  #include "cmds.h"
  #include "cmds_visual.h"
  #include "conf.h"
  #include "version.h"
  #include "file.h"
  #include "format.h"
  #include "utils/string.h"
  #include "digraphs.h"
  
  extern struct dictionary * d_colors_param;
  extern int cmd_pending;
  extern int cmd_multiplier;
  extern char insert_edit_submode;
  
  WINDOW * main_win;
  WINDOW * input_win;
  SCREEN * sstderr;
  SCREEN * sstdout;
  
  /* for the "show_cursor" option */
  int status_line_empty;
  int curwinrow, curwincol;
  
  /**
   * \brief Called to start UI
   *
   * \return none
   */
  
  void ui_start_screen() {
      sstderr = newterm(NULL, stderr, stdin);
      noecho();
      sstdout = newterm(NULL, stdout, stdin);
      set_term(sstdout);
  
      main_win = newwin(LINES - RESROW, COLS, get_conf_int("input_bar_bottom") ? 0 : RESROW, 0);
      input_win = newwin(RESROW, COLS, get_conf_int("input_bar_bottom") ? LINES-RESROW : 0, 0);
      status_line_empty = 1;
  
      #ifdef USECOLORS
      if (has_colors()) {
          start_color();
  
          if (get_d_colors_param() == NULL) {
              start_default_ucolors();
  
              // in case we decide to change colors
              // Create a dictionary and save equivalences between macros and
              // values defined in '.sc' files
              set_colors_param_dict();
          }
          ////wbkgd(main_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS+1) + ucolors[DEFAULT].bg + 2));
          ////wbkgd(input_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS+1) + ucolors[DEFAULT].bg + 2));
      }
      #endif
  
      wtimeout(input_win, TIMEOUT_CURSES);
      noecho();
      curs_set(0);
  
      // Mouse support
      #ifdef MOUSE
      mmask_t old;
      mousemask (ALL_MOUSE_EVENTS, &old);
      #endif
  
      #ifndef NETBSD
      if ((char *) getenv ("ESCDELAY") == NULL) set_escdelay(ESC_DELAY);
      #endif
      cbreak();
      keypad(input_win, 1);
  }
  
  /**
   * \brief Called to stop UI
   *
   * \return none
   */
  
  void ui_stop_screen() {
      #ifdef USECOLORS
          //if (get_d_colors_param() != NULL)
          free_colors_param_dict();
      #endif
      move(0, 0);
      clrtobot();
      refresh();
  
      set_term(sstdout);
      endwin();
      set_term(sstderr);
      endwin();
      return;
  }
  
  /**
   * \brief Asks the user input from stdin (non-blocking)
   *
   * \details This function asks the user for input from stdin. Should be
   * non-blocking and should return -1 when no key was pressed; 0 when
   * key was pressed. It receives wint_t as a parameter. When a valid
   * key is pressed, its value is then updated in that win_t variable.
   *
   * \param[in] wd
   *
   * \return 0 on key press; -1 otherwise
   */
  
  int ui_getch(wint_t * wd) {
      return wget_wch(input_win, wd);
  }
  
  /**
   * \brief Asks the user for input from stdin (blocking)
   *
   * \details This function asks the user for input from stdin. Should be
   * blocking and should return -1 when ESC was pressed; 0 otherwise. It
   * receives * wint_t as a parameter. When a valid key is pressed, its value
   * is then updated in that wint_t variable.
   *
   * \param[in] wd
   *
   * \return -1 on ESC press; 0 otherwise
   */
  
  int ui_getch_b(wint_t * wd) {
      wint_t digraph = 0;
      wtimeout(input_win, -1);
      move(0, rescol + inputline_pos + 1);
      wget_wch(input_win, wd);
      if (*wd == ctl('k')) {
          wget_wch(input_win, wd);
          if (*wd != OKEY_ESC) {
              digraph = *wd;
              wget_wch(input_win, wd);
              if (*wd != OKEY_ESC)
                  *wd = get_digraph(digraph, *wd);
          }
      }
      wtimeout(input_win, TIMEOUT_CURSES);
      if ( *wd != OKEY_ESC ) return 0;
      return -1;
  }
  
  /**
   * \brief Used for sc_info, sc_error, and sc_debug macros
   *
   * \param[in] s
   * \param[in] type
   * \return none
   */
  
  void ui_sc_msg(char * s, int type, ...) {
      if (type == DEBUG_MSG && ! get_conf_int("debug")) return;
      char t[BUFFERSIZE];
      va_list args;
      va_start(args, type);
      vsprintf (t, s, args);
      if ( ! get_conf_int("nocurses")) {
  #ifdef USECOLORS
          if (type == ERROR_MSG)
              ui_set_ucolor(input_win, &ucolors[ERROR_MSG], DEFAULT_COLOR);
          else
              ui_set_ucolor(input_win, &ucolors[INFO_MSG], DEFAULT_COLOR);
  #endif
          mvwprintw(input_win, 1, 0, "%s", t);
          wclrtoeol(input_win);
          wmove(input_win, 1, 0);
          status_line_empty = 0;
  
          if (type == DEBUG_MSG || (loading && type == ERROR_MSG)) {
              wtimeout(input_win, -1);
              wgetch(input_win);
              wtimeout(input_win, TIMEOUT_CURSES);
          }
          wrefresh(input_win);
  
      } else if (type == VALUE_MSG && get_conf_value("output") != NULL && fdoutput != NULL) {
          fwprintf(fdoutput, L"%s\n", t);
      } else if (type == VALUE_MSG) {
          if (fwide(stdout, 0) >0)
              //wprintf(L"wide %s\n", t);
              wprintf(L"%s\n", t);
          else
              //printf("nowide %s\n", t);
              printf("%s\n", t);
          fflush(stdout);
      } else {
          if (fwide(stderr, 0) >0)
              //wprintf(L"wide %s\n", t);
              fwprintf(stderr, L"%s\n", t);
          else
              //printf("nowide %s\n", t);
              fprintf(stderr, "%s\n", t);
          fflush(stderr);
      }
      va_end(args);
      return;
  }
  
  /**
   * \brief Welcome screen function used when starting sc-im without a file
   * as a parameter
   *
   * \return none
   */
  
  void ui_do_welcome() {
      char * msg_title = "SC-IM - SpreadSheet Calculator Improvised";
      char * msg_by = "An SC fork by Andrés Martinelli";
      char * msg_version = rev;
      char * msg_help  = "Press  :help<Enter>  to get help         ";
      char * msg_help2 = "Press  <Enter>       to enter NORMAL mode";
      int i;
  
      #ifdef USECOLORS
  ////    wbkgd(main_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS+1) + ucolors[DEFAULT].bg + 2));
  ////    wbkgd(input_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS+1) + ucolors[DEFAULT].bg + 2));
      #endif
  
      // show headings
      int mxcol = offscr_sc_cols + calc_offscr_sc_cols() - 1;
      int mxrow = offscr_sc_rows + calc_offscr_sc_rows() - 1;
      ui_show_sc_col_headings(main_win, mxcol);
      ui_show_sc_row_headings(main_win, mxrow);
  
      #ifdef USECOLORS
      ui_set_ucolor(main_win, &ucolors[WELCOME], DEFAULT_COLOR);
      #endif
  
      // show message
      mvwaddstr(main_win, LINES/2-3, COLS/2-strlen(msg_title)/2  , msg_title);
      mvwaddstr(main_win, LINES/2-2, COLS/2-strlen(msg_by)/2     , msg_by);
      mvwaddstr(main_win, LINES/2-1, COLS/2-strlen(msg_version)/2, msg_version);
  
      #ifdef USECOLORS
      ui_set_ucolor(main_win, &ucolors[WELCOME], DEFAULT_COLOR);
      #endif
      for (i=0; msg_help[i] != '\0'; i++) {
          if (msg_help[i] == '<') {
              #ifdef USECOLORS
              ui_set_ucolor(main_win, &ucolors[NUMB], DEFAULT_COLOR);
              #endif
          }
          mvwaddstr(main_win, LINES/2, COLS/2-strlen(msg_help)/2+i, &msg_help[i]);
          if (msg_help[i] == '>') {
              #ifdef USECOLORS
              ui_set_ucolor(main_win, &ucolors[WELCOME], DEFAULT_COLOR);
              #endif
          }
      }
      for (i=0; msg_help2[i] != '\0'; i++) {
          if (msg_help2[i] == '<') {
              #ifdef USECOLORS
              ui_set_ucolor(main_win, &ucolors[NUMB], DEFAULT_COLOR);
              #endif
          }
          mvwaddstr(main_win, LINES/2+1, COLS/2-strlen(msg_help2)/2+i, &msg_help2[i]);
          if (msg_help2[i] == '>') {
              #ifdef USECOLORS
              ui_set_ucolor(main_win, &ucolors[WELCOME], DEFAULT_COLOR);
              #endif
          }
      }
      wrefresh(main_win);
  
      if (get_conf_int("show_cursor")) {
          /* land cursor next to the help line */
          curwinrow = LINES/2;
          curwincol = COLS/2-strlen(msg_help)/2-1;
          status_line_empty = 1;
      }
  
      return;
  }
  
  /**
   * \brief Refreshes screen grid
   *
   * \details This function is used to refresh the screen content. If 
   * the header flag is set, the first column of the screen gets
   * refreshed.
   *
   * \param[in] header
   *
   * \return none
   */
  
  void ui_update(int header) {
      if (loading) return;
      if (cmd_multiplier > 1) return;
      if (get_conf_int("nocurses")) return;
  
      if (header) {
      #ifdef USECOLORS
  ////    wbkgd(main_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS+1) + ucolors[DEFAULT].bg + 2));
          // comment this to prevent bold to be reset
          //wbkgd(input_win, COLOR_PAIR((ucolors[DEFAULT].fg+1) * (COLORS+1) + ucolors[DEFAULT].bg + 2));
      #endif
  
          // Clean from top to bottom
          wmove(main_win, 0, rescol);
          wclrtobot(main_win);
          // comment this to prevent info message to be erased
          //wmove(input_win, 0, 0);
          //wclrtobot(input_win);
          ui_show_celldetails(); // always before ui_print_mode
          ui_print_mode();
      }
  
      /*
       * Calculate offscreen rows and columns
       *
       * mxcol is the last visible column in screen grid.
       * for instance, if mxcol is 8, last visible column would be I
       * mxrow is the last visible row in screen grid
       *
       * offscr_sc_cols are the number of columns left at the left of start of grid.
       * for instance, if offscr_sc_cols is 4. the first visible column in grid would be column E.
       *
       * there is a special behaviour when frozen columns or rows exists.
       * center_hidden_rows and center_hidden_columns are the number of rows and columns between
       * a frozen range and the first column or row visible.
       * example: if columns A and B are frozen, and center_hidden_cols is 4,
       * your grid would start with columns A, B, G, H..
       */
      int off_cols = calc_offscr_sc_cols();
      int off_rows = calc_offscr_sc_rows();
      int mxcol = offscr_sc_cols + off_cols - 1;
      int mxrow = offscr_sc_rows + off_rows - 1;
      //sc_debug("out: off:%d, center:%d, mxcol:%d %s, maxcols:%d", offscr_sc_cols, center_hidden_cols, mxcol, coltoa(mxcol), maxcols);
  
      /* You can't hide the last row or col */
      while (row_hidden[currow])
          currow++;
      while (col_hidden[curcol])
          curcol++;
  
  
      // Show the content of the cells
      // Numeric values, strings.
      ui_show_content(main_win, mxrow, mxcol);
  
      // Show sc_col headings: A, B, C, D..
      ui_show_sc_col_headings(main_win, mxcol);
  
      // Show sc_row headings: 0, 1, 2, 3..
      ui_show_sc_row_headings(main_win, mxrow);
  
      if (status_line_empty && get_conf_int("show_cursor")) {
          // Leave cursor on selected cell when no status message
          wmove(main_win, curwinrow, curwincol);
      }
  
      // Refresh curses windows
      wrefresh(main_win);
  
      return;
  }
  
  /**
   * \brief Enable cursor and echo depending on the current screen mode
   *
   * \return none
   */
  
  void ui_handle_cursor() {
      switch (curmode) {
          case COMMAND_MODE:
              noecho();
              curs_set(1);
              break;
          case INSERT_MODE:
          case EDIT_MODE:
              noecho();
              curs_set(1); // changed for NETBSD compatibility
              break;
          default:
              noecho();
              curs_set(0);
      }
      return;
  }
  
  /**
   * \brief Print string with alignment
   *
   * \details Internal function to print a string with alignment.
   *
   * JUSTIF: 0 left shift
   *
   * JUSTIF: 1 right shift
   *
   * \param[in] win
   * \param[in] word
   * \param[in] row
   * \param[in] justify
   *
   * \return none
   */
  
  void ui_write_j(WINDOW * win, const char * word, const unsigned int row, const unsigned int justif) {
      (justif == 0) ? (wmove(win, row, 0) && wclrtoeol(win)) : wmove(win, row, COLS - strlen(word));
      wprintw(win, "%s", word);
      return;
  }
  
  /**
   * \brief Print multiplier and pending operator to the status bar
   *
   * \return none
   */
  
  void ui_print_mult_pend() {
      if (curmode != NORMAL_MODE && curmode != VISUAL_MODE && curmode != EDIT_MODE) return;
  
      int row_orig, col_orig;
      getyx(input_win, row_orig, col_orig);
  
      #ifdef USECOLORS
      ui_set_ucolor(input_win, &ucolors[MODE], DEFAULT_COLOR);
      #endif
      // Show multiplier and pending operator
      char strm[COLS];
      strm[0]='\0';
      if (cmd_multiplier > 0) sprintf(strm, "%d", cmd_multiplier);
      if (cmd_pending) {
          strcat(strm, "?");
      }
  
      char field[rescol+1];
      field[0]='\0';
      sprintf(field, "%0*d", rescol - (int) strlen(strm), 0);
      subst(field, '0', ' ');
      strcat(strm, field);
  
      mvwprintw(input_win, 0, 0, "%s", strm);
  
      // Return cursor to previous position
      wmove(input_win, row_orig, col_orig);
      wrefresh(input_win);
  
      if (status_line_empty && curmode != EDIT_MODE && get_conf_int("show_cursor")) {
          // Leave cursor on selected cell when no status message
          wmove(main_win, curwinrow, curwincol);
          wrefresh(main_win);
      }
  }
  
  /**
   * \brief Show first and second row (header). Handle cursor position.
   *
   * \return none
   */
  
  void ui_show_header() {
      ui_clr_header(0);
      ui_clr_header(1);
  
      ui_print_mult_pend();
  
      // Show current mode
      ui_print_mode();
  
      // Print input text
      switch (curmode) {
          case COMMAND_MODE:
              mvwprintw(input_win, 0, rescol, ":%ls", inputline);
              wmove(input_win, 0, inputline_pos + 1 + rescol);
              break;
          case INSERT_MODE:
              mvwprintw(input_win, 0, 1 + rescol, "%ls", inputline);
              wmove(input_win, 0, inputline_pos + 1 + rescol);
              break;
          case EDIT_MODE:
              mvwprintw(input_win, 0, rescol, " %ls", inputline);
              wmove(input_win, 0, inputline_pos + 1 + rescol);
      }
      wrefresh(input_win);
      return;
  }
  
  /**
   * \brief Clean a whole row
   *
   * \param[in] i Line to clean
   *
   * \return none
   */
  
  void ui_clr_header(int i) {
      int row_orig, col_orig;
      getyx(input_win, row_orig, col_orig);
      if (col_orig > COLS) col_orig = COLS - 1;
  
      wmove(input_win, i, 0);
      wclrtoeol(input_win);
  
      // Return cursor to previous position
      wmove(input_win, row_orig, col_orig);
  
      if (i == 1) status_line_empty = 1;
  
      return;
  }
  
  /**
   * \brief Print current mode in the first row
   *
   * \details Print the current mode in the first row. Print ':' (colon)
   * or submode indicator.
   *
   * \return none
   */
  
  void ui_print_mode() {
      unsigned int row = 0; // Print mode in first row
      char strm[PATHLEN+22] = "";
  
      #ifdef USECOLORS
      ui_set_ucolor(input_win, &ucolors[MODE], DEFAULT_COLOR);
      #endif
  
      strm[0] = '\0';
      if (get_conf_int("filename_with_mode") && curfile[0]) {
          sprintf(strm, "%s ", curfile);
      }
  
      if (curmode == NORMAL_MODE) {
          strcat(strm, " -- NORMAL --");
          ui_write_j(input_win, strm, row, RIGHT);
  
      } else if (curmode == INSERT_MODE) {
          strcat(strm, " -- INSERT --");
          ui_write_j(input_win, strm, row, RIGHT);
  
          #ifdef USECOLORS
          ui_set_ucolor(input_win, &ucolors[INPUT], DEFAULT_COLOR);
          #endif
          // Show submode (INSERT)
          mvwprintw(input_win, 0, 0 + rescol, "%c", insert_edit_submode);
          //wmove(input_win, 0, 1); commented on 01/06
  
      } else if (curmode == EDIT_MODE) {
          strcat(strm, "   -- EDIT --");
          ui_write_j(input_win, strm, row, RIGHT);
  
      } else if (curmode == VISUAL_MODE) {
          strcat(strm, " -- VISUAL --");
          if (visual_submode != '0')
              strcpy(strm, " << VISUAL >>");
          ui_write_j(input_win, strm, row, RIGHT);
  
      } else if (curmode == COMMAND_MODE) {
          strcat(strm, "-- COMMAND --");
  
          ui_write_j(input_win, strm, row, RIGHT);
          #ifdef USECOLORS
          ui_set_ucolor(input_win, &ucolors[INPUT], DEFAULT_COLOR);
          #endif
          // muestro ':'
          mvwprintw(input_win, 0, 0 + rescol, ":");
          wmove(input_win, 0, 1 + rescol);
      }
  
      return;
  }
  
  /**
   * \brief Show sc_row headings: 0, 1, 2...
   *
   * \param[in] win
   * \param[in] mxrow
   *
   * \return none
   */
  
  void ui_show_sc_row_headings(WINDOW * win, int mxrow) {
      int row = 0;
      #ifdef USECOLORS
      if (has_colors()) ui_set_ucolor(win, &ucolors[HEADINGS], DEFAULT_COLOR);
      #endif
      int i;
      int freeze = freeze_ranges && (freeze_ranges->type == 'r' ||  freeze_ranges->type == 'a') ? 1 : 0;
  
      //for (i = 0; i < mxrow && i < maxrows; i++) {
      for (i = 0; i <= mxrow; i++) {
          if (i >= maxrows) {
              sc_error("i >= maxrows in ui_show_sc_row_headings. please check calc_offscr_sc_rows.");
              break;
          }
          // print rows in case freezen rows are before offscr_sc_rows
          if (i < offscr_sc_rows && !(freeze && i >= freeze_ranges->tl->row && i <= freeze_ranges->br->row)) continue;
  
          if (row_hidden[i]) continue;
  
          // skip center_hidden_rows
          if (freeze && ((
           i > freeze_ranges->br->row && i <= freeze_ranges->br->row + center_hidden_rows) || (
           i < freeze_ranges->tl->row && i >= freeze_ranges->tl->row - center_hidden_rows))) continue;
  
          srange * s = get_selected_range();
          if ( (s != NULL && i >= s->tlrow && i <= s->brrow) || i == currow ) {
              #ifdef USECOLORS
              if (has_colors()) ui_set_ucolor(win, &ucolors[CELL_SELECTION], DEFAULT_COLOR);
              #else
              wattron(win, A_REVERSE);
              #endif
          }
          mvwprintw (win, row+1, 0, "%*d ", rescol-1, i);
  
          #ifdef USECOLORS
          if (has_colors()) ui_set_ucolor(win, &ucolors[HEADINGS], DEFAULT_COLOR);
          #else
          wattroff(win, A_REVERSE);
          #endif
          row++;
      }
  }
  
  /**
   * \brief Show sc_col headings: A, B, C...
   *
   * \param[in] win
   * \param[in] mxcol last column printed to the screen
   *
   * \return none
   */
  
  void ui_show_sc_col_headings(WINDOW * win, int mxcol) {
      int i, col = rescol;
      int freeze = freeze_ranges && (freeze_ranges->type == 'c' ||  freeze_ranges->type == 'a') ? 1 : 0;
      //int l;
  
  
      wmove(win, 0, 0);
      wclrtoeol(win);
  
      //for (i = 0; i <= mxcol && i < maxcols; i++) {
      for (i = 0; i <= mxcol; i++) {
          #ifdef USECOLORS
          if (has_colors() && i % 2 == 0)
              ui_set_ucolor(win, &ucolors[HEADINGS], DEFAULT_COLOR);
          else if (i % 2 == 1)
              ui_set_ucolor(win, &ucolors[HEADINGS_ODD], DEFAULT_COLOR);
          #endif
  
          if (i >= maxcols) {
              sc_error("i >= maxcols in ui_show_sc_col_headings. please check calc_offscr_sc_cols.");
              break;
          }
          // print cols in case freezen columns are before offscr_sc_cols
          if (i < offscr_sc_cols && !(freeze && i >= freeze_ranges->tl->col && i <= freeze_ranges->br->col)) continue;
  
          if (col_hidden[i]) continue;
  
          // skip center_hidden_cols
          if (freeze && ((
           i > freeze_ranges->br->col && i <= freeze_ranges->br->col + center_hidden_cols) || (
           i < freeze_ranges->tl->col && i >= freeze_ranges->tl->col - center_hidden_cols))) continue;
  
          srange * s = get_selected_range();
          if ( (s != NULL && i >= s->tlcol && i <= s->brcol) || i == curcol ) {
              #ifdef USECOLORS
              if (has_colors()) ui_set_ucolor(win, &ucolors[CELL_SELECTION], DEFAULT_COLOR);
              #else
              wattron(win, A_REVERSE);
              #endif
          }
  
          int k = (fwidth[i] - 1) / 2;
          mvwprintw(win, 0, col, "%*s%-*s", k, "", fwidth[i] - k, coltoa(i));
  
          /* We also colorize the columns with alternate color
          #ifdef USECOLORS
          if (has_colors() && i % 2 == 0)
              ui_set_ucolor(win, &ucolors[GRID_PAIR], DEFAULT_COLOR);
          else if (i % 2 == 1)
              ui_set_ucolor(win, &ucolors[GRID_ODD], DEFAULT_COLOR);
  
          for (l=1; l<LINES-RESROW; l++) {
              mvwprintw(win, l, col, "%*s%", fwidth[i], " ");
          }
          #endif
          */
  
          col += fwidth[i];
          if (i == mxcol && COLS - col > 0)
              wclrtoeol(win);
  
          #ifdef USECOLORS
          if (has_colors() && i % 2 == 0)
              ui_set_ucolor(win, &ucolors[HEADINGS], DEFAULT_COLOR);
          else if (i % 2 == 1)
              ui_set_ucolor(win, &ucolors[HEADINGS_ODD], DEFAULT_COLOR);
          #else
          wattroff(win, A_REVERSE);
          #endif
      }
  }
  
  
  
  /**
   * \brief Show the content of the cell
   *
   * \param[in] win
   * \param[in] mxrow
   * \param[in] mxcol
   *
   * \return none
   */
  
  void ui_show_content(WINDOW * win, int mxrow, int mxcol) {
      register struct ent ** p;
      int row, col;
      int q_row_hidden = 0;
      int freezer = freeze_ranges && (freeze_ranges->type == 'r' ||  freeze_ranges->type == 'a') ? 1 : 0;
      int freezec = freeze_ranges && (freeze_ranges->type == 'c' ||  freeze_ranges->type == 'a') ? 1 : 0;
  
      //for (row = 0; row <= mxrow && row < maxrows; row++) {
      for (row = 0; row <= mxrow; row++) {
          if (row >= maxrows) {
              sc_error("i >= maxrows in show_content. please check calc_offscr_sc_rows.");
              break;
          }
  
          if (row < offscr_sc_rows
              && (freezer
              && row >= freeze_ranges->tl->row
              && row <= freeze_ranges->br->row)) {
              q_row_hidden--;
              continue;
          } else if (row < offscr_sc_rows
              && !(freezer
              && row >= freeze_ranges->tl->row
              && row <= freeze_ranges->br->row)) {
              //q_row_hidden++;
              continue;
          }
          if (row_hidden[row]) {
              q_row_hidden++;
              continue;
          }
  
          // skip center_hidden_cols
          if (freezer && (
          (row >= freeze_ranges->br->row && row > freeze_ranges->br->row && row <= freeze_ranges->br->row + center_hidden_rows) ||
          (row <= freeze_ranges->tl->row && row < freeze_ranges->tl->row && row >= freeze_ranges->tl->row - center_hidden_rows))) {
              q_row_hidden++;
              continue;
          }
  
          int r = row + 1 - offscr_sc_rows - q_row_hidden;
          int c = rescol;
          int nextcol;
          int fieldlen;
          col = 0;
  
          for (p = ATBL(tbl, row, col); col <= mxcol; p += nextcol - col, col = nextcol, c += fieldlen) {
          //for (p = ATBL(tbl, row, col); col <= mxcol && col < maxcols; p += nextcol - col, col = nextcol, c += fieldlen) {
              if (col >= maxcols) {
                  sc_error("i >= maxcols in show_content. please check calc_offscr_sc_cols.");
                  break;
              }
  
  
              nextcol = col + 1;
              fieldlen = fwidth[col];
  
              // print cols in case frozen columns are before offscr_sc_cols
              if (col < offscr_sc_cols
                  && !(freezec
                  && col >= freeze_ranges->tl->col
                  && col <= freeze_ranges->br->col)) {
                  c -= fieldlen;
                  continue;
              }
  
              if (col_hidden[col]) {
                  c -= fieldlen;
                  continue;
              }
  
              // skip center_hidden_cols
              if (freezec &&
              ((col >= freeze_ranges->br->col && col > freeze_ranges->br->col && col <= freeze_ranges->br->col + center_hidden_cols) ||
               (col <= freeze_ranges->tl->col && col < freeze_ranges->tl->col && col >= freeze_ranges->tl->col - center_hidden_cols))) {
                  c -= fieldlen;
                  continue;
              }
  
              //if ( (*p) == NULL) *p = lookat(row, col);
  
              // Clean format
              #ifdef USECOLORS
  
              if ((*p) && (*p)->cellerror) {                                  // cellerror
                  ui_set_ucolor(win, &ucolors[CELL_ERROR], ucolors[CELL_ERROR].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_PAIR].bg : ucolors[GRID_ODD].bg);
              } else if ((*p) && (*p)->v < 0) {                               // cell negative
                  ui_set_ucolor(win, &ucolors[CELL_NEGATIVE], ucolors[CELL_NEGATIVE].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_PAIR].bg : ucolors[GRID_ODD].bg);
              } else if ((*p) && (*p)->expr) {
                  ui_set_ucolor(win, &ucolors[EXPRESSION], ucolors[EXPRESSION].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_PAIR].bg : ucolors[GRID_ODD].bg);
              } else if ((*p) && (*p)->label) {                               // string
                  ui_set_ucolor(win, &ucolors[STRG], ucolors[STRG].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_PAIR].bg : ucolors[GRID_ODD].bg);
              } else if ((*p) && (*p)->flags & is_valid && ! (*p)->format) {  // numeric value
                  ui_set_ucolor(win, &ucolors[NUMB], ucolors[NUMB].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_PAIR].bg : ucolors[GRID_ODD].bg);
              } else if ((*p) && (*p)->format && (*p)->format[0] == 'd') {    // date format
                  ui_set_ucolor(win, &ucolors[DATEF], ucolors[DATEF].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_PAIR].bg : ucolors[GRID_ODD].bg);
              } else {
                  ui_set_ucolor(win, &ucolors[NORMAL], ucolors[NORMAL].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_PAIR].bg : ucolors[GRID_ODD].bg);
              }
              #endif
  
              // Cell color!
              if ((*p) && (*p)->ucolor != NULL) {
                  ui_set_ucolor(win, (*p)->ucolor, (*p)->ucolor->bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_PAIR].bg : ucolors[GRID_ODD].bg);
              }
  
  
              // Color selected cell
              if ((currow == row) && (curcol == col)) {
                  #ifdef USECOLORS
                      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_PAIR].bg : ucolors[GRID_ODD].bg);
                  #else
                      wattron(win, A_REVERSE);
                  #endif
              }
  
              // Color selected range
              int in_range = 0; // this is for coloring empty cells within a range
              srange * s = get_selected_range();
              if (s != NULL && row >= s->tlrow && row <= s->brrow && col >= s->tlcol && col <= s->brcol ) {
                  #ifdef USECOLORS
                      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_PAIR].bg : ucolors[GRID_ODD].bg);
                  #else
                      wattron(win, A_REVERSE);
                  #endif
                  in_range = 1; // local variable. this is for coloring empty cells within a range
              }
  
              /* Color empty cells inside a range */
              if ( in_range && row >= ranges->tlrow && row <= ranges->brrow &&
                   col >= ranges->tlcol && col <= ranges->brcol
                 ) {
                  #ifdef USECOLORS
                      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_PAIR].bg : ucolors[GRID_ODD].bg);
                  #else
                      wattron(win, A_REVERSE);
                  #endif
              }
  
              char num [FBUFLEN] = "";
              char text[FBUFLEN] = "";
              wchar_t out [FBUFLEN] = L"";
              char formated_s[FBUFLEN] = "";
              int res = -1;
              int align = 1;
  
              // If a numeric value exists
              if ( (*p) && (*p)->flags & is_valid) {
                  //show_numeric_content_of_cell(win, p, col, r, c);
  
                  res = ui_get_formated_value(p, col, formated_s);
                  // res = 0, indicates that in num we store a date
                  // res = 1, indicates a format is applied in num
                  if (res == 0 || res == 1) {
                      strcpy(num, formated_s);
                  } else if (res == -1) {
                      sprintf(num, "%.*f", precision[col], (*p)->v);
                  }
              }
  
              // If a string exists
              if ((*p) && (*p)->label) {
                  strcpy(text, (*p)->label);
                  align = 1;                               // right alignment
                  if ((*p)->flags & is_label) {            // center alignment
                      align = 0;
                  } else if ((*p)->flags & is_leftflush) { // left alignment
                      align = -1;
                  } else if (res == 0) {                   // res must ¿NOT? be zero for label to be printed // TODO CHECK!
                      text[0] = '\0';
                  }
              }
  
              if ((*p) && (*p)->cellerror == CELLERROR) {
                 (void) mvprintw(row + RESROW + 1 - offscr_sc_rows, c, "%*.*s", fieldlen, fieldlen, "ERROR");
                 align = 0;
                 strcpy(text, "ERROR");
                 num[0]='\0';
              }
              if ((*p) && (*p)->cellerror == CELLREF) {
                 (void) mvprintw(row + RESROW + 1 - offscr_sc_rows, c, "%*.*s", fieldlen, fieldlen, "REF");
                 align = 0;
                 strcpy(text, "REF");
                 num[0]='\0';
              }
  
  
              // repaint a blank cell, because of in range, or because we have a coloured empty cell!
              if ( !(*p) || (( !((*p)->flags & is_valid) && !(*p)->label ) && !((*p)->cellerror == CELLERROR)) ) {
                  if ( (currow == row && curcol == col) ||
                  ( in_range && row >= ranges->tlrow && row <= ranges->brrow &&
                  col >= ranges->tlcol && col <= ranges->brcol ) ) {
                      #ifdef USECOLORS
                      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_PAIR].bg : ucolors[GRID_ODD].bg);
                      #else
                      wattron(win, A_REVERSE);
                      #endif
                  } else if ( !(*p) || (*p)->ucolor == NULL) {
                      #ifdef USECOLORS
                      // When a long string does not fit in column.
                      ui_set_ucolor(win, &ucolors[STRG], ucolors[STRG].bg != DEFAULT_COLOR ? DEFAULT_COLOR : col % 2 == 0 ? ucolors[GRID_PAIR].bg : ucolors[GRID_ODD].bg);
                      #endif
                  }
  
                  #ifdef USECOLORS
                  if (has_colors() && get_conf_int("underline_grid")) {
                      attr_t attr;
                      short color;
                      wattr_get(win, &attr, &color, NULL);
                      wattr_set(win, attr | A_UNDERLINE, color, NULL);
                  }
                  #endif
  
                  // new implementation for wide char support
                  cchar_t cht[fieldlen];
                  wchar_t w;
                  int i, j;
                  for (i = 0; i < fieldlen; ) {
                      w = L' ';
                      j = mvwin_wchnstr (win, r, c + i, cht, 1);
                      if (j == OK && cht[0].chars[0] != L'\0')
                          w = cht[0].chars[0];
                      mvwprintw(win, r, c+i, "%lc", w);
                      i += wcwidth(w);
                  }
  
                  // we print text and number
              } else {
                  pad_and_align(text, num, fieldlen, align, (*p)->pad, out);
                  if (col == mxcol && wcswidth(out, wcslen(out)) > fieldlen)
                      out[ count_width_widestring(out, fieldlen) ] = L'\0';
  
              #ifdef USECOLORS
                  if (has_colors() && get_conf_int("underline_grid")) {
                      attr_t attr;
                      short color;
                      wattr_get(win, &attr, &color, NULL);
                      wattr_set(win, attr | A_UNDERLINE, color, NULL);
                  }
              #endif
  
                  mvwprintw(win, r, c, "%ls", out);
                  wclrtoeol(win);
              }
  
              if (currow == row && curcol == col) {
                  curwinrow = r;
                  curwincol = c;
                  if (out[0] == L'\0') {
                      // center the cursor on empty cells
                      curwincol += (fieldlen - 1)/2;;
                  } else if (out[0] == L' ') {
                      // cursor on last space but not beyond the middle
                      int i;
                      for (i = 0; i < (fieldlen - 1)/2 && out[i+1] == L' '; i++);
                      curwincol += i;
                  }
              }
  
              // clean format
              #ifndef USECOLORS
              wattroff(win, A_REVERSE);
              #endif
          }
      }
  }
  
  /**
   * \brief Add details of an ent to a char*
   *
   * \details Add details of an ent to a char * received as a parameter.
   * Used for 'input_win'
    *
   * \param[in] d
   * \param[in] p1
   *
   * \return none
   */
  
  void ui_add_cell_detail(char * d, struct ent * p1) {
      if ( ! p1 ) return;
  
      /* string expressions
      if (p1->expr && (p1->flags & is_strexpr)) {
          if (p1->flags & is_label)
              strcat(d, "|{");
          else
              strcat(d, (p1->flags & is_leftflush) ? "<{" : ">{");
          strcat(d, "??? } ");        // and this '}' is for vi %
  
      } else*/
  
      if (p1->label) {
          /* has constant label only */
          if (p1->flags & is_label)
              strcat(d, "|\"");
          else
              strcat(d, (p1->flags & is_leftflush) ? "<\"" : ">\"");
          strcat(d, p1->label);
          strcat(d, "\" ");
      }
  
      /* Display if cell is locked */
      if (p1 && p1->flags & is_locked)
           strcat(d, "[locked] ");
  
      // value part of cell:
      if (p1->flags & is_valid) {
          /* has value or num expr */
          if ( ( ! (p1->expr) ) || ( p1->flags & is_strexpr ) ) {
              sprintf(d + strlen(d), "%c", '[');
              (void) sprintf(d + strlen(d), "%.15g", p1->v);
              sprintf(d + strlen(d), "%c", ']');
          }
      }
  }
  
  /**
   * \brief Draw cell content detail in header
   *
   * \return none
   */
  
  void ui_show_celldetails() {
      char head[FBUFLEN];
      int inputline_pos = 0;
  
      // show cell in header
      #ifdef USECOLORS
          ui_set_ucolor(input_win, &ucolors[CELL_ID], DEFAULT_COLOR);
      #endif
      sprintf(head, "%s%d ", coltoa(curcol), currow);
      mvwprintw(input_win, 0, 0 + rescol, "%s", head);
      inputline_pos += strlen(head) + rescol;
  
      // show the current cell's format
      #ifdef USECOLORS
          ui_set_ucolor(input_win, &ucolors[CELL_FORMAT], DEFAULT_COLOR);
      #endif
  
      register struct ent *p1 = *ATBL(tbl, currow, curcol);
  
      // show padding
      if (p1 != NULL && p1->pad)
          sprintf(head, "(%d) ", p1->pad);
      else
          head[0]='\0';
  
      // show format
      if ((p1) && p1->format)
          sprintf(head + strlen(head), "(%s) ", p1->format);
      else
          sprintf(head + strlen(head), "(%d %d %d) ", fwidth[curcol], precision[curcol], realfmt[curcol]);
      mvwprintw(input_win, 0, inputline_pos, "%s", head);
      inputline_pos += strlen(head);
  
      // show expr
      #ifdef USECOLORS
          ui_set_ucolor(input_win, &ucolors[CELL_CONTENT], DEFAULT_COLOR);
      #endif
      if (p1 && p1->expr) {
          linelim = 0;
          editexp(currow, curcol);  /* set line to expr */
          linelim = -1;
          sprintf(head, "[%.*s] ", FBUFLEN-4, line);
          mvwprintw(input_win, 0, inputline_pos, "%s", head);
          inputline_pos += strlen(head);
      }
      // add cell content to head string
      head[0] = '\0';
      ui_add_cell_detail(head, p1);
  
      // cut string if its too large!
      if (strlen(head) > COLS - inputline_pos - 1) {
          head[COLS - inputline_pos - 1 - 15]='>';
          head[COLS - inputline_pos - 1 - 14]='>';
          head[COLS - inputline_pos - 1 - 13]='>';
          head[COLS - inputline_pos - 1 - 12]='\0';
      }
  
      mvwprintw(input_win, 0, inputline_pos, "%s", head);
      wclrtoeol(input_win);
      //wrefresh(input_win);
  }
  
  /**
   * \brief Error routine for yacc (gram.y)
   *
   * \param[in] err
  
   * \return none
   */
  
  void yyerror(char * err) {
      mvwprintw(input_win, 1, 0, "%s: %.*s<=%s", err, linelim, line, line + linelim);
      wrefresh(input_win);
      return;
  }
  
  /**
   * \brief Create a string that represents the formatted value of the cell
   *
   * \details This function creates a string (value) that represents
   * the formatted value of the cell.
   *
   * \param[in] err
  
   * \return 0 datetime format - number in p->v represents a date - format "d"
   * \return 1 if format of number - (numbers with format) - puede harber label.
   * \return -1 if there is no format in the cell
   */
  
  int ui_get_formated_value(struct ent ** p, int col, char * value) {
      //char * cfmt = (*p)->format ? (*p)->format : NULL;
      char * cfmt = (*p)->format ? (*p)->format : (realfmt[col] >= 0 && realfmt[col] < COLFORMATS && colformat[realfmt[col]] != NULL) ? colformat[realfmt[col]] : NULL;
  
      if (cfmt) {
          if (*cfmt == 'd') {
              time_t v = (time_t) ((*p)->v);
              strftime(value, sizeof(char) * FBUFLEN, cfmt + 1, localtime(&v));
              return 0;
          } else {
              format(cfmt, precision[col], (*p)->v, value, sizeof(char) * FBUFLEN);
              return 1;
          }
      } else { // there is no format
          // FIXME: error with number and text in same cell and no overlap
          engformat(realfmt[col], fwidth[col], precision[col], (*p)->v, value, sizeof(char) * FBUFLEN);
          ltrim(value, ' ');
          return 1;
      }
  }
  
  /**
   * \brief Shows text in child process
   *
   * \details Shows text in child process. Used for set, version,
   * showmaps, print_graph, showfilters, hiddenrows, and hiddencols commands.
   *
   * \param[in] val
   */
  
  void ui_show_text(char * val) {
      int pid;
      char px[MAXCMD];
      char * pager;
  
      (void) strcpy(px, "| ");
      if ( !(pager = getenv("PAGER")) )
          pager = DFLT_PAGER;
      (void) strcat(px, pager);
      FILE * f = openfile(px, &pid, NULL);
      if ( !f ) {
          sc_error("Can't open pipe to %s", pager);
          return;
      }
      def_prog_mode();
      endwin();
      fprintf(f, "%s\n", val);
      fprintf(f, "Press 'q' and then ENTER to return.");
      closefile(f, pid, 0);
      getchar();
      reset_prog_mode();
      refresh();
      ui_update(TRUE);
  }
  
  
  
  /**
   * \brief
   * UI function thats called after SIGWINCH signal.
   * \return none
   */
  void sig_winchg() {
      endwin();
      refresh();
      clear();
      set_term(sstdout);
  
      wresize(main_win, LINES - RESROW, COLS);
      wresize(input_win, RESROW, COLS);
      ui_mv_bottom_bar();
      ui_update(TRUE);
      flushinp();
  
      return;
  }
  
  #ifdef XLUA
  /**
   * \brief Print error of lua scripts
   *
   * \param[in] L
   * \param[in] msg
   *
   * \return none
   */
  
  void ui_bail(lua_State *L, char * msg) {
      extern char stderr_buffer[1024];
      fprintf(stderr,"FATAL ERROR: %s: %s\n", msg, lua_tostring(L, -1));
      move(0, 0);
      clrtobot();
      wrefresh(stdscr);
      set_term(sstderr);
      move(0, 0);
      clrtobot();
      status_line_empty = 1;
      clearok(stdscr, TRUE);
      mvprintw(0, 0, "%s", stderr_buffer);
      stderr_buffer[0]='\0';
      fseek(stderr, 0, SEEK_END);
      refresh();
      getch();
      set_term(sstdout);
      clearok(stdscr, TRUE);
      ui_show_header();
      refresh();
      ui_update(TRUE);
  }
  #endif
  
  /**
   * \brief Show a message in the screen
   *
   * \details This function shows a message on the screen and waits for user
   * confirmation between a couple of options defined on valid (wchar *)
   * parameter.
   * \return wchar_t indicating user answer
   */
  
  wchar_t ui_query_opt(wchar_t * initial_msg, wchar_t * valid) {
      if (! wcslen(initial_msg)) return L'\0';
      sc_info("%ls", initial_msg);
      wint_t wd = -1;
      wchar_t wdc[2] = L"";
      int res;
  
      curs_set(1);
      wtimeout(input_win, -1);
      move(0, rescol + wcslen(initial_msg) + 1);
      while ((res = wstr_in_wstr(valid, wdc)) == -1) {
          wget_wch(input_win, &wd);
          swprintf(wdc, FBUFLEN, L"%lc", wd);
      }
      wtimeout(input_win, TIMEOUT_CURSES);
      curs_set(0);
      return wdc[0];
  }
  
  /**
   * \brief Read text from stdin
   *
   * \param[in] initial_msg
   *
   * \return user input
   */
  
  char * ui_query(char * initial_msg) {
      char * hline = (char *) malloc(sizeof(char) * BUFFERSIZE);
      hline[0]='\0';
  
      // curses is not enabled
      if ( get_conf_int("nocurses")) {
          if (strlen(initial_msg)) wprintf(L"%s", initial_msg);
  
          if (fgets(hline, BUFFERSIZE-1, stdin) == NULL)
              hline[0]='\0';
  
          clean_carrier(hline);
          return hline;
      }
  
      // curses is enabled
      int loading_o;
      if (loading) {
          loading_o=loading;
          loading=0;
          ui_update(0);
          loading=loading_o;
      }
      curs_set(1);
  
      // show initial message
      if (strlen(initial_msg)) sc_info(initial_msg);
  
      // ask for input
      wtimeout(input_win, -1);
      notimeout(input_win, TRUE);
      wmove(input_win, 0, rescol);
      wclrtoeol(input_win);
      wrefresh(input_win);
      int d = wgetch(input_win);
  
      while (d != OKEY_ENTER && d != OKEY_ESC) {
          if (d == OKEY_BS || d == OKEY_BS2) {
              del_char(hline, strlen(hline) - 1);
          } else {
              sprintf(hline + strlen(hline), "%c", d);
          }
  
          mvwprintw(input_win, 0, rescol, "%s", hline);
          wclrtoeol(input_win);
          wrefresh(input_win);
          d = wgetch(input_win);
      }
      if (d == OKEY_ESC) hline[0]='\0';
  
      // go back to spreadsheet
      noecho();
      curs_set(0);
      wtimeout(input_win, TIMEOUT_CURSES);
      wmove(input_win, 0,0);
      wclrtoeol(input_win);
      wmove(input_win, 1,0);
      wclrtoeol(input_win);
      status_line_empty = 1;
      wrefresh(input_win);
      return hline;
  }
  
  /**
   * \brief Set a color
   * if bg_override != -1 (DEFAULT_COLOR) set this instead of uc->bg
   *
   * \param[in] w
   * \param[in] uc
   * \param[in} bg_override
   * \return none
   */
  
  void ui_set_ucolor(WINDOW * w, struct ucolor * uc, int bg_override) {
      short color;
      long attr = A_NORMAL;
      if (uc->bold)      attr |= A_BOLD;
  
  #ifdef A_ITALIC
      if (uc->italic)    attr |= A_ITALIC;
  #else
      sc_error("Cannot handle italic attribute. Please update your ncurses library");
  #endif
      if (uc->dim)       attr |= A_DIM;
      if (uc->reverse)   attr |= A_REVERSE;
      if (uc->standout)  attr |= A_STANDOUT;
      if (uc->blink)     attr |= A_BLINK;
      if (uc->underline) attr |= A_UNDERLINE;
  
      // see in ui_start_colors() the explanation of this
      int def = 9;
      if (COLORS > 8) def = 33;
      //
  
      if (uc->bg == NONE_COLOR || uc->fg == NONE_COLOR) {
          // leave colors intact
          // just apply other ncurses attributes
          attr_t a;
          wattr_get(w, &a, &color, NULL);
      } else if (bg_override == DEFAULT_COLOR) {
          color = (uc->fg+1)*def + uc->bg + 2;
      } else {
          color = (uc->fg+1)*def + bg_override + 2;
      }
      wattr_set (w, attr | COLOR_PAIR(color), color, NULL);
  }
  
  /**
   * \brief ui_start_colors()
   *
   * \return none
   */
  void ui_start_colors() {
      if (! has_colors()) return;
      int i, j;
  
      // Initialize all possible 81 init pairs
      use_default_colors();
  
      /* ncurses has 8 colors, but we need to keep 1 slot more for
       * default terminal background and foreground.
       * bg can be other than black.
       * we also have 24 custom colors.
       * that makes 33 x 33 combinations
       */
      int def = 9;
      if (COLORS > 8) def = 33;
      for (i=0; i < def; i++) {     // fg
          for (j=0; j < def; j++) { // bg
              /*
               * NOTE: calling init_pair with -1 sets it with default
               * terminal foreground and background colors
               */
  #if defined(NCURSES_VERSION_MAJOR) && (( NCURSES_VERSION_MAJOR > 5 && defined(NCURSES_VERSION_MINOR) && NCURSES_VERSION_MINOR > 0) || NCURSES_VERSION_MAJOR > 6)
              init_extended_pair( i*def+j+1, i-1, j-1); // i is fg and j is bg
  #else
              init_pair(i*def+j+1, i-1, j-1); // i is fg and j is bg
  #endif
          }
      }
  }
  
  /**
   * \brief
   * UI function thats called after SIGTSTP signal.
   * \return none
   */
  void ui_pause() {
      def_prog_mode();
      set_term(sstderr);
      endwin();
      return;
  }
  
  /**
   * \brief
   * UI function thats called after SIGCONT signal.
   * \return none
   */
  void ui_resume() {
      set_term(sstdout);
      reset_prog_mode();
      refresh();
      clearok(stdscr, TRUE);
      ui_show_header();
      ui_update(TRUE);
      return;
  }
  
  /**
   * \brief
   * UI function thats moves input bar to bottom or back to the top
   * \return none
   */
  void ui_mv_bottom_bar() {
      mvwin(main_win, get_conf_int("input_bar_bottom") ? 0 : RESROW, 0);
      mvwin(input_win, get_conf_int("input_bar_bottom") ? LINES-RESROW : 0, 0);
      return;
  }
  
  /**
   * \brief
   * function thats handles mouse movements
   * \return none
   */
  #ifdef MOUSE
  void ui_handle_mouse(MEVENT event) {
      int i, r = 0, c = 0;
      if ( event.x < RESCOL || ( get_conf_int("input_bar_bottom") && (event.y == 0 || event.y >= LINES - RESROW)) ||
         ( !get_conf_int("input_bar_bottom") && (event.y <= RESROW))) return;
  
  #ifdef BUTTON5_PRESSED
      if (event.bstate & BUTTON4_PRESSED || // scroll up
          event.bstate & BUTTON5_PRESSED) { // scroll down
              int n = LINES - RESROW - 1;
              if (get_conf_int("half_page_scroll")) n = n / 2;
              lastcol = curcol;
              lastrow = currow;
              currow = event.bstate & BUTTON5_PRESSED ? forw_row(n)->row : back_row(n)->row;
              if (event.bstate & BUTTON5_PRESSED) scroll_down(n);
              else scroll_up(n);
              unselect_ranges();
              ui_update(TRUE);
          return;
      }
  #else
      sc_error("Cannot handle mouse scroll. Please update your ncurses library");
      return;
  #endif
  
      // return if not a single click
      if (! (event.bstate & BUTTON1_CLICKED)) return;
  
      c = event.x - RESCOL;
      r = event.y - RESROW + (get_conf_int("input_bar_bottom") ? 1 : - 1);
  
      int mxcol = offscr_sc_cols + calc_offscr_sc_cols() - 1;
      int col = 0;
      int freeze = freeze_ranges && (freeze_ranges->type == 'c' ||  freeze_ranges->type == 'a') ? 1 : 0;
  
      for (i = 0; i <= mxcol; i++) {
          if (i >= maxcols) {
              sc_error("i >= maxcols in ui_show_sc_col_headings. please check calc_offscr_sc_cols.");
              break;
          }
          //if (i < offscr_sc_cols && !(freeze && i >= freeze_ranges->tl->col && i <= freeze_ranges->br->col)) continue;
  
          if (col_hidden[i]) continue;
  
          // skip center_hidden_cols
          if (freeze && ((
           i > freeze_ranges->br->col && i <= freeze_ranges->br->col + center_hidden_cols) || (
           i < freeze_ranges->tl->col && i >= freeze_ranges->tl->col - center_hidden_cols))) continue;
  
  
          //sc_debug("i:%d off%d mxcol:%d col:%d c:%d", i,  offscr_sc_cols, mxcol, col, c);
          col += fwidth[i];
          if (col >= c + 1) break;
      }
      currow = offscr_sc_rows + r;
      curcol = i;
      unselect_ranges();
      ui_update(TRUE);
  }
  #endif
  