aboutsummaryrefslogblamecommitdiffstats
path: root/src/day.c
blob: 9d7fe79529222ba02cf27e62129508c067127182 (plain) (tree)
1
2
3
4
5
                                                                  


                                  
                                         




















                                                                       


                      
                  


                 

                   
                   
                
 

                                                       
 


                                                                              
 
                                 
 





                                                

 

                                          
                                                                 
 



                                                                    
                       















                                     
 

                                                 

                                                                       


                                  
 


                                                                    
                       





















                                                     

 








                                                                   
 


                               
 


                                                  

                                                                            
                        
         

                    








                                                                       

                                 





                                                    
                                                                                
                                           
                               

                                                                          






                        




                                                              
   

                            
 
                               
                               
                     
 



                                                      

                                                                               

                        
                                                
 
                    




                                                                        

                                                                    

                                                                     

                                  
 
                                     
                               

                            
 

                                                            

                                                                        
                               

                                                                          
                            

                        
                                                      




                    





                                                                             
   
           
                                                                       
 


                                         
 

                                                
 













                                                         

 






                                                                                
 























                                                                             

 



                                                                             

                                                                 
 



                             


   
























































                                                                                





                                                                       

                                                            

                             
                              
                                              
                            
                               







                                                                        
                                                                     


                                                      



                                                                   




                                                               

                                                               

                                                                               
                               
                                         


                                                                          
                                                             





                                                                          
                                                   

                                                               
                                                               

                                                                                
                         

                                                                              



                                                                            



                 
                                            

                    









                                                                               
            
                                            
                                





                                                                          


                              
                                  
                                      
                          
                               
                                              

                                                      

                                                                      

                                 

                                                              
                                                                        





                                                                 




                                                



                                                                
                                 

                                                


                 
 

                                           

                        








                                                                        
                                                         

                                                                       
                                           
                      
                                         


         





















































































































































                                                                                 
                                    
     
                   
 



                           
 
                             
                                 
                          
                                      


                               
 
                                 
                                   
                                            
 
               

                          

                                                             


                                                                        
                                                







                                                       
                               
                 

                      

                                                                       

                        

                                                             

                                                                       
                                                                
                                               
                                                













                                                                
                               
                 

                      



                                                                             
                                                










                                                              
                               
                 
                      
         

 





                                                                  
    
                                                             
 



                                                                  




                                                         
                        
                              
 
                                        


                                       





                                                               
                                           
                    
                                                     
         
                              
                                                                              
                          
                                     
                                                                               
                          
                
                                                  
                                 
                                                                       
                                                                 
                                                

                                
                                         
                                       



                                         
                                            

                                                                        
                                                
                        
                                                                            
                                  
                 
         
                         
 












                                                       



















                                                   






                                               
                               
                                 
                          







                                   
                                                                           
                               

                                           








                                                                




                                                                       



                                                             




                                                                        
                      
         














                                                                
/*	$calcurse: day.c,v 1.34 2008/01/20 10:45:38 culot Exp $	*/

/*
 * Calcurse - text-based organizer
 * Copyright (c) 2004-2008 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
 *
 */

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <ctype.h>
#include <time.h>

#include "i18n.h"
#include "apoint.h"
#include "event.h"
#include "custom.h"
#include "day.h"

static struct day_item_s 	*day_items_ptr;
static struct day_saved_item_s 	*day_saved_item = NULL;

/* Free the current day linked list containing the events and appointments. */
static void 
day_free_list(void)
{
	struct day_item_s *p, *q;

	for (p = day_items_ptr; p != 0; p = q) {
		q = p->next;
		free(p->mesg);
		free(p);
	}
	day_items_ptr = NULL;
}

/* Add an event in the current day list */
static struct day_item_s *
day_add_event(int type, char *mesg, char *note, long day, int id)
{
	struct day_item_s *o, **i;
	o = (struct day_item_s *) malloc(sizeof(struct day_item_s));
	o->mesg = (char *) malloc(strlen(mesg) + 1);
	strncpy(o->mesg, mesg, strlen(mesg) + 1);
	o->note = note;
	o->type = type;
	o->appt_dur = 0;
	o->appt_pos = 0;
	o->start = day;
	o->evnt_id = id;
	i = &day_items_ptr;
	for (;;) {
		if (*i == 0) {
			o->next = *i;
			*i = o;
			break;
		}
		i = &(*i)->next;
	}
	return o;
}

/* Add an appointment in the current day list. */
static struct day_item_s *
day_add_apoint(int type, char *mesg, char *note, long start, long dur, 
    char state, int real_pos)
{
	struct day_item_s *o, **i;
	int insert_item = 0;

	o = (struct day_item_s *) malloc(sizeof(struct day_item_s));
	o->mesg = (char *) malloc(strlen(mesg) + 1);
	strncpy(o->mesg, mesg, strlen(mesg) + 1);
	o->note = note;
	o->start = start;
	o->appt_dur = dur;
	o->appt_pos = real_pos;
	o->state = state;
	o->type = type;
	o->evnt_id = 0;
	i = &day_items_ptr;
	for (;;) {
		if (*i == 0) {
			insert_item = 1;
		} else if ( ((*i)->start > start) && 
		    ((*i)->type > EVNT) ) {
			insert_item = 1;
		}	
		if (insert_item) {
			o->next = *i;
			*i = o;
			break;
		}
		i = &(*i)->next;
	}
	return o;
}

/* 
 * Store the events for the selected day in structure pointed
 * by day_items_ptr. This is done by copying the events 
 * from the general structure pointed by eventlist to the structure
 * dedicated to the selected day. 
 * Returns the number of events for the selected day.
 */
static int 
day_store_events(long date)
{
	struct event_s *j;
	struct day_item_s *ptr;
	int e_nb = 0;

	for (j = eventlist; j != 0; j = j->next) {
		if (event_inday(j, date)) {
			e_nb++;
			ptr = day_add_event(EVNT, j->mesg, j->note, j->day, 
			    j->id);
		}	
	}

	return e_nb;
}

/* 
 * Store the recurrent events for the selected day in structure pointed
 * by day_items_ptr. This is done by copying the recurrent events 
 * from the general structure pointed by recur_elist to the structure
 * dedicated to the selected day. 
 * Returns the number of recurrent events for the selected day.
 */
static int 
day_store_recur_events(long date)
{
	struct recur_event_s *j;
	struct day_item_s *ptr;
	int e_nb = 0;

	for (j = recur_elist; j != 0; j = j->next) {
		if (recur_item_inday(j->day, j->exc, j->rpt->type, j->rpt->freq,
		    j->rpt->until, date)) {
			e_nb++;
			ptr = day_add_event(RECUR_EVNT, j->mesg, j->note, 
			    j->day, j->id);
		}	
	}

	return e_nb;
}

/* 
 * Store the apoints for the selected day in structure pointed
 * by day_items_ptr. This is done by copying the appointments
 * from the general structure pointed by alist_p->root to the 
 * structure dedicated to the selected day. 
 * Returns the number of appointments for the selected day.
 */
static int 
day_store_apoints(long date)
{
	apoint_llist_node_t *j;
	struct day_item_s *ptr;
	int a_nb = 0;

	pthread_mutex_lock(&(alist_p->mutex));
	for (j = alist_p->root; j != 0; j = j->next) {
		if (apoint_inday(j, date)) {
			a_nb++;
			ptr = day_add_apoint(APPT, j->mesg, j->note, j->start, 
			    j->dur, j->state, 0);
		}	
	}
	pthread_mutex_unlock(&(alist_p->mutex));

	return a_nb;
}

/* 
 * Store the recurrent apoints for the selected day in structure pointed
 * by day_items_ptr. This is done by copying the appointments
 * from the general structure pointed by recur_alist_p->root to the 
 * structure dedicated to the selected day. 
 * Returns the number of recurrent appointments for the selected day.
 */
static int 
day_store_recur_apoints(long date)
{
	recur_apoint_llist_node_t *j;
	struct day_item_s *ptr;
	long real_start;
	int a_nb = 0, n = 0;

	pthread_mutex_lock(&(recur_alist_p->mutex));
	for (j = recur_alist_p->root; j != 0; j = j->next) {
		if ((real_start = recur_item_inday(j->start, j->exc, 
		    j->rpt->type, j->rpt->freq, j->rpt->until, date)) ){
			a_nb++;
			ptr = day_add_apoint(RECUR_APPT, j->mesg, j->note,
			    real_start, j->dur, j->state, n);
			n++;
		}	
	}
	pthread_mutex_unlock(&(recur_alist_p->mutex));

	return a_nb;
}

/* 
 * Store all of the items to be displayed for the selected day.
 * Items are of four types: recursive events, normal events, 
 * recursive appointments and normal appointments.
 * The items are stored in the linked list pointed by *day_items_ptr
 * and the length of the new pad to write is returned.
 * The number of events and appointments in the current day are also updated.
 */
static int 
day_store_items(long date, unsigned *pnb_events, unsigned *pnb_apoints)
{
	int pad_length;
	int nb_events, nb_recur_events;
	int nb_apoints, nb_recur_apoints;

	pad_length = nb_events = nb_apoints = 0;
	nb_recur_events = nb_recur_apoints = 0;

	if (day_items_ptr != 0)
		day_free_list();
	nb_recur_events = day_store_recur_events(date);
	nb_events = day_store_events(date);
	*pnb_events = nb_events;
	nb_recur_apoints = day_store_recur_apoints(date);
	nb_apoints = day_store_apoints(date);
	*pnb_apoints = nb_apoints;
	pad_length = nb_recur_events + nb_events + 1 + 
		3*(nb_recur_apoints + nb_apoints);
	*pnb_apoints += nb_recur_apoints;
	*pnb_events += nb_recur_events;

	return pad_length;
}

/*
 * Store the events and appointments for the selected day, and write
 * those items in a pad. If selected day is null, then store items for current
 * day. This is useful to speed up the appointment panel update.
 */
day_items_nb_t *
day_process_storage(date_t *slctd_date, bool day_changed, day_items_nb_t *inday)
{
	long date;
	date_t day;

	if (slctd_date) 
		day = *slctd_date;
	else
		calendar_store_current_date(&day);

	date = date2sec(day, 0, 0);

	/* Inits */
	if (apad->length != 0)
		delwin(apad->ptrwin);

	/* Store the events and appointments (recursive and normal items). */
	apad->length = day_store_items(date, 
		&inday->nb_events, &inday->nb_apoints);

	/* Create the new pad with its new length. */
	if (day_changed) 
		apad->first_onscreen = 0;
	apad->ptrwin = newpad(apad->length, apad->width);

	return (inday);
}

/*
 * Returns a structure of type apoint_llist_node_t given a structure of type 
 * day_item_s 
 */
static void
day_item_s2apoint_s(apoint_llist_node_t *a, struct day_item_s *p)
{
	a->state = p->state;
	a->start = p->start;
	a->dur = p->appt_dur;
	a->mesg = p->mesg;
}

/* 
 * Print an item date in the appointment panel.
 */
static void 
display_item_date(int incolor, apoint_llist_node_t *i, int type, long date, 
    int y, int x)
{
	WINDOW *win;
	char a_st[100], a_end[100];
	int recur = 0;

	win = apad->ptrwin;
	apoint_sec2str(i, type, date, a_st, a_end);
	if (type == RECUR_EVNT || type == RECUR_APPT)
		recur = 1;
	if (incolor == 0) 
		custom_apply_attr(win, ATTR_HIGHEST);
	if (recur)
		if (i->state & APOINT_NOTIFY)
			mvwprintw(win, y, x, " *!%s -> %s", a_st, a_end);
		else
			mvwprintw(win, y, x, " * %s -> %s", a_st, a_end);
	else
		if (i->state & APOINT_NOTIFY)
			mvwprintw(win, y, x, " -!%s -> %s", a_st, a_end);
		else
			mvwprintw(win, y, x, " - %s -> %s", a_st, a_end);
	if (incolor == 0) 
		custom_remove_attr(win, ATTR_HIGHEST);
}

/* 
 * Print an item description in the corresponding panel window.
 */
static void 
display_item(int incolor, char *msg, int recur, int note, int len, int y, int x)
{
	WINDOW *win;
	int ch_recur, ch_note;
	char buf[len];

	win = apad->ptrwin;
	ch_recur = (recur) ? '*' : ' ';
	ch_note = (note) ? '>' : ' ';
	if (incolor == 0) 
		custom_apply_attr(win, ATTR_HIGHEST);
	if (strlen(msg) < len)
		mvwprintw(win, y, x, " %c%c%s", ch_recur, ch_note, msg);
	else {
		strncpy(buf, msg, len - 1);
		buf[len - 1] = '\0';
		mvwprintw(win, y, x, " %c%c%s...", ch_recur, ch_note, buf);
	}
	if (incolor == 0) 
		custom_remove_attr(win, ATTR_HIGHEST);
}

/* 
 * Write the appointments and events for the selected day in a pad.
 * An horizontal line is drawn between events and appointments, and the
 * item selected by user is highlighted. This item is also saved inside
 * structure (pointed by day_saved_item), to be later displayed in a
 * popup window if requested.
 */
void 
day_write_pad(long date, int width, int length, int incolor)
{
	struct day_item_s *p;
	apoint_llist_node_t a;
	int line, item_number, max_pos, recur;
	const int x_pos = 0;
	bool draw_line = false;

	line = item_number = 0;
	max_pos = length;

	/* Initialize the structure used to store highlited item. */
	if (day_saved_item == NULL) {
		day_saved_item = (struct day_saved_item_s *) 
			malloc(sizeof(struct day_saved_item_s));	
		day_saved_item->mesg = (char *) malloc(sizeof(char));
	} 

	for (p = day_items_ptr; p != 0; p = p->next) {
		if (p->type == RECUR_EVNT || p->type == RECUR_APPT)
			recur = 1;
		else
			recur = 0;
		/* First print the events for current day. */
		if (p->type < RECUR_APPT) {
			item_number++;		
			if (item_number - incolor == 0) {
				day_saved_item->type = p->type;
				day_saved_item->mesg = p->mesg;
			}
			display_item(item_number - incolor, p->mesg, recur,
			    (p->note != NULL) ? 1 : 0, width - 7, line, x_pos);
			line++;
			draw_line = true;
		} else {
			/* Draw a line between events and appointments. */
			if (line > 0 && draw_line){
				wmove(apad->ptrwin, line, 0);
				whline(apad->ptrwin, 0, width);
				draw_line = false;
			}

			/* Last print the appointments for current day. */
			item_number++;
			day_item_s2apoint_s(&a, p);
			if (item_number - incolor == 0) {
				day_saved_item->type = p->type;
				day_saved_item->mesg = p->mesg;
				apoint_sec2str(&a, p->type, date,
				    day_saved_item->start, day_saved_item->end);
			}
			display_item_date(item_number - incolor, &a, p->type, 
			    date, line + 1, x_pos);	
			display_item(item_number - incolor, p->mesg, 0, 
			    (p->note != NULL) ? 1 : 0, width - 7, line + 2, 
			    x_pos);
			line += 3;
		}
	}
}

/* Display an item inside a popup window. */
void 
day_popup_item(void)
{
	char *error = 
		_("FATAL ERROR in day_popup_item: unknown item type\n");

	if (day_saved_item->type == EVNT || day_saved_item->type == RECUR_EVNT)
		item_in_popup(NULL, NULL, day_saved_item->mesg, _("Event :"));
	else if (day_saved_item->type == APPT || 
		day_saved_item->type == RECUR_APPT)
		item_in_popup(day_saved_item->start, day_saved_item->end,
			day_saved_item->mesg, _("Appointment :"));
	else
		ierror(error, IERROR_FATAL);
		/* NOTREACHED */
}

/* 
 * Need to know if there is an item for the current selected day inside
 * calendar. This is used to put the correct colors inside calendar panel.
 */
int 
day_check_if_item(date_t day) 
{
	struct recur_event_s  *re;
	recur_apoint_llist_node_t *ra;
	struct event_s *e;
	apoint_llist_node_t *a;
	const long date = date2sec(day, 0, 0);

	for (re = recur_elist; re != 0; re = re->next)
		if (recur_item_inday(re->day, re->exc, re->rpt->type, 
			re->rpt->freq, re->rpt->until, date))
			return 1;
	
	pthread_mutex_lock(&(recur_alist_p->mutex));
	for (ra = recur_alist_p->root; ra != 0; ra = ra->next)
		if (recur_item_inday(ra->start, ra->exc, ra->rpt->type, 
			ra->rpt->freq, ra->rpt->until, date)) {
				pthread_mutex_unlock(
					&(recur_alist_p->mutex));
				return 1;
		}
	pthread_mutex_unlock(&(recur_alist_p->mutex));

	for (e = eventlist; e != 0; e = e->next)
		if (event_inday(e, date))
			return 1;

	pthread_mutex_lock(&(alist_p->mutex));
	for (a = alist_p->root; a != 0; a = a->next)
		if (apoint_inday(a, date)) {
			pthread_mutex_unlock(&(alist_p->mutex));
			return 1;
		}
	pthread_mutex_unlock(&(alist_p->mutex));

	return 0;
}

/* Request the user to enter a new time. */
static char *
day_edit_time(long time)
{
	char *timestr;
	char *msg_time = _("Enter the new time ([hh:mm] or [h:mm]) : ");
        char *enter_str = _("Press [Enter] to continue");
	char *fmt_msg = 
	_("You entered an invalid time, should be [h:mm] or [hh:mm]");

	while (1) {
		status_mesg(msg_time, "");
		timestr = date_sec2hour_str(time);
		updatestring(win[STA].p, &timestr, 0, 1);
		if (check_time(timestr) != 1 || strlen(timestr) == 0) {
			status_mesg(fmt_msg, enter_str);
			wgetch(win[STA].p);
		} else
			return (timestr);
	}
}

static void
update_start_time(long *start, long *dur)
{
	long newtime;
	unsigned hr, mn;
	int valid_date;
	char *timestr;
	char *msg_wrong_time =
	    _("Invalid time: start time must be before end time!");
        char *msg_enter = _("Press [Enter] to continue");

	do {
		timestr = day_edit_time(*start);
		sscanf(timestr, "%u:%u", &hr, &mn);
		free(timestr);
		newtime = update_time_in_date(*start, hr, mn);
		if (newtime < *start + *dur) {
			*dur -= (newtime - *start);
			*start = newtime;
			valid_date = 1;
		} else {
			status_mesg(msg_wrong_time, msg_enter);
			wgetch(win[STA].p);
			valid_date = 0;
		}
	} while (valid_date == 0);
}

static void
update_duration(long *start, long *dur)
{
	long newtime;
	unsigned hr, mn;
	char *timestr;

	timestr = day_edit_time(*start + *dur);
	sscanf(timestr, "%u:%u", &hr, &mn);
	free(timestr);
	newtime = update_time_in_date(*start, hr, mn);
	*dur = (newtime > *start) ? newtime - *start : 
	    DAYINSEC + newtime - *start;
}

static void
update_desc(char **desc)
{
	status_mesg(_("Enter the new item description:"), "");
	updatestring(win[STA].p, desc, 0, 1);
}

static void
update_rept(struct rpt_s **rpt, const long start)
{
	const int SINGLECHAR = 2;
	int ch, cancel, newfreq, date_entered, valid_date;
	long newuntil;
	char *typstr, *freqstr, *timstr;
	char *msg_rpt_type = 
	    _("Enter the new repetition type: (D)aily, (W)eekly, "
	    "(M)onthly, (Y)early");
	char *msg_rpt_ans = _("[D/W/M/Y] ");
	char *msg_wrong_freq = _("The frequence you entered is not valid.");
	char *msg_wrong_time =
	    _("Invalid time: start time must be before end time!");
	char *msg_wrong_date = _("The entered date is not valid.");
	char *msg_fmts = _("Possible formats are [mm/dd/yyyy] or '0' "
	    "for an endless repetetition");
        char *msg_enter = _("Press [Enter] to continue");

	do {
		status_mesg(msg_rpt_type, msg_rpt_ans);
		typstr = (char *)malloc(sizeof(char) * SINGLECHAR);
		snprintf(typstr, SINGLECHAR, "%c", recur_def2char((*rpt)->type));
		cancel = updatestring(win[STA].p, &typstr, 0, 1);
		if (cancel) {
			free(typstr);
			return;
		} else {
			ch = toupper(*typstr);
			free(typstr);
		}
	} while ((ch != 'D') && (ch != 'W') && (ch != 'M') && (ch != 'Y'));

	do {
		status_mesg(_("Enter the new repetition frequence:"), "");
		freqstr = (char *)malloc(BUFSIZ); 
		snprintf(freqstr, BUFSIZ, "%d", (*rpt)->freq);
		cancel = updatestring(win[STA].p, &freqstr, 0, 1);
		if (cancel) {
			free(freqstr);
			return;
		} else {
			newfreq = atoi(freqstr);
			free(freqstr);
			if (newfreq == 0) {
				status_mesg(msg_wrong_freq, msg_enter);
				wgetch(win[STA].p);
			}
		}
	} while (newfreq == 0);

	do {
		status_mesg(_("Enter the new ending date: [mm/dd/yyyy] or '0'"),
		    "");
		timstr = date_sec2date_str((*rpt)->until);
		cancel = updatestring(win[STA].p, &timstr, 0, 1);
		if (cancel) {
			free(timstr);
			return;
		}
		if (strcmp(timstr, "0") == 0) {
			newuntil = 0;
			date_entered = 1;
		} else {
 			struct tm *lt;
			time_t t;
			date_t new_date;
			int newmonth, newday, newyear;

			valid_date = check_date(timstr);
			if (valid_date) {
				sscanf(timstr, "%d / %d / %d", 
				    &newmonth, &newday, &newyear);	
				t = start; 
				lt = localtime(&t);
				new_date.dd = newday;
				new_date.mm = newmonth;
				new_date.yyyy = newyear;
				newuntil = date2sec(new_date, lt->tm_hour, 
				    lt->tm_min);
				if (newuntil < start) {
					status_mesg(msg_wrong_time, msg_enter);
					wgetch(win[STA].p);
					date_entered = 0;
				} else
					date_entered = 1;
			} else {
				status_mesg(msg_wrong_date, msg_fmts);
				wgetch(win[STA].p);
				date_entered = 0;
			}
		}
	} while (date_entered == 0);

	free(timstr);
	(*rpt)->type = recur_char2def(ch);
	(*rpt)->freq = newfreq;
	(*rpt)->until = newuntil;
}

/* Edit an already existing item. */
void 
day_edit_item(void)
{
#define STRT		'1'
#define END		'2'
#define DESC		'3'
#define REPT		'4'

	struct day_item_s *p;
	struct recur_event_s *re;
	struct event_s *e;
	recur_apoint_llist_node_t *ra;
	apoint_llist_node_t *a;
	long date;
	int item_num, ch;

	item_num = apoint_hilt();
	p = day_get_item(item_num);
	date = calendar_get_slctd_day_sec();

	ch = 0;
	switch (p->type) {
	case RECUR_EVNT:
		re = recur_get_event(date, 
		    day_item_nb(date, item_num, RECUR_EVNT));
		status_mesg(_("Edit: (1)Description or (2)Repetition?"),
		    "[1/2] ");
		while (ch != '1' && ch != '2' && ch != ESCAPE)
			ch = wgetch(win[STA].p);
		switch (ch) {
		case '1':
			update_desc(&re->mesg);
			break;
		case '2':
			update_rept(&re->rpt, re->day);
			break;
		default:
			return;
		}
		break;
	case EVNT:
		e = event_get(date, day_item_nb(date, item_num, EVNT));
		update_desc(&e->mesg);
		break;
	case RECUR_APPT:
		ra = recur_get_apoint(date, 
		    day_item_nb(date, item_num, RECUR_APPT));
		status_mesg(_("Edit: (1)Start time, (2)End time, "
		    "(3)Description or (4)Repetition?"), "[1/2/3/4] ");
		while (ch != STRT && ch != END && ch != DESC && 
		    ch != REPT && ch != ESCAPE)
			ch = wgetch(win[STA].p);
		switch (ch) {
		case STRT:
			update_start_time(&ra->start, &ra->dur);
			break;
		case END:
			update_duration(&ra->start, &ra->dur);
			break;
		case DESC:
			update_desc(&ra->mesg);
			break;
		case REPT:
			update_rept(&ra->rpt, ra->start);
			break;
		case ESCAPE:
			return;
		}
		break;
	case APPT:
		a = apoint_get(date, day_item_nb(date, item_num, APPT));
		status_mesg(_("Edit: (1)Start time, (2)End time "
		    "or (3)Description?"), "[1/2/3] ");
		while (ch != STRT && ch != END && ch != DESC && ch != ESCAPE)
			ch = wgetch(win[STA].p);
		switch (ch) {
		case STRT:
			update_start_time(&a->start, &a->dur);
			break;
		case END:
			update_duration(&a->start, &a->dur);
			break;
		case DESC:
			update_desc(&a->mesg);
			break;
		case ESCAPE:
			return;
		}
		break;
	}
}

/*
 * In order to erase an item, we need to count first the number of
 * items for each type (in order: recurrent events, events, 
 * recurrent appointments and appointments) and then to test the
 * type of the item to be deleted.
 */
int 
day_erase_item(long date, int item_number, erase_flag_e flag)
{
	struct day_item_s *p;
	char *erase_warning =
		_("This item is recurrent. "
		  "Delete (a)ll occurences or just this (o)ne ?");
	char *note_warning =
		_("This item has a note attached to it. "
		  "Delete (i)tem or just its (n)ote ?");
	char *note_choice = _("[i/n] ");
	char *erase_choice = _("[a/o] ");
	int ch = 0, ans;
	unsigned delete_whole;

	p = day_get_item(item_number);	
	if (flag == ERASE_DONT_FORCE) {
		ans = 0;
		if (p->note == NULL)
			ans = 'i';
		while (ans != 'i' && ans != 'n') {
			status_mesg(note_warning, note_choice);
			ans = wgetch(win[STA].p);
		}
		if (ans == 'i')
			flag = ERASE_FORCE;
		else
			flag = ERASE_FORCE_ONLY_NOTE;
	}
	if (p->type == EVNT) {
		event_delete_bynum(date, day_item_nb(date, item_number, EVNT),
		    flag);
	} else if (p->type == APPT) {
		apoint_delete_bynum(date, day_item_nb(date, item_number, APPT),
		    flag);
	} else {
		if (flag == ERASE_FORCE_ONLY_NOTE)
			ch = 'a';
		while ( (ch != 'a') && (ch != 'o') && (ch != ESCAPE)) {
			status_mesg(erase_warning, erase_choice);
			ch = wgetch(win[STA].p);
		}
		if (ch == 'a') {
			delete_whole = 1;
		} else if (ch == 'o') {
			delete_whole = 0;
		} else {
			return 0;
		}
		if (p->type == RECUR_EVNT) {
			recur_event_erase(date, 
			    day_item_nb(date, item_number, RECUR_EVNT), 
			    delete_whole, flag);
		} else {
			recur_apoint_erase(date, p->appt_pos, delete_whole, 
			    flag);
		}
	}
	return (p->type);
}

/* Returns a structure containing the selected item. */
struct day_item_s *day_get_item(int item_number)
{
	struct day_item_s *o;
	int i;
	
	o = day_items_ptr;
	for (i = 1; i < item_number; i++) {
		o = o->next;
	}
	return o;
}

/* Returns the real item number, given its type. */
int
day_item_nb(long date, int day_num, int type)
{
	int i, nb_item[MAX_TYPES];
	struct day_item_s *p;

	for (i = 0; i < MAX_TYPES; i++)
		nb_item[i] = 0;

	p = day_items_ptr;

	for (i = 1; i < day_num; i++) {
		nb_item[p->type - 1]++;
		p = p->next;
	}

	return (nb_item[type - 1]);
}

/* Attach a note to an appointment or event. */
void
day_edit_note(char *editor)
{
	struct day_item_s *p;
	recur_apoint_llist_node_t *ra;
	apoint_llist_node_t *a;
	struct recur_event_s *re;
	struct event_s *e;
	char fullname[BUFSIZ];
	char *filename;
	long date;
	int item_num;

	item_num = apoint_hilt();
	p = day_get_item(item_num);
	if (p->note == NULL) {
		if ((filename = new_tempfile(path_notes, NOTESIZ)) == NULL)
			return;
		else
			p->note = filename;
	}
	snprintf(fullname, BUFSIZ, "%s%s", path_notes, p->note);
	wins_launch_external(fullname, editor);

	date = calendar_get_slctd_day_sec();
	switch (p->type) {
	case RECUR_EVNT:
		re = recur_get_event(date, 
		    day_item_nb(date, item_num, RECUR_EVNT));
		re->note = p->note;
		break;
	case EVNT:
		e = event_get(date, day_item_nb(date, item_num, EVNT));
		e->note = p->note;
		break;
	case RECUR_APPT:
		ra = recur_get_apoint(date, 
		    day_item_nb(date, item_num, RECUR_APPT));
		ra->note = p->note;
		break;
	case APPT:
		a = apoint_get(date, day_item_nb(date, item_num, APPT));
		a->note = p->note;
		break;
	}
}

/* View a note previously attached to an appointment or event */
void 
day_view_note(char *pager)
{
	struct day_item_s *p;
	char fullname[BUFSIZ];

	p = day_get_item(apoint_hilt());
	if (p->note == NULL)
		return;
	snprintf(fullname, BUFSIZ, "%s%s", path_notes, p->note);
	wins_launch_external(fullname, pager);
}