/* $calcurse: day.c,v 1.14 2006/12/15 15:29:16 culot Exp $ */ /* * Calcurse - text-based organizer * Copyright (c) 2004-2006 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 #include #include #include #include #include #include #include "i18n.h" #include "utils.h" #include "apoint.h" #include "event.h" #include "recur.h" #include "day.h" #include "vars.h" #include "args.h" static struct day_item_s *day_items_ptr; static struct day_saved_item_s *day_saved_item = NULL; /* * 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. */ int day_store_items(long date, int *pnb_events, int *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; } /* Free the current day linked list containing the events and appointments. */ 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; } /* * 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. */ 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->day, j->id); } } return e_nb; } /* * 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. */ 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->day, j->id); } } return e_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. */ 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, real_start, j->dur, n); n++; } } pthread_mutex_unlock(&(recur_alist_p->mutex)); return a_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. */ 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->start, j->dur, 0); } } pthread_mutex_unlock(&(alist_p->mutex)); return a_nb; } /* Add an event in the current day list */ struct day_item_s *day_add_event(int type, char *mesg, 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->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. */ struct day_item_s *day_add_apoint(int type, char *mesg, long start, long dur, 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->start = start; o->appt_dur = dur; o->appt_pos = real_pos; 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; } /* * 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, int colr) { struct day_item_s *p; 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(apad->ptrwin, item_number - incolor, p->mesg, recur, width - 5, 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++; if (item_number - incolor == 0) { day_saved_item->type = p->type; day_saved_item->mesg = p->mesg; apoint_sec2str(day_item_s2apoint_s(p), p->type, date, day_saved_item->start, day_saved_item->end); } display_item_date(apad->ptrwin, item_number - incolor, day_item_s2apoint_s(p), p->type, date, line + 1, x_pos); display_item(apad->ptrwin, item_number - incolor, p->mesg, 0, width - 6, line + 2, x_pos + 2); line = line + 3; } } } /* * Returns a structure of type apoint_llist_node_t given a structure of type * day_item_s */ apoint_llist_node_t *day_item_s2apoint_s(struct day_item_s *p) { apoint_llist_node_t *a; a = (apoint_llist_node_t *) malloc(sizeof(apoint_llist_node_t)); a->mesg = (char *) malloc(strlen(p->mesg) + 1); a->start = p->start; a->dur = p->appt_dur; a->mesg = p->mesg; return a; } /* 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 { /* NOT REACHED */ fputs(error, stderr); exit(EXIT_FAILURE); } } /* * 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(int year, int month, int 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(year, month, 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; } /* Edit an already existing item. */ void day_edit_item(int year, int month, int day, int item_num, int colr) { #define STRT '1' #define END '2' #define DESC '3' #define REPT '4' struct day_item_s *p; struct recur_event_s *re; struct rpt_s *rpt; struct tm *lt; time_t t; recur_apoint_llist_node_t *ra, *ra_new; long newtime = 0; const long date = date2sec(year, month, day, 0, 0); int cancel, i, ch = 0, valid_date = 0, newfreq = 0, date_entered = 0; int newmonth, newday, newyear; int nb_item[MAX_TYPES]; unsigned hr, mn; char *timestr, *typestr, *freqstr; char *msg_norecur = _("Edit: (1)Start time, (2)End time or (3)Description?"); char *choice_norecur = "[1/2/3] "; char *msg_recur = _("Edit: (1)Start time, (2)End time, (3)Description or (4)Repetition?"); char *msg_event_recur = _("Edit: (1)Description or (2)Repetition?"); char *choice_recur = "[1/2/3/4] "; char *choice_event_recur = "[1/2] "; char *mesg_wrong_date = _("The entered date is not valid."); char *mesg_possible_fmts = _("Possible formats are [mm/dd/yyyy] or '0' for an endless repetetition"); char *error_msg = _("Invalid time: start time must be before end time!"); char *enter_str = _("Press [Enter] to continue"); char *mesg_desc = _("Enter the new item description:"); char *mesg_type_1 = _("Enter the new repetition type: (D)aily, (W)eekly, (M)onthly, (Y)early"); char *mesg_type_2 = _("[D/W/M/Y] "); char *mesg_freq_1 = _("Enter the new repetition frequence:"); char *mesg_wrong_freq = _("The frequence you entered is not valid."); char *mesg_until_1 = _("Enter the new ending date: [mm/dd/yyyy] or '0'"); for (i = 0; i < MAX_TYPES; i++) nb_item[i] = 0; p = day_items_ptr; for (i = 1; i < item_num; i++) { nb_item[p->type - 1]++; p = p->next; } p = day_get_item(item_num); switch (p->type) { case RECUR_EVNT: re = recur_get_event(date, nb_item[RECUR_EVNT - 1]); rpt = re->rpt; status_mesg(msg_event_recur, choice_event_recur); while (ch != STRT && ch != END) ch = wgetch(swin); ch += 2; break; case EVNT: ch = DESC; break; case RECUR_APPT: ra = recur_get_apoint(date, nb_item[RECUR_APPT - 1]); rpt = ra->rpt; status_mesg(msg_recur, choice_recur); while (ch != STRT && ch != END && ch != DESC && ch != REPT) ch = wgetch(swin); break; case APPT: status_mesg(msg_norecur, choice_norecur); while (ch != STRT && ch != END && ch != DESC) ch = wgetch(swin); break; } switch (ch) { case STRT: while (!valid_date) { timestr = day_edit_time(p->start, colr); sscanf(timestr, "%u:%u", &hr, &mn); free(timestr); newtime = update_time_in_date(p->start, hr, mn); if (newtime < p->start + p->appt_dur) { p->start = newtime; valid_date = 1; } else { status_mesg(error_msg, enter_str); wgetch(swin); } } break; case END: while (!valid_date) { timestr = day_edit_time( p->start + p->appt_dur, colr); sscanf(timestr, "%u:%u", &hr, &mn); free(timestr); newtime = update_time_in_date( p->start + p->appt_dur, hr, mn); if (newtime > p->start) { p->appt_dur = newtime - p->start; valid_date = 1; } else { status_mesg(error_msg, enter_str); wgetch(swin); } } break; case DESC: status_mesg(mesg_desc, ""); updatestring(swin, colr, &p->mesg, 0, 1); break; case REPT: while ( (ch != 'D') && (ch != 'W') && (ch != 'M') && (ch != 'Y') ) { status_mesg(mesg_type_1, mesg_type_2); typestr = (char *) malloc(sizeof(char)); *typestr = recur_def2char(rpt->type); cancel = updatestring(swin, colr, &typestr, 0, 1); ch = toupper(*typestr); free(typestr); if (cancel) return; } while (newfreq == 0) { status_mesg(mesg_freq_1, ""); freqstr = (char *) malloc(MAX_LENGTH); snprintf(freqstr, MAX_LENGTH, "%d", rpt->freq); cancel = updatestring(swin, colr, &freqstr, 0, 1); newfreq = atoi(freqstr); free(freqstr); if (cancel) return; else { if (newfreq == 0) { status_mesg(mesg_wrong_freq, enter_str); wgetch(swin); } } } while (!date_entered) { status_mesg(mesg_until_1, ""); timestr = date_sec2date_str(rpt->until); cancel = updatestring(swin, colr, ×tr, 0, 1); if (cancel) { free(timestr); return; } if (strlen(timestr) == 1 && strncmp(timestr, "0", 1) == 0 ) { rpt->until = 0; date_entered = 1; } else { valid_date = check_date(timestr); if (valid_date) { sscanf(timestr, "%d / %d / %d", &newmonth, &newday, &newyear); t = p->start; lt = localtime(&t); rpt->until = date2sec( newyear, newmonth, newday, lt->tm_hour, lt->tm_min); if (rpt->until < p->start) { status_mesg(error_msg, enter_str); wgetch(swin); date_entered = 0; } else date_entered = 1; } else { status_mesg(mesg_wrong_date, mesg_possible_fmts); wgetch(swin); date_entered = 0; } } } free(timestr); rpt->freq = newfreq; rpt->type = recur_char2def(ch); break; } day_erase_item(date, item_num, 1); switch (p->type) { case RECUR_EVNT: recur_event_new(p->mesg, p->start, p->evnt_id, rpt->type, rpt->freq, rpt->until, NULL); break; case EVNT: event_new(p->mesg, p->start, p->evnt_id); break; case RECUR_APPT: ra_new = recur_apoint_new(p->mesg, p->start, p->appt_dur, rpt->type, rpt->freq, rpt->until, NULL); if (notify_bar()) notify_check_repeated(ra_new); break; case APPT: apoint_new(p->mesg, p->start, p->appt_dur); if (notify_bar()) notify_check_added(p->mesg, p->start); break; } } /* Request the user to enter a new time. */ char *day_edit_time(long time, int colr) { 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(swin, colr, ×tr, 0, 1); if (check_time(timestr) != 1 || strlen(timestr) == 0) { status_mesg(fmt_msg, enter_str); wgetch(swin); } else return timestr; } } /* * 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, int force_erase) { int i; int ch = 0; int nb_item[MAX_TYPES]; unsigned delete_whole; struct day_item_s *p; char *erase_warning = _("This item is recurrent. " "Delete (a)ll occurences or just this (o)ne ?"); char *erase_choice = _("[a/o] "); if (force_erase) ch = 'a'; for (i = 0; i < MAX_TYPES; i++) nb_item[i] = 0; p = day_items_ptr; for (i = 1; i < item_number; i++) { nb_item[p->type - 1]++; p = p->next; } if (p->type == EVNT) { event_delete_bynum(date, nb_item[EVNT - 1]); } else if (p->type == APPT) { apoint_delete_bynum(date, nb_item[APPT - 1]); } else { while ( (ch != 'a') && (ch != 'o') && (ch != ESCAPE)) { status_mesg(erase_warning, erase_choice); ch = wgetch(swin); } 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, nb_item[RECUR_EVNT - 1], delete_whole); } else { recur_apoint_erase(date, p->appt_pos, delete_whole); } } 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; }