Commit Graph
100 Commits
Author SHA1 Message Date
Lars HenriksenandLukas Fleischer dd864316c6 Recurrence rule extensions
Terms and concepts are from RFC 5545 (the iCalendar specification).

Overall design
--------------

Calcurse is extended with full support for BYMONTH, BYDAY and BYMONTHDAY
recurrence rule (rrule) parts. The three rule parts are lists of,
respectively, months, weekdays and monthdays. The lists are added to
'struct rpt' as linked lists of integers, and the data file format is
extended accordingly (details below). Load and save of the lists follow
the pattern of the existing list of exception dates, also in 'struct
rpt'.

The function recur_item_find_occurence() is split into a front-end and a
back-end. The back-end, called find_occurrence(), is the original
function extended with rrule reductions; the front-end retains the
original name and performs rrule expansions. Front-end plus back-end are
backwards compatible and require no changes in calling functions.

There is no user interface in this patch.

Data file extensions
--------------------

The BYMONTH, BYDAY and BYMONTHDAY lists are added to that part of an
item line which describes the recurrence rule (the "{...}" part). Each
list is - like the list of exception days - a space-separated string of
values identified by the initial character.  Each list is optional and,
if present, must follow the until date and precede the exception day
list. The lists must appear in order BYMONTHDAY list, BYDAY list and
BYMONTH list.

The possible list values are

- BYMONTH: m1, m2, ..., m12
- BYDAY: w0, w1, ..., w6, w7, w-7, w8, w-8, ..., w377, w-377
- BYMONTHDAY: d1, d2, ..., d31, d-1, d-2, ..., d-31

which are interpreted as (cf. RFC 5545)

- BYMONTH: January, February, ..., December.
- BYDAY: SU, MO, ..., SA, +1SU, -1SU, +1MO, -1MO, ..., +53SA, -53SA
- BYMONTHDAY: the first, the second, ..., the 31st, the last,
  the last but one, ..., the last but 30 day of the month

Examples:

Thursday, TH, is w4; Saturday, SA, is w6.

The seventh Thursday, +7TH, is w53 (7 * 7 + 4 = 53); the last but second
Saturday, -2SA, is w-20 (2 * 7 + 6 = 20); the last day of the month is
d-1.

Note that the values w-1, w-2, ..., w-6 are not used.

A recurrent appointment with a BYDAY rule part:

06/23/2019 @ 12:00 -> 06/23/2019 @ 13:00 {1W w0 w6} |every week on Sunday and Saturday

An event with a BYDAY and a BYMONTH rule part:

10/27/2019 [1] {1Y w-7 m10} every year on last Sunday in October

An event with until date, a BYMONTH rule part and an exception day:

06/23/2019 [1] {1Y -> 08/31/2021 m5 m6 m7 !07/23/2020} every year on the 23rd in May, June and July for three years, starting on Sunday, 23 June 2019, but not on 23 July 2020.

Recurrence set expansion and reduction
---------------------------------------

In calcurse a recurrence rule is a quadruple (s, d, r, e) consisting of
start, duration, repetition pattern and exception days and is
implemented as:

	(time_t start, long dur, struct rpt *rpt, llist_t *exc)

In RFC 5545 parlance, a recurrence rule defines a recurrence set
consisting of all recurrence instances (occurrences) not earlier than
start which match the rule pattern. With this concept in mind,
recur_item_find_occurremce() may be thought of as a membership function
for a recurrence set. The call

	recur_item_find_occurrence(s, d, r, e, day, occurrence)

returns true if day belongs to the recurrence set of (s, d, r, e); if so
occurrence points to the recurrence instance (the set member).

For a recurrence rule with only the basic DAYLY, WEEKLY, MONTHLY or
YEARLY type and frequency the recurrence set consists of periodically
repeated instances.  The BYxxx rule parts modify the recurrence set by
reducing or expanding it as specified by RFC 5545.

Expansion is implemented in the front-end by modifications of start
and/or frequency of the rule (s, d, r, e), often several times, in such
a way that the desired recurrence instances are included in the
recurrence set. This is possible because the front-end as the very first
thing checks for early days (day < s). When day is known not to be
early, start (s) can safely be moved backwards. Likewise, if frequency
must be changed, the front-end checks whether the frequency repetition
applies to the week, month or year of day.

Reduction is easier and is performed in the back-end along with the
existing validity checks. It consists in checking whether month, day of
month or weekday of a found occurrence is on the appropriate list.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-04-28 07:32:44 -04:00
Lars HenriksenandLukas Fleischer 23eb51456a Refactor edit of exception days
The patch contains no functional changes, but is a necessary
precondition for extensions of update_rept() (in ui-day.c) with further
recurrence rules.

The reason is that recurrence parameters must be treated as a whole: if
an edit session is cancelled at any point, no value should change, and
all parameters should remain as they were. Hence, the new values must
only be set after all of them have been determined. This was not the
case for the list of exception days, but as long as it was treated last,
it did not matter.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-04-28 07:32:44 -04:00
Lars HenriksenandLukas Fleischer bf3dba2ae2 Improve data load error reporting
The last part of loading appointments and events is performed by four
"scan" functions called from io_load_app(). Failure in this part of data
load does not use io_load_error().

The four "scan" functions are changed to return an error message on
failure and NULL otherwise (the previous return value was not used).

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-04-28 07:32:44 -04:00
Lars HenriksenandLukas Fleischer ce81c0fa63 Refactor function calls: recurrence parameters as a single argument
The recurrence parameters are type, frequency, until date and exception
list (in RFC 5545 parlance FREQ, INTERVAL, UNTIL and EXDATE's). When
these are passed in a function call, the argument list becomes long and
not very readable.  When support for extended recurrence rules is
implemented, the number of recurrence parameters increases, and function
signatures must be amended.

Solution: The "struct rpt" is extended with the exception list; any
future recurrence parameters are added here. A pointer to this structure
replaces the recurrence parameters in function calls.

Note: Each recurrent event and appoinment instance has (a pointer to) a
"struct rpt" and in addition an exception list. The latter is retained
to avoid the derived changes, and the exception list in the structure is
initialized to an empty list when the recurrent instance is created.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-04-28 07:32:44 -04:00
Lars HenriksenandLukas Fleischer 3f7bd331c8 Use "struct rpt" to shorten argument lists
Also, prepare for extension of the structure, shorten names and
rearrange comments.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-04-28 07:32:44 -04:00
Lars HenriksenandLukas Fleischer e9deb6fff3 Extend use of note file for iCal import
iCal import to an item note file is extended from DESCRIPTION to
LOCATION, COMMENT and STATUS for both events and todos.

Addresses GitHub issue #9.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-04-28 07:32:44 -04:00
Lars HenriksenandLukas Fleischer 214a761564 Accept empty DESCRIPTION in ical import
Adresses GitHub issue #274.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-04-28 07:32:44 -04:00
Lars HenriksenandLukas Fleischer 41ac4bd96f Fix missing EOL in imported note
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-03-27 13:41:07 -04:00
Lars HenriksenandLukas Fleischer e535eff904 Extend iCalendar import tests
Tests ical-010.sh and ical-011.sh assume that Github issue #265 has been fixed
(escape-encoding by export).

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-03-22 13:45:40 -04:00
Lars HenriksenandLukas Fleischer ffbf714c9e Fix decoding of escaped characters in imported text
Stick strictly to RFC 5545, 3.3.11, Text, for SUMMARY and DESCRIPTION.
Adresses Github issue #271.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-03-22 13:40:28 -04:00
Lars HenriksenandLukas Fleischer 1cb2691342 Fix ical import logging
The following issues have been fixed:

Functions ical_read_event() and ical_read__todo() do not check the return value
of functions which may fail, in which case an item is not skipped even though
a problem may have been logged by the called function.

Function ical_read_note() fails on empty DESCRIPTION, but does not log it.

Function ical_read_exdate() may log a failure, but cannot fail itself.

Function ical_read_summary() can fail, but not log a failure.

Function ical_read_todo() do not skip a todo with an invalid priority.

Additionally:

A safety check has been added to ical_get_value(), and log messages resulting
from failures have been made uniform.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-03-22 13:40:28 -04:00
Lars HenriksenandLukas Fleischer 63c2d4d503 Fix display of interactively imported todos
Todos were imported (and saved), but not loaded into the listbox for display.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-03-22 13:40:28 -04:00
Lars HenriksenandLukas Fleischer 467815d465 Improve ical import logging
The log file is not deleted if items were skipped (adresses Github issue #269).
The log file includes the import file name and time.
The import line numbers have been corrected (and tests amended).

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-03-22 13:40:28 -04:00
Lars HenriksenandLukas Fleischer 12863ff7c0 Refactor cut and paste registers
Register REG_BLACK_HOLE can neither be copied into nor pasted from and
has been removed from the input routine.

Register 36 was not used.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2020-01-13 20:45:25 +01:00
Lars HenriksenandLukas Fleischer a0129d6751 Fix next recurrent appointment deleted
If the notify bar displays a recurrent appointment after midnight as next
upcoming appointment, the bar is not updated when the appointment/occurrence is
deleted.  The problem is not seen in 4.3.0 because of the bug described in
commit 8cbd456, Fix next recurring appointment.  The problem and the solution is
the same, this time in the function notify_same_recur_item().

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-12-23 13:16:39 -05:00
Lars HenriksenandLukas Fleischer b7eb9a9e94 Fix empty string in updatestring()
If the update results in an empty string (return value GETSTRING_RET), the
original string remains whereas it should be empty.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-12-23 13:16:39 -05:00
Lars HenriksenandLukas Fleischer d8d7dce2b8 Make the text for empty days configurable
The default is "--"; a single space makes the text invisible.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-11-03 10:56:47 -05:00
Lars HenriksenandLukas Fleischer 4aae26e601 Fix save of interactively imported data
Adresses Github issue #249.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-11-03 10:56:47 -05:00
Lars HenriksenandLukas Fleischer e25710f650 Test for DST and recurrent items
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-10-18 16:29:34 -04:00
Lars HenriksenandLukas Fleischer 0e46881746 DST and recurrent items
The patch adresses two issues with the function recur_item_find
ocurrence(), one major: mktime(), and one minor: item duration. In
addition, some refactoring is done.

The following recurrent appointments demonstrate the problems (as
described in the message) and are used as test cases in the associated
test commit.

03/29/2019 @ 12:00 -> 03/30/2019 @ 11:00 {2D -> 04/03/2019} |two-day - every other day - not on 1/4
03/31/2019 @ 12:00 -> 03/31/2019 @ 13:00 {1D -> 04/01/2019} |daily - not on 31/3, twice on 1/4
03/31/2019 @ 04:00 -> 03/31/2019 @ 05:00 {1W} |weekly - appears after one week
03/31/2019 @ 12:00 -> 03/31/2019 @ 12:00 {1M} |monthly - never appears
03/31/2019 @ 12:00 -> 03/31/2019 @ 12:00 {1Y} |yearly - never appears
10/20/2019 @ 00:00 -> 10/21/2019 @ 01:00 {1W -> 11/03/2019} |25 hours - ends on 27th, but continues on 28th
03/24/2019 @ 00:00 -> 03/25/2019 @ 00:00 {1W -> 04/07/2019} |24 hours - does not continue on April 1

The root cause is two mktime() calls in recur_item_find_occurrence(),
both of which use an inherited tm_isdst value in the tm structure. In
such cases mktime() will "normalize" the tm stucture if tm_isdst is 0 or
1 and in disagreement with the rest of the tm contents (just like 32 May
will be normalized to 1 June).

Example. In 2019 DST started on 31/3 at 02:00:00 (in the European
Union). If the (local) time "31/3/2018 00:00:00" is passed to mktime()
with tm_isdst = 0, the return value is (say) T sec and the tm structure
is unchanged, because DST is not in effect at midnight. If the same call
is performed with tm_isdst = 1, the return value becomes (T - 3600) sec
and the tm structure is normalized to "30/3/2018 23:00:00", tm_isdst =
0.

In recur_item_find_occurrence(), the normalized tm structure with wrong
day and time is used in ensuing calculations, leading to wrong dates and
the errors observed.

The first mktime() call is used to calculate the "day span" of the
occurrence before the occurrence itself has been determined. But once
the occurence is known, the "day span" is easily determined, and there
is no need for the first mktime() call.

Events have no explicit duration. However, recur_event_find_occurrence()
and recur_event_inday() set the duration of an event to DAYINSEC before
passing it on to recur_item_find_occurrence(). The value is not correct
on the day when DST begins or ends. The interpretation of the daylength
should be left to the called function. Hence, duration is set to -1 to
signal no (explicit) duration.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-10-18 16:29:34 -04:00
Lars HenriksenandLukas Fleischer 1db1108e86 Test cases for "Fix monthly and yearly recurrence algorithms"
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-09-06 18:17:23 -04:00
Lars HenriksenandLukas Fleischer 2ae30b223e Set time-of-day consistently to midnight for until day
A day begins on midnight (inclusive) and ends on midnight (exclusive). A
day as a whole is represented by the initial midnight, i.e. time-of-day
is 00:00.

On load of recurrent appointments (but not events) time-of-day for the
until day is set to 23:59. For a newly created recurrent appointment the
setting depends on the input method: time-of-day is set to 00:00 if
until day is given as a date (day, month and year), but to time-of-day
for the start day if given as an offset (+dd).

The resulting behaviour is only visible in interactive use of calcurse
as proved by the following scenario.

1) Create an appointment with start time 12:00, end time 11:59 (multi
   day).

2) Turn it into a recurrent appointment of type daily, frequency 3,
   until day +3.

The appointment is correctly displayed with two 2-day occurrences three
days apart.

3) Edit the appointment and select Repetition. Accept existing type,
   frequency and end day (now as a date).

The second day of the second occurrence disappears.

4) Repeat 3), but set the end day as an offset (+3).

The second day of the second occurrence reappears.

The inconsistencies have been eliminated, and time-of-day for the until
day is now always 00:00.

Also, until day may equal start day, so midnights should be compared.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-09-06 18:15:36 -04:00
Lars HenriksenandLukas Fleischer a0305d754f DST: fix date_change() thoroughly
Explicit setting of Daylight Saving Time should be avoided before as well as
after the mktime() call.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-09-06 18:15:36 -04:00
Lars HenriksenandLukas Fleischer 5fbc499886 Fix monthly and yearly recurrence algorithms
The calculation of the year of the most recent occurrence for year dates before
the start date (disregarding the year) is incorrect for frequencies greater than
one. The most recent occurrence (for a date as mentioned) is either too far or
too close in the past. In most cases it does no harm because the most recent
ocurrence is in the past and does not span the date (i.e. there is no occurrence
on the day). But the following appointment shows the presence of the bug:

12/31/2019 @ 12:00 -> 01/01/2020 @ 12:00 {2Y} |new year

The occurence on 1 Jan 2020 is missing, because the most recent occurrence is
too far in the past (31 Dec 2018 instead of 31 Dec 2019). An occurrence appears
on 1 Jan 2021, because the most recent occurence is too close in the past (31
Dec 2020 instead of 31 Dec 2019).

A similar miscalculation affects the monthly rule as proved by

3/31/2019 @ 12:00 -> 4/1/2019 @ 11:00 {2M} |change of month

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-09-06 18:15:36 -04:00
Lars HenriksenandLukas Fleischer 8a0354e6da Fix edit of empty exception day list
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-07-10 07:38:50 -04:00
Lars HenriksenandLukas Fleischer cc86516f64 Treat recurrence item parameters as a unit
An edit session, and in particular, a cancelled edit session should encompass
all parameters.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-06-16 15:03:02 -04:00
Lars HenriksenandLukas Fleischer e6099b2d59 Fix recurrent appointment end time when duration is zero
Fixes a regression introduced in 223810c (Major overhaul of
appointment/event input routines., 2017-11-06) and 16d3032 (Refactoring
update_duration/day_edit_duration., 2017-10-18).

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-06-03 18:42:52 -04:00
Lars HenriksenandLukas Fleischer e6ff3f417c Return at least two days in multiple days mode
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-30 07:46:33 -04:00
Lars HenriksenandLukas Fleischer 0edc2e8637 Mark selected day in the appointments panel in multiple days mode
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-28 17:58:06 -04:00
Lars HenriksenandLukas Fleischer 7e0274bc08 Remove appointments panel scrollbar in multiple days mode
A scrollbar gives the impression of a fixed list. But the list on
display is automatically and silently changed as needed for movements in
the panel or the calendar, thus creating the illusion of an endless
list.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-28 17:57:44 -04:00
Lars HenriksenandLukas Fleischer 378db90ada Fix sidebar width to accommodate calendar week column
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-28 17:57:35 -04:00
Lars HenriksenandLukas Fleischer 3674127e80 Fix missing calculation of number of days on resize
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-28 17:57:31 -04:00
Lars HenriksenandLukas Fleischer d15f1e9242 Automate the calculation of number of days to load
Instead of having the user tell how many days to load, calcurse can
calculate an overestimation from the running configuration (panel size,
appearance of headers and separators etc.)

The configuration variable conf.multiple_days is turned into a Boolean
that switches the feature on and off.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-26 10:04:28 -04:00
Lars HenriksenandLukas Fleischer 4db9677119 Make separation of days conspicuous
... by adding a horizontal line from border to border above the day
heading and turning the event separator into an empty line. The
horizontal line is left out for the first day loaded.

Also reduce the number of empty lines at the end of a day to at most
one.

A new configuration variable, header_line, turns the horizontal line on
and off.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-26 10:04:27 -04:00
Lars HenriksenandLukas Fleischer f9cf5fb0c1 Derive selected day from selected item
Previously, with only one day visible at a time, the appointments panel
displayed the details of the day selected in the calendar (slctd_day);
information required for operations on items (day_items) can often be
derived from the selected day. The items available are derived from the
selected day. In particular, the selected item is derived from the
selected day.

With multiple days in the APP panel, the relation between selected day
(in the calendar) and the selected item (in the APP panel) has, in a
way, been turned around. The selected item may now be moved between days
without explicitly changing the selected day. Implicitly it is changed
when the target day of a move is unavailable.

This commit draws the full consequence: the selected day in the calendar
is always (set to) the day of the selected item in the APP panel.

The static variable 'struct date slctd_day' lives in ui_calendar.c and
is accessible through various public functions. To these are added
ui_calendar_set_slctd_day() which sets slctd_day directly.

The selected day retains its significance for load of the day vector (in
day_store_items()): the range of loaded days begins with the selected
day.  Movements (up/down) in the APP panel will change the selected day
as the selected item moves among the already loaded days. Only when the
target of a movement is unreachable, will further days be loaded. On the
other hand, if the same range of days must be reloaded because of a
changed item, the selected item - and with it the selected day - must be
reset to the first day item (see do_storage()).

Movements in the calendar (generic-next-day, etc.) are not affected and
behave as previously, i.e. they will cause a range of days to be loaded
with the selected day as the first and the selected item as the first of
the selected day.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-22 01:56:59 -04:00
Lars HenriksenandLukas Fleischer 80ce812eff Add configuration variables for multiple days
The number of days displayed in the APP panel has been made
configurable, maximum 21 days, default seven days.

With several days in the APP panel, it may be desirable to "squeeze" the
entries by leaving out the final empty line of each appointment and
lower the number of lines between consecutive days (0, 1, or 2). Both
are made general configuration options. To make a uniform display, an
empty line is added to a day without appointments, if appointments have
an empty line.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-22 01:56:59 -04:00
Lars HenriksenandLukas Fleischer 0bb4a59b5f Add week numbers in the calendar and full first and last week
Much in the calendar is based on the selected day, struct date
slctd_day, in ui-calendar.c.

On the screen it is highlighted with a deviating colour. The highlight
effect has been changed to a pair of red square brackets that do not
obscure the day colour.

The week number (in the frame) used to be that of the selected day, but
has no obvious relation to the days in the APP panel. It has been
replaced by the year day number of the selected day. The week numbers of
all visible weeks are displayed to the left of the calendar.

Dates are displayed also for the overlapping parts of the first and last
week of the month (which do not belong to the month).

Days are accessible in the appointments panel as well as in the
calendar. Hence, validation of days (= inside UNIX time limits) must be
extended from the calendar (in ui_calendar_move()) to include loaded
days (in day_store_items()).

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-22 01:56:59 -04:00
Lars HenriksenandLukas Fleischer 4284ca91bc Implement scrolling in the appointments panel
With multiple days in the APP panel, up/down movements should change
behaviour at the top and bottom of the list displayed, and load the
previous/next lot of days.

This requires that the move function returns the result of the
operation.  Furthermore, the ability to move the selection to the
beginning of a day is needed when moving down (in order to move from the
first day to the last day).  For this reason a DAY_SEPARATOR has been
inserted also after the last day of a lot.

Appointments have a listbox height of three to separate them clearly
when there is more than one in a day.  This leaves a spurious empty line
at the end of a day with appointments.  The DAY_SEPARATOR height is
reduced from two to one, and a new EMPTY_SEPARATOR of height one is
inserted in any day with only events.

When scrolling up the DAY_HEADING becomes visible when the selection
reaches the first item of the day.

The length of the separator (between events and appointments) is
adjusted to leave a space to the window border at both ends, thereby
making it a part of the day, not a separation between days.

The dummy event must also be recognisable when not the selected item and
is only inserted in interactive mode.

The test for a saved selection must also recognise caption items which
have item pointer NULL.

The function day_get_nb() has been renamed day_get_days().

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-22 01:56:59 -04:00
Lars HenriksenandLukas Fleischer 1ccfe128cc Redesign selected-item implementation for the APP panel
The day vector, day_items, is displayed in the appointments panel; the
selected day_item object is highlighted (when the panel has the focus).
When items are inserted, edited, moved etc., and when the day is
changed, the day vector is rebuilt and displayed anew.

Problem: How shall the selection be set automatically in the context of
the new day vector?

In previous versions all of the above is mostly handled by the function
do_storage() in calcurse.c The function saves data about the selection
as needed, rebuilds the day vector, loads the listbox and sets the
selection from the saved selection data.  This works well in "single
day" calcurse in cases where the selected item is present in the day
vector both before and after the rebuild, or when the item ordering in
the listbox is unaffected by the changes.  But when a new item is added
the selection cannot be set to the new object by do_storage().  Instead
the necessary operations are performed by ui_day_item_add(), and
do_storage() is bypassed. In general, when an item cannot be found in
the new vector, the item which occupies the old place in the list gets
selected, e.g. when an item is deleted. When an item is turned into a
repeating one, the old item is deleted and a new is created. Here the
new selection is not always the affected item, but in any case not far
away.  Generally, with only one day in the panel an erronous selection
might not be noticed or be accurate by chance.

In "multiple day" calcurse the existing scheme works less well; in
addition the day vector may now contain more than one object that refer
to the same event or appointment (recurrent items or multi-day
appointments). The scheme has therefore been modified. The do_storage()
function is no longer bypassed, but handles day vector rebuild, load of
listbox and item selection exclusively. To make that possible, data
about the selected item is no longer saved in a local automatic
variable, private to do_storage(), but in an external static variable in
day.c, which may be set not only by do_storage(). The variable is
declared as

static struct day_item sel_data;

and used as follows:

1. On startup sel_data is initialized to empty (i.e. no selection).
2. In any operation involving the appointments panel:
   2.1 Do the work and if necessary set sel_data. This is the case when
       deleting, adding or pasting an item, and when turning an ordinary
       item into a recurrent one.
   2.2 Call do_storage().
3. In do_storage():
   3.1 If sel_data is empty, set it to the current selection.
   3.2 Rebuild the day vector.
   3.3 Set the selection from sel_data.
   3.4 Set sel_data to empty.

Further remarks
---------------

The selection is found in the new day vector by searching for the saved
(order, item.<pointer>) pair. Previously the item.<pointer> alone
sufficed and in some cases it still does. In case the item cannot be
found, the selection stays in the same day as before the rebuild.

An attempt at more consistently named APP-related functions has led to:
ui_day_sel_date() replaces ui_day_sel_day() ui_day_get_sel() replaces
ui_day_selitem()

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-22 01:56:59 -04:00
Lars HenriksenandLukas Fleischer 066df02cbf Introduce multiple days in the appointments panel
Overview of existing implementation
-----------------------------------

The APP panel displays the 'day_items' vector in the 'lb_apt' listbox. A
listbox consists of a scrollwin (structure) in which a number of items
is displayed. The listbox keeps track of:

    - the number of items
    - the selected item
    - the type of each item in an array type[]
    - the height of each item (ie. how many screen lines) in an array ch[]
    - how to display an item (on the screen)

The latter three are handled by functions fn_type(), fn_height(),
fn_draw(). The first two are used to fill in the corresponding array
entry, type[] or ch[], for item number i, the third draws item number i.

The items are taken from the global variables

    vector_t day_items
    int      day_items_nb

in day.c. Items include captions (DAY_HEADING, DAY_SEPARATOR).
Everything is sorted for display (DAY_HEADING, events, DAY_SEPARATOR,
appts).  These are filled in ("stored") [by day_store_items() for the
selected day in the calendar], before being "loaded" into the listbox.
See do_storage() in calcurse.c and ui_day_item_add() in ui-day.c.

New APP panel design
--------------------

Several days are displayed in the APP panel by loading them with
day_store_items().

With several days come several headings and separators. DAY_SEPARATOR is
reinterpreted to separate days, and a new separator, EVNT_SEPARATOR,
separates events from appointments. To sort everything, an 'order'
member of type time_t is added to the day_item structure. It is set for
headings and separators as well as for appointments and events as
follows:

    item            order
    ---------------------
    DAY_HEADING     BGNOFDAY (= midnight)
    EVNT_SEPARATOR  BGNOFDAY
    DAY_SEPARATOR   ENDOFDAY
    event           start time (midnight)
    appointment     start time (first day)
                    BGNOFDAY (following days, if any)

The sort function day_cmp() (used by vector_sort) is extended to sort by
order first.

The order field always indicates the day to which an item belongs. This
comes in handy, because with several days in the APP panel it is
necessary to distinguish between the selected day in the calendar and
the selected day in the APP panel.  This raises the question which day
should actions (commands) operate on: the one selected in the calendar
or the one selected in the APP panel? Unquestionably the one on the APP
panel which is the one tacitly implied. In most cases it is not a
problem, though, because actions work on the selected item and the
selected day does not come into play. But in some cases it does:

    delete item     When deleting an occurrence of a repeated item, the
                    selected day is the exception day to add.

    view item       day_popup_item() needs the day of the selected item
                    for display of correct start/end times.

    cut/paste item  Paste needs the selected day in which to paste.

    add item        The day of the new item is taken from the calendar.
                    Instead a dummy event is inserted in an empty day.
                    This makes the day selectable, which is otherwise
                    impossible with only the DAY_HEADING displayed.  The
                    dummy event is selectable but cannot be edited or
                    deleted (but viewed or piped).

With more than one day in the day_items vecter, an appointment spanning
more than one day may occur more than once in the vector (with start/end
times suitably adjusted for display). A day_item is no longer (always)
identified by the aptev_ptr (item) value. Instead the combination
(order, item.<ptr>) is used; order is roughly the day.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-22 01:56:59 -04:00
Lars HenriksenandLukas Fleischer 8dd694b569 Simplify day storage
The function day_process_storage() is a wrapper for day_store_items().
It has an unused second argument, and is only used twice to load the
selected day. It has been removed.

A new function, get_slctd_day(), is the equivalant of get_today() and
replaces the very awkwardly named ui_calendar_get_slctd_day_sec().

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-22 01:56:59 -04:00
Lars HenriksenandLukas Fleischer 06a4449afa Fix initialization of the day_items vector
This patch fixes all failings tests, but one, in PR #193.

Until now the missing initialization of day_items_nb has caused no
problems, because the variable was assigned to (=) before being used. In
the Multiple days implementation it is repeatedly increased (+=) in a
loop without being initialized first. Indeed, this may considered an
easily fixed bug. But the initialization really belongs in
day_init_vector() so that the call day_item_count(0) returns 0 if done
right after the call day_init_vector(). The bug only shows up in command
line mode because day_items_nb is not used in interactive mode.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-22 01:56:59 -04:00
Lars HenriksenandLukas Fleischer df2cb2a9c0 Refactor listbox code
The changes are related to the selected item and the visible lines in
the scroll window viewport.

In particular, the function listbox_fix_visible_region() has been
eliminated, and functions previously only called by it have been
removed. It performed several tasks that are now elsewhere. One was
removed in an earlier commit (scroll window pad improvement). The task
of making a multi-line item visible has been moved to
listbox_item_in_view(). The task of making a caption line above a text
line visible is listbox specific (for the ap_list) and will be moved to
ui_day_sel_move(), where it is needed.

Boundary checks for the listbox selection have been moved to
listbox_fix_sel().

For future use listbox_sel_move() returns success or failure.

The function wins_scrollwin_ensure_visible() has been renamed
wins_scrollwin_in_view().

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-22 01:56:59 -04:00
Lars HenriksenandLukas Fleischer 576994de00 Improve scroll window pad
A scroll window consists of a pad to write on, and a window through
which to view the pad, the viewport. The pad may change in size when new
contents are loaded into the scroll window. If so, the pad size is set
with wins_scrollwin_set_linecount(). But the offset into the pad (the
top line to be displayed in the viewport) is not adjusted, although it
may not be appropriate for the new pad size. The same is the case when a
scroll window is resized and the viewport changes size. This is probably
the cause of the problem solved by commit 0b46ad4, Avoid blank space
after the last list box item, and the fix has been removed.

The wins_scrollwin_set_linecount() has been renamed
wins_scrollwin_set_pad() and sets size as well as offset. The offset is
only changed if necessary.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-22 01:56:59 -04:00
Lars HenriksenandLukas Fleischer 52d52208c8 Test cases for impossible dates in recurrence rule
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-20 09:37:17 -04:00
Lars HenriksenandLukas Fleischer 2be32c4bda Impossible dates in recurrence rules
According to RFC 5545 dates like 30 February and 31 June must be ignored
when derived from a recurrence rule.

calcurse relies on mktime() "normalization" of dates (e.g. turning 32
December 2019 into 1 January 2020 when moving from 31 December to the
next day).

Normalization may also lead to impossible dates in monthly and yearly
recurrence rules, and this must be avoided.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-05-20 09:37:11 -04:00
Lars HenriksenandLukas Fleischer 8741334d83 New test cases for next appoinment
The change by commit "Fix display of time left before next appointment"
is not detected by the existing test because the time set has zero
seconds.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-04-13 11:58:16 +02:00
Lars HenriksenandLukas Fleischer 371c7eb00f Fix display of time left before next appointment
In the notify bar, the clock is shown in hours, minutes and seconds,
whereas the time left to the next appointment is shown in hours and
minutes only. When you read the clock in hours and minutes (discarding
the seconds), you are mentally rounding down to the nearest minute. To
agree with that reading, the time left (in seconds) should be
(programmatically) rounded up to the nearest minute for display.

In that way the time left is counted down whenever the minute "hand" of
the clock "ticks", and reaches zero when the clock reaches the time of
the next appointment, not one minute before. Also, the clock (in hours
and minutes) and the time left always add up to the time of the next
appointment.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-04-13 11:58:04 +02:00
Lars HenriksenandLukas Fleischer c8d5397214 Fix daemon notification for short warning periods
When the warning period for notifications is less than 60 seconds
(DAEMON_SLEEP_TIME), the daemon may be at sleep when the appointment
comes up. If that happens, no notification is launched. In stead, the
daemon should launch the notification early.

Addresses GitHub issue #204, part 2.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-04-13 11:57:59 +02:00
Lars HenriksenandLukas Fleischer a5d8ca5cbf Fix one-second warning period for notifications
Adresses GitHub issue #204, the interactive part.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-04-13 11:57:58 +02:00
Lars HenriksenandLukas Fleischer 78a46ac7cb Avoid deadlock in config_save()
Must not exit with nbar.mutex locked.

Addresses GitHub issue #201.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-03-21 06:41:46 +01:00
Lars HenriksenandLukas Fleischer 90f96059c5 Fix calendar (re)draw
Window erasure is moved to the drawing functions.

Adresses GitHub issue #196.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-03-13 17:02:26 +01:00
Lars HenriksenandLukas Fleischer 45fd62cab4 Fix vector_remove()
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-03-13 17:02:26 +01:00
Lars HenriksenandLukas Fleischer 528368932c View or edit exception days of a recurrent item
The exception days are presented for viewing/editing as a string of
space-separated dates (in the user-preferred input format). After
editing the string is checked for valid dates, but there is no check
that a date is meaningful (an occurrence day of the item between start
day and until day). Although possible, it is best to add exception days
in the usual way by deletion of occurrences.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-02-28 08:03:53 +01:00
Lars HenriksenandLukas Fleischer a47a562322 Fix empty dynamic string
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-02-28 08:02:35 +01:00
Lars HenriksenandLukas Fleischer 99c7493cd5 Fix deletion of a multi-day recurrent appointment
When the selected day in the calendar is a continuation day (not the first day)
of such an appointment, deleting the occurrence does not work. The reason is
that the selected day, and not the first day of the occurrence, is added as an
exception day.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-02-02 18:19:22 +01:00
Lars HenriksenandLukas Fleischer 870fa1aa32 Overflow check for 32-bit types only
Included is a check of the 'until' date for pasted recurrent items.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-18 23:38:33 +01:00
Lars HenriksenandLukas Fleischer 4af76af5ae Legalese for queue.c
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-12 01:53:51 +01:00
Lars HenriksenandLukas Fleischer 8cbd456640 Fix next recurring appointment
Recurring appointments do not show up in the notification bar as next
appointment. This was partly corrected by 2084f35 (Fix notification of
recurrent appointments, 2017-02-09) and 5aa7a09 (Fix another error in
the notification code, 2017-02-11).

The search function recur_apoint_starts_before() had a wrong second
argument, but is really of no use: the start time of a recurring
appointment is the start time of the very first occurrence (in the
past). A comparison against the item in the notify_app structure tells
nothing of the start time of the current day; at most it eliminates some
future recurring appointments. The function can be dropped, and the
entire recurring appointment list looked through.

The proper start time is found in the main search loop (and called
real_recur_start_time) and must be compared against the item in the
notify_app structure.

But because recur_apoint_find_occurrence() is limited to a particular
day (second argument), two searches are necessary to cover 24 hours.

Unrelated cleanups: removed function return value; changed long to
time_t.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-11 18:11:52 +01:00
Lars HenriksenandLukas Fleischer f2ca5980e9 Initialize data paths once only
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-08 01:23:21 +01:00
Lars HenriksenandLukas Fleischer 63b03c6b36 Explicit calendar and todo view configuration
The configuration settings for calendar view (monthly/weekly) and todo
view (hide/show completed) used to be saved automatically on calcurse
exit, with values taken from the current interactive settings. They
could not be set explicitly in the configuration menues. Configuration
settings are no longer saved on program exit, but on exit from the
configuration menu. This means that the saved values are those that were
current when the configuration menu was entered. To change a saved
value, you must set the view as desired and then enter/exit the
configuration menu.

The preferred calendar and todo views are no longer automatically taken
from the interactive settings, but are explicitly set in the general
options menu.  Default values are monthly view and hide completed view.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:58:11 +01:00
Lars HenriksenandLukas Fleischer b9f23e134a Purge option glorified
The invert filter option is automatically set for -P (and cannot be used
on the command line). The intention is that the grep option (-G) is used
to find the items that should be removed. To remove them, the same
command is run with -P instead of -G.

In general, purge will remove matching items (silently).

Backward compatibility for option -F (no invert filter).

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:58:08 +01:00
Lars HenriksenandLukas Fleischer 42abbf5346 Filter option: invert
New filter option: --filter-invert. When present it inverts (negates)
the other filter options combined. This is mostly useful with the -G
option (with -Q the output is limited by the query range (day range)).

The ouput from "calcurse -G <filter options>" is the (set) complement of
"calcurse -G <filter options> --filter-invert". Here <filter options>
may be any combination of filter options.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:58:06 +01:00
Lars HenriksenandLukas Fleischer 9300e9154c CLI: detect conflicting filter options
Several start/end-time filter options set the same filter criterion.
Only allow one such filter option at a time.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:58:03 +01:00
Lars HenriksenandLukas Fleischer 620c4eeca5 CLI: Revert to and update of parse_datetimearg()
An earlier commit ("CLI: take input date format from configuration file,
do not accept time") replaced parse_datetimearg() with parse_datearg()
and eliminated time-of-day from command line date arguments. This made
the full use of filter options impossible.

That earlier commit is reverted and updated. The parse_datearg()
function is replaced by an updated parse_datetimearg() function that

- takes the date format from the configuration file
- accepts date, date-time or time

The updated parse_datetimearg() function has been extended to report
back the type of the date string received in order to set (filter)
options correctly.  Input dates for query ranges (--from, --to, --days)
are still limited to dates only.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:58:01 +01:00
Lars HenriksenandLukas Fleischer bcfc71f4fe Safety exit and read-only mode
The key_generic_command() function provides a "safety exit" in case of
online changes that should be dropped at exit when auto_save is on, or
(NEW) should be saved at exit when in read-only mode. A check for
unsaved changes has been added. The command prompt has been extended
with minimal help information.

After commit 05e0fd0 "Quit, autosave and interactive save" you could not
leave calcurse in read-only mode with the quit command because the
key_generic_quit() function interpreted IO_SAVE_CANCEL as a decision (by
the user) to cancel a save-conflict and cancelled the quit as well. Now
the function will quit unconditionally in read-only mode (as stated in
the man page). In normal mode a check for unsaved changes has been added
in case auto-save is off.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:57:58 +01:00
Lars HenriksenandLukas Fleischer a5e6608992 CLI: tests aligned
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:57:57 +01:00
Lars HenriksenandLukas Fleischer d0602625bc Update man page
Rework man page. Add invert filter and purge option.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:57:56 +01:00
Lars HenriksenandLukas Fleischer daa30ef3bb Fix print_date()
In print_date(date, day, ...) it is silently assumed that day is
midnight (beginning) of the day to be printed. Assume only that it
belongs to the day.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:57:55 +01:00
Lars HenriksenandLukas Fleischer ad183c61a9 CLI: do not start daemon if calcurse is running
Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:57:53 +01:00
Lars HenriksenandLukas Fleischer 7851f46fbb CLI: filter options have no effect for dates before the epoch
With the exception of filter.type_mask, a filter is only applied if set
explicitly on the command line with a filter option. Whether that is the
case, is determined by comparison with the initialization value. For
date related filters (start_from/to, end_from/to) that is -1, hence the
criterion is != -1, not >= 0.

In generel, a filter initialization value should be invalid (i.e. one
that cannot be set explicitly). As times before the epoch (1 January
1970 00:00:00 UTC) are negative, -1 is a valid Unix time. However, as it
cannot be set from the command line, it is probably no problem?

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:57:52 +01:00
Lars HenriksenandLukas Fleischer 4ba2cc46d7 CLI: time assignments for filter options
Explanation. There is an important difference between "to <date>" (e.g.
to 15/11/2018)  and "to <date time>" (e.g. to "15/11/2018 13:30"):
<date> is a time span (of 24 hours), while <date time> is a point in
time.

"To <date>" really means "to the end of <date>", while "before <date>"
means "before the beginning of <date>". There are 24 hours between the
two, whereas there is only one second between "before <date time>" and
"to <date time>".  Similar for from/after.

An earlier commit introduced parse_datearg() that only accepts a date
without a time. Hence, a date should be treated as a time span from
midnight to one second before next midnight.

The commit also fixes an error detection bug (filter.start_from/to and
filter.end_from/to were updated too early).

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:57:51 +01:00
Lars HenriksenandLukas Fleischer 42e296c840 CLI: long options to override configuration file settings
The input and output date formats may be set from the command line.
Intended for scripts that must be independent of the local user
configuration.

Cannot be used in interactive mode.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:57:49 +01:00
Lars HenriksenandLukas Fleischer 3d7bb89c88 CLI: take input date format from configuration file, do not accept time
Before this patch the input date parsing accepts three formats:
yyyy/mm/dd, mm/dd/yyyy, yyyy-mm-dd. They are tried in sequence. It also
accepts an additional time (hh:mm), or a time without a date.

There are several issues with this:

- it is not documented
- the date format dd/mm/yyyy is not accepted
- print_date() and filter option settings (in parse_args()) can only
  handle midnight times (which are the result of a date without time)
- it is highly uncertain what happens if a time (without a date) is
  given; at least the -d option treats a time without colon (1215 for
  12:15) as a number

It seems that acceptance of time input is a by-product and not needed.
For these reasons the input date parsing has been changed:

- the format is taken from the configuration file (as is the case for
  the output date format)
- only a date, and no time, is accepted

Because the input date format is used during parsing of the command
line, the configuration file must be loaded first, i.e. the options -D
or -C must be parsed before the remaining ones. Loading the
configuration file may result in errors (e.g. caused by changes between
versions). For this reason config_load() has been made more tolerant and
issues warnings instead of exiting.

A followup patch will introduce two options to allow the configuration
file settings to be overridden for input and output date formats.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:57:48 +01:00
Lars HenriksenandLukas Fleischer 215e90d348 Command line options: -P, option check, usage, help
Option -F/--filter renamed to -P/--purge; -F retained as deprecated.
Check for non-option arguments and for filter, format and query-range
options only.  Update of help and usage messages.

Fixed --status output when calcurse is not running.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:57:46 +01:00
Lars HenriksenandLukas Fleischer 03880a82bf Fix day range for queries
In "--from a --to z", a is included in the range, z not. This is
non-intuitive and disagrees with the semantics of "to" in filter options
like --filter-start-to, where "to" (and "from") are used inclusively (as
opposed to "before" and "after"). It also has the effect that "--from
today --to tomorrow" has a range of 1 day, "--to z" a range of 0 days
(otherwise not allowed), and "--to today --days -1" is allowed and
displays yesterday!

The implementation has been fixed to agree with "inclusive" semantics.
Options --from and -days with negative range are allowed, while --to and
--days are disallowed also when the range is negative.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2019-01-07 16:57:43 +01:00
Lars HenriksenandLukas Fleischer 4285e88593 Rewrite of io_init()
The introduction of the "-C <confdir>" option is an opportunity to
review the initialization of data paths. It lead to a rewrite.

Two "root" directories are used (data and configuration files); by
default they are identical. The statically allocated path buffers are
turned into dynamically allocated buffers.

Missing files/directories now include hooks.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-12-14 10:35:11 +01:00
Lars HenriksenandLukas Fleischer e6cffdc6bd DST fix: daylength v. DAYINSEC
The number of seconds in a day and daylength in seconds differ when
Daylight Saving Time is in effect on two days of the year. The day when DST
takes effect is 23 hours long, and the day when DST ends is 25 hours long.

In the latter case the date changing thread wóuld enter a loop in the last hour
before midnight (in the former it would set the date an hour too late).
The next midnight is calculated through mktime(), invoked by date2sec().

Wrong daylength prevented appointments from being stored in the day vector and
caused them to be displayed wrongly in the appts panel.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-11-10 12:19:28 +01:00
Lars HenriksenandLukas Fleischer 4263a28504 DST fix: adjusting time in appointments
Calcurse saves time and date information on disk as local time in readable text
file format. When loaded from disk or when entered by the user, local time is
converted to Unix time (seconds since 00:00:00, 1 January 1970). When
displayed, and later when saved to disk, the Unix time is converted back to
readable local time. Both conversions depend on DST.

Hence, if midnight for a day with DST in effect (i.e. local time) is converted,
increased with an amount and converted back, the amount has changed if DST is
_not_ in effect for the resulting time. In general, calculations on Unix time
variables should be used with caution because of the DST-dependent conversions.
Instead, the calculations should be performed on local time data with the help
of mktime().

The commit fixes start time for pasted appointments (ordinary and recurrent)
and the 'until'-date of recurrent appointments, pasted as well as new and
edited. The latter problem is slightly different in that the adjustment is a
number of days, as it is for exception dates.

Update of the date in parse_datetime() has been corrected to be similar to
update of the time, although no problem has been identified.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-11-10 12:19:28 +01:00
Lars HenriksenandLukas Fleischer f91b7cdee0 DST fix: adding appointments on the day when DST starts or stops
A new apppoint inserted on the day when the clock is adjusted backward by an
hour got a wrong start time (an hour late). Reason: mktime() must not use the
Daylight Saving Time information returned by localtime_r().

Also editorial simplifications.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-11-10 12:19:28 +01:00
Lars HenriksenandLukas Fleischer 8104c4aaf4 po/: queue.c added
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:02:57 +02:00
Lars HenriksenandLukas Fleischer d1075e525f Mutex for the system message queue
The main thread only reads and removes events from the queue. All other threads
only insert events in the queue. Hence, only insertion and removal need protection.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:02:57 +02:00
Lars HenriksenandLukas Fleischer 53b55930e8 Configuration variable for system events
After user acknowledgement a system event is deleted from the event queue.
The configuration variable determines whether it is turned into an appointment
(for later inspection) or not.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:02:57 +02:00
Lars HenriksenandLukas Fleischer 3d93ffa7ca Periodic save: report cancelled save
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:02:57 +02:00
Lars HenriksenandLukas Fleischer 2112b1ca2e System message queue
The screen and user interaction is managed by the main thread. Other parts of
calcurse (threads) wishing to use the screen or communicate with the user, must
do it via the main thread.

For this purpose the main input loop is extended with a message queue. A thread
may insert a message in the queue. The main thread tests for messages before
listening for user commands. If a message is present, it is displayed (in a popup
window) for the user to acknowledge. Depending on the message other actions may
be performed, e.g. the message could be turned into a "system appointment/event"
and inserted among the usual appointments.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:02:57 +02:00
Lars HenriksenandLukas Fleischer 2339d78cbd Quit, autosave and interactive save
Quitting calcurse with auto_save on may lead to an interactive conflict
resolution for the save operation. When the result is a cancellation of the
save, the quit command is also cancelled.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:02:57 +02:00
Lars HenriksenandLukas Fleischer c95dd0e685 Remove the configuraton variable conf.progress_bar
When loading/saving the configuration file the entry is silently ignored
(which means it is removed by the first save).

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:02:57 +02:00
Lars HenriksenandLukas Fleischer 4b192c0773 Error return code for io_reload_data()
The return code from new_data() and io_load_data() is explicitly defined as a
bit mask. A file access error is recognised and reported back to the user.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:02:57 +02:00
Lars HenriksenandLukas Fleischer ff402d21ab New save/load mutex strategy
A complete save or reload operation is made up of several cooperating lower
level function calls. In stead of protecting the lower level read or write calls
on the data files, the entire save or load operation is protected in order to
ensure its integrity.

Thus mutex protection has been moved from the level:

	io_load_data(), io_merge_data(), new_data(),

to two functions at the higher level:

	io_save_cal(), io_reload_data().

The protection includes pre- and post-hooks.

The function io_load_data() needs no protection when calcurse starts up
(threads not yet started) or runs in non-interactive mode (no threads involved).

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:02:57 +02:00
Lars HenriksenandLukas Fleischer 837f629341 Periodic save mutex
To protect the periodic save from being cancelled during a save operation.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:02:57 +02:00
Lars HenriksenandLukas Fleischer 657f007cd2 Distinguish between interactive and periodic save
A new argument to io_save_cal() makes it possible for the periodic save thread
to avoid 1) user interaction and 2) overwriting new data.

At the moment the thread has no way to report on the result of the save.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:01:34 +02:00
Lars HenriksenandLukas Fleischer 39ab4665e6 Data save and removal of the progress bar
The function io_save_cal() saves apts, todos, configuration data and key
bindings. The configuration and key files do not belong with the two data
files, but the progress bar function assumes that all four files are saved in
a fixed sequence. Since it is used nowhere else and contains unused parts,
the function has been removed.

A return code for file access error is introduced, and the EXIT macro moved to
the command level in calcurse.c.

Save of configuration and key data were already moved to the configuration menu
in commit 0124618, A save refinement: no action if everything is unchanged.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:01:34 +02:00
Lars HenriksenandLukas Fleischer 8b39637a62 Move user information after save/reload to the command level
Moving user information to calcurse.c makes it easier to perform the actual
save/reload operatons in io.c, e.g. it is possible to load instead of
reload after a merge in conflict resolving.

The save/reload operations are of such importance that the user should always be
informed of the result (it's a bit disquieting when there is no reaction to a
save or reload command). Hence, the save/reload status messages are no longer
conditioned by show_dialogs(). No confirmation is asked for, so a message stays
until the status bar is updated by another action.

Care is taken to inform about save/reload actions that result in no change.

Texts are kept concise because of the limited message area. When conflicts are
present, whether saving or reloading, the "continue/merge/cancel" pattern seems
easier to grasp.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:01:34 +02:00
Lars HenriksenandLukas Fleischer d7777ed44e A save refinement: no action if everything is unchanged
A reload action will do nothing if in-memory data as well as data files are
unchanged. This commit accomplishes the equivalent for a save action.

Because saving of configuration data and key bindings are mixed up with saving
of data files, any changes in those will only be saved if data files also have
changed. Hence, configuration data and key bindings are also saved upon exit
from the configuration menu.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:01:34 +02:00
Lars HenriksenandLukas Fleischer 5ad0019b23 Only reload if data files were changed (replacement)
This is a replacement for commits 57dd3d6 and 912124b.

The idea is to move the check for modified files and the list initialization
into io_load_data(), and let io_load_data() decide what to load. A new
argument is used to force a load.

The return code from new_data() (the renamed version of
io_check_data_files_modified()) tells which files have changed.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:01:34 +02:00
Lars HenriksenandLukas Fleischer ab0fe68225 Reload data after resolving save conflict (improved)
After resolving a save conflict with the merge tool, a save operation has,
in effect, occurred, and data files must be reloaded to import the result of
the conflict resolution.

This is a replacement for commit 2fe9c7e. The operations concerned with the user
interface are kept out the io-operations (as in all other cases) and take place
at the command-level in calcurse.c. and not at the io-level (io.c).

Shorter, more concise prompt texts.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:01:34 +02:00
Lars HenriksenandLukas Fleischer 8c2ccebe35 Handling of modified flag
The flag modified (io.c) keeps track of the memory state of data:
	modified == 0: unchanged since load or last save
	modified == 1: changed since load or last save
It is now unset in io_load_data() and io_save_cal() only.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:01:34 +02:00
Lars HenriksenandLukas Fleischer e97360df71 Update windows at the right level
The wins_update() call is the responsibility of the caller of
io_resolve_save_conflict().

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:01:34 +02:00
Lars HenriksenandLukas Fleischer a5ae7256b3 Stop/start the notify main thread at the right level
The thread is stopped/started in wins_prepare/unprepare_external() when hooks
are run. There is no need to do it in io_reload_data(). In fact, because of the
nested calls

notify_stop_main_thread() <--- io_reload_data()
...
	notify_stop_main_thread() <--- hook/wins_prepare_external()
	...
	notify_start_main_thread() <--- hook/wins_unprepare_external()
...
notify_start_main_thread() <--- io_reload_data()

the thread has been running after the first hook anyway.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:01:34 +02:00
Lars HenriksenandLukas Fleischer a94b0ecde0 Fix window update after hook execution
The introduction of hooks raised a problem with window updates. The diagnosis
in commit feb059e8 (Fix segmentation fault on reload with pre-load hook) was
right, the cure was wrong.

The problem is wins_update(), not the listbox contents. The wins_update() call
does not belong in wins_unprepare_external() (or in io_reload_data()), but at a
higher level. It should be called _after_ reload, as indeed it is in
key_generic_reload() when the listbox contents have been updated (todo as well
as appointments). The call was introduced in commit 8ae75f3 without comment.

The todo updates in io_reload_data() also belong in key_generic_reload() where
they were before commit 7f06c252.

When saving data, all panels must be updated in case a hook was executed.

Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
2018-10-21 20:01:34 +02:00