aboutsummaryrefslogblamecommitdiffstats
path: root/src/calcurse.c
blob: cfd4ede7203f9ecf2e14bb852b972d07aa49ef63 (plain) (tree)
1
2
3
4
5
                                                                          


                                  
                                         
























                                                                       

                      
                        
                   
                   
                    





                    
                  







                     

                   
                
                  
                  

                 
                   

 
                                







                                           
                                          
                  
 



























                                                                               






                                                                           

                           
 
                    

                              
                                       
                            
                             
                                


                                 

                                                                  








                                                               



                                             



                                                                
                                                        
                             
                                      

                                                               




                                                                             
                                    
                           
                                                                             
                                        



                                               

                                         

                              






                                                  
                                      










                                                                   
                                 
 

                                                                               
                                      
                            






                                                                

                                            
                                
                   
                           
                                           

                                        
                                                                             
                                        




                                                                       
                                                               
                                                             


                                                                      
                                     















                                                                      
                             

                                                  
                                            















                                                                    

                                                                    










                                                                        
                               






                                                                              
                                          

                              




                                                     

                                                   
                                           

                                                    














                                                                         
                                           





                                                            

                                                                    

                                                                 
                                                                   






                                                                               

                                                                       


                                              
                                                                     
                                              

                                         
                                                            
                                              
                                 
                                                                            

                                                                   



                                                                            
                                                  
                                                   

                                             



                                                                              

                              
                                                                         
                                              



                                                                         
                                                       



                                                         

                                                 
                                                       
                                                      
                                                  
                                                       
                                                               

                                                                 
                         

                              

                                                           
                                                                      
                                                        
                                                                    
                                                         
                                          

                              

                                                    




                                                                       


                                          

                         
                                                                      
                                                            
                                                  
                              


                                                                      
                                                               

                                                  


                         
                                                                 
                                                                           


                                                                          
                                                    
                                                                       
                                                                
                         
                              
 
                                                        
                                              
                                               



                                                   
                                            

                              

                                                     
                                                                     

                              


                                                 

                                                                       

                                                   
                                                      





                                                

                                                                       

                                                   
                                                     





                                              

                                                                       

                                                   
                                                   
                                

                                                                  

                                                                
                                                              

                                                                  









                                                                           

                                                                       

                                                   
                                                     
                                
                                                                  

                                                                  

                                                                  

                                                                
                                 

                                                           

                                                                             
                                                            
                                                                      



                                 
                                                      
                           
                                           
                                                   

                                                




                                                                      

                                                                    
                                        
                                                           




                                              

                                                            




                                                

                                                                              



                                                           
                                                                  
                                                                              
                                                     

                         


                                                                          

                                     
/*	$calcurse: calcurse.c,v 1.48 2007/07/21 19:32:26 culot Exp $	*/

/*
 * Calcurse - text-based organizer
 * Copyright (c) 2004-2007 Frederic Culot
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Send your feedback or comments to : calcurse@culot.org
 * Calcurse home page : http://culot.org/calcurse
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

#include <sys/types.h>
#include <sys/wait.h>
#include <ncurses.h>	
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <locale.h>
#include <stdio.h>

#include "i18n.h"
#include "io.h"
#include "help.h"
#include "calendar.h"
#include "custom.h"
#include "utils.h"
#include "vars.h"
#include "wins.h"
#include "apoint.h"
#include "day.h"
#include "event.h"
#include "recur.h"
#include "todo.h"
#include "args.h"
#include "notify.h"


/* Variables for appointments */
int first_app_onscreen = 0;
int hilt_app = 0, sav_hilt_app;

/* Variables for todo list */
int nb_tod = 0, hilt_tod = 0, sav_hilt_tod;
int first_todo_onscreen = 1;
char *saved_t_mesg;

/* Variables to handle calcurse windows */
int which_pan = 0;


/* 
 * Catch return values from children (user-defined notification commands).
 * This is needed to avoid zombie processes running on system.
 */
static void
sigchld_handler(int sig)
{
	while (waitpid(WAIT_MYPGRP, NULL, WNOHANG) > 0)
		;
}

/* Signal handling init. */
static void
init_sighandler(struct sigaction *sa)
{
	sa->sa_handler = sigchld_handler;
	sa->sa_flags = 0;
	sigemptyset(&sa->sa_mask);

	if (sigaction(SIGCHLD, sa, NULL) != 0) {
		fprintf(stderr, 
		    "FATAL ERROR: signal handling could not be initialized\n");
		exit (EXIT_FAILURE);
	}
}


/*
 * Calcurse  is  a text-based personal organizer which helps keeping track
 * of events and everyday tasks. It contains a calendar, a 'todo' list,
 * and puts your appointments in order. The user interface is configurable,
 * and one can choose between different color schemes and layouts. 
 * All of the commands are documented within an online help system.
 */
int 
main(int argc, char **argv)
{
	conf_t conf;
	window_t win[NBWINS]; 
	day_items_nb_t inday;
	int ch, background, foreground;
	int non_interactive;
	int no_data_file = 1;
	struct sigaction sigact;
	bool do_storage = false;
	bool day_changed = false;
        char *no_color_support = 
            _("Sorry, colors are not supported by your terminal\n"
            "(Press [ENTER] to continue)");
	char *quit_message = _("Do you really want to quit ?");
	char choices[] = "[y/n] ";

#if ENABLE_NLS
        setlocale (LC_ALL, "");
        bindtextdomain (PACKAGE, LOCALEDIR);
        textdomain (PACKAGE);
#endif /* ENABLE_NLS */
		
	/* Thread-safe data structure init */
	apoint_llist_init();
	recur_apoint_llist_init();

	/* 
	 * Begin by parsing and handling command line arguments.
	 * The data path is also initialized here.
	 */
	non_interactive = parse_args(argc, argv, &conf);
	if (non_interactive) 
		return (EXIT_SUCCESS);

	/* Begin of interactive mode with ncurses interface. */
	init_sighandler(&sigact);	/* signal handling init */
	initscr();			/* start the curses mode */
	cbreak();			/* control chars generate a signal */
	noecho();			/* controls echoing of typed chars */
	curs_set(0);			/* make cursor invisible */
        calendar_set_current_date();
	notify_init_vars();
	wins_get_config(&conf, &win[STATUS], &win[NOTIFY], &win[APPOINTMENT],
	    &win[TODO], &win[CALENDAR]);
	
        /* Check if terminal supports color. */
	if (has_colors()) {
                colorize = true;
		background = COLOR_BLACK;
		foreground = COLOR_WHITE;
		start_color();

#ifdef NCURSES_VERSION
		if (use_default_colors() != ERR) {
			background = -1;
			foreground = -1;
		}
#endif /* NCURSES_VERSION */

		/* Color assignment */
		init_pair(COLR_RED, COLOR_RED, background);
		init_pair(COLR_GREEN, COLOR_GREEN, background);
		init_pair(COLR_YELLOW, COLOR_YELLOW, background);
		init_pair(COLR_BLUE, COLOR_BLUE, background);
		init_pair(COLR_MAGENTA, COLOR_MAGENTA, background);
		init_pair(COLR_CYAN, COLOR_CYAN, background);
		init_pair(COLR_DEFAULT, foreground, background);
		init_pair(COLR_HIGH, COLOR_BLACK, COLOR_GREEN);
		init_pair(COLR_CUSTOM, COLOR_RED, background);

	} else
                colorize = false;

	vars_init(&conf);
	wins_init(&win[CALENDAR], &win[APPOINTMENT], &win[TODO], &win[STATUS]);
	notify_init_bar(&win[NOTIFY]);
	reset_status_page();

	/* 
	 * Read the data from files : first the user
	 * configuration (the display is then updated), and then
	 * the todo list, appointments and events.
	 */
	no_data_file = check_data_files();
	custom_load_conf(&conf, background);
	erase_status_bar();
	nb_tod = load_todo();	
	load_app();
	if (notify_bar()) {
		notify_start_main_thread();
		notify_check_next_app();
	}
	wins_get_config(&conf, &win[STATUS], &win[NOTIFY], &win[APPOINTMENT],
	    &win[TODO], &win[CALENDAR]);
        wins_reinit(&conf, which_pan, &win[STATUS], &win[APPOINTMENT], 
	    &win[TODO], &win[CALENDAR], &win[NOTIFY]);
	wins_update(which_pan, &conf, &win[STATUS], 
	    &win[APPOINTMENT], &win[TODO], hilt_app, hilt_tod,
	    which_pan, nb_tod, first_todo_onscreen, saved_t_mesg);
        startup_screen(conf.skip_system_dialogs, no_data_file);
	inday = *day_process_storage(0, day_changed, &inday);
	wins_update(CALENDAR, &conf, &win[STATUS], &win[APPOINTMENT], 
	    &win[TODO], hilt_app, hilt_tod, which_pan, nb_tod,
	    first_todo_onscreen, saved_t_mesg);
	calendar_start_date_thread();

	/* User input */
	for (;;) {
		
		/* Check terminal size. */
		getmaxyx(stdscr, row, col);
		if ((col < 80) | (row < 24)) {
		        endwin();
			fputs(_("Please resize your terminal screen\n"
				"(to at least 80x24),\n"
				"and restart calcurse.\n"), stderr);
			return EXIT_FAILURE;
		}

		/* Get user input. */
		ch = wgetch(swin);
		switch (ch) {

		case 9:	/* The TAB key was hit. */
			reset_status_page();
			/* Save previously highlighted event. */
			if (which_pan == TODO) {
				sav_hilt_tod = hilt_tod;
				hilt_tod = 0;
			}
			if (which_pan == APPOINTMENT) {
				sav_hilt_app = hilt_app;
				hilt_app = 0;
			}
			/* Switch to the selected panel. */
			if (which_pan == TODO) which_pan = CALENDAR;
			else ++which_pan;

			/* Select the event to highlight. */
			if (which_pan == APPOINTMENT) {
				if ((sav_hilt_app == 0) 
					&& ((inday.nb_events + 
					    inday.nb_apoints) != 0))
					hilt_app = 1;
				else
					hilt_app = sav_hilt_app;
			} else if (which_pan == TODO) {
				if ((sav_hilt_tod == 0) & (nb_tod != 0))
					hilt_tod = 1;
				else
					hilt_tod = sav_hilt_tod;
			}
			break;

		case CTRL('R'):
                        wins_reinit(&conf, which_pan, &win[STATUS], 
			    &win[APPOINTMENT], &win[TODO], &win[CALENDAR], 
			    &win[NOTIFY]);
			wins_update(which_pan, &conf, &win[STATUS], 
			    &win[APPOINTMENT], &win[TODO], hilt_app, hilt_tod,
			    which_pan, nb_tod, first_todo_onscreen, 
			    saved_t_mesg);
			do_storage = true;
			break;

		case 'O':
		case 'o':
			other_status_page(which_pan);
			break;

		case 'G':
		case 'g':	/* Goto function */
			erase_status_bar();
			calendar_set_current_date();
			calendar_change_day();
			do_storage = true;
			day_changed = true;
			break;

		case 'V':
		case 'v':	/* View function */
			if ((which_pan == APPOINTMENT) & (hilt_app != 0))
				day_popup_item();
			else if ((which_pan == TODO) & (hilt_tod != 0)) 
				item_in_popup(NULL, NULL, saved_t_mesg,
						_("To do :"));
			break;

		case 'C':
		case 'c':	/* Configuration menu */
			erase_status_bar();
			config_bar();
			while ((ch = wgetch(swin)) != 'q') {
				switch (ch) {
				case 'C':
				case 'c':
                                        if (has_colors()) {
                                                custom_color_config(
						    notify_bar()); 
                                        } else {
                                                colorize = false;
						erase_status_bar();
                                                mvwprintw(swin, 0, 0, 
                                                          _(no_color_support));
                                                wgetch(swin);
                                        }
					break;
				case 'L':
				case 'l':
					conf.layout = 
					    layout_config(conf.layout);
					break;
				case 'G':
				case 'g':
					custom_general_config(&conf);
					break;
				case 'N':
				case 'n':
					notify_config_bar();
					break;
				}
                                wins_reinit(&conf, which_pan, &win[STATUS], 
				    &win[APPOINTMENT], &win[TODO], 
				    &win[CALENDAR], &win[NOTIFY]);
				wins_update(which_pan, &conf, &win[STATUS], 
				    &win[APPOINTMENT], &win[TODO], hilt_app,
				    hilt_tod, which_pan, nb_tod, 
				    first_todo_onscreen, saved_t_mesg);
				do_storage = true;
				erase_status_bar();
				config_bar();
			}
                        wins_update(which_pan, &conf, &win[STATUS], 
			    &win[APPOINTMENT], &win[TODO], hilt_app, hilt_tod,
			    which_pan, nb_tod, first_todo_onscreen, 
			    saved_t_mesg);
			break;

		case CTRL('A'):	/* Add an app, whatever panel selected */
			apoint_add(&hilt_app);
			do_storage = true;
			break;

		case CTRL('T'):	/* Add a todo, whatever panel selected */
			nb_tod = todo_new_item(nb_tod);
			if (hilt_tod == 0 && nb_tod == 1)
				hilt_tod++;
			break;

		case 'A':
		case 'a':	/* Add an item */
			if (which_pan == APPOINTMENT) {
				apoint_add(&hilt_app);
				do_storage = true;
			} else if (which_pan == TODO) {
				nb_tod = todo_new_item(nb_tod);
				if (hilt_tod == 0 && nb_tod == 1)
					hilt_tod++;
			}
			break;

		case 'E':
		case 'e':	/* Edit an existing item */
			if (which_pan == APPOINTMENT && hilt_app != 0)
				day_edit_item(hilt_app);
			else if (which_pan == TODO && hilt_tod != 0)
				todo_edit_item(hilt_tod);
			do_storage = true;
			break;

		case 'D':
		case 'd':	/* Delete an item */
			if (which_pan == APPOINTMENT && hilt_app != 0)
				apoint_delete(&conf, &inday.nb_events, 
				    &inday.nb_apoints, &hilt_app);
			else if (which_pan == TODO && hilt_tod != 0)
				todo_delete(&conf, &nb_tod, &hilt_tod);
			do_storage = true;
			break;

		case 'R':
		case 'r':
			if (which_pan == APPOINTMENT && hilt_app != 0)
				recur_repeat_item(hilt_app);
				do_storage = true;
			break;

		case '!':
			if (which_pan == APPOINTMENT && hilt_app != 0)
				apoint_switch_notify(hilt_app);
				do_storage = true;
			break;
	
		case '+':
		case '-':
			if (which_pan == TODO && hilt_tod != 0) {
				hilt_tod = todo_chg_priority(ch, hilt_tod);
				if (hilt_tod < first_todo_onscreen)
					first_todo_onscreen = hilt_tod;
				else if (hilt_tod - first_todo_onscreen >=
				    win[TODO].h - 4)
					first_todo_onscreen = hilt_tod 
					    - win[TODO].h + 5;	
			}
			break;

		case '?':	/* Online help system */
			status_bar(which_pan);
			help_screen(which_pan);
			break;

		case 'S':
		case 's':	/* Save function */
			io_save_cal(&conf); 
			break;

		case 'X':
		case 'x':	/* Export function */
			io_export_data(IO_EXPORT_INTERACTIVE, &conf);
			break;

		case (261):	/* right arrow */
		case ('L'):
		case ('l'):
		case CTRL('L'):
			if (which_pan == CALENDAR || ch == CTRL('L')) {
				do_storage = true;
				day_changed = true;
				calendar_move_right();
			}
			break;

		case (260):	/* left arrow */
		case ('H'):
		case ('h'):
		case CTRL('H'):
			if (which_pan == CALENDAR || ch == CTRL('H')) {
				do_storage = true;
				day_changed = true;
				calendar_move_left();
			}
			break;

		case (259):	/* up arrow */
		case ('K'):
		case ('k'):
		case CTRL('K'):
			if (which_pan == CALENDAR || ch == CTRL('K')) {
				do_storage = true;
				day_changed = true;
				calendar_move_up();
			} else {
				if ((which_pan == APPOINTMENT) && 
				    (hilt_app > 1)) {
					hilt_app--;
					scroll_pad_up(hilt_app, 
		    			    inday.nb_events); 
				} else if ((which_pan == TODO) && 
				    (hilt_tod > 1)) {
					hilt_tod--;
					if (hilt_tod < first_todo_onscreen)
						first_todo_onscreen--;
				}
			}
			break;

		case (258):	/* down arrow */
		case ('J'):
		case ('j'):
		case CTRL('J'):
			if (which_pan == CALENDAR || ch == CTRL('J')) {
				do_storage = true;
				day_changed = true;
				calendar_move_down();
			} else {
				if ((which_pan == APPOINTMENT) && 
				    (hilt_app < inday.nb_events + 
				    inday.nb_apoints)) {
					hilt_app++;
					scroll_pad_down(hilt_app, 
					    inday.nb_events, 
					    win[APPOINTMENT].h);
				}
				if ((which_pan == TODO) && 
				    (hilt_tod < nb_tod)) {
					++hilt_tod;
					if (hilt_tod - first_todo_onscreen ==
					    win[TODO].h - 4)
						first_todo_onscreen++;
				}
			}
			break;

		case ('Q'):	/* Quit calcurse :( */
		case ('q'):
			if (conf.auto_save)
				io_save_cal(&conf);

			if (conf.confirm_quit) {
				status_mesg(_(quit_message), choices);
				ch = wgetch(swin);
				if ( ch == 'y' ) {
					endwin();
                                        erase();
					calendar_stop_date_thread();
					return (EXIT_SUCCESS);
				} else {
					erase_status_bar();
					break;
				}
			} else {
				endwin();
                                erase();
				calendar_stop_date_thread();
				return (EXIT_SUCCESS);
			}
			break;

		}	/* end case statement */
		if (do_storage) {
			inday = *day_process_storage(calendar_get_slctd_day(),
			    day_changed, &inday);
			do_storage = !do_storage;
			if (day_changed) {
				sav_hilt_app = 0;
				day_changed = !day_changed;
				if ((which_pan == APPOINTMENT) && 
				    (inday.nb_events + inday.nb_apoints != 0))
					hilt_app = 1;
			}
		}
		wins_update(which_pan, &conf, &win[STATUS], 
		    &win[APPOINTMENT], &win[TODO], hilt_app, hilt_tod, 
		    which_pan, nb_tod, first_todo_onscreen, saved_t_mesg);
	}
}	/* end of interactive mode */