aboutsummaryrefslogblamecommitdiffstats
path: root/src/custom.c
blob: 29a6261f463ede4119c6af5d150cd31d26f65002 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                  
  
                                                              
                       
  


                                                                     
  



















                                                                        
  
                                                        
                                           


   

                   
                 
 


                                        
               










                                   
                         

















                               
 
               
                              
 
                                      
             
                                          
             

      
                                                       
               
     
 
 

                                   



                                                                            
                                               


                         


                                 

                       



                                                                   
         
                                                    

                          

                                          




















                                                 
                                           


                              







                                                




























                                                            
                                         

                          


      
                                                      

                      

 
  






                                                                      

                       
 














                                                      


                            
    
                                             
 



                                          


                             
    
                                              
 



                                           

 
                                  
    
                                                    
 


                                                     
                                   
                    





                                           
                       
                                    
     
                               
                                   

          
                                                     


                
                                                

                  




























                                                               



                                              














                                                    



                                              







































                                                                              
                                                     

                          

                                                  
                                   

                                                           
                                                          
                                      
                                                            
                                        
                                                                 
                                            
                                                               
                                          


                                                                   
                                             
                                                         
                                     
                                                   
                                 

                                                            
                                                             
                                        
                                                             
                                        
                                                              
                                         
                                                                
                                           
                                                                
                                           
                                                            
                                        
                                                           
                                       



                                                                  
     
                                       
                                     

 
                                 
    
                        
 

                       



                                               



                                              
                                              

                                                



                                                            
                                                              

                                                               
 

                            
                   

 

                           
 






                                                               
 
                                                                                 









                                                                     

                                                                 





                                                
                                  




                                                                             
                                                                 



















                                                                                                             
 
                                                                





                                        
 












                                                                           
 
                          
                            
                         
                   







                           

                         





                                                                           
                                        

                                           

                 
                           
                                     



                                                              
     

                     






                          









                                                                    
                                
                        




















                                                         
                                                                  
     

                             

 


                    



                                   
                        
                                                          
                                                       

                                                            
                                                         






                                                                                




                                                                   
                   
 
                                                            
     


                         
                            

                           
                            

                              






                                                                    
                

                   
         




                                                                       


     
           
                                      





                                               

                                                                           

                                     
    
                                                         
 





                                                                           
 
                     
                             
                                              






                                                                       
                           

                           

 
           
                          
 






                                                               
 
 
                               






                                                                   
  



                                                                
                                                                          
                                                                    
 


                                                        
                                                
                                  
 




                                                






























                                                                
                                                       





                                        








                                                          
          

















                                                                             

















                                                                   
                                                           
                                              
                                                       








                                                  
                                                 
                                               
                                                 


                                                                       
                         

                            
                   

                         
 
 

                                
                          
 
                         








                                            
                 
                               
                                                                  
                                                   
 
                                                            




                        










































                                         
                                                                      
                                                       

                      

 
  


                                                        
                                                        

                                                           

    
                                          
 



                                   
 













                                  

                
                                             



                                                       






                                                                           
                                        


                              

                                                                    
     
 

                                 
          
                                                      
 











                       


                     

                           
                          







                                  
 
        
                                                           
                                                     
                                                                 
                              
                                                                         
            





                                                                  
                                                                             


                                                              
                                                        
                                                                    
                              
                                                                             
            
                                                                
                                                          
                                                                      
                              
                                                          
                                             
            
                                                                   
                                                               
                                                                         
                              
                                                       
                                                         
            
                                                                   
                                                             
                                                                        
                              
                                                                    
                                      
            
                                                                    
                                                                       
                                                                          
                              
                                                                      
                                      
            
                                                                 
                                        

                                                                    

                                         
                                                                                
            
                                                                
                                        

                                                                   

                                                                               

                                                                             
 
                  

 
    
                                       










                                                  
 
                            
    
                                         
 
                        
                    


                                                              


                                                                            

                                                                        

                                                                              
         
            

           
                           
                                                            

                                     
                                 


                                                              
                            
                                           
     
                    
 
                 

                        

                             

                                        
                                   
                                             










                                                                  
                        
                                         
                
                        
                                       










                                                         



                                                




















                                                                 
                                                            
















                                                                          
                                     

                                                                  
     
                 
                                
 



























                                                                               
                                               
                                                                        




                                                 
                                                   







                                               
 





                                                                   
                                                        










                                                                             
 





                             







                                                           
 
                               
                                                       








                                                                   
                        
                                                               
                                   



                            
 
           
                           
                                                        
                                                               














                                                                           
         











                                                         

                           










                                                           
                















                                    
                       




                                                                         















                                                                             


                                                          
                                  
 
                                                    

                                                                   





                                                                             
                               
             
                                         
                     

                                    






                                                     

                                               

                     
                              

                                                  
                                                                    
                                            
             

                                        
         





                                                                               
/*
 * Calcurse - text-based organizer
 *
 * Copyright (c) 2004-2010 Frederic Culot <frederic@culot.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the
 *        following disclaimer.
 *
 *      - 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER OR CONTRIBUTORS 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
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Send your feedback or comments to : misc@calcurse.org
 * Calcurse home page : http://calcurse.org
 *
 */

#include <string.h>
#include <stdlib.h>
#include <math.h>

#include "calcurse.h"

/* Available configuration variables. */
enum conf_var {
  CUSTOM_CONF_NOVARIABLE,
  CUSTOM_CONF_AUTOSAVE,
  CUSTOM_CONF_PERIODICSAVE,
  CUSTOM_CONF_CONFIRMQUIT,
  CUSTOM_CONF_CONFIRMDELETE,
  CUSTOM_CONF_SKIPSYSTEMDIALOGS,
  CUSTOM_CONF_SKIPPROGRESSBAR,
  CUSTOM_CONF_CALENDAR_DEFAULTVIEW,
  CUSTOM_CONF_WEEKBEGINSONMONDAY,
  CUSTOM_CONF_COLORTHEME,
  CUSTOM_CONF_LAYOUT,
  CUSTOM_CONF_SBAR_WIDTH,
  CUSTOM_CONF_NOTIFYBARSHOW,
  CUSTOM_CONF_NOTIFYBARDATE,
  CUSTOM_CONF_NOTIFYBARCLOCK,
  CUSTOM_CONF_NOTIFYBARWARNING,
  CUSTOM_CONF_NOTIFYBARCOMMAND,
  CUSTOM_CONF_OUTPUTDATEFMT,
  CUSTOM_CONF_INPUTDATEFMT,
  CUSTOM_CONF_DMON_ENABLE,
  CUSTOM_CONF_DMON_LOG,
  CUSTOM_CONF_VARIABLES
};

struct attribute {
  int color[7];
  int nocolor[7];
};

static struct attribute attr;

static unsigned
fill_config_var (char *string)
{
  if (strncmp (string, "yes", 3) == 0)
    return 1;
  else if (strncmp (string, "no", 2) == 0)
    return 0;
  else
    {
      EXIT (_("wrong configuration variable format."));
      return 0;
    }
}

/*
 * Load user color theme from file.
 * Need to handle calcurse versions prior to 1.8, where colors where handled
 * differently (number between 1 and 8).
 */
static void
custom_load_color (char *color, int background)
{
#define AWAITED_COLORS	2

  int i, len, color_num;
  char c[AWAITED_COLORS][BUFSIZ];
  int colr[AWAITED_COLORS];

  len = strlen (color);
  if (len > 1)
    {
      /* New version configuration */
      if (sscanf (color, "%s on %s", c[0], c[1]) != AWAITED_COLORS)
        {
          EXIT (_("missing colors in config file"));
          /* NOTREACHED */
        }

      for (i = 0; i < AWAITED_COLORS; i++)
        {
          if (!strncmp (c[i], "black", 5))
            colr[i] = COLOR_BLACK;
          else if (!strncmp (c[i], "red", 3))
            colr[i] = COLOR_RED;
          else if (!strncmp (c[i], "green", 5))
            colr[i] = COLOR_GREEN;
          else if (!strncmp (c[i], "yellow", 6))
            colr[i] = COLOR_YELLOW;
          else if (!strncmp (c[i], "blue", 4))
            colr[i] = COLOR_BLUE;
          else if (!strncmp (c[i], "magenta", 7))
            colr[i] = COLOR_MAGENTA;
          else if (!strncmp (c[i], "cyan", 4))
            colr[i] = COLOR_CYAN;
          else if (!strncmp (c[i], "white", 5))
            colr[i] = COLOR_WHITE;
          else if (!strncmp (c[i], "default", 7))
            colr[i] = background;
          else
            {
              EXIT (_("wrong color name"));
              /* NOTREACHED */
            }
        }
      init_pair (COLR_CUSTOM, colr[0], colr[1]);
    }
  else if (len > 0 && len < 2)
    {
      /* Old version configuration */
      color_num = atoi (color);

      switch (color_num)
        {
        case 0:
          colorize = 0;
          break;
        case 1:
          init_pair (COLR_CUSTOM, COLOR_RED, background);
          break;
        case 2:
          init_pair (COLR_CUSTOM, COLOR_GREEN, background);
          break;
        case 3:
          init_pair (COLR_CUSTOM, COLOR_BLUE, background);
          break;
        case 4:
          init_pair (COLR_CUSTOM, COLOR_CYAN, background);
          break;
        case 5:
          init_pair (COLR_CUSTOM, COLOR_YELLOW, background);
          break;
        case 6:
          init_pair (COLR_CUSTOM, COLOR_BLACK, COLR_GREEN);
          break;
        case 7:
          init_pair (COLR_CUSTOM, COLOR_BLACK, COLR_YELLOW);
          break;
        case 8:
          init_pair (COLR_CUSTOM, COLOR_RED, COLR_BLUE);
          break;
        default:
          EXIT (_("wrong color number"));
          /* NOTREACHED */
        }
    }
  else
    {
      EXIT (_("wrong configuration variable format"));
      /* NOTREACHED */
    }
}

/*
 * Define window attributes (for both color and non-color terminals):
 * ATTR_HIGHEST are for window titles
 * ATTR_HIGH are for month and days names
 * ATTR_MIDDLE are for the selected day inside calendar panel
 * ATTR_LOW are for days inside calendar panel which contains an event
 * ATTR_LOWEST are for current day inside calendar panel
 */
void
custom_init_attr (void)
{
  attr.color[ATTR_HIGHEST] = COLOR_PAIR (COLR_CUSTOM);
  attr.color[ATTR_HIGH] = COLOR_PAIR (COLR_HIGH);
  attr.color[ATTR_MIDDLE] = COLOR_PAIR (COLR_RED);
  attr.color[ATTR_LOW] = COLOR_PAIR (COLR_CYAN);
  attr.color[ATTR_LOWEST] = COLOR_PAIR (COLR_YELLOW);
  attr.color[ATTR_TRUE] = COLOR_PAIR (COLR_GREEN);
  attr.color[ATTR_FALSE] = COLOR_PAIR (COLR_RED);

  attr.nocolor[ATTR_HIGHEST] = A_BOLD;
  attr.nocolor[ATTR_HIGH] = A_REVERSE;
  attr.nocolor[ATTR_MIDDLE] = A_REVERSE;
  attr.nocolor[ATTR_LOW] = A_UNDERLINE;
  attr.nocolor[ATTR_LOWEST] = A_BOLD;
  attr.nocolor[ATTR_TRUE] = A_BOLD;
  attr.nocolor[ATTR_FALSE] = A_DIM;
}

/* Apply window attribute */
void
custom_apply_attr (WINDOW *win, int attr_num)
{
  if (colorize)
    wattron (win, attr.color[attr_num]);
  else
    wattron (win, attr.nocolor[attr_num]);
}

/* Remove window attribute */
void
custom_remove_attr (WINDOW *win, int attr_num)
{
  if (colorize)
    wattroff (win, attr.color[attr_num]);
  else
    wattroff (win, attr.nocolor[attr_num]);
}

/* Load the user configuration. */
void
custom_load_conf (struct conf *conf, int background)
{
  FILE *data_file;
  char *mesg_line1 = _("Failed to open config file");
  char *mesg_line2 = _("Press [ENTER] to continue");
  char buf[BUFSIZ], e_conf[BUFSIZ];
  enum conf_var var;

  data_file = fopen (path_conf, "r");
  if (data_file == NULL)
    {
      status_mesg (mesg_line1, mesg_line2);
      wnoutrefresh (win[STA].p);
      wins_doupdate ();
      (void)keys_getch (win[STA].p);
    }
  var = CUSTOM_CONF_NOVARIABLE;
  pthread_mutex_lock (&nbar.mutex);
  for (;;)
    {
      if (fgets (buf, sizeof buf, data_file) == NULL)
        {
          break;
        }
      io_extract_data (e_conf, buf, sizeof buf);

      switch (var)
        {
        case CUSTOM_CONF_NOVARIABLE:
          break;
        case CUSTOM_CONF_AUTOSAVE:
          conf->auto_save = fill_config_var (e_conf);
          var = 0;
          break;
        case CUSTOM_CONF_PERIODICSAVE:
          conf->periodic_save = atoi (e_conf);
          if (conf->periodic_save < 0)
            conf->periodic_save = 0;
          var = 0;
          break;
        case CUSTOM_CONF_CONFIRMQUIT:
          conf->confirm_quit = fill_config_var (e_conf);
          var = 0;
          break;
        case CUSTOM_CONF_CONFIRMDELETE:
          conf->confirm_delete = fill_config_var (e_conf);
          var = 0;
          break;
        case CUSTOM_CONF_SKIPSYSTEMDIALOGS:
          conf->skip_system_dialogs = fill_config_var (e_conf);
          var = 0;
          break;
        case CUSTOM_CONF_SKIPPROGRESSBAR:
          conf->skip_progress_bar = fill_config_var (e_conf);
          var = 0;
          break;
        case CUSTOM_CONF_CALENDAR_DEFAULTVIEW:
          calendar_set_view (atoi (e_conf));
          var = 0;
          break;
        case CUSTOM_CONF_WEEKBEGINSONMONDAY:
          if (fill_config_var (e_conf))
            calendar_set_first_day_of_week (MONDAY);
          else
            calendar_set_first_day_of_week (SUNDAY);
          var = 0;
          break;
        case CUSTOM_CONF_COLORTHEME:
          custom_load_color (e_conf, background);
          var = 0;
          break;
        case CUSTOM_CONF_LAYOUT:
          wins_set_layout (atoi (e_conf));
          var = 0;
          break;
        case CUSTOM_CONF_SBAR_WIDTH:
          wins_set_sbar_width (atoi (e_conf));
          var = 0;
          break;
        case CUSTOM_CONF_NOTIFYBARSHOW:
          nbar.show = fill_config_var (e_conf);
          var = 0;
          break;
        case CUSTOM_CONF_NOTIFYBARDATE:
          (void)strncpy (nbar.datefmt, e_conf, strlen (e_conf) + 1);
          var = 0;
          break;
        case CUSTOM_CONF_NOTIFYBARCLOCK:
          (void)strncpy (nbar.timefmt, e_conf, strlen (e_conf) + 1);
          var = 0;
          break;
        case CUSTOM_CONF_NOTIFYBARWARNING:
          nbar.cntdwn = atoi (e_conf);
          var = 0;
          break;
        case CUSTOM_CONF_NOTIFYBARCOMMAND:
          (void)strncpy (nbar.cmd, e_conf, strlen (e_conf) + 1);
          var = 0;
          break;
        case CUSTOM_CONF_OUTPUTDATEFMT:
          if (e_conf[0] != '\0')
            (void)strncpy (conf->output_datefmt, e_conf, strlen (e_conf) + 1);
          var = 0;
          break;
        case CUSTOM_CONF_INPUTDATEFMT:
          conf->input_datefmt = atoi (e_conf);
          if (conf->input_datefmt <= 0 || conf->input_datefmt >= DATE_FORMATS)
            conf->input_datefmt = 1;
          var = 0;
          break;
        case CUSTOM_CONF_DMON_ENABLE:
          dmon.enable = fill_config_var (e_conf);
          var = 0;
          break;
        case CUSTOM_CONF_DMON_LOG:
          dmon.log = fill_config_var (e_conf);
          var = 0;
          break;
        default:
          EXIT (_("configuration variable unknown"));
          /* NOTREACHED */
        }

      if (strncmp (e_conf, "auto_save=", 10) == 0)
        var = CUSTOM_CONF_AUTOSAVE;
      else if (strncmp (e_conf, "periodic_save=", 14) == 0)
        var = CUSTOM_CONF_PERIODICSAVE;
      else if (strncmp (e_conf, "confirm_quit=", 13) == 0)
        var = CUSTOM_CONF_CONFIRMQUIT;
      else if (strncmp (e_conf, "confirm_delete=", 15) == 0)
        var = CUSTOM_CONF_CONFIRMDELETE;
      else if (strncmp (e_conf, "skip_system_dialogs=", 20) == 0)
        var = CUSTOM_CONF_SKIPSYSTEMDIALOGS;
      else if (strncmp (e_conf, "skip_progress_bar=", 18) == 0)
        var = CUSTOM_CONF_SKIPPROGRESSBAR;
      else if (strncmp (e_conf, "calendar_default_view=", 22) == 0)
        var = CUSTOM_CONF_CALENDAR_DEFAULTVIEW;
      else if (strncmp (e_conf, "week_begins_on_monday=", 22) == 0)
        var = CUSTOM_CONF_WEEKBEGINSONMONDAY;
      else if (strncmp (e_conf, "color-theme=", 12) == 0)
        var = CUSTOM_CONF_COLORTHEME;
      else if (strncmp (e_conf, "layout=", 7) == 0)
        var = CUSTOM_CONF_LAYOUT;
      else if (strncmp (e_conf, "side-bar_width=", 15) == 0)
        var = CUSTOM_CONF_SBAR_WIDTH;
      else if (strncmp (e_conf, "notify-bar_show=", 16) == 0)
        var = CUSTOM_CONF_NOTIFYBARSHOW;
      else if (strncmp (e_conf, "notify-bar_date=", 16) == 0)
        var = CUSTOM_CONF_NOTIFYBARDATE;
      else if (strncmp (e_conf, "notify-bar_clock=", 17) == 0)
        var = CUSTOM_CONF_NOTIFYBARCLOCK;
      else if (strncmp (e_conf, "notify-bar_warning=", 19) == 0)
        var = CUSTOM_CONF_NOTIFYBARWARNING;
      else if (strncmp (e_conf, "notify-bar_command=", 19) == 0)
        var = CUSTOM_CONF_NOTIFYBARCOMMAND;
      else if (strncmp (e_conf, "output_datefmt=", 15) == 0)
        var = CUSTOM_CONF_OUTPUTDATEFMT;
      else if (strncmp (e_conf, "input_datefmt=", 14) == 0)
        var = CUSTOM_CONF_INPUTDATEFMT;
      else if (strncmp (e_conf, "notify-daemon_enable=", 21) == 0)
        var = CUSTOM_CONF_DMON_ENABLE;
      else if (strncmp (e_conf, "notify-daemon_log=", 18) == 0)
        var = CUSTOM_CONF_DMON_LOG;
    }
  file_close (data_file, __FILE_POS__);
  pthread_mutex_unlock (&nbar.mutex);
}

/* Draws the configuration bar */
void
custom_config_bar (void)
{
  const int SMLSPC = 2;
  const int SPC = 15;

  custom_apply_attr (win[STA].p, ATTR_HIGHEST);
  mvwprintw (win[STA].p, 0, 2, "Q");
  mvwprintw (win[STA].p, 1, 2, "G");
  mvwprintw (win[STA].p, 0, 2 + SPC, "L");
  mvwprintw (win[STA].p, 1, 2 + SPC, "S");
  mvwprintw (win[STA].p, 0, 2 + 2 * SPC, "C");
  mvwprintw (win[STA].p, 1, 2 + 2 * SPC, "N");
  mvwprintw (win[STA].p, 0, 2 + 3 * SPC, "K");
  custom_remove_attr (win[STA].p, ATTR_HIGHEST);

  mvwprintw (win[STA].p, 0, 2 + SMLSPC, _("Exit"));
  mvwprintw (win[STA].p, 1, 2 + SMLSPC, _("General"));
  mvwprintw (win[STA].p, 0, 2 + SPC + SMLSPC, _("Layout"));
  mvwprintw (win[STA].p, 1, 2 + SPC + SMLSPC, _("Sidebar"));
  mvwprintw (win[STA].p, 0, 2 + 2 * SPC + SMLSPC, _("Color"));
  mvwprintw (win[STA].p, 1, 2 + 2 * SPC + SMLSPC, _("Notify"));
  mvwprintw (win[STA].p, 0, 2 + 3 * SPC + SMLSPC, _("Keys"));

  wnoutrefresh (win[STA].p);
  wmove (win[STA].p, 0, 0);
  wins_doupdate ();
}

static void
layout_selection_bar (void)
{
  struct binding quit    = {_("Exit"),     KEY_GENERIC_QUIT};
  struct binding select  = {_("Select"),   KEY_GENERIC_SELECT};
  struct binding up      = {_("Up"),       KEY_MOVE_UP};
  struct binding down    = {_("Down"),     KEY_MOVE_DOWN};
  struct binding left    = {_("Left"),     KEY_MOVE_LEFT};
  struct binding right   = {_("Right"),    KEY_MOVE_RIGHT};
  struct binding help    = {_("Help"),     KEY_GENERIC_HELP};

  struct binding *binding[] = {&quit, &select, &up, &down, &left, &right, &help};
  int binding_size = sizeof (binding) / sizeof (binding[0]);

  keys_display_bindings_bar (win[STA].p, binding, 0, binding_size);
}

#define NBLAYOUTS     8
#define LAYOUTSPERCOL 2

/* Used to display available layouts in layout configuration menu. */
static void
display_layout_config (struct window *lwin, int mark, int cursor,
                       int need_reset)
{
#define CURSOR			(32 | A_REVERSE)
#define MARK			88
#define LAYOUTH                  5
#define LAYOUTW                  9
  char *box = "[ ]";
  const int BOXSIZ = strlen (box);
  const int NBCOLS = NBLAYOUTS / LAYOUTSPERCOL;
  const int COLSIZ = LAYOUTW + BOXSIZ + 1;
  const int XSPC = (col - NBCOLS * COLSIZ) / (NBCOLS + 1);
  const int XOFST = (col - NBCOLS * (XSPC + COLSIZ)) / 2;
  const int YSPC = (row - 8 - LAYOUTSPERCOL * LAYOUTH) / (LAYOUTSPERCOL + 1);
  const int YOFST = (row - LAYOUTSPERCOL * (YSPC + LAYOUTH)) / 2;
  enum {YPOS, XPOS, NBPOS};
  int pos[NBLAYOUTS][NBPOS];
  char *layouts[LAYOUTH][NBLAYOUTS] = {
    {"+---+---+", "+---+---+", "+---+---+", "+---+---+", "+---+---+", "+---+---+", "+---+---+", "+---+---+"},
    {"|   | c |", "|   | t |", "| c |   |", "| t |   |", "|   | c |", "|   | a |", "| c |   |", "| a |   |"},
    {"| a +---+", "| a +---+", "+---+ a |", "|---+ a |", "| t +---+", "| t +---+", "+---+ t |", "+---+ t |"},
    {"|   | t |", "|   | c |", "| t |   |", "| c |   |", "|   | a |", "|   | c |", "| a |   |", "| c |   |"},
    {"+---+---+", "+---+---+", "+---+---+", "+---+---+", "+---+---+", "+---+---+", "+---+---+", "+---+---+"}
  };
  int i;

  for (i = 0; i < NBLAYOUTS; i++)
    {
      pos[i][YPOS] = YOFST + (i % LAYOUTSPERCOL) * (YSPC + LAYOUTH);
      pos[i][XPOS] = XOFST + (i / LAYOUTSPERCOL) * (XSPC + COLSIZ);
    }

  if (need_reset)
    {
      char label[BUFSIZ];

      (void)snprintf (label, BUFSIZ, _("layout configuration"));
      custom_confwin_init (lwin, label);
    }

  for (i = 0; i < NBLAYOUTS; i++)
    {
      int j;

      mvwprintw (lwin->p, pos[i][YPOS] + 2, pos[i][XPOS], box);
      if (i == mark)
        custom_apply_attr (lwin->p, ATTR_HIGHEST);
      for (j = 0; j < LAYOUTH; j++)
        {
          mvwprintw (lwin->p, pos[i][YPOS] + j, pos[i][XPOS] + BOXSIZ + 1,
                     layouts[j][i]);
        }
      if (i == mark)
        custom_remove_attr (lwin->p, ATTR_HIGHEST);
    }
  mvwaddch (lwin->p, pos[mark][YPOS] + 2, pos[mark][XPOS] + 1, MARK);
  mvwaddch (lwin->p, pos[cursor][YPOS] + 2, pos[cursor][XPOS] + 1, CURSOR);

  layout_selection_bar ();
  wnoutrefresh (win[STA].p);
  wnoutrefresh (lwin->p);
  wins_doupdate ();
  if (notify_bar ())
    notify_update_bar ();
}

/* Choose the layout */
void
custom_layout_config (void)
{
  struct scrollwin hwin;
  struct window conf_win;
  int ch, mark, cursor, need_reset;
  char *help_text =
    _("With this configuration menu, one can choose where panels will be\n"
      "displayed inside calcurse screen. \n"
      "It is possible to choose between eight different configurations.\n"
      "\nIn the configuration representations, letters correspond to:\n\n"
      "       'c' -> calendar panel\n\n"
      "       'a' -> appointment panel\n\n"
      "       't' -> todo panel\n\n");

  need_reset = 1;
  conf_win.p = (WINDOW *)0;
  cursor = mark = wins_layout () - 1;
  clear ();
  display_layout_config (&conf_win, mark, cursor, need_reset);

  while ((ch = keys_getch (win[STA].p)) != KEY_GENERIC_QUIT)
    {
      need_reset = 0;
      switch (ch)
        {
        case KEY_RESIZE:
          endwin ();
          wins_refresh ();
          curs_set (0);
          need_reset = 1;
          break;
        case KEY_GENERIC_HELP:
          help_wins_init (&hwin, 0, 0,
                          (notify_bar ()) ? row - 3 : row - 2, col);
          mvwprintw (hwin.pad.p, 1, 0, "%s", help_text);
          hwin.total_lines = 7;
          wins_scrollwin_display (&hwin);
          wgetch (hwin.win.p);
          wins_scrollwin_delete (&hwin);
          need_reset = 1;
          break;
        case KEY_GENERIC_SELECT:
          mark = cursor;
          break;
        case KEY_MOVE_DOWN:
          if (cursor % LAYOUTSPERCOL < LAYOUTSPERCOL - 1)
            cursor++;
          break;
        case KEY_MOVE_UP:
          if (cursor % LAYOUTSPERCOL > 0)
            cursor--;
          break;
        case KEY_MOVE_LEFT:
          if (cursor >= LAYOUTSPERCOL)
            cursor -= LAYOUTSPERCOL;
          break;
        case KEY_MOVE_RIGHT:
          if (cursor < NBLAYOUTS - LAYOUTSPERCOL)
            cursor += LAYOUTSPERCOL;
          break;
        case KEY_GENERIC_CANCEL:
          need_reset = 1;
          break;
        }
      display_layout_config (&conf_win, mark, cursor, need_reset);
    }
  wins_set_layout (mark + 1);
  delwin (conf_win.p);
}

#undef NBLAYOUTS
#undef LAYOUTSPERCOL

/* Sidebar configuration screen. */
void
custom_sidebar_config (void)
{
  struct scrollwin hwin;
  struct binding quit = {_("Exit"),     KEY_GENERIC_QUIT};
  struct binding inc  = {_("Width +"),    KEY_MOVE_UP};
  struct binding dec  = {_("Width -"),    KEY_MOVE_DOWN};
  struct binding help = {_("Help"),       KEY_GENERIC_HELP};
  struct binding *binding[] = {&inc, &dec, &help, &quit};
  char *help_text =
    _("This configuration screen is used to change the width of the side bar.\n"
      "The side bar is the part of the screen which contains two panels:\n"
      "the calendar and, depending on the chosen layout, either the todo list\n"
      "or the appointment list.\n\n"
      "The side bar width can be up to 50% of the total screen width, but\n"
      "can't be smaller than " TOSTRING(SBARMINWIDTH) " characters wide.\n\n");
  int ch, binding_size;

  binding_size = sizeof (binding) / sizeof (binding[0]);

  keys_display_bindings_bar (win[STA].p, binding, 0, binding_size);
  wins_doupdate ();

  while ((ch = keys_getch (win[STA].p)) != KEY_GENERIC_QUIT)
    {
      switch (ch)
        {
        case KEY_MOVE_UP:
          wins_sbar_winc ();
          break;
        case KEY_MOVE_DOWN:
          wins_sbar_wdec ();
          break;
        case KEY_GENERIC_HELP:
          help_wins_init (&hwin, 0, 0,
                          (notify_bar ()) ? row - 3 : row - 2, col);
          mvwprintw (hwin.pad.p, 1, 0, "%s", help_text);
          hwin.total_lines = 6;
          wins_scrollwin_display (&hwin);
          wgetch (hwin.win.p);
          wins_scrollwin_delete (&hwin);
          break;
        default:
          continue;
        }
      wins_reinit_panels ();
      wins_update_border ();
      wins_update_panels ();
      keys_display_bindings_bar (win[STA].p, binding, 0, binding_size);
      wins_doupdate ();
    }
}

static void
set_confwin_attr (struct window *cwin)
{
  cwin->h = (notify_bar ())? row - 3 : row - 2;
  cwin->w = col;
  cwin->x = cwin->y = 0;
}

/*
 * Create a configuration window and initialize status and notification bar
 * (useful in case of window resize).
 */
void
custom_confwin_init (struct window *confwin, char *label)
{
  if (confwin->p)
    {
      erase_window_part (confwin->p, confwin->x, confwin->y,
                         confwin->x + confwin->w, confwin->y + confwin->h);
      (void)delwin (confwin->p);
    }

  wins_get_config ();
  set_confwin_attr (confwin);
  confwin->p = newwin (confwin->h, col, 0, 0);
  box (confwin->p, 0, 0);
  wins_show (confwin->p, label);
  delwin (win[STA].p);
  win[STA].p = newwin (win[STA].h, win[STA].w, win[STA].y, win[STA].x);
  keypad (win[STA].p, TRUE);
  if (notify_bar ())
    {
      notify_reinit_bar ();
      notify_update_bar ();
    }
}

static void
color_selection_bar (void)
{
  struct binding quit    = {_("Exit"),     KEY_GENERIC_QUIT};
  struct binding select  = {_("Select"),   KEY_GENERIC_SELECT};
  struct binding nocolor = {_("No color"), KEY_GENERIC_CANCEL};
  struct binding up      = {_("Up"),       KEY_MOVE_UP};
  struct binding down    = {_("Down"),     KEY_MOVE_DOWN};
  struct binding left    = {_("Left"),     KEY_MOVE_LEFT};
  struct binding right   = {_("Right"),    KEY_MOVE_RIGHT};


  struct binding *binding[] = {
    &quit, &nocolor, &up, &down, &left, &right, &select
  };
  int binding_size = sizeof (binding) / sizeof (binding[0]);

  keys_display_bindings_bar (win[STA].p, binding, 0, binding_size);
}

/*
 * Used to display available colors in color configuration menu.
 * This is useful for window resizing.
 */
static void
display_color_config (struct window *cwin, int *mark_fore, int *mark_back,
                      int cursor, int need_reset, int theme_changed)
{
#define	SIZE 			(2 * (NBUSERCOLORS + 1))
#define DEFAULTCOLOR		255
#define DEFAULTCOLOR_EXT	-1
#define CURSOR			(32 | A_REVERSE)
#define MARK			88

  char *fore_txt = _("Foreground");
  char *back_txt = _("Background");
  char *default_txt = _("(terminal's default)");
  char *bar = "          ";
  char *box = "[ ]";
  char label[BUFSIZ];
  const unsigned Y = 3;
  const unsigned XOFST = 5;
  const unsigned YSPC = (row - 8) / (NBUSERCOLORS + 1);
  const unsigned BARSIZ = strlen (bar);
  const unsigned BOXSIZ = strlen (box);
  const unsigned XSPC = (col - 2 * BARSIZ - 2 * BOXSIZ - 6) / 3;
  const unsigned XFORE = XSPC;
  const unsigned XBACK = 2 * XSPC + BOXSIZ + XOFST + BARSIZ;
  enum
  { YPOS, XPOS, NBPOS };
  unsigned i;
  int pos[SIZE][NBPOS];
  short colr_fore, colr_back;
  int colr[SIZE] = {
    COLR_RED, COLR_GREEN, COLR_YELLOW, COLR_BLUE,
    COLR_MAGENTA, COLR_CYAN, COLR_DEFAULT,
    COLR_RED, COLR_GREEN, COLR_YELLOW, COLR_BLUE,
    COLR_MAGENTA, COLR_CYAN, COLR_DEFAULT
  };

  for (i = 0; i < NBUSERCOLORS + 1; i++)
    {
      pos[i][YPOS] = Y + YSPC * (i + 1);
      pos[NBUSERCOLORS + i + 1][YPOS] = Y + YSPC * (i + 1);
      pos[i][XPOS] = XFORE;
      pos[NBUSERCOLORS + i + 1][XPOS] = XBACK;
    }

  if (need_reset)
    {
      (void)snprintf (label, BUFSIZ, _("color theme"));
      custom_confwin_init (cwin, label);
    }

  if (colorize)
    {
      if (theme_changed)
        {
          pair_content (colr[*mark_fore], &colr_fore, 0L);
          if (colr_fore == 255)
            colr_fore = -1;
          pair_content (colr[*mark_back], &colr_back, 0L);
          if (colr_back == 255)
            colr_back = -1;
          init_pair (COLR_CUSTOM, colr_fore, colr_back);
        }
      else
        {
          /* Retrieve the actual color theme. */
          pair_content (COLR_CUSTOM, &colr_fore, &colr_back);

          if ((colr_fore == DEFAULTCOLOR) || (colr_fore == DEFAULTCOLOR_EXT))
            *mark_fore = NBUSERCOLORS;
          else
            for (i = 0; i < NBUSERCOLORS + 1; i++)
              if (colr_fore == colr[i])
                *mark_fore = i;

          if ((colr_back == DEFAULTCOLOR) || (colr_back == DEFAULTCOLOR_EXT))
            *mark_back = SIZE - 1;
          else
            for (i = 0; i < NBUSERCOLORS + 1; i++)
              if (colr_back == colr[NBUSERCOLORS + 1 + i])
                *mark_back = NBUSERCOLORS + 1 + i;
        }
    }

  /* color boxes */
  for (i = 0; i < SIZE - 1; i++)
    {
      mvwprintw (cwin->p, pos[i][YPOS], pos[i][XPOS], box);
      wattron (cwin->p, COLOR_PAIR (colr[i]) | A_REVERSE);
      mvwprintw (cwin->p, pos[i][YPOS], pos[i][XPOS] + XOFST, bar);
      wattroff (cwin->p, COLOR_PAIR (colr[i]) | A_REVERSE);
    }

  /* Terminal's default color */
  i = SIZE - 1;
  mvwprintw (cwin->p, pos[i][YPOS], pos[i][XPOS], box);
  wattron (cwin->p, COLOR_PAIR (colr[i]));
  mvwprintw (cwin->p, pos[i][YPOS], pos[i][XPOS] + XOFST, bar);
  wattroff (cwin->p, COLOR_PAIR (colr[i]));
  mvwprintw (cwin->p, pos[NBUSERCOLORS][YPOS] + 1,
             pos[NBUSERCOLORS][XPOS] + XOFST, default_txt);
  mvwprintw (cwin->p, pos[SIZE - 1][YPOS] + 1,
             pos[SIZE - 1][XPOS] + XOFST, default_txt);

  custom_apply_attr (cwin->p, ATTR_HIGHEST);
  mvwprintw (cwin->p, Y, XFORE + XOFST, fore_txt);
  mvwprintw (cwin->p, Y, XBACK + XOFST, back_txt);
  custom_remove_attr (cwin->p, ATTR_HIGHEST);

  if (colorize)
    {
      mvwaddch (cwin->p, pos[*mark_fore][YPOS],
                pos[*mark_fore][XPOS] + 1, MARK);
      mvwaddch (cwin->p, pos[*mark_back][YPOS],
                pos[*mark_back][XPOS] + 1, MARK);
    }

  mvwaddch (cwin->p, pos[cursor][YPOS], pos[cursor][XPOS] + 1, CURSOR);
  color_selection_bar ();
  wnoutrefresh (win[STA].p);
  wnoutrefresh (cwin->p);
  wins_doupdate ();
  if (notify_bar ())
    notify_update_bar ();
}

/* Color theme configuration. */
void
custom_color_config (void)
{
  struct window conf_win;
  int ch, cursor, need_reset, theme_changed;
  int mark_fore, mark_back;

  mark_fore = NBUSERCOLORS;
  mark_back = SIZE - 1;
  clear ();
  cursor = 0;
  need_reset = 1;
  theme_changed = 0;
  conf_win.p = 0;
  set_confwin_attr (&conf_win);
  display_color_config (&conf_win, &mark_fore, &mark_back, cursor,
                        need_reset, theme_changed);

  while ((ch = keys_getch (win[STA].p)) != KEY_GENERIC_QUIT)
    {
      need_reset = 0;
      theme_changed = 0;

      switch (ch)
        {
        case KEY_RESIZE:
          endwin ();
          wins_refresh ();
          curs_set (0);
          need_reset = 1;
          break;

        case KEY_GENERIC_SELECT:
          colorize = 1;
          need_reset = 1;
          theme_changed = 1;
          if (cursor > NBUSERCOLORS)
            mark_back = cursor;
          else
            mark_fore = cursor;
          break;

        case KEY_MOVE_DOWN:
          if (cursor < SIZE - 1)
            ++cursor;
          break;

        case KEY_MOVE_UP:
          if (cursor > 0)
            --cursor;
          break;

        case KEY_MOVE_LEFT:
          if (cursor > NBUSERCOLORS)
            cursor -= (NBUSERCOLORS + 1);
          break;

        case KEY_MOVE_RIGHT:
          if (cursor <= NBUSERCOLORS)
            cursor += (NBUSERCOLORS + 1);
          break;

        case KEY_GENERIC_CANCEL:
          colorize = 0;
          need_reset = 1;
          break;
        }
      display_color_config (&conf_win, &mark_fore, &mark_back, cursor,
                            need_reset, theme_changed);
    }
  delwin (conf_win.p);
}

/*
 * Return a string defining the color theme in the form:
 *       foreground color 'on' background color
 * in order to dump this data in the configuration file.
 * Color numbers follow the ncurses library definitions.
 * If ncurses library was compiled with --enable-ext-funcs,
 * then default color is -1.
 */
void
custom_color_theme_name (char *theme_name)
{
#define MAXCOLORS		8
#define NBCOLORS		2
#define DEFAULTCOLOR		255
#define DEFAULTCOLOR_EXT	-1

  int i;
  short color[NBCOLORS];
  char *color_name[NBCOLORS];
  char *default_color = "default";
  char *name[MAXCOLORS] = {
    "black",
    "red",
    "green",
    "yellow",
    "blue",
    "magenta",
    "cyan",
    "white"
  };

  if (!colorize)
    (void)snprintf (theme_name, BUFSIZ, "0");
  else
    {
      pair_content (COLR_CUSTOM, &color[0], &color[1]);
      for (i = 0; i < NBCOLORS; i++)
        {
          if ((color[i] == DEFAULTCOLOR) || (color[i] == DEFAULTCOLOR_EXT))
            color_name[i] = default_color;
          else if (color[i] >= 0 && color[i] <= MAXCOLORS)
            color_name[i] = name[color[i]];
          else
            {
              EXIT (_("unknown color"));
              /* NOTREACHED */
            }
        }
      (void)snprintf (theme_name, BUFSIZ, "%s on %s", color_name[0],
                      color_name[1]);
    }
}

/* Prints the general options. */
static int
print_general_options (WINDOW *win, struct conf *conf)
{
  enum {
    AUTO_SAVE,
    PERIODIC_SAVE,
    CONFIRM_QUIT,
    CONFIRM_DELETE,
    SKIP_SYSTEM_DIAGS,
    SKIP_PROGRESS_BAR,
    WEEK_BEGINS_MONDAY,
    OUTPUT_DATE_FMT,
    INPUT_DATE_FMT,
    NB_OPTIONS
  };
  const int XPOS = 1;
  const int YOFF = 3;
  int y;
  char *opt[NB_OPTIONS] = {
    _("auto_save = "),
    _("periodic_save = "),
    _("confirm_quit = "),
    _("confirm_delete = "),
    _("skip_system_dialogs = "),
    _("skip_progress_bar = "),
    _("week_begins_on_monday = "),
    _("output_datefmt = "),
    _("input_datefmt = ")
  };

  y = 0;
  mvwprintw (win, y, XPOS, "[1] %s      ", opt[AUTO_SAVE]);
  print_bool_option_incolor (win, conf->auto_save, y,
                             XPOS + 4 + strlen (opt[AUTO_SAVE]));
  mvwprintw (win, y + 1, XPOS,
             _("(if set to YES, automatic save is done when quitting)"));
  y += YOFF;
  mvwprintw (win, y, XPOS, "[2] %s      ", opt[PERIODIC_SAVE]);
  custom_apply_attr (win, ATTR_HIGHEST);
  mvwprintw (win, y, XPOS + 4 + strlen (opt[PERIODIC_SAVE]), "%d",
             conf->periodic_save);
  custom_remove_attr (win, ATTR_HIGHEST);
  mvwprintw (win, y + 1, XPOS,
             _("(if not null, automatically save data every 'periodic_save' "
               "minutes)"));
  y += YOFF;
  mvwprintw (win, y, XPOS, "[3] %s      ", opt[CONFIRM_QUIT]);
  print_bool_option_incolor (win, conf->confirm_quit, y,
                             XPOS + 4 + strlen (opt[CONFIRM_QUIT]));
  mvwprintw (win, y + 1, XPOS,
             _("(if set to YES, confirmation is required before quitting)"));
  y += YOFF;
  mvwprintw (win, y, XPOS, "[4] %s      ", opt[CONFIRM_DELETE]);
  print_bool_option_incolor (win, conf->confirm_delete, y,
                             XPOS + 4 + strlen (opt[CONFIRM_DELETE]));
  mvwprintw (win, y + 1, XPOS,
             _("(if set to YES, confirmation is required "
               "before deleting an event)"));
  y += YOFF;
  mvwprintw (win, y, XPOS, "[5] %s      ", opt[SKIP_SYSTEM_DIAGS]);
  print_bool_option_incolor (win, conf->skip_system_dialogs, y,
                             XPOS + 4 + strlen (opt[SKIP_SYSTEM_DIAGS]));
  mvwprintw (win, y + 1, XPOS,
             _("(if set to YES, messages about loaded "
               "and saved data will not be displayed)"));
  y += YOFF;
  mvwprintw (win, y, XPOS, "[6] %s      ", opt[SKIP_PROGRESS_BAR]);
  print_bool_option_incolor (win, conf->skip_progress_bar, y,
                            XPOS + 4 + strlen (opt[SKIP_PROGRESS_BAR]));
  mvwprintw (win, y + 1, XPOS,
             _("(if set to YES, progress bar will not be displayed "
                "when saving data)"));
  y += YOFF;
  mvwprintw (win, y, XPOS, "[7] %s      ", opt[WEEK_BEGINS_MONDAY]);
  print_bool_option_incolor (win, calendar_week_begins_on_monday (), y,
                             XPOS + 4 + strlen (opt[WEEK_BEGINS_MONDAY]));
  mvwprintw (win, y + 1, XPOS,
             _("(if set to YES, monday is the first day of the week, "
               "else it is sunday)"));
  y += YOFF;
  mvwprintw (win, y, XPOS, "[8] %s      ", opt[OUTPUT_DATE_FMT]);
  custom_apply_attr (win, ATTR_HIGHEST);
  mvwprintw (win, y, XPOS + 4 + strlen (opt[OUTPUT_DATE_FMT]), "%s",
             conf->output_datefmt);
  custom_remove_attr (win, ATTR_HIGHEST);
  mvwprintw (win, y + 1, XPOS,
             _("(Format of the date to be displayed in non-interactive mode)"));
  y += YOFF;
  mvwprintw (win, y, XPOS, "[9] %s      ", opt[INPUT_DATE_FMT]);
  custom_apply_attr (win, ATTR_HIGHEST);
  mvwprintw (win, y, XPOS + 4 + strlen (opt[INPUT_DATE_FMT]), "%d",
             conf->input_datefmt);
  custom_remove_attr (win, ATTR_HIGHEST);
  mvwprintw (win, y + 1, XPOS, _("(Format to be used when entering a date: "));
  mvwprintw (win, y + 2, XPOS,
             _(" (1)mm/dd/yyyy (2)dd/mm/yyyy (3)yyyy/mm/dd (4)yyyy-mm-dd)"));

  return y + YOFF;
}

void
custom_set_swsiz (struct scrollwin *sw)
{
  sw->win.x = 0;
  sw->win.y = 0;
  sw->win.h = (notify_bar ()) ? row - 3 : row - 2;
  sw->win.w = col;

  sw->pad.x = 1;
  sw->pad.y = 3;
  sw->pad.h = BUFSIZ;
  sw->pad.w = col - 2 * sw->pad.x - 1;
}

/* General configuration. */
void
custom_general_config (struct conf *conf)
{
  struct scrollwin cwin;
  char *number_str =
    _("Enter an option number to change its value");
  char *keys =
    _("(Press '^P' or '^N' to move up or down, 'Q' to quit)");
  char *output_datefmt_str =
    _("Enter the date format (see 'man 3 strftime' for possible formats) ");
  char *input_datefmt_str =
    _("Enter the date format (1)mm/dd/yyyy (2)dd/mm/yyyy (3)yyyy/mm/dd "
      "(4)yyyy-mm-dd");
  char *periodic_save_str =
    _("Enter the delay, in minutes, between automatic saves (0 to disable) ");
  int ch;
  char *buf;

  clear ();
  custom_set_swsiz (&cwin);
  (void)snprintf (cwin.label, BUFSIZ, _("general options"));
  wins_scrollwin_init (&cwin);
  wins_show (cwin.win.p, cwin.label);
  status_mesg (number_str, keys);
  cwin.total_lines = print_general_options (cwin.pad.p, conf);
  wins_scrollwin_display (&cwin);

  buf = mem_malloc (BUFSIZ);
  while ((ch = wgetch (win[STA].p)) != 'q')
    {
      buf[0] = '\0';

      switch (ch)
        {
        case KEY_RESIZE:
          wins_get_config ();
          wins_reset ();
          wins_scrollwin_delete (&cwin);
          wins_scrollwin_init (&cwin);
          custom_set_swsiz (&cwin);
          wins_show (cwin.win.p, cwin.label);
          cwin.first_visible_line = 0;
          delwin (win[STA].p);
          win[STA].p = newwin (win[STA].h, win[STA].w, win[STA].y,
                               win[STA].x);
          keypad (win[STA].p, TRUE);
          if (notify_bar ())
            {
              notify_reinit_bar ();
              notify_update_bar ();
            }
          break;
        case CTRL ('N'):
          wins_scrollwin_down (&cwin, 1);
          break;
        case CTRL ('P'):
          wins_scrollwin_up (&cwin, 1);
          break;
        case '1':
          conf->auto_save = !conf->auto_save;
          break;
        case '2':
          status_mesg (periodic_save_str, "");
          if (updatestring (win[STA].p, &buf, 0, 1) == 0)
            {
              int val = atoi (buf);
              if (val >= 0)
                conf->periodic_save = val;
              if (conf->periodic_save > 0)
                io_start_psave_thread (conf);
              else if (conf->periodic_save == 0)
                io_stop_psave_thread ();
            }
          status_mesg (number_str, keys);
          break;
        case '3':
          conf->confirm_quit = !conf->confirm_quit;
          break;
        case '4':
          conf->confirm_delete = !conf->confirm_delete;
          break;
        case '5':
          conf->skip_system_dialogs = !conf->skip_system_dialogs;
          break;
        case '6':
          conf->skip_progress_bar = !conf->skip_progress_bar;
          break;
        case '7':
          calendar_change_first_day_of_week ();
          break;
        case '8':
          status_mesg (output_datefmt_str, "");
          (void)strncpy (buf, conf->output_datefmt,
                         strlen (conf->output_datefmt) + 1);
          if (updatestring (win[STA].p, &buf, 0, 1) == 0)
            {
              (void)strncpy (conf->output_datefmt, buf, strlen (buf) + 1);
            }
          status_mesg (number_str, keys);
          break;
        case '9':
          status_mesg (input_datefmt_str, "");
          if (updatestring (win[STA].p, &buf, 0, 1) == 0)
            {
              int val = atoi (buf);
              if (val > 0 && val <= DATE_FORMATS)
                conf->input_datefmt = val;
            }
          status_mesg (number_str, keys);
          break;
        }
      status_mesg (number_str, keys);
      cwin.total_lines = print_general_options (cwin.pad.p, conf);
      wins_scrollwin_display (&cwin);
    }
  mem_free (buf);
  wins_scrollwin_delete (&cwin);
}


static void
print_key_incolor (WINDOW *win, char *option, int pos_y, int pos_x)
{
  const int color = ATTR_HIGHEST;

  RETURN_IF (!option, _("Undefined option!"));
  custom_apply_attr (win, color);
  mvwprintw (win, pos_y, pos_x, "%s ", option);
  custom_remove_attr (win, color);
  wnoutrefresh (win);
}

static int
print_keys_bindings (WINDOW *win, int selected_row, int selected_elm, int yoff)
{
  const int XPOS = 1;
  const int EQUALPOS = 23;
  const int KEYPOS = 25;
  int noelm, action, y;

  noelm = y = 0;
  for (action = 0; action < NBKEYS; action++)
    {
      char actionstr[BUFSIZ];
      int nbkeys;

      nbkeys = keys_action_count_keys (action);
      (void)snprintf (actionstr, BUFSIZ, "%s", keys_get_label (action));
      if (action == selected_row)
        custom_apply_attr (win, ATTR_HIGHEST);
      mvwprintw (win, y, XPOS, "%s ", actionstr);
      mvwprintw (win, y, EQUALPOS, "=");
      if (nbkeys == 0)
        mvwprintw (win, y, KEYPOS, _("undefined"));
      if (action == selected_row)
        custom_remove_attr (win, ATTR_HIGHEST);
      if (nbkeys > 0)
        {
          if (action == selected_row)
            {
              char *key;
              int pos;

              pos = KEYPOS;
              while ((key = keys_action_nkey (action, noelm)) != 0)
                {
                  if (noelm == selected_elm)
                    print_key_incolor (win, key, y, pos);
                  else
                    mvwprintw (win, y, pos, "%s ", key);
                  noelm++;
                  pos += strlen (key) + 1;
                }
            }
          else
            {
              mvwprintw (win, y, KEYPOS, "%s", keys_action_allkeys (action));
            }
        }
      y += yoff;
    }

  return noelm;
}

static void
custom_keys_config_bar (void)
{
  struct binding quit  = {_("Exit"),     KEY_GENERIC_QUIT};
  struct binding info  = {_("Key info"), KEY_GENERIC_HELP};
  struct binding add   = {_("Add key"),  KEY_ADD_ITEM};
  struct binding del   = {_("Del key"),  KEY_DEL_ITEM};
  struct binding up    = {_("Up"),       KEY_MOVE_UP};
  struct binding down  = {_("Down"),     KEY_MOVE_DOWN};
  struct binding left  = {_("Prev Key"), KEY_MOVE_LEFT};
  struct binding right = {_("Next Key"), KEY_MOVE_RIGHT};

  struct binding *binding[] = {
    &quit, &info, &add, &del, &up, &down, &left, &right
  };
  int binding_size = sizeof (binding) / sizeof (binding[0]);

  keys_display_bindings_bar (win[STA].p, binding, 0, binding_size);
}

void
custom_keys_config (void)
{
  struct scrollwin kwin;
  int selrow, selelm, firstrow, lastrow, nbrowelm, nbdisplayed;
  int keyval, used, not_recognized;
  char *keystr;
  WINDOW *grabwin;
  const int LINESPERKEY = 2;
  const int LABELLINES = 3;

  clear ();
  custom_set_swsiz (&kwin);
  nbdisplayed = (kwin.win.h - LABELLINES) / LINESPERKEY;
  (void)snprintf (kwin.label, BUFSIZ, _("keys configuration"));
  wins_scrollwin_init (&kwin);
  wins_show (kwin.win.p, kwin.label);
  custom_keys_config_bar ();
  selrow = selelm = 0;
  nbrowelm = print_keys_bindings (kwin.pad.p, selrow, selelm, LINESPERKEY);
  kwin.total_lines = NBKEYS * LINESPERKEY;
  wins_scrollwin_display (&kwin);
  firstrow = 0;
  lastrow = firstrow + nbdisplayed - 1;
  for (;;)
    {
      int ch;

      ch = keys_getch (win[STA].p);
      switch (ch)
        {
        case KEY_MOVE_UP:
          if (selrow > 0)
            {
              selrow--;
              selelm = 0;
              if (selrow == firstrow)
                {
                  firstrow--;
                  lastrow--;
                  wins_scrollwin_up (&kwin, LINESPERKEY);
                }
            }
          break;
        case KEY_MOVE_DOWN:
          if (selrow < NBKEYS - 1)
            {
              selrow++;
              selelm = 0;
              if (selrow == lastrow)
                {
                  firstrow++;
                  lastrow++;
                  wins_scrollwin_down (&kwin, LINESPERKEY);
                }
            }
          break;
        case KEY_MOVE_LEFT:
          if (selelm > 0)
            selelm--;
          break;
        case KEY_MOVE_RIGHT:
          if (selelm < nbrowelm - 1)
            selelm++;
          break;
        case KEY_GENERIC_HELP:
          keys_popup_info (selrow);
          break;
        case KEY_ADD_ITEM:
#define WINROW 10
#define WINCOL 50
          do
            {
              used = 0;
              grabwin = popup (WINROW, WINCOL, (row - WINROW) / 2,
                               (col - WINCOL) / 2,
                               _("Press the key you want to assign to:"),
                               keys_get_label (selrow), 0);
              keyval = wgetch (grabwin);

              /* First check if this key would be recognized by calcurse. */
              if (keys_str2int (keys_int2str (keyval)) == -1)
                {
                  not_recognized = 1;
                  WARN_MSG (_("This key is not yet recognized by calcurse, "
                              "please choose another one."));
                  werase (kwin.pad.p);
                  nbrowelm = print_keys_bindings (kwin.pad.p, selrow, selelm,
                                                  LINESPERKEY);
                  wins_scrollwin_display (&kwin);
                  continue;
                }
              else
                not_recognized = 0;

              used = keys_assign_binding (keyval, selrow);
              if (used)
                {
                  enum key action;

                  action = keys_get_action (keyval);
                  WARN_MSG (_("This key is already in use for %s, "
                              "please choose another one."),
                             keys_get_label (action));
                  werase (kwin.pad.p);
                  nbrowelm = print_keys_bindings (kwin.pad.p, selrow, selelm,
                                                  LINESPERKEY);
                  wins_scrollwin_display (&kwin);
                }
              delwin (grabwin);
            }
          while (used || not_recognized);
          nbrowelm++;
          if (selelm < nbrowelm - 1)
            selelm++;
#undef WINROW
#undef WINCOL
          break;
        case KEY_DEL_ITEM:
          keystr = keys_action_nkey (selrow, selelm);
          keyval = keys_str2int (keystr);
          keys_remove_binding (keyval, selrow);
          nbrowelm--;
          if (selelm > 0 && selelm <= nbrowelm)
            selelm--;
          break;
        case KEY_GENERIC_QUIT:
          if (keys_check_missing_bindings () != 0)
            {
              WARN_MSG (_("Some actions do not have any associated "
                          "key bindings!"));
            }
          wins_scrollwin_delete (&kwin);
          return;
        }
      custom_keys_config_bar ();
      werase (kwin.pad.p);
      nbrowelm = print_keys_bindings (kwin.pad.p, selrow, selelm, LINESPERKEY);
      wins_scrollwin_display (&kwin);
    }
}