diff options
-rw-r--r-- | README | 18 | ||||
-rwxr-xr-x | autogen.sh | 155 | ||||
-rw-r--r-- | doc/calcurse.1.txt | 11 | ||||
-rw-r--r-- | doc/manual.txt | 26 | ||||
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/apoint.c | 325 | ||||
-rw-r--r-- | src/args.c | 134 | ||||
-rw-r--r-- | src/calcurse.c | 814 | ||||
-rw-r--r-- | src/calcurse.h | 149 | ||||
-rw-r--r-- | src/calendar.c | 25 | ||||
-rw-r--r-- | src/custom.c | 8 | ||||
-rw-r--r-- | src/day.c | 947 | ||||
-rw-r--r-- | src/event.c | 75 | ||||
-rw-r--r-- | src/help.c | 33 | ||||
-rw-r--r-- | src/ical.c | 31 | ||||
-rw-r--r-- | src/interaction.c | 899 | ||||
-rw-r--r-- | src/io.c | 93 | ||||
-rw-r--r-- | src/keys.c | 32 | ||||
-rw-r--r-- | src/llist.c | 47 | ||||
-rw-r--r-- | src/llist.h | 10 | ||||
-rw-r--r-- | src/recur.c | 428 | ||||
-rw-r--r-- | src/todo.c | 164 | ||||
-rw-r--r-- | src/utils.c | 24 | ||||
-rw-r--r-- | src/wins.c | 9 | ||||
-rw-r--r-- | test/Makefile.am | 3 | ||||
-rwxr-xr-x | test/bug-002.sh | 16 | ||||
-rw-r--r-- | test/data/apts | 141 | ||||
-rw-r--r-- | test/data/apts-bug-002 | 2 |
29 files changed, 2295 insertions, 2326 deletions
@@ -31,11 +31,19 @@ Contributors ------------ * RegEx support: Erik Saule -* German translation: Michael Schulz, Chris M., Benjamin Moeller -* Spanish translation: Jose Lopez -* Dutch translation: Jeremy Roon -* French translation: Erik Saule -* Russian translation: Aleksey Mechonoshin +* Dutch translation: Jeremy Roon, 2007-2010 +* French translation: Frédéric Culot, 2006-2010 +* French translation: Toucouch, 2007 +* French translation: Erik Saule, 2011-2012 +* French translation: Stéphane Aulery, 2012 +* French translation: Baptiste Jonglez, 2012 +* German translation: Michael Schulz, 2006-2010 +* German translation: Chris M., 2006 +* German translation: Benjamin Moeller, 2010 +* German translation: Lukas Fleischer, 2011-2012 +* Portuguese (Brazil) translation: Rafael Ferreira, 2012 +* Russian translation: Aleksey Mechonoshin, 2011-2012 +* Spanish translation: Jose Lopez, 2006-2010 Also check the `Thanks` section in the manual for a list of people who have contributed by reporting bugs, sending fixes, or suggesting improvements. @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2004-2011 calcurse Development Team <misc@calcurse.org> +# Copyright (c) 2004-2012 calcurse Development Team <misc@calcurse.org> # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -28,153 +28,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -# -# autogen.sh - Generates all the necessary files to build calcurse from -# cvs tree. -# - -PKG_NAME=calcurse -AC_VERSION="2 59" -AUTOCONF_VERSION=2.59 -AC_FLAGS= -AM_VERSION="1 9" -AUTOMAKE_VERSION=1.9 -AM_FLAGS="--foreign --copy --add-missing" -GETTEXT_VERSION="0 14" -GETTEXT_FLAGS="--copy --no-changelog" -ACLOCAL_FLAGS="-I m4" -SRCDIR=`dirname $0` -test -z "$SRCDIR" && SRCDIR=. -export AUTOMAKE_VERSION AUTOCONF_VERSION - -# Function to check if we are at the top level of calcurse package. -check_directory_level() -{ - (test -f $SRCDIR/configure.ac) || { - printf "\n\n**Error**: Directory "\`$SRCDIR\'" does not appear to" - printf "\nbe the top-level $PKG_NAME directory.\n" - exit 1 - } -} - -# Clean previous files before running scripts -clean_old_files() -{ - printf "Cleaning old files ... " - rm -rf configure config.log aclocal.m4 \ - config.status config autom4te.cache \ - po/Makefile.in.in ABOUT-NLS - printf "done\n" -} - -# Clean useless backup files -clean_backup_files() -{ - printf "Cleaning backup files ... " - rm -rf configure.ac\~ Makefile.am\~ - printf "done\n" -} - -# Function to check for a program availability -check_program() -{ - PROGRAM=$1 - printf "Checking for $PROGRAM ... " - ($PROGRAM --version) < /dev/null > /dev/null 2>&1 || { - printf "\n\n**Error**: You must have $PROGRAM installed." - printf "\nDownload the appropriate package for your distribution," - printf "\nor get the source tarball at ftp://ftp.gnu.org/pub/gnu/" - printf "\n" - exit 1 - } - FOUND=`which $PROGRAM` - printf "$FOUND\n" -} - -# Function to check a program's version -# (there must be a better way, but I am not good at sed...) -check_program_version() -{ - PROGRAM=$1; MAJOR=$2; MINOR=$3 - printf "Checking that $PROGRAM version is at least $MAJOR.$MINOR ... " - VERSION=`$PROGRAM --version | head -n 1 | rev | cut -d' ' -f1 | rev` - MAJOR_FOUND=`echo $VERSION | cut -d. -f1` - MINOR_FOUND=`echo $VERSION | sed 's/[a-zA-Z-].*//' | cut -d. -f2` - [ -z "$MINOR_FOUND" ] && MINOR_FOUND=0 - - WRONG= - if [ "$MAJOR_FOUND" -lt "$MAJOR" ]; then - WRONG=1 - elif [ "$MAJOR_FOUND" -eq "$MAJOR" ]; then - if [ "$MINOR_FOUND" -lt "$MINOR" ]; then - WRONG=1 - fi - fi - if [ ! -z "$WRONG" ]; then - printf "\n\n**Error**: found version $MAJOR_FOUND.$MINOR_FOUND," - printf "\nwhich is too old. You should upgrade $PROGRAM." - printf "\nDownload the appropriate package for your distribution," - printf "\nor get the source tarball at ftp://ftp.gnu.org/pub/gnu/" - printf "\n" - exit 1 - else - printf "OK, found $MAJOR_FOUND.$MINOR_FOUND\n" - fi -} - -# Dirty hack to run gettextize: problem is that it demands to -# press Return no matter what... This gets rid of that demand. -run_gettext() -{ - PROGRAM=gettextize - printf "Running $PROGRAM $GETTEXT_FLAGS ... " - sed 's:read .*< /dev/tty::' `which $PROGRAM` > my-gettextize - chmod +x my-gettextize - (printf "\n" | ./my-gettextize $GETTEXT_FLAGS > /dev/null 2>&1) || { - printf "\n\n**Error**: $PROGRAM failed.\n" - exit 1 - } - - # now restore the files modified by gettextize - (test -f configure.ac~) && mv -f configure.ac~ configure.ac - (test -f Makefile.am~) && mv -f Makefile.am~ Makefile.am - mv -f po/Makevars.template po/Makevars - rm my-gettextize - printf "OK\n" -} - -# Function to run a program -run_program() -{ - PROGRAM=$1 - shift - PROGRAM_FLAGS=$@ - printf "Running $PROGRAM $PROGRAM_FLAGS ... " - $PROGRAM $PROGRAM_FLAGS > /dev/null 2>&1 || { - printf "\n\n**Error**: $PROGRAM failed.\n" - exit 1 - } - printf "OK\n" -} - -# Main -echo " --- $PKG_NAME autogen script ---\n" -check_directory_level -clean_old_files -check_program gettext -check_program gettextize -check_program_version gettext $GETTEXT_VERSION -check_program aclocal -check_program autoheader -check_program automake -check_program_version automake $AM_VERSION -check_program autoconf -check_program_version autoconf $AC_VERSION -run_gettext -run_program aclocal $ACLOCAL_FLAGS -run_program autoheader -run_program automake $AM_FLAGS -run_program autoconf $AC_FLAGS -clean_backup_files -printf "\nYou can now run the configure script to obtain $PKG_NAME Makefile.\n" +aclocal -I m4 +autoheader +automake --foreign --copy --add-missing +autoconf diff --git a/doc/calcurse.1.txt b/doc/calcurse.1.txt index 6768088..2383e95 100644 --- a/doc/calcurse.1.txt +++ b/doc/calcurse.1.txt @@ -42,7 +42,7 @@ Synopsis -------- [verse] -*calcurse* [*-h*|*-v*] [*-an*] [*-t*[num]] [*-c*<file> | *-D*<dir>] +*calcurse* [*-h*|*-v*] [*-an*] [*-t*[num]] [*-c*<file>] [*-D*<dir>] [*-i*<file>] [*-x*[format]] [*-d* <date>|<num>] [*-s*[date]] [*-r*[range]] [*-S* <regex>] [*--status*] @@ -65,9 +65,8 @@ The following options are supported: from which to read the appointments can be specified using the *-c* flag. *-c* <file>, *--calendar* <file>:: - Specify the calendar file to use. The default calendar is - *~/.calcurse/apts* (see section 'FILES' below). This option is incompatible - with -*D*. + Specify the calendar file to use. The default calendar is *~/.calcurse/apts* + (see section 'FILES' below). This option has precedence over *-D*. *-d* <date|num>, *--day* <date|num>:: Print the appointments for the given date or for the given number of @@ -100,8 +99,8 @@ menu. Four formats are available: appointments can be specified using the *-c* flag. *-D* <dir>, *--directory* <dir>:: - Specify the data directory to use. This option is incompatible with -c. - If not specified, the default directory is *~/.calcurse/*. + Specify the data directory to use. If not specified, the default directory is + *~/.calcurse/*. *--format-apt* <format>:: Specify a format to control the output of appointments in non-interactive diff --git a/doc/manual.txt b/doc/manual.txt index 2ff8a9a..7f8ac93 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -181,8 +181,9 @@ long options are supported): from which to read the appointments can be specified using the `-c` flag. `-c <file>, --calendar <file>`:: - Specify the calendar file to use. The default calendar is - `~/.calcurse/apts` (see section <<basics_files,calcurse files>>). + Specify the calendar file to use. The default calendar is `~/.calcurse/apts` + (see section <<basics_files,calcurse files>>). This option has precedence + over `-D`. `-d <date|num>, --day <date|num>`:: Print the appointments for the given date or for the given number of @@ -206,8 +207,8 @@ Note: as for the `-a` flag, the calendar from which to read the appointments can be specified using the `-c` flag. `-D <dir>, --directory <dir>`:: - Specify the data directory to use. This option is incompatible with -c. - If not specified, the default directory is `~/.calcurse/`. + Specify the data directory to use. If not specified, the default directory is + `~/.calcurse/`. `--format-apt <format>`:: Specify a format to control the output of appointments in non-interactive @@ -1291,20 +1292,23 @@ So here is a list of contributing persons I would like to thank : * Joel for its calendar script which inspired `calcurse` calendar view -* Michael Schulz and Chris M. for the german translation of `calcurse` and its - manual +* Jeremy Roon for the Dutch translation -* Jose Lopez for the spanish translation of `calcurse` and its manual +* Frédéric Culot, Toucouch, Erik Saule, Stéphane Aulery + and Baptiste Jonglez for the French translation -* Neil Williams for the english translation +* Michael Schulz, Chris M., Benjamin Moeller and Lukas Fleischer for the German + translation -* Leandro Noferini for the italian translation +* Rafael Ferreira for the Portuguese (Brazil) translation + +* Aleksey Mechonoshin for the Russian translation + +* Jose Lopez for the Spanish translation * Tony for its patch which helped improving the recur_item_inday() function, and for implementing the date format configuration options -* Jeremy Roon for the dutch translation - * Erik Saule for its patch implementing the `-N`, `-s`, `-S`, `-r` and `-D` flags diff --git a/po/POTFILES.in b/po/POTFILES.in index 6b3969a..0827a52 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -11,6 +11,7 @@ src/event.c src/getstring.c src/help.c src/ical.c +src/interaction.c src/io.c src/keys.c src/llist.c diff --git a/src/Makefile.am b/src/Makefile.am index 7eab77f..cb44177 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,6 +21,7 @@ calcurse_SOURCES = \ getstring.c \ help.c \ ical.c \ + interaction.c \ io.c \ keys.c \ llist.c \ diff --git a/src/apoint.c b/src/apoint.c index 03ccb36..a756fac 100644 --- a/src/apoint.c +++ b/src/apoint.c @@ -42,35 +42,30 @@ #include "calcurse.h" llist_ts_t alist_p; -static struct apoint bkp_cut_apoint; static int hilt; -void apoint_free_bkp(void) -{ - if (bkp_cut_apoint.mesg) { - mem_free(bkp_cut_apoint.mesg); - bkp_cut_apoint.mesg = 0; - } - erase_note(&bkp_cut_apoint.note); -} - -static void apoint_free(struct apoint *apt) +void apoint_free(struct apoint *apt) { mem_free(apt->mesg); erase_note(&apt->note); mem_free(apt); } -static void apoint_dup(struct apoint *in, struct apoint *bkp) +struct apoint *apoint_dup(struct apoint *in) { - EXIT_IF(!in || !bkp, _("null pointer")); + EXIT_IF(!in, _("null pointer")); - bkp->start = in->start; - bkp->dur = in->dur; - bkp->state = in->state; - bkp->mesg = mem_strdup(in->mesg); + struct apoint *apt = mem_malloc(sizeof(struct apoint)); + apt->start = in->start; + apt->dur = in->dur; + apt->state = in->state; + apt->mesg = mem_strdup(in->mesg); if (in->note) - bkp->note = mem_strdup(in->note); + apt->note = mem_strdup(in->note); + else + apt->note = NULL; + + return apt; } void apoint_llist_init(void) @@ -135,204 +130,9 @@ struct apoint *apoint_new(char *mesg, char *note, long start, long dur, return apt; } -/* - * Add an item in either the appointment or the event list, - * depending if the start time is entered or not. - */ -void apoint_add(void) +unsigned apoint_inday(struct apoint *i, long *start) { -#define LTIME 6 -#define LDUR 12 - const char *mesg_1 = - _("Enter start time ([hh:mm]), leave blank for an all-day event : "); - const char *mesg_2 = - _ - ("Enter end time ([hh:mm]) or duration ([+hh:mm], [+xxxdxxhxxm] or [+mm]) : "); - const char *mesg_3 = _("Enter description :"); - const char *format_message_1 = - _("You entered an invalid start time, should be [hh:mm]"); - const char *format_message_2 = - _ - ("Invalid end time/duration, should be [hh:mm], [+hh:mm], [+xxxdxxhxxm] or [+mm]"); - const char *enter_str = _("Press [Enter] to continue"); - int Id = 1; - char item_time[LDUR] = ""; - char item_mesg[BUFSIZ] = ""; - long apoint_start; - unsigned heures, minutes; - unsigned apoint_duration; - unsigned end_h, end_m; - int is_appointment = 1; - - /* Get the starting time */ - for (;;) { - status_mesg(mesg_1, ""); - if (getstring(win[STA].p, item_time, LTIME, 0, 1) != GETSTRING_ESC) { - if (strlen(item_time) == 0) { - is_appointment = 0; - break; - } - - if (parse_time(item_time, &heures, &minutes) == 1) - break; - else { - status_mesg(format_message_1, enter_str); - wgetch(win[STA].p); - } - } else - return; - } - - /* - * Check if an event or appointment is entered, - * depending on the starting time, and record the - * corresponding item. - */ - if (is_appointment) { /* Get the appointment duration */ - item_time[0] = '\0'; - for (;;) { - status_mesg(mesg_2, ""); - if (getstring(win[STA].p, item_time, LDUR, 0, 1) != GETSTRING_ESC) { - if (*item_time == '+' && parse_duration(item_time + 1, - &apoint_duration) == 1) - break; - else if (parse_time(item_time, &end_h, &end_m) == 1) { - if (end_h < heures || ((end_h == heures) && (end_m < minutes))) { - apoint_duration = MININSEC - minutes + end_m - + (24 + end_h - (heures + 1)) * MININSEC; - } else { - apoint_duration = MININSEC - minutes - + end_m + (end_h - (heures + 1)) * MININSEC; - } - break; - } else { - status_mesg(format_message_2, enter_str); - wgetch(win[STA].p); - } - } else - return; - } - } else /* Insert the event Id */ - Id = 1; - - status_mesg(mesg_3, ""); - if (getstring(win[STA].p, item_mesg, BUFSIZ, 0, 1) == GETSTRING_VALID) { - if (is_appointment) { - apoint_start = date2sec(*calendar_get_slctd_day(), heures, minutes); - apoint_new(item_mesg, 0L, apoint_start, min2sec(apoint_duration), 0L); - if (notify_bar()) - notify_check_added(item_mesg, apoint_start, 0L); - } else - event_new(item_mesg, 0L, date2sec(*calendar_get_slctd_day(), 0, 0), Id); - - if (hilt == 0) - hilt++; - } - wins_erase_status_bar(); -} - -/* Delete an item from the appointment list. */ -void apoint_delete(unsigned *nb_events, unsigned *nb_apoints) -{ - const char *del_app_str = _("Do you really want to delete this item ?"); - long date; - int nb_items = *nb_apoints + *nb_events; - int to_be_removed = 0; - - date = calendar_get_slctd_day_sec(); - - if (nb_items == 0) - return; - - if (conf.confirm_delete) { - if (status_ask_bool(del_app_str) != 1) { - wins_erase_status_bar(); - return; - } - } - - if (nb_items != 0) { - switch (day_erase_item(date, hilt, ERASE_DONT_FORCE)) { - case EVNT: - case RECUR_EVNT: - (*nb_events)--; - to_be_removed = 1; - break; - case APPT: - case RECUR_APPT: - (*nb_apoints)--; - to_be_removed = 3; - break; - case 0: - return; - default: - EXIT(_("no such type")); - /* NOTREACHED */ - } - - if (hilt > 1) - hilt--; - if (apad.first_onscreen >= to_be_removed) - apad.first_onscreen = apad.first_onscreen - to_be_removed; - if (nb_items == 1) - hilt = 0; - } -} - -/* Cut an item, so that it can be pasted somewhere else later. */ -int apoint_cut(unsigned *nb_events, unsigned *nb_apoints) -{ - const int NBITEMS = *nb_apoints + *nb_events; - int item_type, to_be_removed; - long date; - - if (NBITEMS == 0) - return 0; - - date = calendar_get_slctd_day_sec(); - item_type = day_cut_item(date, hilt); - if (item_type == EVNT || item_type == RECUR_EVNT) { - (*nb_events)--; - to_be_removed = 1; - } else if (item_type == APPT || item_type == RECUR_APPT) { - (*nb_apoints)--; - to_be_removed = 3; - } else - EXIT(_("no such type")); - /* NOTREACHED */ - - if (hilt > 1) - hilt--; - if (apad.first_onscreen >= to_be_removed) - apad.first_onscreen = apad.first_onscreen - to_be_removed; - if (NBITEMS == 1) - hilt = 0; - - return item_type; -} - -/* Paste a previously cut item. */ -void apoint_paste(unsigned *nb_events, unsigned *nb_apoints, int cut_item_type) -{ - int item_type; - long date; - - date = calendar_get_slctd_day_sec(); - item_type = day_paste_item(date, cut_item_type); - if (item_type == EVNT || item_type == RECUR_EVNT) - (*nb_events)++; - else if (item_type == APPT || item_type == RECUR_APPT) - (*nb_apoints)++; - else - return; - - if (hilt == 0) - hilt++; -} - -unsigned apoint_inday(struct apoint *i, long start) -{ - return (i->start <= start + DAYINSEC && i->start + i->dur > start); + return (i->start <= *start + DAYINSEC && i->start + i->dur > *start); } void apoint_sec2str(struct apoint *o, long day, char *start, char *end) @@ -410,49 +210,21 @@ struct apoint *apoint_scan(FILE * f, struct tm start, struct tm end, char state, return apoint_new(buf, note, tstart, tend - tstart, state); } -/* Retrieve an appointment from the list, given the day and item position. */ -struct apoint *apoint_get(long day, int pos) +void apoint_delete(struct apoint *apt) { - llist_item_t *i = LLIST_TS_FIND_NTH(&alist_p, pos, day, apoint_inday); - - if (i) - return LLIST_TS_GET_DATA(i); - - EXIT(_("item not found")); - /* NOTREACHED */ -} + LLIST_TS_LOCK(&alist_p); -void apoint_delete_bynum(long start, unsigned num, enum eraseflg flag) -{ - llist_item_t *i; + llist_item_t *i = LLIST_TS_FIND_FIRST(&alist_p, apt, NULL); int need_check_notify = 0; - LLIST_TS_LOCK(&alist_p); - i = LLIST_TS_FIND_NTH(&alist_p, num, start, apoint_inday); - if (!i) EXIT(_("no such appointment")); - struct apoint *apt = LLIST_TS_GET_DATA(i); - - switch (flag) { - case ERASE_FORCE_ONLY_NOTE: - erase_note(&apt->note); - break; - case ERASE_CUT: - apoint_free_bkp(); - apoint_dup(apt, &bkp_cut_apoint); - erase_note(&apt->note); - /* FALLTHROUGH */ - default: - if (notify_bar()) - need_check_notify = notify_same_item(apt->start); - LLIST_TS_REMOVE(&alist_p, i); - mem_free(apt->mesg); - mem_free(apt); - if (need_check_notify) - notify_check_next_app(0); - break; - } + + if (notify_bar()) + need_check_notify = notify_same_item(apt->start); + LLIST_TS_REMOVE(&alist_p, i); + if (need_check_notify) + notify_check_next_app(0); LLIST_TS_UNLOCK(&alist_p); } @@ -509,9 +281,9 @@ void apoint_scroll_pad_up(int nb_events_inday) apad.first_onscreen = item_first_line; } -static int apoint_starts_after(struct apoint *apt, long time) +static int apoint_starts_after(struct apoint *apt, long *time) { - return apt->start > time; + return apt->start > *time; } /* @@ -523,7 +295,7 @@ struct notify_app *apoint_check_next(struct notify_app *app, long start) llist_item_t *i; LLIST_TS_LOCK(&alist_p); - i = LLIST_TS_FIND_FIRST(&alist_p, start, apoint_starts_after); + i = LLIST_TS_FIND_FIRST(&alist_p, &start, apoint_starts_after); if (i) { struct apoint *apt = LLIST_TS_GET_DATA(i); @@ -544,34 +316,13 @@ struct notify_app *apoint_check_next(struct notify_app *app, long start) /* * Switch notification state. */ -void apoint_switch_notify(void) +void apoint_switch_notify(struct apoint *apt) { - struct day_item *p; - long date; - int apoint_nb = 0, need_chk_notify; - - p = day_get_item(hilt); - if (p->type != APPT && p->type != RECUR_APPT) - return; - - date = calendar_get_slctd_day_sec(); - - if (p->type == RECUR_APPT) { - recur_apoint_switch_notify(date, p->appt_pos); - return; - } else if (p->type == APPT) - apoint_nb = day_item_nb(date, hilt, APPT); - - need_chk_notify = 0; LLIST_TS_LOCK(&alist_p); - struct apoint *apt = apoint_get(date, apoint_nb); - apt->state ^= APOINT_NOTIFY; if (notify_bar()) notify_check_added(apt->mesg, apt->start, apt->state); - if (need_chk_notify) - notify_check_next_app(0); LLIST_TS_UNLOCK(&alist_p); } @@ -605,9 +356,8 @@ void apoint_update_panel(int which_pan) /* Draw the scrollbar if necessary. */ if ((apad.length >= app_length) || (apad.first_onscreen > 0)) { - float ratio = ((float)app_length) / ((float)apad.length); - int sbar_length = (int)(ratio * app_length); - int highend = (int)(ratio * apad.first_onscreen); + int sbar_length = app_length * app_length / apad.length; + int highend = app_length * apad.first_onscreen / apad.length; unsigned hilt_bar = (which_pan == APP) ? 1 : 0; int sbar_top = highend + title_lines + 1; @@ -624,17 +374,14 @@ void apoint_update_panel(int which_pan) win[APP].x + win[APP].w - 3 * bordr); } -void apoint_paste_item(void) +void apoint_paste_item(struct apoint *apt, long date) { - long bkp_time, bkp_start; + apt->start = date + get_item_time(apt->start); - bkp_time = get_item_time(bkp_cut_apoint.start); - bkp_start = calendar_get_slctd_day_sec() + bkp_time; - apoint_new(bkp_cut_apoint.mesg, bkp_cut_apoint.note, bkp_start, - bkp_cut_apoint.dur, bkp_cut_apoint.state); + LLIST_TS_LOCK(&alist_p); + LLIST_TS_ADD_SORTED(&alist_p, apt, apoint_cmp_start); + LLIST_TS_UNLOCK(&alist_p); if (notify_bar()) - notify_check_added(bkp_cut_apoint.mesg, bkp_start, bkp_cut_apoint.state); - - apoint_free_bkp(); + notify_check_added(apt->mesg, apt->start, apt->state); } @@ -41,7 +41,6 @@ #include <limits.h> #include <getopt.h> #include <time.h> -#include <regex.h> #include "calcurse.h" @@ -63,7 +62,7 @@ static void usage(void) const char *arg_usage = _("Usage: calcurse [-g|-h|-v] [-an] [-t[num]] [-i<file>] [-x[format]]\n" " [-d <date>|<num>] [-s[date]] [-r[range]]\n" - " [-c<file> | -D<dir>] [-S<regex>] [--status]\n" + " [-c<file>] [-D<dir>] [-S<regex>] [--status]\n" " [--read-only]\n"); fputs(arg_usage, stdout); } @@ -113,9 +112,9 @@ static void help_arg(void) " Don't save configuration nor appointments/todos. Use with care.\n" "\nFiles:\n" " -c <file>, --calendar <file>\n" - " specify the calendar <file> to use (incompatible with '-D').\n" + " specify the calendar <file> to use (has precedence over '-D').\n" "\n -D <dir>, --directory <dir>\n" - " specify the data directory to use (incompatible with '-c').\n" + " specify the data directory to use.\n" "\tIf not specified, the default directory is ~/.calcurse\n" "\nNon-interactive:\n" " -a, --appointment\n" @@ -289,123 +288,21 @@ static void arg_print_date(long date) static int app_arg(int add_line, struct date *day, long date, const char *fmt_apt, const char *fmt_rapt, const char *fmt_ev, const char *fmt_rev, - regex_t * regex) + regex_t *regex) { - llist_item_t *i, *j; - long today; - unsigned print_date = 1; - int app_found = 0; - if (date == 0) - today = get_sec_date(*day); - else - today = date; + date = get_sec_date(*day); - /* - * Calculate and print the selected date if there is an event for - * that date and it is the first one, and then print all the events for - * that date. - */ - LLIST_FIND_FOREACH(&recur_elist, today, recur_event_inday, i) { - struct recur_event *re = LLIST_GET_DATA(i); - if (regex && regexec(regex, re->mesg, 0, 0, 0) != 0) - continue; + int n = day_store_items(date, NULL, NULL, regex); - app_found = 1; - if (add_line) { + if (n > 0) { + if (add_line) fputs("\n", stdout); - add_line = 0; - } - if (print_date) { - arg_print_date(today); - print_date = 0; - } - print_recur_event(fmt_rev, today, re); - } - - LLIST_FIND_FOREACH_CONT(&eventlist, today, event_inday, i) { - struct event *ev = LLIST_TS_GET_DATA(i); - if (regex && regexec(regex, ev->mesg, 0, 0, 0) != 0) - continue; - - app_found = 1; - if (add_line) { - fputs("\n", stdout); - add_line = 0; - } - if (print_date) { - arg_print_date(today); - print_date = 0; - } - print_event(fmt_ev, today, ev); - } - - /* Same process is performed but this time on the appointments. */ - LLIST_TS_LOCK(&alist_p); - LLIST_TS_LOCK(&recur_alist_p); - - /* - * Iterate over regular appointments and recurrent ones simultaneously (fixes - * http://lists.calcurse.org/bugs/msg00002.html). - */ - i = LLIST_TS_FIND_FIRST(&alist_p, today, apoint_inday); - j = LLIST_TS_FIND_FIRST(&recur_alist_p, today, recur_apoint_inday); - while (i || j) { - struct apoint *apt = LLIST_TS_GET_DATA(i); - struct recur_apoint *ra = LLIST_TS_GET_DATA(j); - unsigned occurrence; - - while (i && regex && regexec(regex, apt->mesg, 0, 0, 0) != 0) { - i = LLIST_TS_FIND_NEXT(i, today, apoint_inday); - apt = LLIST_TS_GET_DATA(i); - } - - while (j && regex && regexec(regex, ra->mesg, 0, 0, 0) != 0) { - j = LLIST_TS_FIND_NEXT(j, today, recur_apoint_inday); - ra = LLIST_TS_GET_DATA(j); - } - - if (apt && ra) { - if (recur_apoint_find_occurrence(ra, today, &occurrence) && - apt->start <= occurrence) - ra = NULL; - else - apt = NULL; - } - - if (apt) { - app_found = 1; - if (add_line) { - fputs("\n", stdout); - add_line = 0; - } - if (print_date) { - arg_print_date(today); - print_date = 0; - } - print_apoint(fmt_apt, today, apt); - i = LLIST_TS_FIND_NEXT(i, today, apoint_inday); - } else if (ra) { - app_found = 1; - if (add_line) { - fputs("\n", stdout); - add_line = 0; - } - if (print_date) { - arg_print_date(today); - print_date = 0; - } - recur_apoint_find_occurrence(ra, today, &occurrence); - print_recur_apoint(fmt_rapt, today, occurrence, ra); - apt = NULL; - j = LLIST_TS_FIND_NEXT(j, today, recur_apoint_inday); - } + arg_print_date(date); + day_write_stdout(date, fmt_apt, fmt_rapt, fmt_ev, fmt_rev); } - LLIST_TS_UNLOCK(&recur_alist_p); - LLIST_TS_UNLOCK(&alist_p); - - return app_found; + return n; } /* @@ -548,9 +445,7 @@ int parse_args(int argc, char **argv) int unknown_flag = 0; /* Command-line flags */ int aflag = 0; /* -a: print appointments for current day */ - int cflag = 0; /* -c: specify the calendar file to use */ int dflag = 0; /* -d: print appointments for a specified days */ - int Dflag = 0; /* -D: specify data directory to use */ int hflag = 0; /* -h: print help text */ int gflag = 0; /* -g: run garbage collector */ int iflag = 0; /* -i: import data */ @@ -619,7 +514,6 @@ int parse_args(int argc, char **argv) load_data++; break; case 'c': - cflag = 1; multiple_flag++; cfile = optarg; load_data++; @@ -631,7 +525,6 @@ int parse_args(int argc, char **argv) ddate = optarg; break; case 'D': - Dflag = 1; datadir = optarg; break; case 'h': @@ -741,11 +634,6 @@ int parse_args(int argc, char **argv) usage_try(); return EXIT_FAILURE; /* Incorrect arguments */ - } else if (Dflag && cflag) { - fputs(_("Options '-D' and '-c' cannot be used at the same time\n"), stderr); - usage(); - usage_try(); - return EXIT_FAILURE; } else if (Sflag && !(aflag || dflag || rflag || sflag || tflag)) { fputs(_("Option '-S' must be used with either '-d', '-r', '-s', " "'-a' or '-t'\n"), stderr); diff --git a/src/calcurse.c b/src/calcurse.c index 09baab5..000436c 100644 --- a/src/calcurse.c +++ b/src/calcurse.c @@ -38,6 +38,11 @@ #include "calcurse.h" +#define HANDLE_KEY(key, fn) case key: fn(); break; + +struct day_items_nb inday; +int count, reg; + /* * Store the events and appointments for the selected day and reset the * appointment highlight pointer if a new day was selected. @@ -53,6 +58,418 @@ static struct day_items_nb do_storage(int day_changed) return inday; } +static inline void key_generic_change_view(void) +{ + wins_reset_status_page(); + wins_slctd_next(); + + /* Select the event to highlight. */ + switch (wins_slctd()) { + case TOD: + if ((todo_hilt() == 0) && (todo_nb() > 0)) + todo_hilt_set(1); + break; + case APP: + if ((apoint_hilt() == 0) && ((inday.nb_events + inday.nb_apoints) > 0)) + apoint_hilt_set(1); + break; + default: + break; + } + wins_update(FLAG_ALL); +} + +static inline void key_generic_other_cmd(void) +{ + wins_other_status_page(wins_slctd()); + wins_update(FLAG_STA); +} + +static inline void key_generic_goto(void) +{ + wins_erase_status_bar(); + calendar_set_current_date(); + calendar_change_day(conf.input_datefmt); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); +} + +static inline void key_generic_goto_today(void) +{ + wins_erase_status_bar(); + calendar_set_current_date(); + calendar_goto_today(); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); +} + +static inline void key_view_item(void) +{ + if ((wins_slctd() == APP) && (apoint_hilt() != 0)) + day_popup_item(day_get_item(apoint_hilt())); + else if ((wins_slctd() == TOD) && (todo_hilt() != 0)) + item_in_popup(NULL, NULL, todo_saved_mesg(), _("To do :")); + wins_update(FLAG_ALL); +} + +static inline void key_generic_config_menu(void) +{ + wins_erase_status_bar(); + custom_config_main(); + inday = do_storage(0); + wins_update(FLAG_ALL); +} + +static inline void key_generic_add_appt(void) +{ + interact_day_item_add(); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); +} + +static inline void key_generic_add_todo(void) +{ + interact_todo_add(); + if (todo_hilt() == 0 && todo_nb() == 1) + todo_hilt_increase(1); + wins_update(FLAG_TOD | FLAG_STA); +} + +static inline void key_add_item(void) +{ + switch (wins_slctd()) { + case APP: + interact_day_item_add(); + inday = do_storage(0); + wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); + case TOD: + interact_todo_add(); + if (todo_hilt() == 0 && todo_nb() == 1) + todo_hilt_increase(1); + wins_update(FLAG_TOD | FLAG_STA); + break; + default: + break; + } +} + +static inline void key_edit_item(void) +{ + if (wins_slctd() == APP && apoint_hilt() != 0) { + interact_day_item_edit(); + inday = do_storage(0); + wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); + } else if (wins_slctd() == TOD && todo_hilt() != 0) { + interact_todo_edit(); + wins_update(FLAG_TOD | FLAG_STA); + } +} + +static inline void key_del_item(void) +{ + if (wins_slctd() == APP && apoint_hilt() != 0) { + interact_day_item_delete(&inday.nb_events, &inday.nb_apoints, reg); + inday = do_storage(0); + wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); + } else if (wins_slctd() == TOD && todo_hilt() != 0) { + interact_todo_delete(); + wins_update(FLAG_TOD | FLAG_STA); + } +} + +static inline void key_generic_copy(void) +{ + if (wins_slctd() == APP && apoint_hilt() != 0) { + interact_day_item_copy(&inday.nb_events, &inday.nb_apoints, reg); + inday = do_storage(0); + wins_update(FLAG_CAL | FLAG_APP); + } +} + +static inline void key_generic_paste(void) +{ + if (wins_slctd() == APP) { + interact_day_item_paste(&inday.nb_events, &inday.nb_apoints, reg); + inday = do_storage(0); + wins_update(FLAG_CAL | FLAG_APP); + } +} + +static inline void key_repeat_item(void) +{ + if (wins_slctd() == APP && apoint_hilt() != 0) + interact_day_item_repeat(); + inday = do_storage(0); + wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); +} + +static inline void key_flag_item(void) +{ + if (wins_slctd() == APP && apoint_hilt() != 0) { + day_item_switch_notify(day_get_item(apoint_hilt())); + inday = do_storage(0); + wins_update(FLAG_APP); + } else if (wins_slctd() == TOD && todo_hilt() != 0) { + todo_flag(todo_get_item(todo_hilt())); + wins_update(FLAG_TOD); + } +} + +static inline void key_pipe_item(void) +{ + if (wins_slctd() == APP && apoint_hilt() != 0) + interact_day_item_pipe(); + else if (wins_slctd() == TOD && todo_hilt() != 0) + interact_todo_pipe(); + wins_update(FLAG_ALL); +} + +static inline void change_priority(int diff) +{ + if (wins_slctd() == TOD && todo_hilt() != 0) { + todo_chg_priority(todo_get_item(todo_hilt()), diff); + if (todo_hilt_pos() < 0) + todo_set_first(todo_hilt()); + else if (todo_hilt_pos() >= win[TOD].h - 4) + todo_set_first(todo_hilt() - win[TOD].h + 5); + wins_update(FLAG_TOD); + } +} + +static inline void key_raise_priority(void) +{ + change_priority(1); +} + +static inline void key_lower_priority(void) +{ + change_priority(-1); +} + +static inline void key_edit_note(void) +{ + if (wins_slctd() == APP && apoint_hilt() != 0) { + day_edit_note(day_get_item(apoint_hilt()), conf.editor); + inday = do_storage(0); + } else if (wins_slctd() == TOD && todo_hilt() != 0) + todo_edit_note(todo_get_item(todo_hilt()), conf.editor); + wins_update(FLAG_ALL); +} + +static inline void key_view_note(void) +{ + if (wins_slctd() == APP && apoint_hilt() != 0) + day_view_note(day_get_item(apoint_hilt()), conf.pager); + else if (wins_slctd() == TOD && todo_hilt() != 0) + todo_view_note(todo_get_item(todo_hilt()), conf.pager); + wins_update(FLAG_ALL); +} + +static inline void key_generic_help(void) +{ + wins_status_bar(); + help_screen(); + wins_update(FLAG_ALL); +} + +static inline void key_generic_save(void) +{ + io_save_cal(IO_SAVE_DISPLAY_BAR); + wins_update(FLAG_STA); +} + +static inline void key_generic_import(void) +{ + wins_erase_status_bar(); + io_import_data(IO_IMPORT_ICAL, NULL); + calendar_monthly_view_cache_set_invalid(); + inday = do_storage(0); + wins_update(FLAG_ALL); +} + +static inline void key_generic_export() +{ + const char *export_msg = _("Export to (i)cal or (p)cal format?"); + const char *export_choices = _("[ip]"); + const int nb_export_choices = 2; + + wins_erase_status_bar(); + + switch (status_ask_choice(export_msg, export_choices, nb_export_choices)) { + case 1: + io_export_data(IO_EXPORT_ICAL); + break; + case 2: + io_export_data(IO_EXPORT_PCAL); + break; + default: /* User escaped */ + break; + } + + inday = do_storage(0); + wins_update(FLAG_ALL); +} + +static inline void key_generic_prev_day(void) +{ + calendar_move(DAY_PREV, count); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP); +} + +static inline void key_move_left(void) +{ + if (wins_slctd() == CAL) + key_generic_prev_day(); +} + +static inline void key_generic_next_day(void) +{ + calendar_move(DAY_NEXT, count); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP); +} + +static inline void key_move_right(void) +{ + if (wins_slctd() == CAL) + key_generic_next_day(); +} + +static inline void key_generic_prev_week(void) +{ + calendar_move(WEEK_PREV, count); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP); +} + +static inline void key_move_up(void) +{ + if (wins_slctd() == CAL) { + key_generic_prev_week(); + } else if (wins_slctd() == APP) { + if (count >= apoint_hilt()) + count = apoint_hilt() - 1; + apoint_hilt_decrease(count); + apoint_scroll_pad_up(inday.nb_events); + wins_update(FLAG_APP); + } else if (wins_slctd() == TOD) { + if (count >= todo_hilt()) + count = todo_hilt() - 1; + todo_hilt_decrease(count); + if (todo_hilt_pos() < 0) + todo_first_increase(todo_hilt_pos()); + wins_update(FLAG_TOD); + } +} + +static inline void key_generic_next_week(void) +{ + calendar_move(WEEK_NEXT, count); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP); +} + +static inline void key_move_down(void) +{ + if (wins_slctd() == CAL) { + key_generic_next_week(); + } else if (wins_slctd() == APP) { + if (count > inday.nb_events + inday.nb_apoints - apoint_hilt()) + count = inday.nb_events + inday.nb_apoints - apoint_hilt(); + apoint_hilt_increase(count); + apoint_scroll_pad_down(inday.nb_events, win[APP].h); + wins_update(FLAG_APP); + } else if (wins_slctd() == TOD) { + if (count > todo_nb() - todo_hilt()) + count = todo_nb() - todo_hilt(); + todo_hilt_increase(count); + if (todo_hilt_pos() >= win[TOD].h - 4) + todo_first_increase(todo_hilt_pos() - win[TOD].h + 5); + wins_update(FLAG_TOD); + } +} + +static inline void key_generic_prev_month(void) +{ + calendar_move(MONTH_PREV, count); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP); +} + +static inline void key_generic_next_month(void) +{ + calendar_move(MONTH_NEXT, count); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP); +} + +static inline void key_generic_prev_year(void) +{ + calendar_move(YEAR_PREV, count); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP); +} + +static inline void key_generic_next_year(void) +{ + calendar_move(YEAR_NEXT, count); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP); +} + +static inline void key_start_of_week(void) +{ + if (wins_slctd() == CAL) { + calendar_move(WEEK_START, count); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP); + } +} + +static inline void key_end_of_week(void) +{ + if (wins_slctd() == CAL) { + calendar_move(WEEK_END, count); + inday = do_storage(1); + wins_update(FLAG_CAL | FLAG_APP); + } +} + +static inline void key_generic_scroll_up(void) +{ + if (wins_slctd() == CAL) { + calendar_view_prev(); + wins_update(FLAG_CAL | FLAG_APP); + } +} + +static inline void key_generic_scroll_down(void) +{ + if (wins_slctd() == CAL) { + calendar_view_next(); + wins_update(FLAG_CAL | FLAG_APP); + } +} + +static inline void key_generic_quit(void) +{ + if (conf.auto_save) + io_save_cal(IO_SAVE_DISPLAY_BAR); + if (conf.auto_gc) + note_gc(); + + if (conf.confirm_quit) { + if (status_ask_bool(_("Do you really want to quit ?")) == 1) + exit_calcurse(EXIT_SUCCESS); + else { + wins_erase_status_bar(); + wins_update(FLAG_STA); + } + } else + exit_calcurse(EXIT_SUCCESS); +} + /* * Calcurse is a text-based personal organizer which helps keeping track * of events and everyday tasks. It contains a calendar, a 'todo' list, @@ -62,10 +479,7 @@ static struct day_items_nb do_storage(int day_changed) */ int main(int argc, char **argv) { - struct day_items_nb inday; int no_data_file = 1; - int cut_item = 0; - int count; #if ENABLE_NLS setlocale(LC_ALL, ""); @@ -174,363 +588,53 @@ int main(int argc, char **argv) wins_reset(); } - key = keys_getch(win[STA].p, &count); + key = keys_getch(win[STA].p, &count, ®); switch (key) { case KEY_GENERIC_REDRAW: resize = 1; break; - case KEY_GENERIC_CHANGE_VIEW: - wins_reset_status_page(); - wins_slctd_next(); - - /* Select the event to highlight. */ - switch (wins_slctd()) { - case TOD: - if ((todo_hilt() == 0) && (todo_nb() > 0)) - todo_hilt_set(1); - break; - case APP: - if ((apoint_hilt() == 0) && ((inday.nb_events + inday.nb_apoints) > 0)) - apoint_hilt_set(1); - break; - default: - break; - } - wins_update(FLAG_ALL); - break; - - case KEY_GENERIC_OTHER_CMD: - wins_other_status_page(wins_slctd()); - wins_update(FLAG_STA); - break; - - case KEY_GENERIC_GOTO: - case KEY_GENERIC_GOTO_TODAY: - wins_erase_status_bar(); - calendar_set_current_date(); - if (key == KEY_GENERIC_GOTO_TODAY) - calendar_goto_today(); - else - calendar_change_day(conf.input_datefmt); - inday = do_storage(1); - wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); - break; - - case KEY_VIEW_ITEM: - if ((wins_slctd() == APP) && (apoint_hilt() != 0)) - day_popup_item(); - else if ((wins_slctd() == TOD) && (todo_hilt() != 0)) - item_in_popup(NULL, NULL, todo_saved_mesg(), _("To do :")); - wins_update(FLAG_ALL); - break; - - case KEY_GENERIC_CONFIG_MENU: - wins_erase_status_bar(); - custom_config_main(); - inday = do_storage(0); - wins_update(FLAG_ALL); - break; - - case KEY_GENERIC_ADD_APPT: - apoint_add(); - inday = do_storage(1); - wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); - break; - - case KEY_GENERIC_ADD_TODO: - todo_new_item(); - if (todo_hilt() == 0 && todo_nb() == 1) - todo_hilt_increase(1); - wins_update(FLAG_TOD | FLAG_STA); - break; - - case KEY_ADD_ITEM: - switch (wins_slctd()) { - case APP: - apoint_add(); - inday = do_storage(0); - wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); - break; - case TOD: - todo_new_item(); - if (todo_hilt() == 0 && todo_nb() == 1) - todo_hilt_increase(1); - wins_update(FLAG_TOD | FLAG_STA); - break; - default: - break; - } - break; - - case KEY_EDIT_ITEM: - if (wins_slctd() == APP && apoint_hilt() != 0) { - day_edit_item(); - inday = do_storage(0); - wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); - } else if (wins_slctd() == TOD && todo_hilt() != 0) { - todo_edit_item(); - wins_update(FLAG_TOD | FLAG_STA); - } - break; - - case KEY_DEL_ITEM: - if (wins_slctd() == APP && apoint_hilt() != 0) { - apoint_delete(&inday.nb_events, &inday.nb_apoints); - inday = do_storage(0); - wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); - } else if (wins_slctd() == TOD && todo_hilt() != 0) { - todo_delete(); - wins_update(FLAG_TOD | FLAG_STA); - } - break; - - case KEY_GENERIC_CUT: - if (wins_slctd() == APP && apoint_hilt() != 0) { - cut_item = apoint_cut(&inday.nb_events, &inday.nb_apoints); - inday = do_storage(0); - wins_update(FLAG_CAL | FLAG_APP); - } - break; - - case KEY_GENERIC_PASTE: - if (wins_slctd() == APP) { - apoint_paste(&inday.nb_events, &inday.nb_apoints, cut_item); - cut_item = 0; - inday = do_storage(0); - wins_update(FLAG_CAL | FLAG_APP); - } - break; - - case KEY_REPEAT_ITEM: - if (wins_slctd() == APP && apoint_hilt() != 0) - recur_repeat_item(); - inday = do_storage(0); - wins_update(FLAG_CAL | FLAG_APP | FLAG_STA); - break; - - case KEY_FLAG_ITEM: - if (wins_slctd() == APP && apoint_hilt() != 0) { - apoint_switch_notify(); - inday = do_storage(0); - wins_update(FLAG_APP); - } else if (wins_slctd() == TOD && todo_hilt() != 0) { - todo_flag(); - wins_update(FLAG_TOD); - } - break; - - case KEY_PIPE_ITEM: - if (wins_slctd() == APP && apoint_hilt() != 0) - day_pipe_item(); - else if (wins_slctd() == TOD && todo_hilt() != 0) - todo_pipe_item(); - wins_update(FLAG_ALL); - break; - - case KEY_RAISE_PRIORITY: - case KEY_LOWER_PRIORITY: - if (wins_slctd() == TOD && todo_hilt() != 0) { - todo_chg_priority(key); - if (todo_hilt_pos() < 0) - todo_set_first(todo_hilt()); - else if (todo_hilt_pos() >= win[TOD].h - 4) - todo_set_first(todo_hilt() - win[TOD].h + 5); - wins_update(FLAG_TOD); - } - break; - - case KEY_EDIT_NOTE: - if (wins_slctd() == APP && apoint_hilt() != 0) { - day_edit_note(conf.editor); - inday = do_storage(0); - } else if (wins_slctd() == TOD && todo_hilt() != 0) - todo_edit_note(conf.editor); - wins_update(FLAG_ALL); - break; - - case KEY_VIEW_NOTE: - if (wins_slctd() == APP && apoint_hilt() != 0) - day_view_note(conf.pager); - else if (wins_slctd() == TOD && todo_hilt() != 0) - todo_view_note(conf.pager); - wins_update(FLAG_ALL); - break; - - case KEY_GENERIC_HELP: - wins_status_bar(); - help_screen(); - wins_update(FLAG_ALL); - break; - - case KEY_GENERIC_SAVE: - io_save_cal(IO_SAVE_DISPLAY_BAR); - wins_update(FLAG_STA); - break; - - case KEY_GENERIC_IMPORT: - wins_erase_status_bar(); - io_import_data(IO_IMPORT_ICAL, NULL); - inday = do_storage(0); - wins_update(FLAG_ALL); - break; - - case KEY_GENERIC_EXPORT: - wins_erase_status_bar(); - io_export_bar(); - while ((key = wgetch(win[STA].p)) != 'q') { - switch (key) { - case 'I': - case 'i': - io_export_data(IO_EXPORT_ICAL); - break; - case 'P': - case 'p': - io_export_data(IO_EXPORT_PCAL); - break; - } - wins_reset(); - wins_update(FLAG_ALL); - wins_erase_status_bar(); - io_export_bar(); - } - inday = do_storage(0); - wins_update(FLAG_ALL); - break; - - case KEY_GENERIC_PREV_DAY: - case KEY_MOVE_LEFT: - if (wins_slctd() == CAL || key == KEY_GENERIC_PREV_DAY) { - calendar_move(DAY_PREV, count); - inday = do_storage(1); - wins_update(FLAG_CAL | FLAG_APP); - } - break; - - case KEY_GENERIC_NEXT_DAY: - case KEY_MOVE_RIGHT: - if (wins_slctd() == CAL || key == KEY_GENERIC_NEXT_DAY) { - calendar_move(DAY_NEXT, count); - inday = do_storage(1); - wins_update(FLAG_CAL | FLAG_APP); - } - break; - - case KEY_GENERIC_PREV_WEEK: - case KEY_MOVE_UP: - if (wins_slctd() == CAL || key == KEY_GENERIC_PREV_WEEK) { - calendar_move(WEEK_PREV, count); - inday = do_storage(1); - wins_update(FLAG_CAL | FLAG_APP); - } else if (wins_slctd() == APP) { - if (count >= apoint_hilt()) - count = apoint_hilt() - 1; - apoint_hilt_decrease(count); - apoint_scroll_pad_up(inday.nb_events); - wins_update(FLAG_APP); - } else if (wins_slctd() == TOD) { - if (count >= todo_hilt()) - count = todo_hilt() - 1; - todo_hilt_decrease(count); - if (todo_hilt_pos() < 0) - todo_first_increase(todo_hilt_pos()); - wins_update(FLAG_TOD); - } - break; - - case KEY_GENERIC_NEXT_WEEK: - case KEY_MOVE_DOWN: - if (wins_slctd() == CAL || key == KEY_GENERIC_NEXT_WEEK) { - calendar_move(WEEK_NEXT, count); - inday = do_storage(1); - wins_update(FLAG_CAL | FLAG_APP); - } else if (wins_slctd() == APP) { - if (count > inday.nb_events + inday.nb_apoints - apoint_hilt()) - count = inday.nb_events + inday.nb_apoints - apoint_hilt(); - apoint_hilt_increase(count); - apoint_scroll_pad_down(inday.nb_events, win[APP].h); - wins_update(FLAG_APP); - } else if (wins_slctd() == TOD) { - if (count > todo_nb() - todo_hilt()) - count = todo_nb() - todo_hilt(); - todo_hilt_increase(count); - if (todo_hilt_pos() >= win[TOD].h - 4) - todo_first_increase(todo_hilt_pos() - win[TOD].h + 5); - wins_update(FLAG_TOD); - } - break; - - case KEY_GENERIC_PREV_MONTH: - calendar_move(MONTH_PREV, count); - inday = do_storage(1); - wins_update(FLAG_CAL | FLAG_APP); - break; - - case KEY_GENERIC_NEXT_MONTH: - calendar_move(MONTH_NEXT, count); - inday = do_storage(1); - wins_update(FLAG_CAL | FLAG_APP); - break; - - case KEY_GENERIC_PREV_YEAR: - calendar_move(YEAR_PREV, count); - inday = do_storage(1); - wins_update(FLAG_CAL | FLAG_APP); - break; - - case KEY_GENERIC_NEXT_YEAR: - calendar_move(YEAR_NEXT, count); - inday = do_storage(1); - wins_update(FLAG_CAL | FLAG_APP); - break; - - case KEY_START_OF_WEEK: - if (wins_slctd() == CAL) { - calendar_move(WEEK_START, count); - inday = do_storage(1); - wins_update(FLAG_CAL | FLAG_APP); - } - break; - - case KEY_END_OF_WEEK: - if (wins_slctd() == CAL) { - calendar_move(WEEK_END, count); - inday = do_storage(1); - wins_update(FLAG_CAL | FLAG_APP); - } - break; - - case KEY_GENERIC_SCROLL_UP: - if (wins_slctd() == CAL) { - calendar_view_prev(); - wins_update(FLAG_CAL | FLAG_APP); - } - break; - - case KEY_GENERIC_SCROLL_DOWN: - if (wins_slctd() == CAL) { - calendar_view_next(); - wins_update(FLAG_CAL | FLAG_APP); - } - break; - - case KEY_GENERIC_QUIT: - if (conf.auto_save) - io_save_cal(IO_SAVE_DISPLAY_BAR); - if (conf.auto_gc) - note_gc(); - - if (conf.confirm_quit) { - if (status_ask_bool(_("Do you really want to quit ?")) == 1) - exit_calcurse(EXIT_SUCCESS); - else { - wins_erase_status_bar(); - wins_update(FLAG_STA); - break; - } - } else - exit_calcurse(EXIT_SUCCESS); - break; + HANDLE_KEY(KEY_GENERIC_CHANGE_VIEW, key_generic_change_view); + HANDLE_KEY(KEY_GENERIC_OTHER_CMD, key_generic_other_cmd); + HANDLE_KEY(KEY_GENERIC_GOTO, key_generic_goto); + HANDLE_KEY(KEY_GENERIC_GOTO_TODAY, key_generic_goto_today); + HANDLE_KEY(KEY_VIEW_ITEM, key_view_item); + HANDLE_KEY(KEY_GENERIC_CONFIG_MENU, key_generic_config_menu); + HANDLE_KEY(KEY_GENERIC_ADD_APPT, key_generic_add_appt); + HANDLE_KEY(KEY_GENERIC_ADD_TODO, key_generic_add_todo); + HANDLE_KEY(KEY_ADD_ITEM, key_add_item); + HANDLE_KEY(KEY_EDIT_ITEM, key_edit_item); + HANDLE_KEY(KEY_DEL_ITEM, key_del_item); + HANDLE_KEY(KEY_GENERIC_COPY, key_generic_copy); + HANDLE_KEY(KEY_GENERIC_PASTE, key_generic_paste); + HANDLE_KEY(KEY_REPEAT_ITEM, key_repeat_item); + HANDLE_KEY(KEY_FLAG_ITEM, key_flag_item); + HANDLE_KEY(KEY_PIPE_ITEM, key_pipe_item); + HANDLE_KEY(KEY_RAISE_PRIORITY, key_raise_priority); + HANDLE_KEY(KEY_LOWER_PRIORITY, key_lower_priority); + HANDLE_KEY(KEY_EDIT_NOTE, key_edit_note); + HANDLE_KEY(KEY_VIEW_NOTE, key_view_note); + HANDLE_KEY(KEY_GENERIC_HELP, key_generic_help); + HANDLE_KEY(KEY_GENERIC_SAVE, key_generic_save); + HANDLE_KEY(KEY_GENERIC_IMPORT, key_generic_import); + HANDLE_KEY(KEY_GENERIC_EXPORT, key_generic_export); + HANDLE_KEY(KEY_GENERIC_PREV_DAY, key_generic_prev_day); + HANDLE_KEY(KEY_MOVE_LEFT, key_move_left); + HANDLE_KEY(KEY_GENERIC_NEXT_DAY, key_generic_next_day); + HANDLE_KEY(KEY_MOVE_RIGHT, key_move_right); + HANDLE_KEY(KEY_GENERIC_PREV_WEEK, key_generic_prev_week); + HANDLE_KEY(KEY_MOVE_UP, key_move_up); + HANDLE_KEY(KEY_GENERIC_NEXT_WEEK, key_generic_next_week); + HANDLE_KEY(KEY_MOVE_DOWN, key_move_down); + HANDLE_KEY(KEY_GENERIC_PREV_MONTH, key_generic_prev_month); + HANDLE_KEY(KEY_GENERIC_NEXT_MONTH, key_generic_next_month); + HANDLE_KEY(KEY_GENERIC_PREV_YEAR, key_generic_prev_year); + HANDLE_KEY(KEY_GENERIC_NEXT_YEAR, key_generic_next_year); + HANDLE_KEY(KEY_START_OF_WEEK, key_start_of_week); + HANDLE_KEY(KEY_END_OF_WEEK, key_end_of_week); + HANDLE_KEY(KEY_GENERIC_SCROLL_UP, key_generic_scroll_up); + HANDLE_KEY(KEY_GENERIC_SCROLL_DOWN, key_generic_scroll_down); + HANDLE_KEY(KEY_GENERIC_QUIT, key_generic_quit); case KEY_RESIZE: case ERR: diff --git a/src/calcurse.h b/src/calcurse.h index d7b5093..6a6deaf 100644 --- a/src/calcurse.h +++ b/src/calcurse.h @@ -53,6 +53,7 @@ #include <time.h> #include <stdlib.h> #include <stdio.h> +#include <regex.h> #include "llist.h" #include "htable.h" @@ -143,6 +144,8 @@ #define DAYINSEC (DAYINMIN * MININSEC) #define HOURINSEC (HOURINMIN * MININSEC) +#define MAXDAYSPERMONTH 31 + /* Calendar window. */ #define CALHEIGHT 12 @@ -157,6 +160,9 @@ #define KEYS_LABELEN 8 /* length of command description */ #define KEYS_CMDS_PER_LINE 6 /* max number of commands per line */ +/* Register definitions. */ +#define REG_BLACK_HOLE 37 + /* Size of the hash table the note garbage collector uses. */ #define NOTE_GC_HSIZE 1024 @@ -308,18 +314,6 @@ struct day_items_nb { unsigned nb_apoints; }; -/* Generic item description (to hold appointments, events...). */ -struct day_item { - long start; /* seconds since 1 jan 1970 */ - long appt_dur; /* appointment duration in seconds */ - int type; /* (recursive or normal) event or appointment */ - int evnt_id; /* event identifier */ - int appt_pos; /* real position in recurrent list */ - char state; /* appointment state */ - char *mesg; /* item description */ - char *note; /* note attached to item */ -}; - struct excp { long st; /* beggining of the considered day, in seconds */ }; @@ -361,6 +355,21 @@ struct recur_event { char *note; /* note attached to event */ }; +/* Generic pointer data type for appointments and events. */ +union aptev_ptr { + struct apoint *apt; + struct event *ev; + struct recur_apoint *rapt; + struct recur_event *rev; +}; + +/* Generic item description (to hold appointments, events...). */ +struct day_item { + int type; /* (recursive or normal) event or appointment */ + long start; /* start time of the repetition occurrence */ + union aptev_ptr item; /* pointer to the actual item */ +}; + /* Available view for the calendar panel. */ enum { CAL_MONTH_VIEW, @@ -389,7 +398,7 @@ enum key { KEY_GENERIC_HELP, KEY_GENERIC_QUIT, KEY_GENERIC_SAVE, - KEY_GENERIC_CUT, + KEY_GENERIC_COPY, KEY_GENERIC_PASTE, KEY_GENERIC_CHANGE_VIEW, KEY_GENERIC_IMPORT, @@ -526,14 +535,6 @@ enum item_type { MAX_TYPES = APPT }; -/* Flags used to adapt processing when erasing an item. */ -enum eraseflg { - ERASE_DONT_FORCE, - ERASE_FORCE, - ERASE_FORCE_ONLY_NOTE, - ERASE_CUT -}; - /* Return codes for the getstring() function. */ enum getstr { GETSTRING_VALID, @@ -602,6 +603,8 @@ enum save_display { /* apoint.c */ extern llist_ts_t alist_p; void apoint_free_bkp(void); +struct apoint *apoint_dup(struct apoint *); +void apoint_free(struct apoint *); void apoint_llist_init(void); void apoint_llist_free(void); void apoint_hilt_set(int); @@ -609,22 +612,17 @@ void apoint_hilt_decrease(int); void apoint_hilt_increase(int); int apoint_hilt(void); struct apoint *apoint_new(char *, char *, long, long, char); -void apoint_add(void); -void apoint_delete(unsigned *, unsigned *); -int apoint_cut(unsigned *, unsigned *); -void apoint_paste(unsigned *, unsigned *, int); -unsigned apoint_inday(struct apoint *, long); +unsigned apoint_inday(struct apoint *, long *); void apoint_sec2str(struct apoint *, long, char *, char *); void apoint_write(struct apoint *, FILE *); struct apoint *apoint_scan(FILE *, struct tm, struct tm, char, char *); -struct apoint *apoint_get(long, int); -void apoint_delete_bynum(long, unsigned, enum eraseflg); +void apoint_delete(struct apoint *); void apoint_scroll_pad_down(int, int); void apoint_scroll_pad_up(int); struct notify_app *apoint_check_next(struct notify_app *, long); -void apoint_switch_notify(void); +void apoint_switch_notify(struct apoint *); void apoint_update_panel(int); -void apoint_paste_item(void); +void apoint_paste_item(struct apoint *, long); /* args.c */ int parse_args(int, char **); @@ -644,6 +642,7 @@ void calendar_store_current_date(struct date *); void calendar_init_slctd_day(void); struct date *calendar_get_slctd_day(void); long calendar_get_slctd_day_sec(void); +void calendar_monthly_view_cache_set_invalid(void); void calendar_update_panel(struct window *); void calendar_goto_today(void); void calendar_change_day(int); @@ -674,21 +673,28 @@ void custom_config_main(void); /* day.c */ void day_free_list(void); +char *day_item_get_mesg(struct day_item *); +char *day_item_get_note(struct day_item *); +void day_item_erase_note(struct day_item *); +long day_item_get_duration(struct day_item *); +int day_item_get_state(struct day_item *); +void day_item_add_exc(struct day_item *, long); +void day_item_fork(struct day_item *, struct day_item *); +int day_store_items(long, unsigned *, unsigned *, regex_t *); struct day_items_nb *day_process_storage(struct date *, unsigned, struct day_items_nb *); void day_write_pad(long, int, int, int); -void day_popup_item(void); +void day_write_stdout(long, const char *, const char *, const char *, + const char *); +void day_popup_item(struct day_item *); int day_check_if_item(struct date); unsigned day_chk_busy_slices(struct date, int, int *); -void day_edit_item(void); -int day_erase_item(long, int, enum eraseflg); -int day_cut_item(long, int); -int day_paste_item(long, int); +struct day_item *day_cut_item(long, int); +int day_paste_item(struct day_item *, long); struct day_item *day_get_item(int); -int day_item_nb(long, int, int); -void day_edit_note(const char *); -void day_view_note(const char *); -void day_pipe_item(void); +void day_edit_note(struct day_item *, const char *); +void day_view_note(struct day_item *, const char *); +void day_item_switch_notify(struct day_item *); /* dmon.c */ void dmon_start(int); @@ -697,15 +703,16 @@ void dmon_stop(void); /* event.c */ extern llist_t eventlist; void event_free_bkp(void); +struct event *event_dup(struct event *); +void event_free(struct event *); void event_llist_init(void); void event_llist_free(void); struct event *event_new(char *, char *, long, int); -unsigned event_inday(struct event *, long); +unsigned event_inday(struct event *, long *); void event_write(struct event *, FILE *); struct event *event_scan(FILE *, struct tm, int, char *); -struct event *event_get(long, int); -void event_delete_bynum(long, unsigned, enum eraseflg); -void event_paste_item(void); +void event_delete(struct event *); +void event_paste_item(struct event *, long); /* help.c */ void help_wins_init(struct scrollwin *, int, int, int, int); @@ -720,6 +727,20 @@ void ical_import_data(FILE *, FILE *, unsigned *, unsigned *, unsigned *, unsigned *, unsigned *); void ical_export_data(FILE *); +/* interaction.c */ +void interact_day_item_add(void); +void interact_day_item_delete(unsigned *, unsigned *, unsigned); +void interact_day_item_edit(void); +void interact_day_item_pipe(void); +void interact_day_item_repeat(void); +void interact_day_item_cut_free(unsigned); +void interact_day_item_copy(unsigned *, unsigned *, unsigned); +void interact_day_item_paste(unsigned *, unsigned *, unsigned); +void interact_todo_add(void); +void interact_todo_delete(void); +void interact_todo_edit(void); +void interact_todo_pipe(void); + /* io.c */ unsigned io_fprintln(const char *, const char *, ...); void io_init(const char *, const char *); @@ -737,7 +758,6 @@ void io_check_file(char *, int *); int io_check_data_files(void); void io_startup_screen(int); void io_export_data(enum export_type); -void io_export_bar(void); void io_import_data(enum import_type, const char *); struct io_file *io_log_init(void); void io_log_print(struct io_file *, int, const char *); @@ -757,7 +777,7 @@ void keys_free(void); void keys_dump_defaults(char *); const char *keys_get_label(enum key); enum key keys_get_action(int); -enum key keys_getch(WINDOW * win, int *); +enum key keys_getch(WINDOW * win, int *, int *); int keys_assign_binding(int, enum key); void keys_remove_binding(int, enum key); int keys_str2int(const char *); @@ -843,8 +863,12 @@ void pcal_export_data(FILE *); /* recur.c */ extern llist_ts_t recur_alist_p; extern llist_t recur_elist; +struct recur_event *recur_event_dup(struct recur_event *); +struct recur_apoint *recur_apoint_dup(struct recur_apoint *); void recur_event_free_bkp(void); void recur_apoint_free_bkp(void); +void recur_event_free(struct recur_event *); +void recur_apoint_free(struct recur_apoint *); void recur_apoint_llist_init(void); void recur_apoint_llist_free(void); void recur_event_llist_free(void); @@ -867,18 +891,17 @@ unsigned recur_item_find_occurrence(long, long, llist_t *, int, unsigned recur_apoint_find_occurrence(struct recur_apoint *, long, unsigned *); unsigned recur_event_find_occurrence(struct recur_event *, long, unsigned *); unsigned recur_item_inday(long, long, llist_t *, int, int, long, long); -unsigned recur_apoint_inday(struct recur_apoint *, long); -unsigned recur_event_inday(struct recur_event *, long); -void recur_event_erase(long, unsigned, unsigned, enum eraseflg); -void recur_apoint_erase(long, unsigned, unsigned, enum eraseflg); -void recur_repeat_item(void); +unsigned recur_apoint_inday(struct recur_apoint *, long *); +unsigned recur_event_inday(struct recur_event *, long *); +void recur_event_add_exc(struct recur_event *, long); +void recur_apoint_add_exc(struct recur_apoint *, long); +void recur_event_erase(struct recur_event *); +void recur_apoint_erase(struct recur_apoint *); void recur_exc_scan(llist_t *, FILE *); struct notify_app *recur_apoint_check_next(struct notify_app *, long, long); -struct recur_apoint *recur_get_apoint(long, int); -struct recur_event *recur_get_event(long, int); -void recur_apoint_switch_notify(long, int); -void recur_event_paste_item(void); -void recur_apoint_paste_item(void); +void recur_apoint_switch_notify(struct recur_apoint *); +void recur_event_paste_item(struct recur_event *, long); +void recur_apoint_paste_item(struct recur_apoint *, long); /* sigs.c */ void sigs_init(void); @@ -886,6 +909,7 @@ unsigned sigs_set_hdlr(int, void (*)(int)); /* todo.c */ extern llist_t todolist; +struct todo *todo_get_item(int); void todo_hilt_set(int); void todo_hilt_decrease(int); void todo_hilt_increase(int); @@ -897,17 +921,16 @@ void todo_first_increase(int); void todo_first_decrease(int); int todo_hilt_pos(void); char *todo_saved_mesg(void); -void todo_new_item(void); struct todo *todo_add(char *, int, char *); void todo_write(struct todo *, FILE *); -void todo_flag(void); -void todo_delete(void); -void todo_chg_priority(int); -void todo_edit_item(void); +void todo_delete_note(struct todo *); +void todo_delete(struct todo *); +void todo_flag(struct todo *); +void todo_chg_priority(struct todo *, int); void todo_update_panel(int); -void todo_edit_note(const char *); -void todo_view_note(const char *); -void todo_pipe_item(void); +void todo_edit_note(struct todo *, const char *); +void todo_view_note(struct todo *, const char *); +void todo_free(struct todo *); void todo_init_list(void); void todo_free_list(void); diff --git a/src/calendar.c b/src/calendar.c index df247df..3d7698e 100644 --- a/src/calendar.c +++ b/src/calendar.c @@ -77,6 +77,10 @@ static void (*draw_calendar[CAL_VIEWS]) (struct window *, struct date *, unsigned) = { draw_monthly_view, draw_weekly_view}; +static int monthly_view_cache[MAXDAYSPERMONTH]; +static int monthly_view_cache_valid = 0; +static int monthly_view_cache_month = 0; + /* Switch between calendar views (monthly view is selected by default). */ void calendar_view_next(void) { @@ -264,6 +268,11 @@ static int date_change(struct tm *date, int delta_month, int delta_day) } } +void calendar_monthly_view_cache_set_invalid(void) +{ + monthly_view_cache_valid = 0; +} + /* Draw the monthly view inside calendar panel. */ static void draw_monthly_view(struct window *cwin, struct date *current_day, @@ -315,13 +324,25 @@ draw_monthly_view(struct window *cwin, struct date *current_day, day_1_sav = (c_day_1 + 1) * 3 + c_day_1 - 7; + /* invalidate cache if a new month is selected */ + if (yr * YEARINMONTHS + mo != monthly_view_cache_month) { + monthly_view_cache_month = yr * YEARINMONTHS + mo; + monthly_view_cache_valid = 0; + } + for (c_day = 1; c_day <= numdays; ++c_day, ++c_day_1, c_day_1 %= 7) { check_day.dd = c_day; check_day.mm = slctd_day.mm; check_day.yyyy = slctd_day.yyyy; /* check if the day contains an event or an appointment */ - item_this_day = day_check_if_item(check_day); + if (monthly_view_cache_valid) { + item_this_day = monthly_view_cache[c_day - 1]; + } + else { + item_this_day = monthly_view_cache[c_day - 1] = + day_check_if_item(check_day); + } /* Go to next line, the week is over. */ if (!c_day_1 && 1 != c_day) { @@ -357,6 +378,8 @@ draw_monthly_view(struct window *cwin, struct date *current_day, } WINS_CALENDAR_UNLOCK; } + + monthly_view_cache_valid = 1; } static int weeknum(const struct tm *t, int firstweekday) diff --git a/src/custom.c b/src/custom.c index 72c531b..185f65a 100644 --- a/src/custom.c +++ b/src/custom.c @@ -226,7 +226,7 @@ void custom_layout_config(void) display_layout_config(&conf_win, mark, cursor); clear(); - while ((ch = keys_getch(win[STA].p, NULL)) != KEY_GENERIC_QUIT) { + while ((ch = keys_getch(win[STA].p, NULL, NULL)) != KEY_GENERIC_QUIT) { need_reset = 0; switch (ch) { case KEY_GENERIC_HELP: @@ -310,7 +310,7 @@ void custom_sidebar_config(void) bindings_size, NULL); wins_doupdate(); - while ((ch = keys_getch(win[STA].p, NULL)) != KEY_GENERIC_QUIT) { + while ((ch = keys_getch(win[STA].p, NULL, NULL)) != KEY_GENERIC_QUIT) { switch (ch) { case KEY_MOVE_UP: wins_sbar_winc(); @@ -528,7 +528,7 @@ void custom_color_config(void) theme_changed); clear(); - while ((ch = keys_getch(win[STA].p, NULL)) != KEY_GENERIC_QUIT) { + while ((ch = keys_getch(win[STA].p, NULL, NULL)) != KEY_GENERIC_QUIT) { need_reset = 0; theme_changed = 0; @@ -921,7 +921,7 @@ void custom_keys_config(void) for (;;) { int ch; - ch = keys_getch(win[STA].p, NULL); + ch = keys_getch(win[STA].p, NULL, NULL); switch (ch) { case KEY_MOVE_UP: if (selrow > 0) { @@ -42,16 +42,7 @@ #include "calcurse.h" -struct day_saved_item { - char start[BUFSIZ]; - char end[BUFSIZ]; - char state; - char type; - char *mesg; -}; - static llist_t day_items; -static struct day_saved_item day_saved_item; static void day_free(struct day_item *day) { @@ -74,26 +65,6 @@ void day_free_list(void) LLIST_FREE(&day_items); } -/* Add an event in the current day list */ -static struct day_item *day_add_event(int type, char *mesg, char *note, - long nday, int id) -{ - struct day_item *day; - - day = mem_malloc(sizeof(struct day_item)); - day->mesg = mesg; - day->note = note; - day->type = type; - day->appt_dur = 0; - day->appt_pos = 0; - day->start = nday; - day->evnt_id = id; - - LLIST_ADD(&day_items, day); - - return day; -} - static int day_cmp_start(struct day_item *a, struct day_item *b) { if (a->type <= EVNT) { @@ -107,26 +78,130 @@ static int day_cmp_start(struct day_item *a, struct day_item *b) return a->start < b->start ? -1 : (a->start == b->start ? 0 : 1); } -/* Add an appointment in the current day list. */ -static struct day_item *day_add_apoint(int type, char *mesg, char *note, - long start, long dur, char state, - int real_pos) +/* Add an item to the current day list. */ +static void day_add_item(int type, long start, union aptev_ptr item) { - struct day_item *day; - - day = mem_malloc(sizeof(struct day_item)); - day->mesg = mesg; - day->note = note; - day->start = start; - day->appt_dur = dur; - day->appt_pos = real_pos; - day->state = state; + struct day_item *day = mem_malloc(sizeof(struct day_item)); day->type = type; - day->evnt_id = 0; + day->start = start; + day->item = item; LLIST_ADD_SORTED(&day_items, day, day_cmp_start); +} - return day; +/* Get the message of an item. */ +char *day_item_get_mesg(struct day_item *day) +{ + switch (day->type) { + case APPT: + return day->item.apt->mesg; + case EVNT: + return day->item.ev->mesg; + case RECUR_APPT: + return day->item.rapt->mesg; + case RECUR_EVNT: + return day->item.rev->mesg; + default: + return NULL; + } +} + +/* Get the note attached to an item. */ +char *day_item_get_note(struct day_item *day) +{ + switch (day->type) { + case APPT: + return day->item.apt->note; + case EVNT: + return day->item.ev->note; + case RECUR_APPT: + return day->item.rapt->note; + case RECUR_EVNT: + return day->item.rev->note; + default: + return NULL; + } +} + +/* Get the note attached to an item. */ +void day_item_erase_note(struct day_item *day) +{ + switch (day->type) { + case APPT: + erase_note(&day->item.apt->note); + break; + case EVNT: + erase_note(&day->item.ev->note); + break; + case RECUR_APPT: + erase_note(&day->item.rapt->note); + break; + case RECUR_EVNT: + erase_note(&day->item.rev->note); + break; + } +} + +/* Get the duration of an item. */ +long day_item_get_duration(struct day_item *day) +{ + switch (day->type) { + case APPT: + return day->item.apt->dur; + case RECUR_APPT: + return day->item.rapt->dur; + default: + return 0; + } +} + +/* Get the notification state of an item. */ +int day_item_get_state(struct day_item *day) +{ + switch (day->type) { + case APPT: + return day->item.apt->state; + case RECUR_APPT: + return day->item.rapt->state; + default: + return APOINT_NULL; + } +} + +/* Add an exception to an item. */ +void day_item_add_exc(struct day_item *day, long date) +{ + switch (day->type) { + case RECUR_EVNT: + recur_event_add_exc(day->item.rev, date); + case RECUR_APPT: + recur_apoint_add_exc(day->item.rapt, date); + } +} + +/* Clone the actual item. */ +void day_item_fork(struct day_item *day_in, struct day_item *day_out) +{ + day_out->type = day_in->type; + day_out->start = day_in->start; + + switch (day_in->type) { + case APPT: + day_out->item.apt = apoint_dup(day_in->item.apt); + break; + case EVNT: + day_out->item.ev = event_dup(day_in->item.ev); + break; + case RECUR_APPT: + day_out->item.rapt = recur_apoint_dup(day_in->item.rapt); + break; + case RECUR_EVNT: + day_out->item.rev = recur_event_dup(day_in->item.rev); + break; + default: + EXIT(_("unknown item type")); + /* NOTREACHED */ + } } /* @@ -136,14 +211,20 @@ static struct day_item *day_add_apoint(int type, char *mesg, char *note, * dedicated to the selected day. * Returns the number of events for the selected day. */ -static int day_store_events(long date) +static int day_store_events(long date, regex_t *regex) { llist_item_t *i; + union aptev_ptr p; int e_nb = 0; - LLIST_FIND_FOREACH_CONT(&eventlist, date, event_inday, i) { + LLIST_FIND_FOREACH_CONT(&eventlist, &date, event_inday, i) { struct event *ev = LLIST_TS_GET_DATA(i); - day_add_event(EVNT, ev->mesg, ev->note, ev->day, ev->id); + + if (regex && regexec(regex, ev->mesg, 0, 0, 0) != 0) + continue; + + p.ev = ev; + day_add_item(EVNT, ev->day, p); e_nb++; } @@ -157,14 +238,20 @@ static int day_store_events(long date) * dedicated to the selected day. * Returns the number of recurrent events for the selected day. */ -static int day_store_recur_events(long date) +static int day_store_recur_events(long date, regex_t *regex) { llist_item_t *i; + union aptev_ptr p; int e_nb = 0; - LLIST_FIND_FOREACH(&recur_elist, date, recur_event_inday, i) { + LLIST_FIND_FOREACH(&recur_elist, &date, recur_event_inday, i) { struct recur_event *rev = LLIST_TS_GET_DATA(i); - day_add_event(RECUR_EVNT, rev->mesg, rev->note, rev->day, rev->id); + + if (regex && regexec(regex, rev->mesg, 0, 0, 0) != 0) + continue; + + p.rev = rev; + day_add_item(RECUR_EVNT, rev->day, p); e_nb++; } @@ -178,20 +265,25 @@ static int day_store_recur_events(long date) * structure dedicated to the selected day. * Returns the number of appointments for the selected day. */ -static int day_store_apoints(long date) +static int day_store_apoints(long date, regex_t *regex) { llist_item_t *i; + union aptev_ptr p; int a_nb = 0; LLIST_TS_LOCK(&alist_p); - LLIST_TS_FIND_FOREACH(&alist_p, date, apoint_inday, i) { + LLIST_TS_FIND_FOREACH(&alist_p, &date, apoint_inday, i) { struct apoint *apt = LLIST_TS_GET_DATA(i); + if (regex && regexec(regex, apt->mesg, 0, 0, 0) != 0) + continue; + + p.apt = apt; + if (apt->start >= date + DAYINSEC) break; - day_add_apoint(APPT, apt->mesg, apt->note, apt->start, apt->dur, - apt->state, 0); + day_add_item(APPT, apt->start, p); a_nb++; } LLIST_TS_UNLOCK(&alist_p); @@ -206,18 +298,24 @@ static int day_store_apoints(long date) * structure dedicated to the selected day. * Returns the number of recurrent appointments for the selected day. */ -static int day_store_recur_apoints(long date) +static int day_store_recur_apoints(long date, regex_t *regex) { llist_item_t *i; + union aptev_ptr p; int a_nb = 0; LLIST_TS_LOCK(&recur_alist_p); - LLIST_TS_FIND_FOREACH(&recur_alist_p, date, recur_apoint_inday, i) { + LLIST_TS_FIND_FOREACH(&recur_alist_p, &date, recur_apoint_inday, i) { struct recur_apoint *rapt = LLIST_TS_GET_DATA(i); + + if (regex && regexec(regex, rapt->mesg, 0, 0, 0) != 0) + continue; + + p.rapt = rapt; + unsigned real_start; if (recur_apoint_find_occurrence(rapt, date, &real_start)) { - day_add_apoint(RECUR_APPT, rapt->mesg, rapt->note, real_start, - rapt->dur, rapt->state, a_nb); + day_add_item(RECUR_APPT, real_start, p); a_nb++; } } @@ -234,27 +332,27 @@ static int day_store_recur_apoints(long date) * 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 +day_store_items(long date, unsigned *pnb_events, unsigned *pnb_apoints, + regex_t *regex) { - int pad_length; int nb_events, nb_recur_events; int nb_apoints, nb_recur_apoints; day_free_list(); day_init_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; + + nb_recur_events = day_store_recur_events(date, regex); + nb_events = day_store_events(date, regex); + nb_recur_apoints = day_store_recur_apoints(date, regex); + nb_apoints = day_store_apoints(date, regex); + + if (pnb_apoints) + *pnb_apoints = nb_apoints + nb_recur_apoints; + if (pnb_events) + *pnb_events = nb_events + nb_recur_events; + + return nb_events + nb_recur_events + nb_apoints + nb_recur_apoints; } /* @@ -281,7 +379,8 @@ struct day_items_nb *day_process_storage(struct date *slctd_date, delwin(apad.ptrwin); /* Store the events and appointments (recursive and normal items). */ - apad.length = day_store_items(date, &inday->nb_events, &inday->nb_apoints); + day_store_items(date, &inday->nb_events, &inday->nb_apoints, NULL); + apad.length = (inday->nb_events + 1 + 3 * inday->nb_apoints); /* Create the new pad with its new length. */ if (day_changed) @@ -292,40 +391,36 @@ struct day_items_nb *day_process_storage(struct date *slctd_date, } /* - * Returns a structure of type apoint_llist_node_t given a structure of type - * day_item_s - */ -static void day_item_s2apoint_s(struct apoint *a, struct day_item *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, struct apoint *i, int type, long date, - int y, int x) +display_item_date(struct day_item *day, int incolor, long date, int y, int x) { WINDOW *win; char a_st[100], a_end[100]; + /* FIXME: Redesign apoint_sec2str() and remove the need for a temporary + * appointment item here. */ + struct apoint apt_tmp; + apt_tmp.start = day->start; + apt_tmp.dur = day_item_get_duration(day); + win = apad.ptrwin; - apoint_sec2str(i, date, a_st, a_end); + apoint_sec2str(&apt_tmp, date, a_st, a_end); if (incolor == 0) custom_apply_attr(win, ATTR_HIGHEST); - if (type == RECUR_EVNT || type == RECUR_APPT) - if (i->state & APOINT_NOTIFY) + + if (day->type == RECUR_EVNT || day->type == RECUR_APPT) { + if (day_item_get_state(day) & 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) + } else if (day_item_get_state(day) & APOINT_NOTIFY) { mvwprintw(win, y, x, " -!%s -> %s", a_st, a_end); - else + } else { mvwprintw(win, y, x, " - %s -> %s", a_st, a_end); + } + if (incolor == 0) custom_remove_attr(win, ATTR_HIGHEST); } @@ -334,8 +429,7 @@ display_item_date(int incolor, struct apoint *i, int type, long date, * Print an item description in the corresponding panel window. */ static void -display_item(int incolor, char *msg, int recur, int note, int width, int y, - int x) +display_item(struct day_item *day, int incolor, int width, int y, int x) { WINDOW *win; int ch_recur, ch_note; @@ -345,18 +439,20 @@ display_item(int incolor, char *msg, int recur, int note, int width, int y, if (width <= 0) return; + char *mesg = day_item_get_mesg(day); + win = apad.ptrwin; - ch_recur = (recur) ? '*' : ' '; - ch_note = (note) ? '>' : ' '; + ch_recur = (day->type == RECUR_EVNT || day->type == RECUR_APPT) ? '*' : ' '; + ch_note = day_item_get_note(day) ? '>' : ' '; if (incolor == 0) custom_apply_attr(win, ATTR_HIGHEST); - if (utf8_strwidth(msg) < width) - mvwprintw(win, y, x, " %c%c%s", ch_recur, ch_note, msg); + if (utf8_strwidth(mesg) < width) + mvwprintw(win, y, x, " %c%c%s", ch_recur, ch_note, mesg); else { - for (i = 0; msg[i] && width > 0; i++) { - if (!UTF8_ISCONT(msg[i])) - width -= utf8_width(&msg[i]); - buf[i] = msg[i]; + for (i = 0; mesg[i] && width > 0; i++) { + if (!UTF8_ISCONT(mesg[i])) + width -= utf8_width(&mesg[i]); + buf[i] = mesg[i]; } if (i) buf[i - 1] = 0; @@ -371,15 +467,12 @@ display_item(int incolor, char *msg, int recur, int note, int width, int y, /* * 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. + * item selected by user is highlighted. */ void day_write_pad(long date, int width, int length, int incolor) { llist_item_t *i; - struct apoint a; - int line, item_number, recur; + int line, item_number; const int x_pos = 0; unsigned draw_line = 0; @@ -387,19 +480,11 @@ void day_write_pad(long date, int width, int length, int incolor) LLIST_FOREACH(&day_items, i) { struct day_item *day = LLIST_TS_GET_DATA(i); - if (day->type == RECUR_EVNT || day->type == RECUR_APPT) - recur = 1; - else - recur = 0; + /* First print the events for current day. */ if (day->type < RECUR_APPT) { item_number++; - if (item_number - incolor == 0) { - day_saved_item.type = day->type; - day_saved_item.mesg = day->mesg; - } - display_item(item_number - incolor, day->mesg, recur, - (day->note != NULL) ? 1 : 0, width - 7, line, x_pos); + display_item(day, item_number - incolor, width - 7, line, x_pos); line++; draw_line = 1; } else { @@ -411,32 +496,62 @@ void day_write_pad(long date, int width, int length, int incolor) } /* Last print the appointments for current day. */ item_number++; - day_item_s2apoint_s(&a, day); - if (item_number - incolor == 0) { - day_saved_item.type = day->type; - day_saved_item.mesg = day->mesg; - apoint_sec2str(&a, date, day_saved_item.start, day_saved_item.end); - } - display_item_date(item_number - incolor, &a, day->type, - date, line + 1, x_pos); - display_item(item_number - incolor, day->mesg, 0, - (day->note != NULL) ? 1 : 0, width - 7, line + 2, x_pos); + display_item_date(day, item_number - incolor, date, line + 1, x_pos); + display_item(day, item_number - incolor, width - 7, line + 2, x_pos); line += 3; } } } +/* Write the appointments and events for the selected day to stdout. */ +void day_write_stdout(long date, const char *fmt_apt, const char *fmt_rapt, + const char *fmt_ev, const char *fmt_rev) +{ + llist_item_t *i; + + LLIST_FOREACH(&day_items, i) { + struct day_item *day = LLIST_TS_GET_DATA(i); + + switch (day->type) { + case APPT: + print_apoint(fmt_apt, date, day->item.apt); + break; + case EVNT: + print_event(fmt_ev, date, day->item.ev); + break; + case RECUR_APPT: + print_recur_apoint(fmt_rapt, date, day->start, day->item.rapt); + break; + case RECUR_EVNT: + print_recur_event(fmt_rev, date, day->item.rev); + break; + default: + EXIT(_("unknown item type")); + /* NOTREACHED */ + } + } +} + /* Display an item inside a popup window. */ -void day_popup_item(void) +void day_popup_item(struct day_item *day) { - 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 + if (day->type == EVNT || day->type == RECUR_EVNT) { + item_in_popup(NULL, NULL, day_item_get_mesg(day), _("Event :")); + } else if (day->type == APPT || day->type == RECUR_APPT) { + char a_st[100], a_end[100]; + + /* FIXME: Redesign apoint_sec2str() and remove the need for a temporary + * appointment item here. */ + struct apoint apt_tmp; + apt_tmp.start = day->start; + apt_tmp.dur = day_item_get_duration(day); + apoint_sec2str(&apt_tmp, calendar_get_slctd_day_sec(), a_st, a_end); + + item_in_popup(a_st, a_end, day_item_get_mesg(day), _("Appointment :")); + } else { EXIT(_("unknown item type")); - /* NOTREACHED */ + /* NOTREACHED */ + } } /* @@ -447,21 +562,21 @@ int day_check_if_item(struct date day) { const long date = date2sec(day, 0, 0); - if (LLIST_FIND_FIRST(&recur_elist, date, recur_event_inday)) + if (LLIST_FIND_FIRST(&recur_elist, (long *)&date, recur_event_inday)) return 1; LLIST_TS_LOCK(&recur_alist_p); - if (LLIST_TS_FIND_FIRST(&recur_alist_p, date, recur_apoint_inday)) { + if (LLIST_TS_FIND_FIRST(&recur_alist_p, (long *)&date, recur_apoint_inday)) { LLIST_TS_UNLOCK(&recur_alist_p); return 1; } LLIST_TS_UNLOCK(&recur_alist_p); - if (LLIST_FIND_FIRST(&eventlist, date, event_inday)) + if (LLIST_FIND_FIRST(&eventlist, (long *)&date, event_inday)) return 1; LLIST_TS_LOCK(&alist_p); - if (LLIST_TS_FIND_FIRST(&alist_p, date, apoint_inday)) { + if (LLIST_TS_FIND_FIRST(&alist_p, (long *)&date, apoint_inday)) { LLIST_TS_UNLOCK(&alist_p); return 1; } @@ -502,7 +617,7 @@ unsigned day_chk_busy_slices(struct date day, int slicesno, int *slices) #define SLICENUM(tsec) ((tsec) / slicelen % slicesno) LLIST_TS_LOCK(&recur_alist_p); - LLIST_TS_FIND_FOREACH(&recur_alist_p, date, recur_apoint_inday, i) { + LLIST_TS_FIND_FOREACH(&recur_alist_p, (long *)&date, recur_apoint_inday, i) { struct apoint *rapt = LLIST_TS_GET_DATA(i); long start = get_item_time(rapt->start); long end = get_item_time(rapt->start + rapt->dur); @@ -515,7 +630,7 @@ unsigned day_chk_busy_slices(struct date day, int slicesno, int *slices) LLIST_TS_UNLOCK(&recur_alist_p); LLIST_TS_LOCK(&alist_p); - LLIST_TS_FIND_FOREACH(&alist_p, date, apoint_inday, i) { + LLIST_TS_FIND_FOREACH(&alist_p, (long *)&date, apoint_inday, i) { struct apoint *apt = LLIST_TS_GET_DATA(i); long start = get_item_time(apt->start); long end = get_item_time(apt->start + apt->dur); @@ -534,466 +649,56 @@ unsigned day_chk_busy_slices(struct date day, int slicesno, int *slices) return 1; } -/* Request the user to enter a new time. */ -static int day_edit_time(int time, unsigned *new_hour, unsigned *new_minute) -{ - char *timestr = date_sec2date_str(time, "%H:%M"); - const char *msg_time = _("Enter the new time ([hh:mm]) : "); - const char *enter_str = _("Press [Enter] to continue"); - const char *fmt_msg = _("You entered an invalid time, should be [hh:mm]"); - - for (;;) { - status_mesg(msg_time, ""); - if (updatestring(win[STA].p, ×tr, 0, 1) == GETSTRING_VALID) { - if (parse_time(timestr, new_hour, new_minute) == 1) { - mem_free(timestr); - return 1; - } else { - status_mesg(fmt_msg, enter_str); - wgetch(win[STA].p); - } - } else - return 0; - } -} - -/* Request the user to enter a new time or duration. */ -static int day_edit_duration(int start, int dur, unsigned *new_duration) -{ - char *timestr = date_sec2date_str(start + dur, "%H:%M"); - const char *msg_time = - _ - ("Enter new end time ([hh:mm]) or duration ([+hh:mm], [+xxxdxxhxxm] or [+mm]) : "); - const char *enter_str = _("Press [Enter] to continue"); - const char *fmt_msg = _("You entered an invalid time, should be [hh:mm]"); - long newtime; - unsigned hr, mn; - - for (;;) { - status_mesg(msg_time, ""); - if (updatestring(win[STA].p, ×tr, 0, 1) == GETSTRING_VALID) { - if (*timestr == '+' && parse_duration(timestr + 1, new_duration) == 1) { - *new_duration *= MININSEC; - break; - } else if (parse_time(timestr, &hr, &mn) == 1) { - newtime = update_time_in_date(start + dur, hr, mn); - *new_duration = (newtime > start) ? newtime - start : - DAYINSEC + newtime - start; - break; - } else { - status_mesg(fmt_msg, enter_str); - wgetch(win[STA].p); - } - } else - return 0; - } - - mem_free(timestr); - return 1; -} - -/* Request the user to enter a new end time or duration. */ -static void update_start_time(long *start, long *dur) -{ - long newtime; - unsigned hr, mn; - int valid_date; - const char *msg_wrong_time = - _("Invalid time: start time must be before end time!"); - const char *msg_enter = _("Press [Enter] to continue"); - - do { - day_edit_time(*start, &hr, &mn); - 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) -{ - unsigned newdur; - - day_edit_duration(*start, *dur, &newdur); - *dur = newdur; -} - -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 **rpt, const long start) -{ - int newtype, newfreq, date_entered; - long newuntil; - char outstr[BUFSIZ]; - char *freqstr, *timstr; - const char *msg_rpt_prefix = _("Enter the new repetition type:"); - const char *msg_rpt_daily = _("(d)aily"); - const char *msg_rpt_weekly = _("(w)eekly"); - const char *msg_rpt_monthly = _("(m)onthly"); - const char *msg_rpt_yearly = _("(y)early"); - - /* Find the current repetition type. */ - const char *rpt_current; - char msg_rpt_current[BUFSIZ]; - switch (recur_def2char((*rpt)->type)) { - case 'D': - rpt_current = msg_rpt_daily; - break; - case 'W': - rpt_current = msg_rpt_weekly; - break; - case 'M': - rpt_current = msg_rpt_monthly; - break; - case 'Y': - rpt_current = msg_rpt_yearly; - break; - default: - /* NOTREACHED, but makes the compiler happier. */ - rpt_current = msg_rpt_daily; - } - - snprintf(msg_rpt_current, BUFSIZ, _("(currently using %s)"), rpt_current); - - char msg_rpt_asktype[BUFSIZ]; - snprintf(msg_rpt_asktype, BUFSIZ, "%s %s, %s, %s, %s ? %s", - msg_rpt_prefix, - msg_rpt_daily, - msg_rpt_weekly, msg_rpt_monthly, msg_rpt_yearly, msg_rpt_current); - - const char *msg_rpt_choice = _("[dwmy]"); - const char *msg_wrong_freq = _("The frequence you entered is not valid."); - const char *msg_wrong_time = - _("Invalid time: start time must be before end time!"); - const char *msg_wrong_date = _("The entered date is not valid."); - const char *msg_fmts = - _("Possible formats are [%s] or '0' for an endless repetition."); - const char *msg_enter = _("Press [Enter] to continue"); - - switch (status_ask_choice(msg_rpt_asktype, msg_rpt_choice, 4)) { - case 1: - newtype = 'D'; - break; - case 2: - newtype = 'W'; - break; - case 3: - newtype = 'M'; - break; - case 4: - newtype = 'Y'; - break; - default: - return; - } - - do { - status_mesg(_("Enter the new repetition frequence:"), ""); - freqstr = mem_malloc(BUFSIZ); - snprintf(freqstr, BUFSIZ, "%d", (*rpt)->freq); - if (updatestring(win[STA].p, &freqstr, 0, 1) == GETSTRING_VALID) { - newfreq = atoi(freqstr); - mem_free(freqstr); - if (newfreq == 0) { - status_mesg(msg_wrong_freq, msg_enter); - wgetch(win[STA].p); - } - } else { - mem_free(freqstr); - return; - } - } - while (newfreq == 0); - - do { - snprintf(outstr, BUFSIZ, _("Enter the new ending date: [%s] or '0'"), - DATEFMT_DESC(conf.input_datefmt)); - status_mesg(outstr, ""); - timstr = date_sec2date_str((*rpt)->until, DATEFMT(conf.input_datefmt)); - if (updatestring(win[STA].p, &timstr, 0, 1) != GETSTRING_VALID) { - mem_free(timstr); - return; - } - if (strcmp(timstr, "0") == 0) { - newuntil = 0; - date_entered = 1; - } else { - struct tm lt; - time_t t; - struct date new_date; - int newmonth, newday, newyear; - - if (parse_date(timstr, conf.input_datefmt, &newyear, &newmonth, - &newday, calendar_get_slctd_day())) { - t = start; - localtime_r(&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 { - snprintf(outstr, BUFSIZ, msg_fmts, DATEFMT_DESC(conf.input_datefmt)); - status_mesg(msg_wrong_date, outstr); - wgetch(win[STA].p); - date_entered = 0; - } - } - } - while (date_entered == 0); - - mem_free(timstr); - (*rpt)->type = recur_char2def(newtype); - (*rpt)->freq = newfreq; - (*rpt)->until = newuntil; -} - -/* Edit an already existing item. */ -void day_edit_item(void) -{ - struct day_item *p; - struct recur_event *re; - struct event *e; - struct recur_apoint *ra; - struct apoint *a; - long date; - int item_num; - int need_check_notify = 0; - - item_num = apoint_hilt(); - p = day_get_item(item_num); - 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)); - const char *choice_recur_evnt[2] = { - _("Description"), - _("Repetition"), - }; - switch (status_ask_simplechoice(_("Edit: "), choice_recur_evnt, 2)) { - 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)); - const char *choice_recur_appt[4] = { - _("Start time"), - _("End time"), - _("Description"), - _("Repetition"), - }; - switch (status_ask_simplechoice(_("Edit: "), choice_recur_appt, 4)) { - case 1: - need_check_notify = 1; - update_start_time(&ra->start, &ra->dur); - break; - case 2: - update_duration(&ra->start, &ra->dur); - break; - case 3: - if (notify_bar()) - need_check_notify = notify_same_recur_item(ra); - update_desc(&ra->mesg); - break; - case 4: - need_check_notify = 1; - update_rept(&ra->rpt, ra->start); - break; - default: - return; - } - break; - case APPT: - a = apoint_get(date, day_item_nb(date, item_num, APPT)); - const char *choice_appt[3] = { - _("Start time"), - _("End time"), - _("Description"), - }; - switch (status_ask_simplechoice(_("Edit: "), choice_appt, 3)) { - case 1: - need_check_notify = 1; - update_start_time(&a->start, &a->dur); - break; - case 2: - update_duration(&a->start, &a->dur); - break; - case 3: - if (notify_bar()) - need_check_notify = notify_same_item(a->start); - update_desc(&a->mesg); - break; - default: - return; - } - break; - } - - if (need_check_notify) - notify_check_next_app(1); -} - -/* - * 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, enum eraseflg flag) -{ - struct day_item *p; - - const char *erase_warning = - _("This item is recurrent. " - "Delete (a)ll occurences or just this (o)ne ?"); - const char *erase_choices = _("[ao]"); - const int nb_erase_choices = 2; - - const char *note_warning = - _("This item has a note attached to it. " - "Delete (i)tem or just its (n)ote ?"); - const char *note_choices = _("[in]"); - const int nb_note_choices = 2; - int ans; - unsigned delete_whole; - - p = day_get_item(item_number); - if (flag == ERASE_DONT_FORCE) { - if (p->note == NULL) - ans = 1; - else - ans = status_ask_choice(note_warning, note_choices, nb_note_choices); - - switch (ans) { - case 1: - flag = ERASE_FORCE; - break; - case 2: - flag = ERASE_FORCE_ONLY_NOTE; - break; - default: /* User escaped */ - return 0; - } - } - 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) - ans = 1; - else - ans = status_ask_choice(erase_warning, erase_choices, nb_erase_choices); - - switch (ans) { - case 1: - delete_whole = 1; - break; - case 2: - delete_whole = 0; - break; - default: - 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); - } - } - if (flag == ERASE_FORCE_ONLY_NOTE) - return 0; - else - return p->type; -} - /* Cut an item so it can be pasted somewhere else later. */ -int day_cut_item(long date, int item_number) +struct day_item *day_cut_item(long date, int item_number) { - const int DELETE_WHOLE = 1; - struct day_item *p; + struct day_item *p = day_get_item(item_number); - p = day_get_item(item_number); switch (p->type) { case EVNT: - event_delete_bynum(date, day_item_nb(date, item_number, EVNT), ERASE_CUT); + event_delete(p->item.ev); break; case RECUR_EVNT: - recur_event_erase(date, day_item_nb(date, item_number, RECUR_EVNT), - DELETE_WHOLE, ERASE_CUT); + recur_event_erase(p->item.rev); break; case APPT: - apoint_delete_bynum(date, day_item_nb(date, item_number, APPT), ERASE_CUT); + apoint_delete(p->item.apt); break; case RECUR_APPT: - recur_apoint_erase(date, p->appt_pos, DELETE_WHOLE, ERASE_CUT); + recur_apoint_erase(p->item.rapt); break; default: EXIT(_("unknwon type")); /* NOTREACHED */ } - return p->type; + return p; } /* Paste a previously cut item. */ -int day_paste_item(long date, int cut_item_type) +int day_paste_item(struct day_item *p, long date) { - int pasted_item_type; - - pasted_item_type = cut_item_type; - switch (cut_item_type) { + switch (p->type) { case 0: return 0; case EVNT: - event_paste_item(); + event_paste_item(p->item.ev, date); break; case RECUR_EVNT: - recur_event_paste_item(); + recur_event_paste_item(p->item.rev, date); break; case APPT: - apoint_paste_item(); + apoint_paste_item(p->item.apt, date); break; case RECUR_APPT: - recur_apoint_paste_item(); + recur_apoint_paste_item(p->item.rapt, date); break; default: EXIT(_("unknwon type")); /* NOTREACHED */ } - return pasted_item_type; + return p->type; } /* Returns a structure containing the selected item. */ @@ -1002,117 +707,45 @@ struct day_item *day_get_item(int item_number) return LLIST_GET_DATA(LLIST_NTH(&day_items, item_number - 1)); } -/* 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]; - llist_item_t *j; - - for (i = 0; i < MAX_TYPES; i++) - nb_item[i] = 0; - - j = LLIST_FIRST(&day_items); - for (i = 1; i < day_num; i++) { - struct day_item *day = LLIST_TS_GET_DATA(j); - nb_item[day->type - 1]++; - j = LLIST_TS_NEXT(j); - } - - return nb_item[type - 1]; -} - /* Attach a note to an appointment or event. */ -void day_edit_note(const char *editor) +void day_edit_note(struct day_item *p, const char *editor) { - struct day_item *p; - struct recur_apoint *ra; - struct apoint *a; - struct recur_event *re; - struct event *e; - long date; - int item_num; + char *note; - item_num = apoint_hilt(); - p = day_get_item(item_num); - edit_note(&p->note, editor); + note = day_item_get_note(p); + edit_note(¬e, 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; + p->item.rev->note = note; break; case EVNT: - e = event_get(date, day_item_nb(date, item_num, EVNT)); - e->note = p->note; + p->item.ev->note = note; break; case RECUR_APPT: - ra = recur_get_apoint(date, day_item_nb(date, item_num, RECUR_APPT)); - ra->note = p->note; + p->item.rapt->note = note; break; case APPT: - a = apoint_get(date, day_item_nb(date, item_num, APPT)); - a->note = p->note; + p->item.apt->note = note; break; } } /* View a note previously attached to an appointment or event */ -void day_view_note(const char *pager) +void day_view_note(struct day_item *p, const char *pager) { - struct day_item *p = day_get_item(apoint_hilt()); - view_note(p->note, pager); + view_note(day_item_get_note(p), pager); } -/* Pipe an appointment or event to an external program. */ -void day_pipe_item(void) +/* Switch notification state for an item. */ +void day_item_switch_notify(struct day_item *p) { - char cmd[BUFSIZ] = ""; - char const *arg[] = { cmd, NULL }; - int pout; - int pid; - FILE *fpout; - int item_num; - long date; - struct day_item *p; - struct recur_apoint *ra; - struct apoint *a; - struct recur_event *re; - struct event *e; - - status_mesg(_("Pipe item to external command:"), ""); - if (getstring(win[STA].p, cmd, BUFSIZ, 0, 1) != GETSTRING_VALID) - return; - - wins_prepare_external(); - if ((pid = shell_exec(NULL, &pout, *arg, arg))) { - fpout = fdopen(pout, "w"); - - item_num = apoint_hilt(); - p = day_get_item(item_num); - 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)); - recur_event_write(re, fpout); - break; - case EVNT: - e = event_get(date, day_item_nb(date, item_num, EVNT)); - event_write(e, fpout); - break; - case RECUR_APPT: - ra = recur_get_apoint(date, day_item_nb(date, item_num, RECUR_APPT)); - recur_apoint_write(ra, fpout); - break; - case APPT: - a = apoint_get(date, day_item_nb(date, item_num, APPT)); - apoint_write(a, fpout); - break; - } - - fclose(fpout); - child_wait(NULL, &pout, pid); - press_any_key(); + switch (p->type) { + case RECUR_APPT: + recur_apoint_switch_notify(p->item.rapt); + break; + case APPT: + apoint_switch_notify(p->item.apt); + break; } - wins_unprepare_external(); } diff --git a/src/event.c b/src/event.c index 203af44..dca6820 100644 --- a/src/event.c +++ b/src/event.c @@ -42,33 +42,28 @@ #include "calcurse.h" llist_t eventlist; -static struct event bkp_cut_event; -void event_free_bkp(void) -{ - if (bkp_cut_event.mesg) { - mem_free(bkp_cut_event.mesg); - bkp_cut_event.mesg = 0; - } - erase_note(&bkp_cut_event.note); -} - -static void event_free(struct event *ev) +void event_free(struct event *ev) { mem_free(ev->mesg); erase_note(&ev->note); mem_free(ev); } -static void event_dup(struct event *in, struct event *bkp) +struct event *event_dup(struct event *in) { - EXIT_IF(!in || !bkp, _("null pointer")); + EXIT_IF(!in, _("null pointer")); - bkp->id = in->id; - bkp->day = in->day; - bkp->mesg = mem_strdup(in->mesg); + struct event *ev = mem_malloc(sizeof(struct event)); + ev->id = in->id; + ev->day = in->day; + ev->mesg = mem_strdup(in->mesg); if (in->note) - bkp->note = mem_strdup(in->note); + ev->note = mem_strdup(in->note); + else + ev->note = NULL; + + return ev; } void event_llist_init(void) @@ -104,9 +99,9 @@ struct event *event_new(char *mesg, char *note, long day, int id) } /* Check if the event belongs to the selected day */ -unsigned event_inday(struct event *i, long start) +unsigned event_inday(struct event *i, long *start) { - return (i->day < start + DAYINSEC && i->day >= start); + return (i->day < *start + DAYINSEC && i->day >= *start); } /* Write to file the event in user-friendly format */ @@ -151,47 +146,19 @@ struct event *event_scan(FILE * f, struct tm start, int id, char *note) return event_new(buf, note, tstart, id); } -/* Retrieve an event from the list, given the day and item position. */ -struct event *event_get(long day, int pos) -{ - llist_item_t *i = LLIST_FIND_NTH(&eventlist, pos, day, event_inday); - - if (i) - return LLIST_TS_GET_DATA(i); - - EXIT(_("event not found")); - /* NOTREACHED */ -} - /* Delete an event from the list. */ -void event_delete_bynum(long start, unsigned num, enum eraseflg flag) +void event_delete(struct event *ev) { - llist_item_t *i = LLIST_FIND_NTH(&eventlist, num, start, event_inday); + llist_item_t *i = LLIST_FIND_FIRST(&eventlist, ev, NULL); if (!i) EXIT(_("no such appointment")); - struct event *ev = LLIST_TS_GET_DATA(i); - - switch (flag) { - case ERASE_FORCE_ONLY_NOTE: - erase_note(&ev->note); - break; - case ERASE_CUT: - event_free_bkp(); - event_dup(ev, &bkp_cut_event); - erase_note(&ev->note); - /* FALLTHROUGH */ - default: - LLIST_REMOVE(&eventlist, i); - mem_free(ev->mesg); - mem_free(ev); - break; - } + + LLIST_REMOVE(&eventlist, i); } -void event_paste_item(void) +void event_paste_item(struct event *ev, long date) { - event_new(bkp_cut_event.mesg, bkp_cut_event.note, - date2sec(*calendar_get_slctd_day(), 0, 0), bkp_cut_event.id); - event_free_bkp(); + ev->day = date; + LLIST_ADD_SORTED(&eventlist, ev, event_cmp_day); } @@ -60,7 +60,7 @@ typedef enum { HELP_GOTO, HELP_DELETE, HELP_ADD, - HELP_CUT_PASTE, + HELP_COPY_PASTE, HELP_EDIT, HELP_ENOTE, HELP_VNOTE, @@ -126,7 +126,7 @@ help_write_pad(struct window *win, char *title, char *text, enum key action) case KEY_GENERIC_NEXT_YEAR: case KEY_GENERIC_GOTO_TODAY: case KEY_GENERIC_CREDITS: - case KEY_GENERIC_CUT: + case KEY_GENERIC_COPY: case KEY_GENERIC_PASTE: break; default: @@ -261,9 +261,9 @@ static int wanted_page(int ch) page = HELP_DELETE; break; - case KEY_GENERIC_CUT: + case KEY_GENERIC_COPY: case KEY_GENERIC_PASTE: - page = HELP_CUT_PASTE; + page = HELP_COPY_PASTE; break; case KEY_EDIT_ITEM: @@ -536,21 +536,18 @@ void help_screen(void) keys_action_firstkey(KEY_ADD_ITEM), keys_action_firstkey(KEY_ADD_ITEM)); - hscr[HELP_CUT_PASTE].title = _("Cut and Paste\n"); - snprintf(hscr[HELP_CUT_PASTE].text, HELPTEXTSIZ, + hscr[HELP_COPY_PASTE].title = _("Copy and Paste\n"); + snprintf(hscr[HELP_COPY_PASTE].text, HELPTEXTSIZ, _ - ("Cut and paste the currently selected item. This is useful to quickly\n" - "move an item from one date to another.\n" - "To do so, one must first highlight the item that needs to be moved,\n" - "then press '%s' to cut this item. It will be removed from the panel.\n" + ( + "Copy and paste the currently selected item. This is useful to quickly\n" + "copy an item from one date to another. To do so, one must first\n" + "highlight the item that needs to be copied, then press '%s' to copy.\n" "Once the new date is chosen in the calendar, the appointment panel must\n" - "be selected and the '%s' key must be pressed to paste the item.\n" - "The item will appear again in the appointment panel, assigned to the\n" - "newly selected date.\n\n" - "Be careful that if two cuts are performed successively without pasting\n" - "between them, the item that was cut at first will be lost, together\n" - "with its associated note if it had one."), - keys_action_firstkey(KEY_GENERIC_CUT), + "be selected and the '%s' key must be pressed to paste the item. The item\n" + "will appear in the appointment panel, assigned to the newly selected\n" + "date.\n\n"), + keys_action_firstkey(KEY_GENERIC_COPY), keys_action_firstkey(KEY_GENERIC_PASTE)); hscr[HELP_EDIT].title = _("Edit Item\n"); @@ -796,7 +793,7 @@ void help_screen(void) } wins_scrollwin_display(&hwin); - ch = keys_getch(win[STA].p, NULL); + ch = keys_getch(win[STA].p, NULL, NULL); } wins_scrollwin_delete(&hwin); if (need_resize) @@ -237,13 +237,13 @@ static void ical_export_todo(FILE * stream) } /* Print a header to describe import log report format. */ -static void ical_log_init(FILE * log, float version) +static void ical_log_init(FILE * log, int major, int minor) { const char *header = "+-------------------------------------------------------------------+\n" "| Calcurse icalendar import log. |\n" "| |\n" - "| Items imported from icalendar file, version %1.1f |\n" + "| Items imported from icalendar file, version %d.%d |\n" "| Some items could not be imported, they are described hereafter. |\n" "| The log line format is as follows: |\n" "| |\n" @@ -256,7 +256,7 @@ static void ical_log_init(FILE * log, float version) "+-------------------------------------------------------------------+\n\n"; if (log) - fprintf(log, header, version); + fprintf(log, header, major, minor); } /* @@ -419,25 +419,25 @@ static int ical_readline(FILE * fdi, char *buf, char *lstore, unsigned *ln) return 1; } -static float -ical_chk_header(FILE * fd, char *buf, char *lstore, unsigned *lineno) +static int +ical_chk_header(FILE * fd, char *buf, char *lstore, unsigned *lineno, + int *major, int *minor) { - const int HEADER_MALFORMED = -1; const char icalheader[] = "BEGIN:VCALENDAR"; - float version; if (!ical_readline(fd, buf, lstore, lineno)) - return HEADER_MALFORMED; + return 0; str_toupper(buf); if (strncmp(buf, icalheader, sizeof(icalheader) - 1) != 0) - return HEADER_MALFORMED; + return 0; - while (!sscanf(buf, "VERSION:%f", &version)) { + while (!sscanf(buf, "VERSION:%d.%d", major, minor)) { if (!ical_readline(fd, buf, lstore, lineno)) - return HEADER_MALFORMED; + return 0; } - return version; + + return 1; } /* @@ -1055,15 +1055,14 @@ ical_import_data(FILE * stream, FILE * log, unsigned *events, unsigned *apoints, const char vevent[] = "BEGIN:VEVENT"; const char vtodo[] = "BEGIN:VTODO"; char buf[BUFSIZ], lstore[BUFSIZ]; - float ical_version; + int major, minor; ical_readline_init(stream, buf, lstore, lines); - ical_version = ical_chk_header(stream, buf, lstore, lines); - RETURN_IF(ical_version < 0, + RETURN_IF(!ical_chk_header(stream, buf, lstore, lines, &major, &minor), _("Warning: ical header malformed or wrong version number. " "Aborting...")); - ical_log_init(log, ical_version); + ical_log_init(log, major, minor); while (ical_readline(stream, buf, lstore, lines)) { (*lines)++; diff --git a/src/interaction.c b/src/interaction.c new file mode 100644 index 0000000..635f78a --- /dev/null +++ b/src/interaction.c @@ -0,0 +1,899 @@ +/* + * Calcurse - text-based organizer + * + * Copyright (c) 2004-2012 calcurse Development Team <misc@calcurse.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 "calcurse.h" + +struct day_item day_cut[38] = { { 0, 0, { NULL } } }; + +/* Request the user to enter a new time. */ +static int day_edit_time(int time, unsigned *new_hour, unsigned *new_minute) +{ + char *timestr = date_sec2date_str(time, "%H:%M"); + const char *msg_time = _("Enter the new time ([hh:mm] or [hhmm]) : "); + const char *enter_str = _("Press [Enter] to continue"); + const char *fmt_msg = _("You entered an invalid time, should be [hh:mm] or [hhmm]"); + + for (;;) { + status_mesg(msg_time, ""); + if (updatestring(win[STA].p, ×tr, 0, 1) == GETSTRING_VALID) { + if (parse_time(timestr, new_hour, new_minute) == 1) { + mem_free(timestr); + return 1; + } else { + status_mesg(fmt_msg, enter_str); + wgetch(win[STA].p); + } + } else + return 0; + } +} + +/* Request the user to enter a new time or duration. */ +static int day_edit_duration(int start, int dur, unsigned *new_duration) +{ + char *timestr = date_sec2date_str(start + dur, "%H:%M"); + const char *msg_time = + _ + ("Enter new end time ([hh:mm], [hhmm]) or duration ([+hh:mm], [+xxxdxxhxxm] or [+mm]) : "); + const char *enter_str = _("Press [Enter] to continue"); + const char *fmt_msg = _("You entered an invalid time, should be [hh:mm] or [hhmm]"); + long newtime; + unsigned hr, mn; + + for (;;) { + status_mesg(msg_time, ""); + if (updatestring(win[STA].p, ×tr, 0, 1) == GETSTRING_VALID) { + if (*timestr == '+' && parse_duration(timestr + 1, new_duration) == 1) { + *new_duration *= MININSEC; + break; + } else if (parse_time(timestr, &hr, &mn) == 1) { + newtime = update_time_in_date(start + dur, hr, mn); + *new_duration = (newtime > start) ? newtime - start : + DAYINSEC + newtime - start; + break; + } else { + status_mesg(fmt_msg, enter_str); + wgetch(win[STA].p); + } + } else + return 0; + } + + mem_free(timestr); + return 1; +} + +/* Request the user to enter a new end time or duration. */ +static void update_start_time(long *start, long *dur) +{ + long newtime; + unsigned hr, mn; + int valid_date; + const char *msg_wrong_time = + _("Invalid time: start time must be before end time!"); + const char *msg_enter = _("Press [Enter] to continue"); + + do { + day_edit_time(*start, &hr, &mn); + 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) +{ + unsigned newdur; + + day_edit_duration(*start, *dur, &newdur); + *dur = newdur; +} + +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 **rpt, const long start) +{ + int newtype, newfreq, date_entered; + long newuntil; + char outstr[BUFSIZ]; + char *freqstr, *timstr; + const char *msg_rpt_prefix = _("Enter the new repetition type:"); + const char *msg_rpt_daily = _("(d)aily"); + const char *msg_rpt_weekly = _("(w)eekly"); + const char *msg_rpt_monthly = _("(m)onthly"); + const char *msg_rpt_yearly = _("(y)early"); + + /* Find the current repetition type. */ + const char *rpt_current; + char msg_rpt_current[BUFSIZ]; + switch (recur_def2char((*rpt)->type)) { + case 'D': + rpt_current = msg_rpt_daily; + break; + case 'W': + rpt_current = msg_rpt_weekly; + break; + case 'M': + rpt_current = msg_rpt_monthly; + break; + case 'Y': + rpt_current = msg_rpt_yearly; + break; + default: + /* NOTREACHED, but makes the compiler happier. */ + rpt_current = msg_rpt_daily; + } + + snprintf(msg_rpt_current, BUFSIZ, _("(currently using %s)"), rpt_current); + + char msg_rpt_asktype[BUFSIZ]; + snprintf(msg_rpt_asktype, BUFSIZ, "%s %s, %s, %s, %s ? %s", + msg_rpt_prefix, + msg_rpt_daily, + msg_rpt_weekly, msg_rpt_monthly, msg_rpt_yearly, msg_rpt_current); + + const char *msg_rpt_choice = _("[dwmy]"); + const char *msg_wrong_freq = _("The frequence you entered is not valid."); + const char *msg_wrong_time = + _("Invalid time: start time must be before end time!"); + const char *msg_wrong_date = _("The entered date is not valid."); + const char *msg_fmts = + _("Possible formats are [%s] or '0' for an endless repetition."); + const char *msg_enter = _("Press [Enter] to continue"); + + switch (status_ask_choice(msg_rpt_asktype, msg_rpt_choice, 4)) { + case 1: + newtype = 'D'; + break; + case 2: + newtype = 'W'; + break; + case 3: + newtype = 'M'; + break; + case 4: + newtype = 'Y'; + break; + default: + return; + } + + do { + status_mesg(_("Enter the new repetition frequence:"), ""); + freqstr = mem_malloc(BUFSIZ); + snprintf(freqstr, BUFSIZ, "%d", (*rpt)->freq); + if (updatestring(win[STA].p, &freqstr, 0, 1) == GETSTRING_VALID) { + newfreq = atoi(freqstr); + mem_free(freqstr); + if (newfreq == 0) { + status_mesg(msg_wrong_freq, msg_enter); + wgetch(win[STA].p); + } + } else { + mem_free(freqstr); + return; + } + } + while (newfreq == 0); + + do { + snprintf(outstr, BUFSIZ, _("Enter the new ending date: [%s] or '0'"), + DATEFMT_DESC(conf.input_datefmt)); + status_mesg(outstr, ""); + timstr = date_sec2date_str((*rpt)->until, DATEFMT(conf.input_datefmt)); + if (updatestring(win[STA].p, &timstr, 0, 1) != GETSTRING_VALID) { + mem_free(timstr); + return; + } + if (strcmp(timstr, "0") == 0) { + newuntil = 0; + date_entered = 1; + } else { + struct tm lt; + time_t t; + struct date new_date; + int newmonth, newday, newyear; + + if (parse_date(timstr, conf.input_datefmt, &newyear, &newmonth, + &newday, calendar_get_slctd_day())) { + t = start; + localtime_r(&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 { + snprintf(outstr, BUFSIZ, msg_fmts, DATEFMT_DESC(conf.input_datefmt)); + status_mesg(msg_wrong_date, outstr); + wgetch(win[STA].p); + date_entered = 0; + } + } + } + while (date_entered == 0); + + mem_free(timstr); + (*rpt)->type = recur_char2def(newtype); + (*rpt)->freq = newfreq; + (*rpt)->until = newuntil; +} + +/* Edit an already existing item. */ +void interact_day_item_edit(void) +{ + struct day_item *p; + struct recur_event *re; + struct event *e; + struct recur_apoint *ra; + struct apoint *a; + int need_check_notify = 0; + + p = day_get_item(apoint_hilt()); + + switch (p->type) { + case RECUR_EVNT: + re = p->item.rev; + const char *choice_recur_evnt[2] = { + _("Description"), + _("Repetition"), + }; + switch (status_ask_simplechoice(_("Edit: "), choice_recur_evnt, 2)) { + case 1: + update_desc(&re->mesg); + break; + case 2: + update_rept(&re->rpt, re->day); + break; + default: + return; + } + break; + case EVNT: + e = p->item.ev; + update_desc(&e->mesg); + break; + case RECUR_APPT: + ra = p->item.rapt; + const char *choice_recur_appt[4] = { + _("Start time"), + _("End time"), + _("Description"), + _("Repetition"), + }; + switch (status_ask_simplechoice(_("Edit: "), choice_recur_appt, 4)) { + case 1: + need_check_notify = 1; + update_start_time(&ra->start, &ra->dur); + break; + case 2: + update_duration(&ra->start, &ra->dur); + break; + case 3: + if (notify_bar()) + need_check_notify = notify_same_recur_item(ra); + update_desc(&ra->mesg); + break; + case 4: + need_check_notify = 1; + update_rept(&ra->rpt, ra->start); + break; + default: + return; + } + break; + case APPT: + a = p->item.apt; + const char *choice_appt[3] = { + _("Start time"), + _("End time"), + _("Description"), + }; + switch (status_ask_simplechoice(_("Edit: "), choice_appt, 3)) { + case 1: + need_check_notify = 1; + update_start_time(&a->start, &a->dur); + break; + case 2: + update_duration(&a->start, &a->dur); + break; + case 3: + if (notify_bar()) + need_check_notify = notify_same_item(a->start); + update_desc(&a->mesg); + break; + default: + return; + } + break; + } + + calendar_monthly_view_cache_set_invalid(); + + if (need_check_notify) + notify_check_next_app(1); +} + +/* Pipe an appointment or event to an external program. */ +void interact_day_item_pipe(void) +{ + char cmd[BUFSIZ] = ""; + char const *arg[] = { cmd, NULL }; + int pout; + int pid; + FILE *fpout; + struct day_item *p; + + status_mesg(_("Pipe item to external command:"), ""); + if (getstring(win[STA].p, cmd, BUFSIZ, 0, 1) != GETSTRING_VALID) + return; + + wins_prepare_external(); + if ((pid = shell_exec(NULL, &pout, *arg, arg))) { + fpout = fdopen(pout, "w"); + + p = day_get_item(apoint_hilt()); + switch (p->type) { + case RECUR_EVNT: + recur_event_write(p->item.rev, fpout); + break; + case EVNT: + event_write(p->item.ev, fpout); + break; + case RECUR_APPT: + recur_apoint_write(p->item.rapt, fpout); + break; + case APPT: + apoint_write(p->item.apt, fpout); + break; + } + + fclose(fpout); + child_wait(NULL, &pout, pid); + press_any_key(); + } + wins_unprepare_external(); +} + +/* + * Add an item in either the appointment or the event list, + * depending if the start time is entered or not. + */ +void interact_day_item_add(void) +{ +#define LTIME 6 +#define LDUR 12 + const char *mesg_1 = + _("Enter start time ([hh:mm] or [hhmm]), leave blank for an all-day event : "); + const char *mesg_2 = + _ + ("Enter end time ([hh:mm] or [hhmm]) or duration ([+hh:mm], [+xxxdxxhxxm] or [+mm]) : "); + const char *mesg_3 = _("Enter description :"); + const char *format_message_1 = + _("You entered an invalid start time, should be [hh:mm] or [hhmm]"); + const char *format_message_2 = + _ + ("Invalid end time/duration, should be [hh:mm], [hhmm], [+hh:mm], [+xxxdxxhxxm] or [+mm]"); + const char *enter_str = _("Press [Enter] to continue"); + int Id = 1; + char item_time[LDUR] = ""; + char item_mesg[BUFSIZ] = ""; + long apoint_start; + unsigned heures, minutes; + unsigned apoint_duration; + unsigned end_h, end_m; + int is_appointment = 1; + + /* Get the starting time */ + for (;;) { + status_mesg(mesg_1, ""); + if (getstring(win[STA].p, item_time, LTIME, 0, 1) != GETSTRING_ESC) { + if (strlen(item_time) == 0) { + is_appointment = 0; + break; + } + + if (parse_time(item_time, &heures, &minutes) == 1) + break; + else { + status_mesg(format_message_1, enter_str); + wgetch(win[STA].p); + } + } else + return; + } + + /* + * Check if an event or appointment is entered, + * depending on the starting time, and record the + * corresponding item. + */ + if (is_appointment) { /* Get the appointment duration */ + item_time[0] = '\0'; + for (;;) { + status_mesg(mesg_2, ""); + if (getstring(win[STA].p, item_time, LDUR, 0, 1) != GETSTRING_ESC) { + if (*item_time == '+' && parse_duration(item_time + 1, + &apoint_duration) == 1) + break; + else if (parse_time(item_time, &end_h, &end_m) == 1) { + if (end_h < heures || ((end_h == heures) && (end_m < minutes))) { + apoint_duration = MININSEC - minutes + end_m + + (24 + end_h - (heures + 1)) * MININSEC; + } else { + apoint_duration = MININSEC - minutes + + end_m + (end_h - (heures + 1)) * MININSEC; + } + break; + } else { + status_mesg(format_message_2, enter_str); + wgetch(win[STA].p); + } + } else + return; + } + } else /* Insert the event Id */ + Id = 1; + + status_mesg(mesg_3, ""); + if (getstring(win[STA].p, item_mesg, BUFSIZ, 0, 1) == GETSTRING_VALID) { + if (is_appointment) { + apoint_start = date2sec(*calendar_get_slctd_day(), heures, minutes); + apoint_new(item_mesg, 0L, apoint_start, min2sec(apoint_duration), 0L); + if (notify_bar()) + notify_check_added(item_mesg, apoint_start, 0L); + } else + event_new(item_mesg, 0L, date2sec(*calendar_get_slctd_day(), 0, 0), Id); + + if (apoint_hilt() == 0) + apoint_hilt_increase(1); + } + + calendar_monthly_view_cache_set_invalid(); + + wins_erase_status_bar(); +} + +/* Delete an item from the appointment list. */ +void interact_day_item_delete(unsigned *nb_events, unsigned *nb_apoints, + unsigned reg) +{ + const char *del_app_str = _("Do you really want to delete this item ?"); + + const char *erase_warning = + _("This item is recurrent. " + "Delete (a)ll occurences or just this (o)ne ?"); + const char *erase_choices = _("[ao]"); + const int nb_erase_choices = 2; + + const char *note_warning = + _("This item has a note attached to it. " + "Delete (i)tem or just its (n)ote ?"); + const char *note_choices = _("[in]"); + const int nb_note_choices = 2; + + long date = calendar_get_slctd_day_sec(); + int nb_items = *nb_apoints + *nb_events; + int to_be_removed = 0; + + if (nb_items == 0) + return; + + struct day_item *p = day_get_item(apoint_hilt()); + + if (conf.confirm_delete) { + if (status_ask_bool(del_app_str) != 1) { + wins_erase_status_bar(); + return; + } + } + + if (day_item_get_note(p)) { + switch (status_ask_choice(note_warning, note_choices, nb_note_choices)) { + case 1: + break; + case 2: + day_item_erase_note(p); + return; + default: /* User escaped */ + return; + } + } + + if (p->type == RECUR_EVNT || p->type == RECUR_APPT) { + switch (status_ask_choice(erase_warning, erase_choices, nb_erase_choices)) { + case 1: + break; + case 2: + day_item_add_exc(p, date); + return; + default: + return; + } + } + + interact_day_item_cut_free(reg); + p = day_cut_item(date, apoint_hilt()); + day_cut[reg].type = p->type; + day_cut[reg].item = p->item; + + switch (p->type) { + case EVNT: + case RECUR_EVNT: + (*nb_events)--; + to_be_removed = 1; + break; + case APPT: + case RECUR_APPT: + (*nb_apoints)--; + to_be_removed = 3; + break; + default: + EXIT(_("no such type")); + /* NOTREACHED */ + } + + calendar_monthly_view_cache_set_invalid(); + + if (apoint_hilt() > 1) + apoint_hilt_decrease(1); + if (apad.first_onscreen >= to_be_removed) + apad.first_onscreen = apad.first_onscreen - to_be_removed; + if (nb_items == 1) + apoint_hilt_set(0); +} + +/* Request user to enter a new todo item. */ +void interact_todo_add(void) +{ + int ch = 0; + const char *mesg = _("Enter the new ToDo item : "); + const char *mesg_id = + _("Enter the ToDo priority [1 (highest) - 9 (lowest)] :"); + char todo_input[BUFSIZ] = ""; + + status_mesg(mesg, ""); + if (getstring(win[STA].p, todo_input, BUFSIZ, 0, 1) == GETSTRING_VALID) { + while ((ch < '1') || (ch > '9')) { + status_mesg(mesg_id, ""); + ch = wgetch(win[STA].p); + } + todo_add(todo_input, ch - '0', NULL); + todo_set_nb(todo_nb() + 1); + } +} + +/* Delete an item from the ToDo list. */ +void interact_todo_delete(void) +{ + const char *del_todo_str = _("Do you really want to delete this task ?"); + const char *erase_warning = + _("This item has a note attached to it. " + "Delete (t)odo or just its (n)ote ?"); + const char *erase_choice = _("[tn]"); + const int nb_erase_choice = 2; + int answer; + + if ((todo_nb() <= 0) || + (conf.confirm_delete && (status_ask_bool(del_todo_str) != 1))) { + wins_erase_status_bar(); + return; + } + + /* This todo item doesn't have any note associated. */ + if (todo_get_item(todo_hilt())->note == NULL) + answer = 1; + else + answer = status_ask_choice(erase_warning, erase_choice, nb_erase_choice); + + switch (answer) { + case 1: + todo_delete(todo_get_item(todo_hilt())); + todo_set_nb(todo_nb() - 1); + if (todo_hilt() > 1) + todo_hilt_decrease(1); + if (todo_nb() == 0) + todo_hilt_set(0); + if (todo_hilt_pos() < 0) + todo_first_decrease(1); + break; + case 2: + todo_delete_note(todo_get_item(todo_hilt())); + break; + default: + wins_erase_status_bar(); + return; + } +} + +/* Edit the description of an already existing todo item. */ +void interact_todo_edit(void) +{ + struct todo *i; + const char *mesg = _("Enter the new ToDo description :"); + + status_mesg(mesg, ""); + i = todo_get_item(todo_hilt()); + updatestring(win[STA].p, &i->mesg, 0, 1); +} + +/* Pipe a todo item to an external program. */ +void interact_todo_pipe(void) +{ + char cmd[BUFSIZ] = ""; + char const *arg[] = { cmd, NULL }; + int pout; + int pid; + FILE *fpout; + struct todo *todo; + + status_mesg(_("Pipe item to external command:"), ""); + if (getstring(win[STA].p, cmd, BUFSIZ, 0, 1) != GETSTRING_VALID) + return; + + wins_prepare_external(); + if ((pid = shell_exec(NULL, &pout, *arg, arg))) { + fpout = fdopen(pout, "w"); + + todo = todo_get_item(todo_hilt()); + todo_write(todo, fpout); + + fclose(fpout); + child_wait(NULL, &pout, pid); + press_any_key(); + } + wins_unprepare_external(); +} + +/* + * Ask user for repetition characteristics: + * o repetition type: daily, weekly, monthly, yearly + * o repetition frequence: every X days, weeks, ... + * o repetition end date + * and then delete the selected item to recreate it as a recurrent one + */ +void interact_day_item_repeat(void) +{ + struct tm lt; + time_t t; + int date_entered = 0; + int year = 0, month = 0, day = 0; + struct date until_date; + char outstr[BUFSIZ]; + char user_input[BUFSIZ] = ""; + const char *msg_rpt_prefix = _("Enter the repetition type:"); + const char *msg_rpt_daily = _("(d)aily"); + const char *msg_rpt_weekly = _("(w)eekly"); + const char *msg_rpt_monthly = _("(m)onthly"); + const char *msg_rpt_yearly = _("(y)early"); + const char *msg_type_choice = _("[dwmy]"); + const char *mesg_freq_1 = _("Enter the repetition frequence:"); + const char *mesg_wrong_freq = _("The frequence you entered is not valid."); + const char *mesg_until_1 = + _("Enter the ending date: [%s] or '0' for an endless repetition"); + const char *mesg_wrong_1 = _("The entered date is not valid."); + const char *mesg_wrong_2 = + _("Possible formats are [%s] or '0' for an endless repetition"); + const char *wrong_type_1 = _("This item is already a repeated one."); + const char *wrong_type_2 = _("Press [ENTER] to continue."); + const char *mesg_older = + _("Sorry, the date you entered is older than the item start time."); + + char msg_asktype[BUFSIZ]; + snprintf(msg_asktype, BUFSIZ, "%s %s, %s, %s, %s", + msg_rpt_prefix, + msg_rpt_daily, msg_rpt_weekly, msg_rpt_monthly, msg_rpt_yearly); + + int type = 0, freq = 0; + int item_nb; + struct day_item *p; + struct recur_apoint *ra; + long until, date; + + item_nb = apoint_hilt(); + p = day_get_item(item_nb); + if (p->type != APPT && p->type != EVNT) { + status_mesg(wrong_type_1, wrong_type_2); + wgetch(win[STA].p); + return; + } + + switch (status_ask_choice(msg_asktype, msg_type_choice, 4)) { + case 1: + type = RECUR_DAILY; + break; + case 2: + type = RECUR_WEEKLY; + break; + case 3: + type = RECUR_MONTHLY; + break; + case 4: + type = RECUR_YEARLY; + break; + default: + return; + } + + while (freq == 0) { + status_mesg(mesg_freq_1, ""); + if (getstring(win[STA].p, user_input, BUFSIZ, 0, 1) == GETSTRING_VALID) { + freq = atoi(user_input); + if (freq == 0) { + status_mesg(mesg_wrong_freq, wrong_type_2); + wgetch(win[STA].p); + } + user_input[0] = '\0'; + } else + return; + } + + while (!date_entered) { + snprintf(outstr, BUFSIZ, mesg_until_1, DATEFMT_DESC(conf.input_datefmt)); + status_mesg(outstr, ""); + if (getstring(win[STA].p, user_input, BUFSIZ, 0, 1) == GETSTRING_VALID) { + if (strlen(user_input) == 1 && strcmp(user_input, "0") == 0) { + until = 0; + date_entered = 1; + } else { + if (parse_date(user_input, conf.input_datefmt, + &year, &month, &day, calendar_get_slctd_day())) { + t = p->start; + localtime_r(&t, <); + until_date.dd = day; + until_date.mm = month; + until_date.yyyy = year; + until = date2sec(until_date, lt.tm_hour, lt.tm_min); + if (until < p->start) { + status_mesg(mesg_older, wrong_type_2); + wgetch(win[STA].p); + date_entered = 0; + } else { + date_entered = 1; + } + } else { + snprintf(outstr, BUFSIZ, mesg_wrong_2, + DATEFMT_DESC(conf.input_datefmt)); + status_mesg(mesg_wrong_1, outstr); + wgetch(win[STA].p); + date_entered = 0; + } + } + } else + return; + } + + date = calendar_get_slctd_day_sec(); + if (p->type == EVNT) { + struct event *ev = p->item.ev; + recur_event_new(ev->mesg, ev->note, ev->day, ev->id, type, freq, until, + NULL); + } else if (p->type == APPT) { + struct apoint *apt = p->item.apt; + ra = recur_apoint_new(apt->mesg, apt->note, apt->start, apt->dur, + apt->state, type, freq, until, NULL); + if (notify_bar()) + notify_check_repeated(ra); + } else { + EXIT(_("wrong item type")); + /* NOTREACHED */ + } + + interact_day_item_cut_free(REG_BLACK_HOLE); + p = day_cut_item(date, item_nb); + day_cut[REG_BLACK_HOLE].type = p->type; + day_cut[REG_BLACK_HOLE].item = p->item; + + calendar_monthly_view_cache_set_invalid(); +} + +/* Free the current cut item, if any. */ +void interact_day_item_cut_free(unsigned reg) +{ + switch (day_cut[reg].type) { + case 0: + /* No previous item, don't free anything. */ + break; + case APPT: + apoint_free(day_cut[reg].item.apt); + break; + case EVNT: + event_free(day_cut[reg].item.ev); + break; + case RECUR_APPT: + recur_apoint_free(day_cut[reg].item.rapt); + break; + case RECUR_EVNT: + recur_event_free(day_cut[reg].item.rev); + break; + } +} + +/* Copy an item, so that it can be pasted somewhere else later. */ +void interact_day_item_copy(unsigned *nb_events, unsigned *nb_apoints, + unsigned reg) +{ + const int NBITEMS = *nb_apoints + *nb_events; + + if (NBITEMS == 0 || reg == REG_BLACK_HOLE) + return; + + interact_day_item_cut_free(reg); + day_item_fork(day_get_item(apoint_hilt()), &day_cut[reg]); +} + +/* Paste a previously cut item. */ +void interact_day_item_paste(unsigned *nb_events, unsigned *nb_apoints, + unsigned reg) +{ + int item_type; + struct day_item day; + + if (reg == REG_BLACK_HOLE || !day_cut[reg].type) + return; + + day_item_fork(&day_cut[reg], &day); + item_type = day_paste_item(&day, calendar_get_slctd_day_sec()); + + calendar_monthly_view_cache_set_invalid(); + + if (item_type == EVNT || item_type == RECUR_EVNT) + (*nb_events)++; + else if (item_type == APPT || item_type == RECUR_APPT) + (*nb_apoints)++; + else + return; + + if (apoint_hilt() == 0) + apoint_hilt_increase(1); +} @@ -231,7 +231,6 @@ void io_init(const char *cfile, const char *datadir) snprintf(path_todo, BUFSIZ, "%s/" TODO_PATH_NAME, home); snprintf(path_conf, BUFSIZ, "%s/" CONF_PATH_NAME, home); snprintf(path_notes, BUFSIZ, "%s/" NOTES_DIR_NAME, home); - snprintf(path_apts, BUFSIZ, "%s/" APTS_PATH_NAME, home); snprintf(path_keys, BUFSIZ, "%s/" KEYS_PATH_NAME, home); snprintf(path_cpid, BUFSIZ, "%s/" CPID_PATH_NAME, home); snprintf(path_dpid, BUFSIZ, "%s/" DPID_PATH_NAME, home); @@ -249,43 +248,48 @@ void io_init(const char *cfile, const char *datadir) snprintf(path_dpid, BUFSIZ, "%s/" DPID_PATH, home); snprintf(path_dmon_log, BUFSIZ, "%s/" DLOG_PATH, home); snprintf(path_notes, BUFSIZ, "%s/" NOTES_DIR, home); - if (cfile == NULL) { - snprintf(path_apts, BUFSIZ, "%s/" APTS_PATH, home); + } + + if (cfile == NULL) { + if (datadir != NULL) { + snprintf(path_apts, BUFSIZ, "%s/" APTS_PATH_NAME, home); } else { - snprintf(apts_file, BUFSIZ, "%s", cfile); - strncpy(path_apts, apts_file, BUFSIZ); - /* check if the file exists, otherwise create it */ - data_file = fopen(path_apts, "r"); - if (data_file == NULL) { - printf(_("%s does not exist, create it now [y or n] ? "), path_apts); - ch = getchar(); - switch (ch) { - case 'N': - case 'n': - puts(_("aborting...\n")); - exit_calcurse(EXIT_FAILURE); - break; - - case 'Y': - case 'y': - data_file = fopen(path_apts, "w"); - if (data_file == NULL) { - perror(path_apts); - exit_calcurse(EXIT_FAILURE); - } else { - printf(_("%s successfully created\n"), path_apts); - puts(_("starting interactive mode...\n")); - } - break; - - default: - puts(_("aborting...\n")); + snprintf(path_apts, BUFSIZ, "%s/" APTS_PATH, home); + } + } else { + snprintf(apts_file, BUFSIZ, "%s", cfile); + strncpy(path_apts, apts_file, BUFSIZ); + /* check if the file exists, otherwise create it */ + data_file = fopen(path_apts, "r"); + if (data_file == NULL) { + printf(_("%s does not exist, create it now [y or n] ? "), path_apts); + ch = getchar(); + switch (ch) { + case 'N': + case 'n': + puts(_("aborting...\n")); + exit_calcurse(EXIT_FAILURE); + break; + + case 'Y': + case 'y': + data_file = fopen(path_apts, "w"); + if (data_file == NULL) { + perror(path_apts); exit_calcurse(EXIT_FAILURE); - break; + } else { + printf(_("%s successfully created\n"), path_apts); + puts(_("starting interactive mode...\n")); } + break; + + default: + puts(_("aborting...\n")); + exit_calcurse(EXIT_FAILURE); + break; } - file_close(data_file, __FILE_POS__); } + file_close(data_file, __FILE_POS__); } } @@ -913,29 +917,6 @@ void io_export_data(enum export_type type) } } -/* Draws the export format selection bar */ -void io_export_bar(void) -{ - int smlspc, spc; - - smlspc = 2; - spc = 15; - - custom_apply_attr(win[STA].p, ATTR_HIGHEST); - mvwaddstr(win[STA].p, 0, 2, "Q"); - mvwaddstr(win[STA].p, 1, 2, "I"); - mvwaddstr(win[STA].p, 0, 2 + spc, "P"); - custom_remove_attr(win[STA].p, ATTR_HIGHEST); - - mvwaddstr(win[STA].p, 0, 2 + smlspc, _("Exit")); - mvwaddstr(win[STA].p, 1, 2 + smlspc, _("Ical")); - mvwaddstr(win[STA].p, 0, 2 + spc + smlspc, _("Pcal")); - - wnoutrefresh(win[STA].p); - wmove(win[STA].p, 0, 0); - wins_doupdate(); -} - static FILE *get_import_stream(enum export_type type) { FILE *stream; @@ -56,14 +56,14 @@ static struct keydef_s keydef[NBKEYS] = { {"generic-help", "?"}, {"generic-quit", "q Q"}, {"generic-save", "s S C-s"}, - {"generic-cut", "C-x"}, - {"generic-paste", "C-v"}, + {"generic-copy", "c"}, + {"generic-paste", "p C-v"}, {"generic-change-view", "TAB"}, {"generic-import", "i I"}, {"generic-export", "x X"}, {"generic-goto", "g G"}, {"generic-other-cmd", "o O"}, - {"generic-config-menu", "c C"}, + {"generic-config-menu", "C"}, {"generic-redraw", "C-r"}, {"generic-add-appt", "C-a"}, {"generic-add-todo", "C-t"}, @@ -179,12 +179,13 @@ enum key keys_get_action(int pressed) return actions[pressed]; } -enum key keys_getch(WINDOW * win, int *count) +enum key keys_getch(WINDOW * win, int *count, int *reg) { int ch = '0'; - if (count) { + if (count && reg) { *count = 0; + *reg = 0; do { *count = *count * 10 + ch - '0'; ch = wgetch(win); @@ -193,8 +194,23 @@ enum key keys_getch(WINDOW * win, int *count) if (*count == 0) *count = 1; - } else + + if (ch == '"') { + ch = wgetch(win); + if (ch >= '1' && ch <= '9') { + *reg = ch - '1' + 1; + } + else if (ch >= 'a' && ch <= 'z') { + *reg = ch - 'a' + 10; + } + else if (ch == '_') { + *reg = REG_BLACK_HOLE; + } + ch = wgetch(win); + } + } else { ch = wgetch(win); + } switch (ch) { case KEY_RESIZE: @@ -444,7 +460,7 @@ void keys_popup_info(enum key key) _("Display hints whenever some help screens are available."); info[KEY_GENERIC_QUIT] = _("Exit from the current menu, or quit calcurse."); info[KEY_GENERIC_SAVE] = _("Save calcurse data."); - info[KEY_GENERIC_CUT] = _("Cut the item that is currently selected."); + info[KEY_GENERIC_COPY] = _("Copy the item that is currently selected."); info[KEY_GENERIC_PASTE] = _("Paste an item at the current position."); info[KEY_GENERIC_CHANGE_VIEW] = _("Select next panel in calcurse main screen."); @@ -522,7 +538,7 @@ void keys_popup_info(enum key key) #define WINCOL (col - 4) infowin = popup(WINROW, WINCOL, (row - WINROW) / 2, (col - WINCOL) / 2, keydef[key].label, info[key], 1); - keys_getch(infowin, NULL); + keys_getch(infowin, NULL, NULL); delwin(infowin); #undef WINROW #undef WINCOL diff --git a/src/llist.c b/src/llist.c index 847b795..f771ef3 100644 --- a/src/llist.c +++ b/src/llist.c @@ -112,7 +112,7 @@ llist_item_t *llist_next(llist_item_t * i) * Return the successor of a list item if it is matched by some filter * callback. Return NULL otherwise. */ -llist_item_t *llist_next_filter(llist_item_t * i, long data, +llist_item_t *llist_next_filter(llist_item_t * i, void *data, llist_fn_match_t fn_match) { if (i && i->next && fn_match(i->next->data, data)) @@ -205,14 +205,21 @@ void llist_remove(llist_t * l, llist_item_t * i) /* * Find the first item matched by some filter callback. */ -llist_item_t *llist_find_first(llist_t * l, long data, +llist_item_t *llist_find_first(llist_t * l, void *data, llist_fn_match_t fn_match) { llist_item_t *i; - for (i = l->head; i; i = i->next) { - if (fn_match(i->data, data)) - return i; + if (fn_match) { + for (i = l->head; i; i = i->next) { + if (fn_match(i->data, data)) + return i; + } + } else { + for (i = l->head; i; i = i->next) { + if (i->data == data) + return i; + } } return NULL; @@ -221,14 +228,21 @@ llist_item_t *llist_find_first(llist_t * l, long data, /* * Find the next item matched by some filter callback. */ -llist_item_t *llist_find_next(llist_item_t * i, long data, +llist_item_t *llist_find_next(llist_item_t * i, void *data, llist_fn_match_t fn_match) { if (i) { i = i->next; - for (; i; i = i->next) { - if (fn_match(i->data, data)) - return i; + if (fn_match) { + for (; i; i = i->next) { + if (fn_match(i->data, data)) + return i; + } + } else { + for (; i; i = i->next) { + if (i->data == data) + return i; + } } } @@ -238,7 +252,7 @@ llist_item_t *llist_find_next(llist_item_t * i, long data, /* * Find the nth item matched by some filter callback. */ -llist_item_t *llist_find_nth(llist_t * l, int n, long data, +llist_item_t *llist_find_nth(llist_t * l, int n, void *data, llist_fn_match_t fn_match) { llist_item_t *i; @@ -246,9 +260,16 @@ llist_item_t *llist_find_nth(llist_t * l, int n, long data, if (n < 0) return NULL; - for (i = l->head; i; i = i->next) { - if (fn_match(i->data, data) && (n-- == 0)) - return i; + if (fn_match) { + for (i = l->head; i; i = i->next) { + if (fn_match(i->data, data) && (n-- == 0)) + return i; + } + } else { + for (i = l->head; i; i = i->next) { + if ((i->data == data) && (n-- == 0)) + return i; + } } return NULL; diff --git a/src/llist.h b/src/llist.h index c795f37..a786358 100644 --- a/src/llist.h +++ b/src/llist.h @@ -48,7 +48,7 @@ struct llist { }; typedef int (*llist_fn_cmp_t) (void *, void *); -typedef int (*llist_fn_match_t) (void *, long); +typedef int (*llist_fn_match_t) (void *, void *); typedef void (*llist_fn_free_t) (void *); /* Initialization and deallocation. */ @@ -65,10 +65,10 @@ void llist_free_inner(llist_t *, llist_fn_free_t); llist_item_t *llist_first(llist_t *); llist_item_t *llist_nth(llist_t *, int); llist_item_t *llist_next(llist_item_t *); -llist_item_t *llist_next_filter(llist_item_t *, long, llist_fn_match_t); -llist_item_t *llist_find_first(llist_t *, long, llist_fn_match_t); -llist_item_t *llist_find_next(llist_item_t *, long, llist_fn_match_t); -llist_item_t *llist_find_nth(llist_t *, int, long, llist_fn_match_t); +llist_item_t *llist_next_filter(llist_item_t *, void *, llist_fn_match_t); +llist_item_t *llist_find_first(llist_t *, void *, llist_fn_match_t); +llist_item_t *llist_find_next(llist_item_t *, void *, llist_fn_match_t); +llist_item_t *llist_find_nth(llist_t *, int, void *, llist_fn_match_t); #define LLIST_FIRST(l) llist_first(l) #define LLIST_NTH(l, n) llist_nth(l, n) diff --git a/src/recur.c b/src/recur.c index 5c32bca..1c593c2 100644 --- a/src/recur.c +++ b/src/recur.c @@ -44,8 +44,6 @@ llist_ts_t recur_alist_p; llist_t recur_elist; -static struct recur_event bkp_cut_recur_event; -static struct recur_apoint bkp_cut_recur_apoint; static void free_exc(struct excp *exc) { @@ -85,71 +83,55 @@ static void exc_dup(llist_t * in, llist_t * exc) } } -void recur_event_free_bkp(void) +struct recur_event *recur_event_dup(struct recur_event *in) { - if (bkp_cut_recur_event.mesg) { - mem_free(bkp_cut_recur_event.mesg); - bkp_cut_recur_event.mesg = 0; - } - if (bkp_cut_recur_event.rpt) { - mem_free(bkp_cut_recur_event.rpt); - bkp_cut_recur_event.rpt = 0; - } - free_exc_list(&bkp_cut_recur_event.exc); - erase_note(&bkp_cut_recur_event.note); -} - -void recur_apoint_free_bkp(void) -{ - if (bkp_cut_recur_apoint.mesg) { - mem_free(bkp_cut_recur_apoint.mesg); - bkp_cut_recur_apoint.mesg = 0; - } - if (bkp_cut_recur_apoint.rpt) { - mem_free(bkp_cut_recur_apoint.rpt); - bkp_cut_recur_apoint.rpt = 0; - } - free_exc_list(&bkp_cut_recur_apoint.exc); - erase_note(&bkp_cut_recur_apoint.note); -} + EXIT_IF(!in, _("null pointer")); -static void recur_event_dup(struct recur_event *in, struct recur_event *bkp) -{ - EXIT_IF(!in || !bkp, _("null pointer")); + struct recur_event *rev = mem_malloc(sizeof(struct recur_event)); - bkp->id = in->id; - bkp->day = in->day; - bkp->mesg = mem_strdup(in->mesg); + rev->id = in->id; + rev->day = in->day; + rev->mesg = mem_strdup(in->mesg); - bkp->rpt = mem_malloc(sizeof(struct rpt)); - bkp->rpt->type = in->rpt->type; - bkp->rpt->freq = in->rpt->freq; - bkp->rpt->until = in->rpt->until; + rev->rpt = mem_malloc(sizeof(struct rpt)); + rev->rpt->type = in->rpt->type; + rev->rpt->freq = in->rpt->freq; + rev->rpt->until = in->rpt->until; - exc_dup(&bkp->exc, &in->exc); + exc_dup(&rev->exc, &in->exc); if (in->note) - bkp->note = mem_strdup(in->note); + rev->note = mem_strdup(in->note); + else + rev->note = NULL; + + return rev; } -static void recur_apoint_dup(struct recur_apoint *in, struct recur_apoint *bkp) +struct recur_apoint *recur_apoint_dup(struct recur_apoint *in) { - EXIT_IF(!in || !bkp, _("null pointer")); + EXIT_IF(!in, _("null pointer")); + + struct recur_apoint *rapt = mem_malloc(sizeof(struct recur_apoint)); - bkp->start = in->start; - bkp->dur = in->dur; - bkp->state = in->state; - bkp->mesg = mem_strdup(in->mesg); + rapt->start = in->start; + rapt->dur = in->dur; + rapt->state = in->state; + rapt->mesg = mem_strdup(in->mesg); - bkp->rpt = mem_malloc(sizeof(struct rpt)); - bkp->rpt->type = in->rpt->type; - bkp->rpt->freq = in->rpt->freq; - bkp->rpt->until = in->rpt->until; + rapt->rpt = mem_malloc(sizeof(struct rpt)); + rapt->rpt->type = in->rpt->type; + rapt->rpt->freq = in->rpt->freq; + rapt->rpt->until = in->rpt->until; - exc_dup(&bkp->exc, &in->exc); + exc_dup(&rapt->exc, &in->exc); if (in->note) - bkp->note = mem_strdup(in->note); + rapt->note = mem_strdup(in->note); + else + rapt->note = NULL; + + return rapt; } void recur_apoint_llist_init(void) @@ -157,7 +139,7 @@ void recur_apoint_llist_init(void) LLIST_TS_INIT(&recur_alist_p); } -static void recur_apoint_free(struct recur_apoint *rapt) +void recur_apoint_free(struct recur_apoint *rapt) { mem_free(rapt->mesg); if (rapt->note) @@ -168,7 +150,7 @@ static void recur_apoint_free(struct recur_apoint *rapt) mem_free(rapt); } -static void recur_event_free(struct recur_event *rev) +void recur_event_free(struct recur_event *rev) { mem_free(rev->mesg); if (rev->note) @@ -550,9 +532,9 @@ static long diff_years(struct tm lt_start, struct tm lt_end) return lt_end.tm_year - lt_start.tm_year; } -static int exc_inday(struct excp *exc, long day_start) +static int exc_inday(struct excp *exc, long *day_start) { - return (exc->st >= day_start && exc->st < day_start + DAYINSEC); + return (exc->st >= *day_start && exc->st < *day_start + DAYINSEC); } /* @@ -626,7 +608,7 @@ recur_item_find_occurrence(long item_start, long item_dur, llist_t * item_exc, lt_item_day.tm_isdst = lt_day.tm_isdst; t = mktime(<_item_day); - if (LLIST_FIND_FIRST(item_exc, t, exc_inday)) + if (LLIST_FIND_FIRST(item_exc, &t, exc_inday)) return 0; if (rpt_until != 0 && t > rpt_until) @@ -678,16 +660,36 @@ recur_item_inday(long item_start, long item_dur, llist_t * item_exc, rpt_freq, rpt_until, day_start, NULL); } -unsigned recur_apoint_inday(struct recur_apoint *rapt, long day_start) +unsigned recur_apoint_inday(struct recur_apoint *rapt, long *day_start) { return recur_item_inday(rapt->start, rapt->dur, &rapt->exc, rapt->rpt->type, - rapt->rpt->freq, rapt->rpt->until, day_start); + rapt->rpt->freq, rapt->rpt->until, *day_start); } -unsigned recur_event_inday(struct recur_event *rev, long day_start) +unsigned recur_event_inday(struct recur_event *rev, long *day_start) { return recur_item_inday(rev->day, DAYINSEC, &rev->exc, rev->rpt->type, - rev->rpt->freq, rev->rpt->until, day_start); + rev->rpt->freq, rev->rpt->until, *day_start); +} + +/* Add an exception to a recurrent event. */ +void +recur_event_add_exc(struct recur_event *rev, long date) +{ + recur_add_exc(&rev->exc, date); +} + +/* Add an exception to a recurrent appointment. */ +void +recur_apoint_add_exc(struct recur_apoint *rapt, long date) +{ + int need_check_notify = 0; + + if (notify_bar()) + need_check_notify = notify_same_recur_item(rapt); + recur_add_exc(&rapt->exc, date); + if (need_check_notify) + notify_check_next_app(0); } /* @@ -695,40 +697,14 @@ unsigned recur_event_inday(struct recur_event *rev, long day_start) * or delete only one occurence of the recurrent event. */ void -recur_event_erase(long start, unsigned num, unsigned delete_whole, - enum eraseflg flag) +recur_event_erase(struct recur_event *rev) { - llist_item_t *i; - - i = LLIST_FIND_NTH(&recur_elist, num, start, recur_event_inday); + llist_item_t *i = LLIST_FIND_FIRST(&recur_elist, rev, NULL); if (!i) EXIT(_("event not found")); - struct recur_event *rev = LLIST_GET_DATA(i); - - if (delete_whole) { - switch (flag) { - case ERASE_FORCE_ONLY_NOTE: - erase_note(&rev->note); - break; - case ERASE_CUT: - recur_event_free_bkp(); - recur_event_dup(rev, &bkp_cut_recur_event); - erase_note(&rev->note); - /* FALLTHROUGH */ - default: - LLIST_REMOVE(&recur_elist, i); - mem_free(rev->mesg); - if (rev->rpt) { - mem_free(rev->rpt); - rev->rpt = 0; - } - free_exc_list(&rev->exc); - mem_free(rev); - break; - } - } else - recur_add_exc(&rev->exc, start); + + LLIST_REMOVE(&recur_elist, i); } /* @@ -736,184 +712,23 @@ recur_event_erase(long start, unsigned num, unsigned delete_whole, * or delete only one occurence of the recurrent appointment. */ void -recur_apoint_erase(long start, unsigned num, unsigned delete_whole, - enum eraseflg flag) +recur_apoint_erase(struct recur_apoint *rapt) { - llist_item_t *i; - int need_check_notify = 0; + LLIST_TS_LOCK(&recur_alist_p); - i = LLIST_TS_FIND_NTH(&recur_alist_p, num, start, recur_apoint_inday); + llist_item_t *i = LLIST_TS_FIND_FIRST(&recur_alist_p, rapt, NULL); + int need_check_notify = 0; if (!i) EXIT(_("appointment not found")); - struct recur_apoint *rapt = LLIST_GET_DATA(i); - LLIST_TS_LOCK(&recur_alist_p); - if (notify_bar() && flag != ERASE_FORCE_ONLY_NOTE) + if (notify_bar()) need_check_notify = notify_same_recur_item(rapt); - if (delete_whole) { - switch (flag) { - case ERASE_FORCE_ONLY_NOTE: - erase_note(&rapt->note); - break; - case ERASE_CUT: - recur_apoint_free_bkp(); - recur_apoint_dup(rapt, &bkp_cut_recur_apoint); - erase_note(&rapt->note); - /* FALLTHROUGH */ - default: - LLIST_TS_REMOVE(&recur_alist_p, i); - mem_free(rapt->mesg); - if (rapt->rpt) { - mem_free(rapt->rpt); - rapt->rpt = 0; - } - free_exc_list(&rapt->exc); - mem_free(rapt); - if (need_check_notify) - notify_check_next_app(0); - break; - } - } else { - recur_add_exc(&rapt->exc, start); - if (need_check_notify) - notify_check_next_app(0); - } - LLIST_TS_UNLOCK(&recur_alist_p); -} - -/* - * Ask user for repetition characteristics: - * o repetition type: daily, weekly, monthly, yearly - * o repetition frequence: every X days, weeks, ... - * o repetition end date - * and then delete the selected item to recreate it as a recurrent one - */ -void recur_repeat_item(void) -{ - struct tm lt; - time_t t; - int date_entered = 0; - int year = 0, month = 0, day = 0; - struct date until_date; - char outstr[BUFSIZ]; - char user_input[BUFSIZ] = ""; - const char *msg_rpt_prefix = _("Enter the repetition type:"); - const char *msg_rpt_daily = _("(d)aily"); - const char *msg_rpt_weekly = _("(w)eekly"); - const char *msg_rpt_monthly = _("(m)onthly"); - const char *msg_rpt_yearly = _("(y)early"); - const char *msg_type_choice = _("[dwmy]"); - const char *mesg_freq_1 = _("Enter the repetition frequence:"); - const char *mesg_wrong_freq = _("The frequence you entered is not valid."); - const char *mesg_until_1 = - _("Enter the ending date: [%s] or '0' for an endless repetition"); - const char *mesg_wrong_1 = _("The entered date is not valid."); - const char *mesg_wrong_2 = - _("Possible formats are [%s] or '0' for an endless repetition"); - const char *wrong_type_1 = _("This item is already a repeated one."); - const char *wrong_type_2 = _("Press [ENTER] to continue."); - const char *mesg_older = - _("Sorry, the date you entered is older than the item start time."); - - char msg_asktype[BUFSIZ]; - snprintf(msg_asktype, BUFSIZ, "%s %s, %s, %s, %s", - msg_rpt_prefix, - msg_rpt_daily, msg_rpt_weekly, msg_rpt_monthly, msg_rpt_yearly); - - int type = 0, freq = 0; - int item_nb; - struct day_item *p; - struct recur_apoint *ra; - long until, date; - - item_nb = apoint_hilt(); - p = day_get_item(item_nb); - if (p->type != APPT && p->type != EVNT) { - status_mesg(wrong_type_1, wrong_type_2); - wgetch(win[STA].p); - return; - } + LLIST_TS_REMOVE(&recur_alist_p, i); + if (need_check_notify) + notify_check_next_app(0); - switch (status_ask_choice(msg_asktype, msg_type_choice, 4)) { - case 1: - type = RECUR_DAILY; - break; - case 2: - type = RECUR_WEEKLY; - break; - case 3: - type = RECUR_MONTHLY; - break; - case 4: - type = RECUR_YEARLY; - break; - default: - return; - } - - while (freq == 0) { - status_mesg(mesg_freq_1, ""); - if (getstring(win[STA].p, user_input, BUFSIZ, 0, 1) == GETSTRING_VALID) { - freq = atoi(user_input); - if (freq == 0) { - status_mesg(mesg_wrong_freq, wrong_type_2); - wgetch(win[STA].p); - } - user_input[0] = '\0'; - } else - return; - } - - while (!date_entered) { - snprintf(outstr, BUFSIZ, mesg_until_1, DATEFMT_DESC(conf.input_datefmt)); - status_mesg(outstr, ""); - if (getstring(win[STA].p, user_input, BUFSIZ, 0, 1) == GETSTRING_VALID) { - if (strlen(user_input) == 1 && strcmp(user_input, "0") == 0) { - until = 0; - date_entered = 1; - } else { - if (parse_date(user_input, conf.input_datefmt, - &year, &month, &day, calendar_get_slctd_day())) { - t = p->start; - localtime_r(&t, <); - until_date.dd = day; - until_date.mm = month; - until_date.yyyy = year; - until = date2sec(until_date, lt.tm_hour, lt.tm_min); - if (until < p->start) { - status_mesg(mesg_older, wrong_type_2); - wgetch(win[STA].p); - date_entered = 0; - } else { - date_entered = 1; - } - } else { - snprintf(outstr, BUFSIZ, mesg_wrong_2, - DATEFMT_DESC(conf.input_datefmt)); - status_mesg(mesg_wrong_1, outstr); - wgetch(win[STA].p); - date_entered = 0; - } - } - } else - return; - } - - date = calendar_get_slctd_day_sec(); - if (p->type == EVNT) { - recur_event_new(p->mesg, p->note, p->start, p->evnt_id, type, freq, - until, NULL); - } else if (p->type == APPT) { - ra = recur_apoint_new(p->mesg, p->note, p->start, p->appt_dur, - p->state, type, freq, until, NULL); - if (notify_bar()) - notify_check_repeated(ra); - } else { - EXIT(_("wrong item type")); - /* NOTREACHED */ - } - day_erase_item(date, item_nb, ERASE_FORCE); + LLIST_TS_UNLOCK(&recur_alist_p); } /* @@ -959,7 +774,7 @@ struct notify_app *recur_apoint_check_next(struct notify_app *app, long start, unsigned real_recur_start_time; LLIST_TS_LOCK(&recur_alist_p); - LLIST_TS_FIND_FOREACH(&recur_alist_p, app->time, recur_apoint_starts_before, + LLIST_TS_FIND_FOREACH(&recur_alist_p, &app->time, recur_apoint_starts_before, i) { struct recur_apoint *rapt = LLIST_TS_GET_DATA(i); @@ -976,102 +791,57 @@ struct notify_app *recur_apoint_check_next(struct notify_app *app, long start, return app; } -/* Returns a structure containing the selected recurrent appointment. */ -struct recur_apoint *recur_get_apoint(long date, int num) -{ - llist_item_t *i = LLIST_TS_FIND_NTH(&recur_alist_p, num, date, - recur_apoint_inday); - - if (i) - return LLIST_TS_GET_DATA(i); - - EXIT(_("item not found")); - /* NOTREACHED */ -} - -/* Returns a structure containing the selected recurrent event. */ -struct recur_event *recur_get_event(long date, int num) -{ - llist_item_t *i = LLIST_FIND_NTH(&recur_elist, num, date, - recur_event_inday); - - if (i) - return LLIST_GET_DATA(i); - - EXIT(_("item not found")); - /* NOTREACHED */ -} - /* Switch recurrent item notification state. */ -void recur_apoint_switch_notify(long date, int recur_nb) +void recur_apoint_switch_notify(struct recur_apoint *rapt) { - llist_item_t *i; - LLIST_TS_LOCK(&recur_alist_p); - i = LLIST_TS_FIND_NTH(&recur_alist_p, recur_nb, date, recur_apoint_inday); - - if (!i) - EXIT(_("item not found")); - struct recur_apoint *rapt = LLIST_TS_GET_DATA(i); rapt->state ^= APOINT_NOTIFY; - if (notify_bar()) notify_check_repeated(rapt); LLIST_TS_UNLOCK(&recur_alist_p); } -void recur_event_paste_item(void) +void recur_event_paste_item(struct recur_event *rev, long date) { - long new_start, time_shift; + long time_shift; llist_item_t *i; - new_start = date2sec(*calendar_get_slctd_day(), 0, 0); - time_shift = new_start - bkp_cut_recur_event.day; + time_shift = date - rev->day; + rev->day += time_shift; - bkp_cut_recur_event.day += time_shift; - if (bkp_cut_recur_event.rpt->until != 0) - bkp_cut_recur_event.rpt->until += time_shift; - LLIST_FOREACH(&bkp_cut_recur_event.exc, i) { + if (rev->rpt->until != 0) + rev->rpt->until += time_shift; + + LLIST_FOREACH(&rev->exc, i) { struct excp *exc = LLIST_GET_DATA(i); exc->st += time_shift; } - recur_event_new(bkp_cut_recur_event.mesg, bkp_cut_recur_event.note, - bkp_cut_recur_event.day, bkp_cut_recur_event.id, - bkp_cut_recur_event.rpt->type, - bkp_cut_recur_event.rpt->freq, - bkp_cut_recur_event.rpt->until, &bkp_cut_recur_event.exc); - recur_event_free_bkp(); + LLIST_ADD_SORTED(&recur_elist, rev, recur_event_cmp_day); } -void recur_apoint_paste_item(void) +void recur_apoint_paste_item(struct recur_apoint *rapt, long date) { - long new_start, time_shift; + long time_shift; llist_item_t *i; - new_start = date2sec(*calendar_get_slctd_day(), - get_item_hour(bkp_cut_recur_apoint.start), - get_item_min(bkp_cut_recur_apoint.start)); - time_shift = new_start - bkp_cut_recur_apoint.start; + time_shift = (date + get_item_time(rapt->start)) - rapt->start; + rapt->start += time_shift; + + if (rapt->rpt->until != 0) + rapt->rpt->until += time_shift; - bkp_cut_recur_apoint.start += time_shift; - if (bkp_cut_recur_apoint.rpt->until != 0) - bkp_cut_recur_apoint.rpt->until += time_shift; - LLIST_FOREACH(&bkp_cut_recur_event.exc, i) { + LLIST_FOREACH(&rapt->exc, i) { struct excp *exc = LLIST_GET_DATA(i); exc->st += time_shift; } - recur_apoint_new(bkp_cut_recur_apoint.mesg, bkp_cut_recur_apoint.note, - bkp_cut_recur_apoint.start, bkp_cut_recur_apoint.dur, - bkp_cut_recur_apoint.state, bkp_cut_recur_apoint.rpt->type, - bkp_cut_recur_apoint.rpt->freq, - bkp_cut_recur_apoint.rpt->until, &bkp_cut_recur_apoint.exc); + LLIST_TS_LOCK(&recur_alist_p); + LLIST_TS_ADD_SORTED(&recur_alist_p, rapt, recur_apoint_cmp_start); + LLIST_TS_UNLOCK(&recur_alist_p); if (notify_bar()) - notify_check_repeated(&bkp_cut_recur_apoint); - - recur_apoint_free_bkp(); + notify_check_repeated(rapt); } @@ -47,7 +47,7 @@ static int first = 1; static char *msgsav; /* Returns a structure containing the selected item. */ -static struct todo *todo_get_item(int item_number) +struct todo *todo_get_item(int item_number) { return LLIST_GET_DATA(LLIST_NTH(&todolist, item_number - 1)); } @@ -117,26 +117,6 @@ char *todo_saved_mesg(void) return msgsav; } -/* Request user to enter a new todo item. */ -void todo_new_item(void) -{ - int ch = 0; - const char *mesg = _("Enter the new ToDo item : "); - const char *mesg_id = - _("Enter the ToDo priority [1 (highest) - 9 (lowest)] :"); - char todo_input[BUFSIZ] = ""; - - status_mesg(mesg, ""); - if (getstring(win[STA].p, todo_input, BUFSIZ, 0, 1) == GETSTRING_VALID) { - while ((ch < '1') || (ch > '9')) { - status_mesg(mesg_id, ""); - ch = wgetch(win[STA].p); - } - todo_add(todo_input, ch - '0', NULL); - todos++; - } -} - static int todo_cmp_id(struct todo *a, struct todo *b) { /* @@ -176,27 +156,20 @@ void todo_write(struct todo *todo, FILE * f) } /* Delete a note previously attached to a todo item. */ -static void todo_delete_note_bynum(unsigned num) +void todo_delete_note(struct todo *todo) { - llist_item_t *i = LLIST_NTH(&todolist, num); - - if (!i) - EXIT(_("no such todo")); - struct todo *todo = LLIST_TS_GET_DATA(i); - if (!todo->note) EXIT(_("no note attached")); erase_note(&todo->note); } /* Delete an item from the todo linked list. */ -static void todo_delete_bynum(unsigned num) +void todo_delete(struct todo *todo) { - llist_item_t *i = LLIST_NTH(&todolist, num); + llist_item_t *i = LLIST_FIND_FIRST(&todolist, todo, NULL); if (!i) EXIT(_("no such todo")); - struct todo *todo = LLIST_TS_GET_DATA(i); LLIST_REMOVE(&todolist, i); mem_free(todo->mesg); @@ -210,57 +183,11 @@ static void todo_delete_bynum(unsigned num) * This way, it is easy to retrive its original priority if the user decides * that in fact it was not completed. */ -void todo_flag(void) +void todo_flag(struct todo *t) { - struct todo *t; - - t = todo_get_item(hilt); t->id = -t->id; } -/* Delete an item from the ToDo list. */ -void todo_delete(void) -{ - const char *del_todo_str = _("Do you really want to delete this task ?"); - const char *erase_warning = - _("This item has a note attached to it. " - "Delete (t)odo or just its (n)ote ?"); - const char *erase_choice = _("[tn]"); - const int nb_erase_choice = 2; - int answer; - - if ((todos <= 0) || - (conf.confirm_delete && (status_ask_bool(del_todo_str) != 1))) { - wins_erase_status_bar(); - return; - } - - /* This todo item doesn't have any note associated. */ - if (todo_get_item(hilt)->note == NULL) - answer = 1; - else - answer = status_ask_choice(erase_warning, erase_choice, nb_erase_choice); - - switch (answer) { - case 1: - todo_delete_bynum(hilt - 1); - todos--; - if (hilt > 1) - hilt--; - if (todos == 0) - hilt = 0; - if (hilt - first < 0) - first--; - break; - case 2: - todo_delete_note_bynum(hilt - 1); - break; - default: - wins_erase_status_bar(); - return; - } -} - /* * Returns the position into the linked list corresponding to the * given todo item. @@ -281,54 +208,30 @@ static int todo_get_position(struct todo *needle) } /* Change an item priority by pressing '+' or '-' inside TODO panel. */ -void todo_chg_priority(int action) +void todo_chg_priority(struct todo *backup, int diff) { - struct todo *backup; char backup_mesg[BUFSIZ]; int backup_id; char backup_note[MAX_NOTESIZ + 1]; - backup = todo_get_item(hilt); strncpy(backup_mesg, backup->mesg, strlen(backup->mesg) + 1); backup_id = backup->id; if (backup->note) strncpy(backup_note, backup->note, MAX_NOTESIZ + 1); else backup_note[0] = '\0'; - switch (action) { - case KEY_RAISE_PRIORITY: - if (backup_id > 1) - backup_id--; - else - return; - break; - case KEY_LOWER_PRIORITY: - if (backup_id > 0 && backup_id < 9) - backup_id++; - else - return; - break; - default: - EXIT(_("no such action")); - /* NOTREACHED */ - } - todo_delete_bynum(hilt - 1); + backup_id += diff; + if (backup_id < 1) + backup_id = 1; + else if (backup_id > 9) + backup_id = 9; + + todo_delete(todo_get_item(hilt)); backup = todo_add(backup_mesg, backup_id, backup_note); hilt = todo_get_position(backup); } -/* Edit the description of an already existing todo item. */ -void todo_edit_item(void) -{ - struct todo *i; - const char *mesg = _("Enter the new ToDo description :"); - - status_mesg(mesg, ""); - i = todo_get_item(hilt); - updatestring(win[STA].p, &i->mesg, 0, 1); -} - /* Display todo items in the corresponding panel. */ static void display_todo_item(int incolor, char *msg, int prio, int note, int width, int y, @@ -397,9 +300,8 @@ void todo_update_panel(int which_pan) /* Draw the scrollbar if necessary. */ if (todos > max_items) { - float ratio = ((float)max_items) / ((float)todos); - int sbar_length = (int)(ratio * (max_items + 1)); - int highend = (int)(ratio * first); + int sbar_length = max_items * (max_items + 1) / todos; + int highend = max_items * first / todos; unsigned hilt_bar = (which_pan == TOD) ? 1 : 0; int sbar_top = highend + title_lines; @@ -413,48 +315,18 @@ void todo_update_panel(int which_pan) } /* Attach a note to a todo */ -void todo_edit_note(const char *editor) +void todo_edit_note(struct todo *i, const char *editor) { - struct todo *i = todo_get_item(hilt); edit_note(&i->note, editor); } /* View a note previously attached to a todo */ -void todo_view_note(const char *pager) +void todo_view_note(struct todo *i, const char *pager) { - struct todo *i = todo_get_item(hilt); view_note(i->note, pager); } -/* Pipe a todo item to an external program. */ -void todo_pipe_item(void) -{ - char cmd[BUFSIZ] = ""; - char const *arg[] = { cmd, NULL }; - int pout; - int pid; - FILE *fpout; - struct todo *todo; - - status_mesg(_("Pipe item to external command:"), ""); - if (getstring(win[STA].p, cmd, BUFSIZ, 0, 1) != GETSTRING_VALID) - return; - - wins_prepare_external(); - if ((pid = shell_exec(NULL, &pout, *arg, arg))) { - fpout = fdopen(pout, "w"); - - todo = todo_get_item(hilt); - todo_write(todo, fpout); - - fclose(fpout); - child_wait(NULL, &pout, pid); - press_any_key(); - } - wins_unprepare_external(); -} - -static void todo_free(struct todo *todo) +void todo_free(struct todo *todo) { mem_free(todo->mesg); erase_note(&todo->note); diff --git a/src/utils.c b/src/utils.c index da8eade..60b0278 100644 --- a/src/utils.c +++ b/src/utils.c @@ -97,15 +97,15 @@ void exit_calcurse(int status) void free_user_data(void) { + unsigned i; + day_free_list(); event_llist_free(); - event_free_bkp(); apoint_llist_free(); - apoint_free_bkp(); recur_apoint_llist_free(); recur_event_llist_free(); - recur_apoint_free_bkp(); - recur_event_free_bkp(); + for (i = 0; i <= 37; i++) + interact_day_item_cut_free(i); todo_free_list(); notify_free_app(); } @@ -501,8 +501,8 @@ draw_scrollbar(WINDOW * win, int y, int x, int length, * long to fit in its corresponding panel window. */ void -item_in_popup(const char *saved_a_start, const char *saved_a_end, - const char *msg, const char *pop_title) +item_in_popup(const char *a_start, const char *a_end, const char *msg, + const char *pop_title) { WINDOW *popup_win, *pad; const int margin_left = 4, margin_top = 4; @@ -511,9 +511,9 @@ item_in_popup(const char *saved_a_start, const char *saved_a_end, pad = newpad(padl, padw); popup_win = popup(winl, winw, 1, 2, pop_title, NULL, 1); - if (strcmp(pop_title, _("Appointment")) == 0) { - mvwprintw(popup_win, margin_top, margin_left, "- %s -> %s", - saved_a_start, saved_a_end); + if (a_start && a_end) { + mvwprintw(popup_win, margin_top, margin_left, "- %s -> %s", a_start, + a_end); } mvwaddstr(pad, 0, margin_left, msg); wmove(win[STA].p, 0, 0); @@ -751,9 +751,11 @@ int parse_time(const char *string, unsigned *hour, unsigned *minute) if (*p == ':') { if ((++n) > 1) return 0; - } else if ((*p >= '0') && (*p <= '9')) + } else if ((*p >= '0') && (*p <= '9')) { + if ((n == 0) && (p == (string + 2)) && *(p + 1)) + n++; in[n] = in[n] * 10 + (int)(*p - '0'); - else + } else return 0; } @@ -301,9 +301,8 @@ void wins_scrollwin_display(struct scrollwin *sw) const int visible_lines = sw->win.h - sw->pad.y - 1; if (sw->total_lines > visible_lines) { - float ratio = ((float)visible_lines) / ((float)sw->total_lines); - int sbar_length = (int)(ratio * visible_lines); - int highend = (int)(ratio * sw->first_visible_line); + int sbar_length = visible_lines * visible_lines / sw->total_lines; + int highend = visible_lines * sw->first_visible_line / sw->total_lines; int sbar_top = highend + sw->pad.y + 1; if ((sbar_top + sbar_length) > sw->win.h - 1) @@ -637,7 +636,7 @@ void wins_status_bar(void) struct binding help = { _("Help"), KEY_GENERIC_HELP }; struct binding quit = { _("Quit"), KEY_GENERIC_QUIT }; struct binding save = { _("Save"), KEY_GENERIC_SAVE }; - struct binding cut = { _("Cut"), KEY_GENERIC_CUT }; + struct binding copy = { _("Copy"), KEY_GENERIC_COPY }; struct binding paste = { _("Paste"), KEY_GENERIC_PASTE }; struct binding chgvu = { _("Chg Win"), KEY_GENERIC_CHANGE_VIEW }; struct binding import = { _("Import"), KEY_GENERIC_IMPORT }; @@ -688,7 +687,7 @@ void wins_status_bar(void) &help, &quit, &save, &chgvu, &import, &export, &add, &del, &edit, &view, &pipe, &draw, &rept, &flag, &enote, &vnote, &up, &down, &gpday, &gnday, &gpweek, &gnweek, &gpmonth, &gnmonth, &gpyear, &gnyear, &togo, &today, - &conf, &appt, &todo, &cut, &paste + &conf, &appt, &todo, ©, &paste }; struct binding *bindings_todo[] = { diff --git a/test/Makefile.am b/test/Makefile.am index 85c8a1d..354a46e 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -15,7 +15,8 @@ TESTS = \ range-003.sh \ appointment-001.sh \ next-001.sh \ - search-001.sh + search-001.sh \ + bug-002.sh TESTS_ENVIRONMENT = \ CALCURSE='$(top_builddir)/src/calcurse' \ diff --git a/test/bug-002.sh b/test/bug-002.sh new file mode 100755 index 0000000..4784b59 --- /dev/null +++ b/test/bug-002.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ "$1" = 'actual' ]; then + "$CALCURSE" --read-only -D "$DATA_DIR"/ -c "$DATA_DIR/apts-bug-002" \ + -d05/03/2012 +elif [ "$1" = 'expected' ]; then + cat <<EOD +05/03/12: + - 10:45 -> 12:45 + Quantum Mechanics + - 18:30 -> 21:30 + German Class +EOD +else + ./run-test "$0" +fi diff --git a/test/data/apts b/test/data/apts index 33de89b..c21bf24 100644 --- a/test/data/apts +++ b/test/data/apts @@ -658,3 +658,144 @@ 04/27/2032 [1] Cringing Osborne 05/26/2032 [1] Confine lames 08/03/2032 [1] Ceremonial straw's antelope's Mercer Kathiawar's +01/01/1902 [1] Swastikas seeking +01/01/1902 [1] Elongate wallpaper's midterms classify +01/01/1902 [1] Seedy locoweed persecutor +01/01/1902 [1] Acidifies flack's evaporating +01/01/1902 [1] Aniakchak Pantagruel +01/01/1902 [1] Imperishables +01/01/1902 [1] Stuff hysteresis +01/01/1902 [1] Area +01/01/1902 [1] Brandished eyrie cloying emcees +01/01/1902 [1] Exceptionable +01/01/1902 [1] Acanthi kinked hardtack's mumps +01/01/1902 [1] Cesspool's murdered cod's Washingtonians +01/01/1902 [1] Snow +01/01/1902 [1] Kibitz subcontinent hogwash's displaying quarto +01/01/1902 [1] Mischievousness's species adultery's petrochemicals Remus +01/01/1902 [1] Insecure Taiyuan Chungking's Tm's +01/01/1902 [1] Minuscules pompadours fourfold incognito +01/01/1902 [1] Geography's Delaney's +01/01/1902 [1] Skilled bastardized dormers Buckingham munitions +01/01/1902 [1] Also +01/01/1902 [1] Marquis's +01/01/1902 [1] Malamud's +01/01/1902 [1] Gilda +01/01/1902 [1] Roe's apace disinfectants metered spinals +01/01/1902 [1] Locals goutiest Gomulka's +01/01/1902 [1] Surgery's apple covertly +01/01/1902 [1] Shantung Earlene pillage leer complainant's +01/01/1902 [1] Bach's +01/01/1902 [1] Luisa's Chimborazo's shuffleboard's +01/01/1902 [1] Tiffed Garcia Elton's +01/01/1902 [1] Peculiarities Jewishnesses attenuate +01/01/1902 [1] Wallpapered tampers Dhaka transgression's alohas +01/01/1902 [1] Roam psycho's +01/01/1902 [1] Automobiles beguiles +01/01/1902 [1] Dumfounds medial lark's +01/01/1902 [1] Haggler's ablative safeguard +01/01/1902 [1] Broomsticks +01/01/1902 [1] Therapist cooker's flutes He's +01/01/1902 [1] Bonny strode Pasteur's inconsequentially Gamble's +01/01/1902 [1] Corporate +01/01/1902 [1] Lina's +01/01/1902 [1] Kodachrome wicks callus's Genaro's +01/01/1902 [1] Brokers horsefly repudiating knob +01/01/1902 [1] Reflecting championing stringent Talmudic +01/01/1902 [1] Noncommercial employes +01/01/1902 [1] Competitively +01/01/1902 [1] Garish duplicator edible battery's mock +01/01/1902 [1] Flocks subornation's trawlers naming's +01/01/1902 [1] Gruffness Gethsemane +01/01/1902 [1] Absalom humankind editorship +01/01/1902 [1] Ampule's Orinoco's nontransferable misspends +01/01/1902 [1] Each Moselle's discussants flashlight's +01/01/1902 [1] Electron's reproaches picker grayer +01/01/1902 [1] Armrests Jamestown's nuke's motif undertaker +01/01/1902 [1] Navigation's +01/01/1902 [1] Pearl's Morin telescopes Emanuel's +01/01/1902 [1] Mutating postnatal Tate familiarize discomfort +01/01/1902 [1] Offsets debits +01/01/1902 [1] Institutes's Canton susceptibility's hankie's +01/01/1902 [1] Haleakala's Goldie's Set +01/01/1902 [1] Hypocrite's bridal populars +01/01/1902 [1] Lankiness +01/01/1902 [1] Rinds weekdays +01/01/1902 [1] Win hydrangea's display pelvic yukking +01/01/1902 [1] Homer's strafes +01/01/1902 [1] Rigor's sociopaths bashing outwore catalepsy +01/01/1902 [1] Montaigne loophole +01/01/1902 @ 00:01 -> 01/02/1902 @ 09:18 |Calibrator's +01/01/1902 @ 05:03 -> 01/06/1902 @ 02:46 |Refresh prepackaged wieners +01/01/1902 @ 01:58 -> 01/06/1902 @ 07:39 |Strontium's +01/01/1902 @ 06:11 -> 01/01/1902 @ 10:15 |Abets reject pullbacks finaglers unroll +01/01/1902 @ 02:55 -> 01/01/1902 @ 17:43 |Monterrey apprehensive Lonnie's +01/01/1902 @ 03:19 -> 01/03/1902 @ 16:25 |Gill machination geranium's fathomless extraordinary +01/01/1902 @ 06:15 -> 01/02/1902 @ 06:07 |Protruded vanguard +01/01/1902 @ 02:31 -> 01/01/1902 @ 13:18 |Expo +01/01/1902 @ 06:11 -> 01/05/1902 @ 10:50 |Placebos hugeness flailing ironing's +01/01/1902 @ 07:40 -> 01/02/1902 @ 23:52 |Septembers astuter Jarvis caliper +01/01/1902 @ 06:51 -> 01/05/1902 @ 07:33 |Asthma +01/01/1902 @ 09:01 -> 01/04/1902 @ 14:26 |Monograph's +01/01/1902 @ 00:37 -> 01/01/1902 @ 00:53 |Portal's Leach's Sara Asiatic Holly +01/01/1902 @ 02:05 -> 01/02/1902 @ 00:41 |Yesteryear's +01/01/1902 @ 06:52 -> 01/05/1902 @ 13:44 |Colluding steamrolled +01/01/1902 @ 06:28 -> 01/06/1902 @ 07:57 |Incubuses flat prison Ryukyu's +01/01/1902 @ 06:04 -> 01/02/1902 @ 09:50 |Synods +01/01/1902 @ 05:17 -> 01/01/1902 @ 20:13 |Eucalyptus's Araby +01/01/1902 @ 04:12 -> 01/02/1902 @ 11:28 |Superstitious +01/01/1902 @ 03:07 -> 01/02/1902 @ 14:53 |Chanted pumice's scalding prier +01/01/1902 @ 01:26 -> 01/05/1902 @ 11:35 |Beethoven materialism signposting bucktoothed +01/01/1902 @ 05:31 -> 01/02/1902 @ 11:34 |Isolated affair ritual's Hanukkahs Riel +01/01/1902 @ 03:26 -> 01/02/1902 @ 15:16 |Supernumeraries incontrovertibly embolden iterate +01/01/1902 @ 07:17 -> 01/05/1902 @ 00:07 |Federals spanner +01/01/1902 @ 07:02 -> 01/03/1902 @ 22:41 |Unzipped earthing alleyways bankers +01/01/1902 @ 08:24 -> 01/03/1902 @ 20:18 |Diesel ferrules Valkyrie's +01/01/1902 @ 03:32 -> 01/01/1902 @ 04:06 |Winch +01/01/1902 @ 09:01 -> 01/04/1902 @ 09:21 |Smartens promulgates uncharted McIntosh's +01/01/1902 @ 00:59 -> 01/05/1902 @ 04:39 |Ho Nikolayev succumbing observances +01/01/1902 @ 07:03 -> 01/02/1902 @ 11:52 |Kiwanis's Iceland +01/01/1902 @ 08:13 -> 01/03/1902 @ 07:30 |Bawdily anviled crayfishes neuters +01/01/1902 @ 01:46 -> 01/01/1902 @ 18:09 |Blenheim +01/01/1902 @ 01:02 -> 01/04/1902 @ 12:06 |Posse overspreads psalm lamebrain's primps +01/01/1902 @ 02:16 -> 01/04/1902 @ 13:18 |Sass organism's horses Melanesian +01/01/1902 @ 09:01 -> 01/03/1902 @ 04:00 |Commons's +01/01/1902 @ 06:16 -> 01/05/1902 @ 05:33 |Bungle head radiation's +01/01/1902 @ 03:31 -> 01/03/1902 @ 05:20 |Psycho's +01/01/1902 @ 06:58 -> 01/03/1902 @ 02:04 |Nomadic Gewürztraminer overrules +01/01/1902 @ 07:25 -> 01/02/1902 @ 11:22 |Crematory amorphousness +01/01/1902 @ 08:18 -> 01/04/1902 @ 11:42 |Charity's +01/01/1902 @ 05:19 -> 01/04/1902 @ 22:02 |Fronde petrochemical's capitalistic +01/01/1902 @ 02:09 -> 01/02/1902 @ 14:02 |Concurs windowed +01/01/1902 @ 03:20 -> 01/03/1902 @ 10:01 |Condillac +01/01/1902 @ 04:28 -> 01/02/1902 @ 09:33 |Carrie cued melodramatics +01/01/1902 @ 06:55 -> 01/02/1902 @ 03:06 |Ferocity +01/01/1902 @ 08:42 -> 01/01/1902 @ 20:57 |Simenon Kojak amening plagiarist +01/01/1902 @ 08:30 -> 01/05/1902 @ 02:27 |Marshall +01/01/1902 @ 05:38 -> 01/03/1902 @ 22:47 |Hokkaido's diseases +01/01/1902 @ 07:21 -> 01/02/1902 @ 04:40 |Senility's +01/01/1902 @ 03:29 -> 01/03/1902 @ 23:54 |Albumin altimeters Senghor's +01/01/1902 @ 04:46 -> 01/03/1902 @ 12:02 |Lynnette Zane kimono's backlash +01/01/1902 @ 05:32 -> 01/04/1902 @ 23:07 |Interfaced Hepplewhite slipped +01/01/1902 @ 07:28 -> 01/02/1902 @ 19:34 |Discretion bauble varsity's +01/01/1902 @ 06:18 -> 01/05/1902 @ 02:47 |Damned +01/01/1902 @ 01:43 -> 01/04/1902 @ 22:12 |Doting +01/01/1902 @ 03:23 -> 01/03/1902 @ 12:31 |Access Yang bethinks vectored broad +01/01/1902 @ 02:50 -> 01/03/1902 @ 20:32 |Tasseled +01/01/1902 @ 06:52 -> 01/03/1902 @ 10:06 |Ventriloquist's indisputable squats Fenian's slowdown's +01/01/1902 @ 06:31 -> 01/04/1902 @ 21:25 |Learning +01/01/1902 @ 00:44 -> 01/01/1902 @ 14:29 |Blondes Sasquatch cablecasted +01/01/1902 @ 06:16 -> 01/04/1902 @ 20:13 |Papillae hairpin ailerons +01/01/1902 @ 00:22 -> 01/02/1902 @ 11:54 |Menses enrichment afloat failed incorruptible +01/01/1902 @ 08:13 -> 01/01/1902 @ 17:50 |Motown's factors disappearing +01/01/1902 @ 08:40 -> 01/06/1902 @ 04:00 |Observable parleys industrialization Cambrian boxwood's +01/01/1902 @ 04:56 -> 01/03/1902 @ 00:14 |Summer Mujib humbles fatherless foretelling +01/01/1902 @ 00:08 -> 01/02/1902 @ 20:46 |Binnacles +01/01/1902 @ 04:38 -> 01/03/1902 @ 00:50 |Packard's +01/01/1902 @ 08:49 -> 01/03/1902 @ 12:10 |Hypnotist reappraisal rehiring Castaneda +01/01/1902 @ 02:26 -> 01/06/1902 @ 03:30 |Jataka backwards +01/01/1902 @ 00:07 -> 01/05/1902 @ 01:15 |Zeno Goldberg's Iberia's truants coiffured +01/01/1902 @ 00:59 -> 01/04/1902 @ 19:16 |Lounges +01/01/1902 @ 00:05 -> 01/03/1902 @ 13:11 |Heisenberg Jewries hookier misfortunes auspiciousness +01/01/1902 @ 08:02 -> 01/01/1902 @ 11:59 |District +01/01/1902 @ 02:54 -> 01/05/1902 @ 06:18 |Grin menstruation's diff --git a/test/data/apts-bug-002 b/test/data/apts-bug-002 new file mode 100644 index 0000000..6ced520 --- /dev/null +++ b/test/data/apts-bug-002 @@ -0,0 +1,2 @@ +03/22/2012 @ 18:30 -> 03/22/2012 @ 21:30 {1W -> 06/21/2012} |German Class +04/19/2012 @ 10:45 -> 04/19/2012 @ 12:45 {1W -> 05/06/2012} |Quantum Mechanics |