diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | README | 18 | ||||
-rwxr-xr-x | autogen.sh | 156 | ||||
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | doc/calcurse.1.txt | 11 | ||||
-rw-r--r-- | doc/manual.txt | 26 | ||||
-rw-r--r-- | po/Makevars | 41 | ||||
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | po/calcurse.pot | 380 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/apoint.c | 327 | ||||
-rw-r--r-- | src/args.c | 206 | ||||
-rw-r--r-- | src/calcurse.c | 819 | ||||
-rw-r--r-- | src/calcurse.h | 178 | ||||
-rw-r--r-- | src/calendar.c | 38 | ||||
-rw-r--r-- | src/config.c | 31 | ||||
-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 | 209 | ||||
-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 | 171 | ||||
-rw-r--r-- | src/utils.c | 24 | ||||
-rw-r--r-- | src/vars.c | 2 | ||||
-rw-r--r-- | src/wins.c | 130 | ||||
-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 |
35 files changed, 2707 insertions, 2740 deletions
@@ -3,7 +3,6 @@ ABOUT-NLS Makefile Makefile.in Makefile.in.in -Makevars aclocal.m4 configure depcomp @@ -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,9 @@ # (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" +autopoint --force +aclocal -I m4 +autoheader +automake --foreign --copy --add-missing +autoconf diff --git a/configure.ac b/configure.ac index 33b72ca..e32b027 100644 --- a/configure.ac +++ b/configure.ac @@ -8,6 +8,7 @@ AC_INIT([calcurse], AM_INIT_AUTOMAKE #m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) AM_GNU_GETTEXT([external]) +AM_GNU_GETTEXT_VERSION([0.14.1]) AC_CONFIG_SRCDIR([src/calcurse.c]) AC_CONFIG_HEADER([config.h]) #------------------------------------------------------------------------------- @@ -139,10 +140,6 @@ AC_MSG_CHECKING([if memory debug should be used]) AC_MSG_RESULT($memdebug) AM_CONDITIONAL(CALCURSE_MEMORY_DEBUG, test x$memdebug = xyes) #------------------------------------------------------------------------------- -# xgettext options -#------------------------------------------------------------------------------- -AM_XGETTEXT_OPTION([--no-location --keyword=_ --keyword=N_]) -#------------------------------------------------------------------------------- # Create Makefiles #------------------------------------------------------------------------------- AC_OUTPUT(Makefile doc/Makefile src/Makefile test/Makefile scripts/Makefile \ 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/Makevars b/po/Makevars new file mode 100644 index 0000000..9f5b66a --- /dev/null +++ b/po/Makevars @@ -0,0 +1,41 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --no-location --keyword=_ --keyword=N_ + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = calcurse Development Team <misc@calcurse.org> + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = bugs@calcurse.org + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = 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/po/calcurse.pot b/po/calcurse.pot index ec032a0..e97e741 100644 --- a/po/calcurse.pot +++ b/po/calcurse.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: calcurse 3.0.0\n" "Report-Msgid-Bugs-To: bugs@calcurse.org\n" -"POT-Creation-Date: 2012-06-24 01:09+0200\n" +"POT-Creation-Date: 2012-11-23 22:49+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -21,46 +21,16 @@ msgstr "" msgid "null pointer" msgstr "" -msgid "Enter start time ([hh:mm]), leave blank for an all-day event : " -msgstr "" - -msgid "" -"Enter end time ([hh:mm]) or duration ([+hh:mm], [+xxxdxxhxxm] or [+mm]) : " -msgstr "" - -msgid "Enter description :" -msgstr "" - -msgid "You entered an invalid start time, should be [hh:mm]" -msgstr "" - -msgid "" -"Invalid end time/duration, should be [hh:mm], [+hh:mm], [+xxxdxxhxxm] or " -"[+mm]" -msgstr "" - -msgid "Press [Enter] to continue" -msgstr "" - -msgid "Do you really want to delete this item ?" -msgstr "" - -msgid "no such type" -msgstr "" - msgid "date error in appointment" msgstr "" -msgid "item not found" -msgstr "" - msgid "no such appointment" msgstr "" msgid "" "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" msgstr "" @@ -105,10 +75,10 @@ msgid "" "\n" "Files:\n" " -c <file>, --calendar <file>\n" -"\tspecify the calendar <file> to use (incompatible with '-D').\n" +"\tspecify the calendar <file> to use (has precedence over '-D').\n" "\n" " -D <dir>, --directory <dir>\n" -"\tspecify the data directory to use (incompatible with '-c').\n" +"\tspecify the data directory to use.\n" "\tIf not specified, the default directory is ~/.calcurse\n" "\n" "Non-interactive:\n" @@ -212,15 +182,18 @@ msgstr "" msgid "Argument for '-x' should be either 'ical' or 'pcal'\n" msgstr "" -msgid "Options '-D' and '-c' cannot be used at the same time\n" -msgstr "" - msgid "Option '-S' must be used with either '-d', '-r', '-s', '-a' or '-t'\n" msgstr "" msgid "To do :" msgstr "" +msgid "Export to (i)cal or (p)cal format?" +msgstr "" + +msgid "[ip]" +msgstr "" + msgid "Do you really want to quit ?" msgstr "" @@ -447,104 +420,18 @@ msgid "" "(Press [ENTER] to continue)" msgstr "" -msgid "Event :" -msgstr "" - -msgid "Appointment :" -msgstr "" - msgid "unknown item type" msgstr "" -msgid "Enter the new time ([hh:mm]) : " -msgstr "" - -msgid "You entered an invalid time, should be [hh:mm]" -msgstr "" - -msgid "" -"Enter new end time ([hh:mm]) or duration ([+hh:mm], [+xxxdxxhxxm] or " -"[+mm]) : " -msgstr "" - -msgid "Invalid time: start time must be before end time!" -msgstr "" - -msgid "Enter the new item description:" -msgstr "" - -msgid "Enter the new repetition type:" -msgstr "" - -msgid "(d)aily" -msgstr "" - -msgid "(w)eekly" -msgstr "" - -msgid "(m)onthly" -msgstr "" - -msgid "(y)early" -msgstr "" - -#, c-format -msgid "(currently using %s)" -msgstr "" - -msgid "[dwmy]" -msgstr "" - -msgid "The frequence you entered is not valid." -msgstr "" - -msgid "The entered date is not valid." -msgstr "" - -#, c-format -msgid "Possible formats are [%s] or '0' for an endless repetition." -msgstr "" - -msgid "Enter the new repetition frequence:" -msgstr "" - -#, c-format -msgid "Enter the new ending date: [%s] or '0'" -msgstr "" - -msgid "Description" -msgstr "" - -msgid "Repetition" -msgstr "" - -msgid "Edit: " -msgstr "" - -msgid "Start time" -msgstr "" - -msgid "End time" -msgstr "" - -msgid "This item is recurrent. Delete (a)ll occurences or just this (o)ne ?" -msgstr "" - -msgid "[ao]" -msgstr "" - -msgid "This item has a note attached to it. Delete (i)tem or just its (n)ote ?" +msgid "Event :" msgstr "" -msgid "[in]" +msgid "Appointment :" msgstr "" msgid "unknwon type" msgstr "" -msgid "Pipe item to external command:" -msgstr "" - #, c-format msgid "Could not stop daemon properly: %s\n" msgstr "" @@ -610,9 +497,6 @@ msgstr "" msgid "date error in the event\n" msgstr "" -msgid "event not found" -msgstr "" - msgid "Internal error: line too long" msgstr "" @@ -872,23 +756,19 @@ msgid "" " event next time you launch Calcurse." msgstr "" -msgid "Cut and Paste\n" +msgid "Copy and Paste\n" msgstr "" #, c-format msgid "" -"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" +"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" -"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." msgstr "" msgid "Edit Item\n" @@ -1182,6 +1062,165 @@ msgstr "" msgid "Warning: ical header malformed or wrong version number. Aborting..." msgstr "" +msgid "Enter the new time ([hh:mm] or [hhmm]) : " +msgstr "" + +msgid "Press [Enter] to continue" +msgstr "" + +msgid "You entered an invalid time, should be [hh:mm] or [hhmm]" +msgstr "" + +msgid "" +"Enter new end time ([hh:mm], [hhmm]) or duration ([+hh:mm], [+xxxdxxhxxm] or " +"[+mm]) : " +msgstr "" + +msgid "Invalid time: start time must be before end time!" +msgstr "" + +msgid "Enter the new item description:" +msgstr "" + +msgid "Enter the new repetition type:" +msgstr "" + +msgid "(d)aily" +msgstr "" + +msgid "(w)eekly" +msgstr "" + +msgid "(m)onthly" +msgstr "" + +msgid "(y)early" +msgstr "" + +#, c-format +msgid "(currently using %s)" +msgstr "" + +msgid "[dwmy]" +msgstr "" + +msgid "The frequence you entered is not valid." +msgstr "" + +msgid "The entered date is not valid." +msgstr "" + +#, c-format +msgid "Possible formats are [%s] or '0' for an endless repetition." +msgstr "" + +msgid "Enter the new repetition frequence:" +msgstr "" + +#, c-format +msgid "Enter the new ending date: [%s] or '0'" +msgstr "" + +msgid "Description" +msgstr "" + +msgid "Repetition" +msgstr "" + +msgid "Edit: " +msgstr "" + +msgid "Start time" +msgstr "" + +msgid "End time" +msgstr "" + +msgid "Pipe item to external command:" +msgstr "" + +msgid "" +"Enter start time ([hh:mm] or [hhmm]), leave blank for an all-day event : " +msgstr "" + +msgid "" +"Enter end time ([hh:mm] or [hhmm]) or duration ([+hh:mm], [+xxxdxxhxxm] or " +"[+mm]) : " +msgstr "" + +msgid "Enter description :" +msgstr "" + +msgid "You entered an invalid start time, should be [hh:mm] or [hhmm]" +msgstr "" + +msgid "" +"Invalid end time/duration, should be [hh:mm], [hhmm], [+hh:mm], " +"[+xxxdxxhxxm] or [+mm]" +msgstr "" + +msgid "Do you really want to delete this item ?" +msgstr "" + +msgid "This item is recurrent. Delete (a)ll occurences or just this (o)ne ?" +msgstr "" + +msgid "[ao]" +msgstr "" + +msgid "This item has a note attached to it. Delete (i)tem or just its (n)ote ?" +msgstr "" + +msgid "[in]" +msgstr "" + +msgid "no such type" +msgstr "" + +msgid "Enter the new ToDo item : " +msgstr "" + +msgid "Enter the ToDo priority [1 (highest) - 9 (lowest)] :" +msgstr "" + +msgid "Do you really want to delete this task ?" +msgstr "" + +msgid "This item has a note attached to it. Delete (t)odo or just its (n)ote ?" +msgstr "" + +msgid "[tn]" +msgstr "" + +msgid "Enter the new ToDo description :" +msgstr "" + +msgid "Enter the repetition type:" +msgstr "" + +msgid "Enter the repetition frequence:" +msgstr "" + +#, c-format +msgid "Enter the ending date: [%s] or '0' for an endless repetition" +msgstr "" + +#, c-format +msgid "Possible formats are [%s] or '0' for an endless repetition" +msgstr "" + +msgid "This item is already a repeated one." +msgstr "" + +msgid "Press [ENTER] to continue." +msgstr "" + +msgid "Sorry, the date you entered is older than the item start time." +msgstr "" + +msgid "wrong item type" +msgstr "" + msgid "Saving..." msgstr "" @@ -1200,9 +1239,6 @@ msgstr "" msgid "The file cannot be accessed, please enter another file name." msgstr "" -msgid "Press [ENTER] to continue." -msgstr "" - #, c-format msgid "Failed to open \"%s\", - %s\n" msgstr "" @@ -1311,12 +1347,6 @@ msgstr "" msgid "wrong export mode" msgstr "" -msgid "Ical" -msgstr "" - -msgid "Pcal" -msgstr "" - msgid "Enter the file name to import data from:" msgstr "" @@ -1432,7 +1462,7 @@ msgstr "" msgid "Save calcurse data." msgstr "" -msgid "Cut the item that is currently selected." +msgid "Copy the item that is currently selected." msgstr "" msgid "Paste an item at the current position." @@ -1709,30 +1739,10 @@ msgstr "" msgid "date error in event" msgstr "" -msgid "appointment not found" -msgstr "" - -msgid "Enter the repetition type:" -msgstr "" - -msgid "Enter the repetition frequence:" -msgstr "" - -#, c-format -msgid "Enter the ending date: [%s] or '0' for an endless repetition" -msgstr "" - -#, c-format -msgid "Possible formats are [%s] or '0' for an endless repetition" -msgstr "" - -msgid "This item is already a repeated one." -msgstr "" - -msgid "Sorry, the date you entered is older than the item start time." +msgid "event not found" msgstr "" -msgid "wrong item type" +msgid "appointment not found" msgstr "" msgid "syntax error in item date" @@ -1746,36 +1756,15 @@ msgstr "" msgid "Error setting signal #%d : %s\n" msgstr "" -msgid "Enter the new ToDo item : " -msgstr "" - -msgid "Enter the ToDo priority [1 (highest) - 9 (lowest)] :" -msgstr "" - -msgid "no such todo" -msgstr "" - msgid "no note attached" msgstr "" -msgid "Do you really want to delete this task ?" -msgstr "" - -msgid "This item has a note attached to it. Delete (t)odo or just its (n)ote ?" -msgstr "" - -msgid "[tn]" +msgid "no such todo" msgstr "" msgid "todo not found" msgstr "" -msgid "no such action" -msgstr "" - -msgid "Enter the new ToDo description :" -msgstr "" - msgid "/!\\ INTERNAL ERROR /!\\" msgstr "" @@ -1794,9 +1783,6 @@ msgstr "" msgid "error in mktime" msgstr "" -msgid "Appointment" -msgstr "" - msgid "could not convert string" msgstr "" @@ -1907,7 +1893,7 @@ msgstr "" msgid "Save" msgstr "" -msgid "Cut" +msgid "Copy" msgstr "" msgid "Paste" 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..9b9257f 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); } @@ -581,7 +332,7 @@ void apoint_update_panel(int which_pan) { int title_xpos; int bordr = 1; - int title_lines = 3; + int title_lines = conf.compact_panels ? 1 : 3; int app_width = win[APP].w - bordr; int app_length = win[APP].h - bordr - title_lines; long date; @@ -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" @@ -270,11 +269,9 @@ static void next_arg(void) static void arg_print_date(long date) { char date_str[BUFSIZ]; - time_t t; struct tm lt; - t = date; - localtime_r(&t, <); + localtime_r((time_t *)&date, <); strftime(date_str, BUFSIZ, conf.output_datefmt, <); fputs(date_str, stdout); fputs(":\n", stdout); @@ -289,123 +286,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; } /* @@ -443,10 +338,7 @@ date_arg(const char *ddate, int add_line, const char *fmt_apt, const char *fmt_rapt, const char *fmt_ev, const char *fmt_rev, regex_t * regex) { - int i; struct date day; - int numdays = 0, num_digit = 0; - int arg_len = 0; static struct tm t; time_t timer; @@ -454,25 +346,18 @@ date_arg(const char *ddate, int add_line, const char *fmt_apt, * Check (with the argument length) if a date or a number of days * was entered, and then call app_arg() to print appointments */ - arg_len = strlen(ddate); - if (arg_len <= 4) { /* a number of days was entered */ - for (i = 0; i <= arg_len - 1; i++) { - if (isdigit(ddate[i])) - num_digit++; - } - if (num_digit == arg_len) - numdays = atoi(ddate); - + if (strlen(ddate) <= 4 && is_all_digit(ddate)) { /* - * Get current date, and print appointments for each day - * in the chosen interval. app_found and add_line are used - * to format the output correctly. + * A number of days was entered. Get current date and print appointments + * for each day in the chosen interval. app_found and add_line are used to + * format the output correctly. */ timer = time(NULL); localtime_r(&timer, &t); - display_app(&t, numdays, add_line, fmt_apt, fmt_rapt, fmt_ev, fmt_rev, + display_app(&t, atoi(ddate), add_line, fmt_apt, fmt_rapt, fmt_ev, fmt_rev, regex); - } else { /* a date was entered */ + } else { + /* A date was entered. */ if (parse_date(ddate, conf.input_datefmt, (int *)&day.yyyy, (int *)&day.mm, (int *)&day.dd, NULL)) { app_arg(add_line, &day, 0, fmt_apt, fmt_rapt, fmt_ev, fmt_rev, regex); @@ -498,7 +383,7 @@ date_arg_extended(const char *startday, const char *range, int add_line, const char *fmt_apt, const char *fmt_rapt, const char *fmt_ev, const char *fmt_rev, regex_t * regex) { - int i, numdays = 1, error = 0, arg_len = 0; + int numdays = 1, error = 0; static struct tm t; time_t timer; @@ -506,13 +391,11 @@ date_arg_extended(const char *startday, const char *range, int add_line, * Check arguments and extract information */ if (range != NULL) { - arg_len = strlen(range); - for (i = 0; i <= arg_len - 1; i++) { - if (!isdigit(range[i])) - error = 1; - } - if (!error) + if (is_all_digit(range)) { numdays = atoi(range); + } else { + error = 1; + } } timer = time(NULL); localtime_r(&timer, &t); @@ -548,9 +431,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 +500,6 @@ int parse_args(int argc, char **argv) load_data++; break; case 'c': - cflag = 1; multiple_flag++; cfile = optarg; load_data++; @@ -631,7 +511,6 @@ int parse_args(int argc, char **argv) ddate = optarg; break; case 'D': - Dflag = 1; datadir = optarg; break; case 'h': @@ -741,11 +620,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); @@ -767,10 +641,10 @@ int parse_args(int argc, char **argv) non_interactive = 1; } else if (gflag) { io_init(cfile, datadir); - io_check_dir(path_dir, NULL); - io_check_dir(path_notes, NULL); - io_check_file(path_apts, NULL); - io_check_file(path_todo, NULL); + io_check_dir(path_dir); + io_check_dir(path_notes); + io_check_file(path_apts); + io_check_file(path_todo); io_load_app(); io_load_todo(); note_gc(); @@ -778,12 +652,12 @@ int parse_args(int argc, char **argv) } else if (multiple_flag) { if (load_data) { io_init(cfile, datadir); - io_check_dir(path_dir, NULL); - io_check_dir(path_notes, NULL); + io_check_dir(path_dir); + io_check_dir(path_notes); } if (iflag) { - io_check_file(path_apts, NULL); - io_check_file(path_todo, NULL); + io_check_file(path_apts); + io_check_file(path_todo); /* Get default pager in case we need to show a log file. */ vars_init(); io_load_app(); @@ -794,8 +668,8 @@ int parse_args(int argc, char **argv) non_interactive = 1; } if (xflag) { - io_check_file(path_apts, NULL); - io_check_file(path_todo, NULL); + io_check_file(path_apts); + io_check_file(path_todo); io_load_app(); io_load_todo(); io_export_data(xfmt); @@ -803,20 +677,20 @@ int parse_args(int argc, char **argv) return non_interactive; } if (tflag) { - io_check_file(path_todo, NULL); + io_check_file(path_todo); io_load_todo(); todo_arg(tnum, fmt_todo, preg); non_interactive = 1; } if (nflag) { - io_check_file(path_apts, NULL); + io_check_file(path_apts); io_load_app(); next_arg(); non_interactive = 1; } if (dflag || rflag || sflag) { - io_check_file(path_apts, NULL); - io_check_file(path_conf, NULL); + io_check_file(path_apts); + io_check_file(path_conf); io_load_app(); config_load(); /* To get output date format. */ if (dflag) @@ -828,8 +702,8 @@ int parse_args(int argc, char **argv) } else if (aflag) { struct date day; - io_check_file(path_apts, NULL); - io_check_file(path_conf, NULL); + io_check_file(path_apts); + io_check_file(path_conf); vars_init(); config_load(); /* To get output date format. */ io_load_app(); diff --git a/src/calcurse.c b/src/calcurse.c index 09baab5..c55db55 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, ""); @@ -135,7 +549,8 @@ int main(int argc, char **argv) vars_init(); wins_init(); - wins_slctd_init(); + /* Default to the calendar panel -- this is overridden later. */ + wins_slctd_set(CAL); notify_init_bar(); wins_reset_status_page(); @@ -155,7 +570,7 @@ int main(int argc, char **argv) io_startup_screen(no_data_file); } inday = *day_process_storage(0, 0, &inday); - wins_slctd_set(CAL); + wins_slctd_set(conf.default_panel); wins_update(FLAG_ALL); /* Start miscellaneous threads. */ @@ -174,363 +589,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..0feefcc 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,8 +144,10 @@ #define DAYINSEC (DAYINMIN * MININSEC) #define HOURINSEC (HOURINMIN * MININSEC) +#define MAXDAYSPERMONTH 31 + /* Calendar window. */ -#define CALHEIGHT 12 +#define CALHEIGHT 8 /* Key definitions. */ #define CTRLVAL 0x1F @@ -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 @@ -227,6 +233,15 @@ #define MAX(x,y) ((x)>(y)?(x):(y)) #define MIN(x,y) ((x)<(y)?(x):(y)) +enum win { + CAL, + APP, + TOD, + NOT, + STA, + NBWINS +}; + /* General configuration variables. */ struct conf { unsigned auto_save; @@ -234,6 +249,8 @@ struct conf { unsigned periodic_save; unsigned confirm_quit; unsigned confirm_delete; + enum win default_panel; + unsigned compact_panels; unsigned system_dialogs; unsigned progress_bar; const char *editor; @@ -308,18 +325,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 +366,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 +409,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, @@ -440,15 +460,6 @@ struct binding { enum key action; }; -enum win { - CAL, - APP, - TOD, - NOT, - STA, - NBWINS -}; - #define FLAG_CAL (1 << CAL) #define FLAG_APP (1 << APP) #define FLAG_TOD (1 << TOD) @@ -526,14 +537,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 +605,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 +614,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 +644,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 +675,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 +705,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 +729,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 *); @@ -731,13 +754,12 @@ void io_save_cal(enum save_display); void io_load_app(void); void io_load_todo(void); void io_load_keys(const char *); -void io_check_dir(char *, int *); -unsigned io_file_exist(char *); -void io_check_file(char *, int *); +int io_check_dir(const char *); +unsigned io_file_exist(const char *); +int io_check_file(const char *); 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 +779,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 +865,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 +893,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 +911,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 +923,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); @@ -1007,7 +1032,6 @@ unsigned wins_sbar_wperc(void); void wins_set_sbar_width(unsigned); void wins_sbar_winc(void); void wins_sbar_wdec(void); -void wins_slctd_init(void); enum win wins_slctd(void); void wins_slctd_set(enum win); void wins_slctd_next(void); diff --git a/src/calendar.c b/src/calendar.c index df247df..0eebd4e 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,12 +268,17 @@ 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, unsigned sunday_first) { - const int OFFY = 2 + (CALHEIGHT - 9) / 2; + const int OFFY = CALHEIGHT / 2 - (conf.compact_panels ? 3 : 1); struct date check_day; int c_day, c_day_1, day_1_sav, numdays, j; unsigned yr, mo; @@ -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) @@ -437,7 +460,7 @@ draw_weekly_view(struct window *cwin, struct date *current_day, { #define DAYSLICESNO 6 const int WCALWIDTH = 30; - const int OFFY = 2 + (CALHEIGHT - 9) / 2; + const int OFFY = CALHEIGHT / 2 - (conf.compact_panels ? 3 : 1); struct tm t; int OFFX, j, c_wday, days_to_remove, weeknum; @@ -461,7 +484,8 @@ draw_weekly_view(struct window *cwin, struct date *current_day, weeknum = ISO8601weeknum(&t); WINS_CALENDAR_LOCK; custom_apply_attr(cwin->p, ATTR_HIGHEST); - mvwprintw(cwin->p, 2, cwin->w - 9, "(# %02d)", weeknum); + mvwprintw(cwin->p, conf.compact_panels ? 0 : 2, cwin->w - 9, "(# %02d)", + weeknum); custom_remove_attr(cwin->p, ATTR_HIGHEST); WINS_CALENDAR_UNLOCK; @@ -555,8 +579,10 @@ void calendar_update_panel(struct window *cwin) calendar_store_current_date(¤t_day); WINS_CALENDAR_LOCK; - erase_window_part(cwin->p, 1, 3, cwin->w - 2, cwin->h - 2); - mvwhline(cwin->p, 2, 1, ACS_HLINE, cwin->w - 2); + erase_window_part(cwin->p, 1, conf.compact_panels ? 1 : 3, cwin->w - 2, + cwin->h - 2); + if (!conf.compact_panels) + mvwhline(cwin->p, 2, 1, ACS_HLINE, cwin->w - 2); WINS_CALENDAR_UNLOCK; sunday_first = calendar_week_begins_on_monday()? 0 : 1; diff --git a/src/config.c b/src/config.c index 06a8124..da562b2 100644 --- a/src/config.c +++ b/src/config.c @@ -59,6 +59,8 @@ static int config_parse_str(char *, const char *); static int config_serialize_str(char *, const char *); static int config_parse_calendar_view(void *, const char *); static int config_serialize_calendar_view(char *, void *); +static int config_parse_default_panel(void *, const char *); +static int config_serialize_default_panel(char *, void *); static int config_parse_first_day_of_week(void *, const char *); static int config_serialize_first_day_of_week(char *, void *); static int config_parse_color_theme(void *, const char *); @@ -84,6 +86,9 @@ static int config_serialize_input_datefmt(char *, void *); static const struct confvar confmap[] = { {"appearance.calendarview", config_parse_calendar_view, config_serialize_calendar_view, NULL}, + {"appearance.compactpanels", CONFIG_HANDLER_BOOL(conf.compact_panels)}, + {"appearance.defaultpanel", config_parse_default_panel, + config_serialize_default_panel, NULL}, {"appearance.layout", config_parse_layout, config_serialize_layout, NULL}, {"appearance.notifybar", CONFIG_HANDLER_BOOL(nbar.show)}, {"appearance.sidebarwidth", config_parse_sidebar_width, @@ -206,6 +211,20 @@ static int config_parse_calendar_view(void *dummy, const char *val) return 1; } +static int config_parse_default_panel(void *dummy, const char *val) +{ + if (!strcmp(val, "calendar")) + conf.default_panel = CAL; + else if (!strcmp(val, "appointments")) + conf.default_panel = APP; + else if (!strcmp(val, "todo")) + conf.default_panel = TOD; + else + return 0; + + return 1; +} + static int config_parse_first_day_of_week(void *dummy, const char *val) { if (!strcmp(val, "monday")) @@ -368,6 +387,18 @@ static int config_serialize_calendar_view(char *buf, void *dummy) return 1; } +static int config_serialize_default_panel(char *buf, void *dummy) +{ + if (conf.default_panel == CAL) + strcpy(buf, "calendar"); + else if (conf.default_panel == APP) + strcpy(buf, "appointments"); + else + strcpy(buf, "todo"); + + return 1; +} + static int config_serialize_first_day_of_week(char *buf, void *dummy) { if (calendar_week_begins_on_monday()) 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); @@ -520,7 +635,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); @@ -543,466 +658,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. */ @@ -1011,117 +716,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); +} @@ -151,7 +151,6 @@ static void progress_bar(progress_bar_t type, int progress) static FILE *get_export_stream(enum export_type type) { FILE *stream; - int cancel; char *home, *stream_name; const char *question = _("Choose the file used to export calcurse data:"); const char *wrong_name = @@ -169,8 +168,7 @@ static FILE *get_export_stream(enum export_type type) while (stream == NULL) { status_mesg(question, ""); - cancel = updatestring(win[STA].p, &stream_name, 0, 1); - if (cancel) { + if (updatestring(win[STA].p, &stream_name, 0, 1)) { mem_free(stream_name); return NULL; } @@ -231,7 +229,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 +246,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__); } } @@ -425,6 +427,12 @@ void io_save_cal(enum save_display display) pthread_mutex_unlock(&io_save_mutex); } +static void io_load_error(const char *filename, unsigned line, + const char *mesg) +{ + EXIT("%s:%u: %s", filename, line, mesg); +} + /* * Check what type of data is written in the appointment file, * and then load either: a new appointment, a new event, or a new @@ -441,6 +449,7 @@ void io_load_app(void) int freq; char type, state = 0L; char note[MAX_NOTESIZ + 1], *notep; + unsigned line = 0; t = time(NULL); localtime_r(&t, <); @@ -452,6 +461,7 @@ void io_load_app(void) for (;;) { LLIST_INIT(&exc); is_appointment = is_event = is_recursive = 0; + line++; c = getc(data_file); if (c == EOF) break; @@ -462,7 +472,7 @@ void io_load_app(void) */ if (fscanf(data_file, "%d / %d / %d ", &start.tm_mon, &start.tm_mday, &start.tm_year) != 3) - EXIT(_("syntax error in the item date")); + io_load_error(path_apts, line, _("syntax error in the item date")); /* Read the next character : if it is an '@' then we have * an appointment, else if it is an '[' we have en event. @@ -474,7 +484,7 @@ void io_load_app(void) else if (c == '[') is_event = 1; else - EXIT(_("no event nor appointment found")); + io_load_error(path_apts, line, _("no event nor appointment found")); /* Read the remaining informations. */ if (is_appointment) { @@ -482,14 +492,16 @@ void io_load_app(void) &start.tm_hour, &start.tm_min, &end.tm_mon, &end.tm_mday, &end.tm_year, &end.tm_hour, &end.tm_min) != 7) - EXIT(_("syntax error in item time or duration")); + io_load_error(path_apts, line, + _("syntax error in item time or duration")); } else if (is_event) { if (fscanf(data_file, " %d ", &id) != 1 || getc(data_file) != ']') - EXIT(_("syntax error in item identifier")); + io_load_error(path_apts, line, _("syntax error in item identifier")); while ((c = getc(data_file)) == ' ') ; ungetc(c, data_file); } else { - EXIT(_("wrong format in the appointment or event")); + io_load_error(path_apts, line, + _("wrong format in the appointment or event")); /* NOTREACHED */ } @@ -499,7 +511,7 @@ void io_load_app(void) if (c == '{') { is_recursive = 1; if (fscanf(data_file, " %d%c ", &freq, &type) != 2) - EXIT(_("syntax error in item repetition")); + io_load_error(path_apts, line, _("syntax error in item repetition")); c = getc(data_file); if (c == '}') { /* endless recurrent item */ @@ -509,7 +521,7 @@ void io_load_app(void) } else if (c == '-' && getc(data_file) == '>') { if (fscanf(data_file, " %d / %d / %d ", &until.tm_mon, &until.tm_mday, &until.tm_year) != 3) - EXIT(_("syntax error in item repetition")); + io_load_error(path_apts, line, _("syntax error in item repetition")); c = getc(data_file); if (c == '!') { ungetc(c, data_file); @@ -519,14 +531,15 @@ void io_load_app(void) while ((c = getc(data_file)) == ' ') ; ungetc(c, data_file); } else - EXIT(_("syntax error in item repetition")); + io_load_error(path_apts, line, _("syntax error in item repetition")); } else if (c == '!') { /* endless item with exceptions */ ungetc(c, data_file); recur_exc_scan(&exc, data_file); c = getc(data_file); until.tm_year = 0; } else { - EXIT(_("wrong format in the appointment or event")); + io_load_error(path_apts, line, + _("wrong format in the appointment or event")); /* NOTREACHED */ } } else @@ -557,7 +570,7 @@ void io_load_app(void) while ((c = getc(data_file)) == ' ') ; ungetc(c, data_file); } else - EXIT(_("syntax error in item repetition")); + io_load_error(path_apts, line, _("syntax error in item repetition")); if (is_recursive) { recur_apoint_scan(data_file, start, end, type, freq, until, notep, &exc, state); @@ -571,7 +584,8 @@ void io_load_app(void) event_scan(data_file, start, id, notep); } } else { - EXIT(_("wrong format in the appointment or event")); + io_load_error(path_apts, line, + _("wrong format in the appointment or event")); /* NOTREACHED */ } } @@ -586,17 +600,19 @@ void io_load_todo(void) int nb_tod = 0; int c, id; char buf[BUFSIZ], e_todo[BUFSIZ], note[MAX_NOTESIZ + 1]; + unsigned line = 0; data_file = fopen(path_todo, "r"); EXIT_IF(data_file == NULL, _("failed to open todo file")); for (;;) { + line++; c = getc(data_file); if (c == EOF) break; else if (c == '[') { /* new style with id */ if (fscanf(data_file, " %d ", &id) != 1 || getc(data_file) != ']') - EXIT(_("syntax error in item identifier")); + io_load_error(path_todo, line, _("syntax error in item identifier")); while ((c = getc(data_file)) == ' ') ; ungetc(c, data_file); } else { @@ -777,10 +793,10 @@ void io_load_keys(const char *pager) WARN_MSG(_("Some actions do not have any associated key bindings!")); } -void io_check_dir(char *dir, int *missing) +int io_check_dir(const char *dir) { if (read_only) - return; + return -1; errno = 0; if (mkdir(dir, 0700) != 0) { @@ -788,45 +804,46 @@ void io_check_dir(char *dir, int *missing) fprintf(stderr, _("FATAL ERROR: could not create %s: %s\n"), dir, strerror(errno)); exit_calcurse(EXIT_FAILURE); + } else { + return 1; } } else { - if (missing) - (*missing)++; + return 0; } } -unsigned io_file_exist(char *file) +unsigned io_file_exist(const char *file) { FILE *fd; - if (!file) - return 0; - - if ((fd = fopen(file, "r")) == NULL) + if (file && (fd = fopen(file, "r")) != NULL) { + fclose(fd); + return 1; + } + else { return 0; - - fclose(fd); - - return 1; + } } -void io_check_file(char *file, int *missing) +int io_check_file(const char *file) { if (read_only) - return; + return -1; errno = 0; - if (!io_file_exist(file)) { + if (io_file_exist(file)) { + return 1; + } else { FILE *fd; - if (missing) - (*missing)++; if ((fd = fopen(file, "w")) == NULL) { fprintf(stderr, _("FATAL ERROR: could not create %s: %s\n"), file, strerror(errno)); exit_calcurse(EXIT_FAILURE); } file_close(fd, __FILE_POS__); + + return 0; } } @@ -844,17 +861,15 @@ void io_check_file(char *file, int *missing) */ int io_check_data_files(void) { - int missing, missing_keys; + int missing = 0; - missing = missing_keys = 0; - errno = 0; - io_check_dir(path_dir, &missing); - io_check_dir(path_notes, &missing); - io_check_file(path_todo, &missing); - io_check_file(path_apts, &missing); - io_check_file(path_conf, &missing); - io_check_file(path_keys, &missing_keys); - if (missing_keys) { + missing += io_check_dir(path_dir) ? 0 : 1; + missing += io_check_dir(path_notes) ? 0 : 1; + missing += io_check_file(path_todo) ? 0 : 1; + missing += io_check_file(path_apts) ? 0 : 1; + missing += io_check_file(path_conf) ? 0 : 1; + + if (!io_check_file(path_keys)) { missing++; keys_dump_defaults(path_keys); } @@ -879,14 +894,13 @@ void io_startup_screen(int no_data_file) /* Export calcurse data. */ void io_export_data(enum export_type type) { - FILE *stream; + FILE *stream = NULL; const char *success = _("The data were successfully exported"); const char *enter = _("Press [ENTER] to continue"); if (type < IO_EXPORT_ICAL || type >= IO_EXPORT_NBTYPES) EXIT(_("unknown export type")); - stream = 0; switch (ui_mode) { case UI_CMDLINE: stream = stdout; @@ -913,46 +927,20 @@ 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; + FILE *stream = NULL; char *stream_name; const char *ask_fname = _("Enter the file name to import data from:"); const char *wrong_file = _("The file cannot be accessed, please enter another file name."); const char *press_enter = _("Press [ENTER] to continue."); - int cancel; - stream = NULL; stream_name = mem_malloc(BUFSIZ); memset(stream_name, 0, BUFSIZ); while (stream == NULL) { status_mesg(ask_fname, ""); - cancel = updatestring(win[STA].p, &stream_name, 0, 1); - if (cancel) { + if (updatestring(win[STA].p, &stream_name, 0, 1)) { mem_free(stream_name); return NULL; } @@ -1090,13 +1078,10 @@ void io_log_print(struct io_file *log, int line, const char *msg) void io_log_display(struct io_file *log, const char *msg, const char *pager) { - int ans; - RETURN_IF(log == NULL, _("No log file to display!")); if (ui_mode == UI_CMDLINE) { printf("\n%s [y/n] ", msg); - ans = fgetc(stdin); - if (ans == 'y') { + if (fgetc(stdin) == 'y') { const char *arg[] = { pager, log->name, NULL }; int pid; @@ -1125,9 +1110,7 @@ static pthread_t io_t_psave; /* Thread used to periodically save data. */ static void *io_psave_thread(void *arg) { - int delay; - - delay = conf.periodic_save; + int delay = conf.periodic_save; EXIT_IF(delay < 0, _("Invalid delay")); for (;;) { @@ -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, @@ -372,13 +275,16 @@ void todo_update_panel(int which_pan) llist_item_t *i; int len = win[TOD].w - 8; int num_todo = 0; - int y_offset = 3, x_offset = 1; + int title_lines = conf.compact_panels ? 1 : 3; + int y_offset = title_lines, x_offset = 1; int t_realpos = -1; - int title_lines = 3; int todo_lines = 1; int max_items = win[TOD].h - 4; int incolor = -1; + if ((int)win[TOD].h < 4) + return; + /* Print todo item in the panel. */ erase_window_part(win[TOD].p, 1, title_lines, win[TOD].w - 2, win[TOD].h - 2); LLIST_FOREACH(&todolist, i) { @@ -397,9 +303,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 +318,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; } @@ -134,6 +134,8 @@ void vars_init(void) conf.auto_save = 1; conf.auto_gc = 0; conf.periodic_save = 0; + conf.default_panel = CAL; + conf.compact_panels = 0; conf.system_dialogs = 1; conf.progress_bar = 1; strncpy(conf.output_datefmt, "%D", 3); @@ -216,12 +216,6 @@ void wins_sbar_wdec(void) sbarwidth_perc--; } -/* Initialize the selected window in calcurse's interface. */ -void wins_slctd_init(void) -{ - wins_slctd_set(CAL); -} - /* Returns an enum which corresponds to the window which is selected. */ enum win wins_slctd(void) { @@ -245,7 +239,8 @@ void wins_slctd_next(void) static void wins_init_panels(void) { - win[CAL].p = newwin(CALHEIGHT, wins_sbar_width(), win[CAL].y, win[CAL].x); + win[CAL].p = newwin(CALHEIGHT + (conf.compact_panels ? 2 : 4), + wins_sbar_width(), win[CAL].y, win[CAL].x); wins_show(win[CAL].p, _("Calendar")); win[APP].p = newwin(win[APP].h, win[APP].w, win[APP].y, win[APP].x); @@ -301,9 +296,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) @@ -363,11 +357,14 @@ void wins_show(WINDOW * win, const char *label) int width = getmaxx(win); box(win, 0, 0); - mvwaddch(win, 2, 0, ACS_LTEE); - mvwhline(win, 2, 1, ACS_HLINE, width - 2); - mvwaddch(win, 2, width - 1, ACS_RTEE); - print_in_middle(win, 1, 0, width, label); + if (!conf.compact_panels) { + mvwaddch(win, 2, 0, ACS_LTEE); + mvwhline(win, 2, 1, ACS_HLINE, width - 2); + mvwaddch(win, 2, width - 1, ACS_RTEE); + + print_in_middle(win, 1, 0, width, label); + } } /* @@ -375,6 +372,10 @@ void wins_show(WINDOW * win, const char *label) */ void wins_get_config(void) { + enum win win_master; + enum win win_slave[1]; + unsigned master_is_left; + /* Get the screen configuration */ getmaxyx(stdscr, row, col); @@ -396,88 +397,31 @@ void wins_get_config(void) win[NOT].x = 0; } - win[CAL].w = wins_sbar_width(); - win[CAL].h = CALHEIGHT; + win[CAL].h = CALHEIGHT + (conf.compact_panels ? 2 : 4); - if (layout <= 4) { /* APPOINTMENT is the biggest panel */ - win[APP].w = col - win[CAL].w; - win[APP].h = row - (win[STA].h + win[NOT].h); - win[TOD].w = win[CAL].w; + if (layout <= 4) { + win_master = APP; + win_slave[0] = ((layout - 1) % 2 == 0) ? CAL : TOD; + win_slave[1] = ((layout - 1) % 2 == 1) ? CAL : TOD; win[TOD].h = row - (win[CAL].h + win[STA].h + win[NOT].h); - } else { /* TODO is the biggest panel */ - win[TOD].w = col - win[CAL].w; - win[TOD].h = row - (win[STA].h + win[NOT].h); - win[APP].w = win[CAL].w; + } else { + win_master = TOD; + win_slave[0] = ((layout - 1) % 2 == 0) ? CAL : APP; + win_slave[1] = ((layout - 1) % 2 == 1) ? CAL : APP; win[APP].h = row - (win[CAL].h + win[STA].h + win[NOT].h); } + master_is_left = ((layout - 1) % 4 < 2); - /* defining the layout */ - switch (layout) { - case 1: - win[APP].y = 0; - win[APP].x = 0; - win[CAL].y = 0; - win[TOD].x = win[APP].w; - win[TOD].y = win[CAL].h; - win[CAL].x = win[APP].w; - break; - case 2: - win[APP].y = 0; - win[APP].x = 0; - win[TOD].y = 0; - win[TOD].x = win[APP].w; - win[CAL].x = win[APP].w; - win[CAL].y = win[TOD].h; - break; - case 3: - win[APP].y = 0; - win[TOD].x = 0; - win[CAL].x = 0; - win[CAL].y = 0; - win[APP].x = win[CAL].w; - win[TOD].y = win[CAL].h; - break; - case 4: - win[APP].y = 0; - win[TOD].x = 0; - win[TOD].y = 0; - win[CAL].x = 0; - win[APP].x = win[CAL].w; - win[CAL].y = win[TOD].h; - break; - case 5: - win[TOD].y = 0; - win[TOD].x = 0; - win[CAL].y = 0; - win[APP].y = win[CAL].h; - win[APP].x = win[TOD].w; - win[CAL].x = win[TOD].w; - break; - case 6: - win[TOD].y = 0; - win[TOD].x = 0; - win[APP].y = 0; - win[APP].x = win[TOD].w; - win[CAL].x = win[TOD].w; - win[CAL].y = win[APP].h; - break; - case 7: - win[TOD].y = 0; - win[APP].x = 0; - win[CAL].x = 0; - win[CAL].y = 0; - win[TOD].x = win[CAL].w; - win[APP].y = win[CAL].h; - break; - case 8: - win[TOD].y = 0; - win[APP].x = 0; - win[CAL].x = 0; - win[APP].y = 0; - win[TOD].x = win[CAL].w; - win[CAL].y = win[APP].h; - break; - } + win[win_master].x = master_is_left ? 0 : wins_sbar_width(); + win[win_master].y = 0; + win[win_master].w = col - wins_sbar_width(); + win[win_master].h = row - (win[STA].h + win[NOT].h); + + win[win_slave[0]].x = win[win_slave[1]].x = + master_is_left ? win[win_master].w : 0; + win[win_slave[0]].y = 0; + win[win_slave[1]].y = win[win_slave[0]].h; + win[win_slave[0]].w = win[win_slave[1]].w = wins_sbar_width(); } /* draw panel border in color */ @@ -637,7 +581,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 +632,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 |