aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Henriksen <LarsHenriksen@get2net.dk>2018-12-17 10:42:13 +0100
committerLukas Fleischer <lfleischer@calcurse.org>2019-05-22 01:56:59 -0400
commit066df02cbf44d58a9b1e3a30b1310dc0f460e4c4 (patch)
tree6b873c9ceccada341913096186c7c165f46c9ea7
parent8dd694b56956a1c4ec3519743904222b465e10a5 (diff)
downloadcalcurse-066df02cbf44d58a9b1e3a30b1310dc0f460e4c4.tar.gz
calcurse-066df02cbf44d58a9b1e3a30b1310dc0f460e4c4.zip
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>
-rw-r--r--src/args.c2
-rw-r--r--src/calcurse.c14
-rw-r--r--src/calcurse.h13
-rw-r--r--src/day.c96
-rw-r--r--src/event.c8
-rw-r--r--src/ui-day.c31
6 files changed, 115 insertions, 49 deletions
diff --git a/src/args.c b/src/args.c
index 66584cf..e6444e4 100644
--- a/src/args.c
+++ b/src/args.c
@@ -275,7 +275,7 @@ date_arg_from_to(long from, long to, int add_line, const char *fmt_apt,
long date;
for (date = from; date <= to; date = date_sec_change(date, 0, 1)) {
- day_store_items(date, 0);
+ day_store_items(date, 0, 1);
if (day_item_count(0) == 0)
continue;
if (add_line)
diff --git a/src/calcurse.c b/src/calcurse.c
index d470661..757b3cd 100644
--- a/src/calcurse.c
+++ b/src/calcurse.c
@@ -54,7 +54,7 @@ static void do_storage(int day_changed)
if (day)
item = day->item;
- day_store_items(get_slctd_day(), 1);
+ day_store_items(get_slctd_day(), 1, day_get_nb());
ui_day_load_items();
if (day_changed)
@@ -143,7 +143,7 @@ static inline void key_add_item(void)
static inline void key_edit_item(void)
{
- if (wins_slctd() == APP) {
+ if (wins_slctd() == APP && !event_dummy(ui_day_selitem())) {
ui_day_item_edit();
do_storage(0);
wins_update(FLAG_CAL | FLAG_APP | FLAG_STA);
@@ -155,7 +155,7 @@ static inline void key_edit_item(void)
static inline void key_del_item(void)
{
- if (wins_slctd() == APP) {
+ if (wins_slctd() == APP && !event_dummy(ui_day_selitem())) {
ui_day_item_delete(reg);
do_storage(0);
wins_update(FLAG_CAL | FLAG_APP | FLAG_STA);
@@ -167,7 +167,7 @@ static inline void key_del_item(void)
static inline void key_generic_copy(void)
{
- if (wins_slctd() == APP) {
+ if (wins_slctd() == APP && !event_dummy(ui_day_selitem())) {
ui_day_item_copy(reg);
do_storage(0);
wins_update(FLAG_CAL | FLAG_APP);
@@ -185,7 +185,7 @@ static inline void key_generic_paste(void)
static inline void key_repeat_item(void)
{
- if (wins_slctd() == APP)
+ if (wins_slctd() == APP && !event_dummy(ui_day_selitem()))
ui_day_item_repeat();
do_storage(0);
wins_update(FLAG_CAL | FLAG_APP | FLAG_STA);
@@ -193,7 +193,7 @@ static inline void key_repeat_item(void)
static inline void key_flag_item(void)
{
- if (wins_slctd() == APP) {
+ if (wins_slctd() == APP && !event_dummy(ui_day_selitem())) {
ui_day_flag();
do_storage(0);
wins_update(FLAG_APP);
@@ -232,7 +232,7 @@ static inline void key_lower_priority(void)
static inline void key_edit_note(void)
{
- if (wins_slctd() == APP) {
+ if (wins_slctd() == APP && !event_dummy(ui_day_selitem())) {
ui_day_edit_note();
do_storage(0);
} else if (wins_slctd() == TOD) {
diff --git a/src/calcurse.h b/src/calcurse.h
index 6d6f205..3ee0900 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -172,6 +172,7 @@
#define NOHILT 0 /* 'No highlight' argument */
#define NOFORCE 0
#define FORCE 1
+#define DUMMY -1 /* The dummy event */
#define ERROR_MSG(...) do { \
char msg[BUFSIZ]; \
@@ -417,9 +418,10 @@ enum day_item_type {
DAY_HEADING = 1,
RECUR_EVNT,
EVNT,
- DAY_SEPARATOR,
+ EVNT_SEPARATOR,
RECUR_APPT,
- APPT
+ APPT,
+ DAY_SEPARATOR
};
/* Available item types. */
@@ -460,6 +462,7 @@ struct item_filter {
struct day_item {
enum day_item_type type;
time_t start;
+ time_t order;
union aptev_ptr item;
};
@@ -797,6 +800,7 @@ void custom_keys_config(void);
void custom_config_main(void);
/* day.c */
+int day_get_nb(void);
void day_free_vector(void);
char *day_item_get_mesg(struct day_item *);
char *day_item_get_note(struct day_item *);
@@ -805,7 +809,7 @@ long day_item_get_duration(struct day_item *);
int day_item_get_state(struct day_item *);
void day_item_add_exc(struct day_item *, time_t);
void day_item_fork(struct day_item *, struct day_item *);
-void day_store_items(time_t, int);
+void day_store_items(time_t, int, int);
void day_display_item_date(struct day_item *, WINDOW *, int, time_t, int, int);
void day_display_item(struct day_item *, WINDOW *, int, int, int, int);
void day_write_stdout(time_t, const char *, const char *, const char *,
@@ -829,6 +833,7 @@ void dmon_stop(void);
/* event.c */
extern llist_t eventlist;
+extern struct event dummy;
void event_free_bkp(void);
struct event *event_dup(struct event *);
void event_free(struct event *);
@@ -842,6 +847,7 @@ void event_write(struct event *, FILE *);
struct event *event_scan(FILE *, struct tm, int, char *, struct item_filter *);
void event_delete(struct event *);
void event_paste_item(struct event *, time_t);
+int event_dummy(struct day_item *);
/* getstring.c */
enum getstr getstring(WINDOW *, char *, int, int, int);
@@ -1092,6 +1098,7 @@ void todo_free_list(void);
/* ui-day.c */
struct day_item *ui_day_selitem(void);
+time_t ui_day_selday(void);
void ui_day_set_selitem_by_aptev_ptr(union aptev_ptr);
void ui_day_set_selitem(struct day_item *);
void ui_day_item_add(void);
diff --git a/src/day.c b/src/day.c
index c4d1cac..e480732 100644
--- a/src/day.c
+++ b/src/day.c
@@ -42,9 +42,15 @@
#include "calcurse.h"
+static unsigned day_nb = 7;
static vector_t day_items;
static unsigned day_items_nb = 0;
+int day_get_nb(void)
+{
+ return day_nb;
+}
+
static void day_free(struct day_item *day)
{
mem_free(day);
@@ -73,6 +79,11 @@ static int day_cmp(struct day_item **pa, struct day_item **pb)
struct day_item *b = *pb;
int a_state, b_state;
+ if (a->order < b->order)
+ return -1;
+ if (a->order > b->order)
+ return 1;
+
if ((a->type == APPT || a->type == RECUR_APPT) &&
(b->type == APPT || b->type == RECUR_APPT)) {
if (a->start < b->start)
@@ -97,11 +108,12 @@ static int day_cmp(struct day_item **pa, struct day_item **pb)
}
/* Add an item to the current day list. */
-static void day_add_item(int type, time_t start, union aptev_ptr item)
+static void day_add_item(int type, time_t start, time_t order, union aptev_ptr item)
{
struct day_item *day = mem_malloc(sizeof(struct day_item));
day->type = type;
day->start = start;
+ day->order = order;
day->item = item;
VECTOR_ADD(&day_items, day);
@@ -208,6 +220,7 @@ 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;
+ day_out->order = day_in->order;
switch (day_in->type) {
case APPT:
@@ -245,7 +258,7 @@ static int day_store_events(time_t date)
struct event *ev = LLIST_TS_GET_DATA(i);
p.ev = ev;
- day_add_item(EVNT, ev->day, p);
+ day_add_item(EVNT, ev->day, ev->day, p);
e_nb++;
}
@@ -269,8 +282,11 @@ static int day_store_recur_events(time_t date)
struct recur_event *rev = LLIST_TS_GET_DATA(i);
p.rev = rev;
- day_add_item(RECUR_EVNT, rev->day, p);
- e_nb++;
+ time_t occurrence;
+ if (recur_event_find_occurrence(rev, date, &occurrence)) {
+ day_add_item(RECUR_EVNT, rev->day, occurrence, p);
+ e_nb++;
+ }
}
return e_nb;
@@ -294,11 +310,14 @@ static int day_store_apoints(time_t date)
struct apoint *apt = LLIST_TS_GET_DATA(i);
p.apt = apt;
-
- if (apt->start >= date + DAYLEN(date))
- break;
-
- day_add_item(APPT, apt->start, p);
+ /*
+ * For appointments continuing from the previous day, order is
+ * set to midnight to sort it before appointments of the day.
+ */
+ day_add_item(APPT,
+ apt->start,
+ apt->start < date ? date : apt->start,
+ p);
a_nb++;
}
LLIST_TS_UNLOCK(&alist_p);
@@ -325,9 +344,13 @@ static int day_store_recur_apoints(time_t date)
p.rapt = rapt;
- time_t real_start;
- if (recur_apoint_find_occurrence(rapt, date, &real_start)) {
- day_add_item(RECUR_APPT, real_start, p);
+ time_t occurrence;
+ /* As for appointments */
+ if (recur_apoint_find_occurrence(rapt, date, &occurrence)) {
+ day_add_item(RECUR_APPT,
+ occurrence,
+ occurrence < date ? date : occurrence,
+ p);
a_nb++;
}
}
@@ -337,35 +360,49 @@ static int day_store_recur_apoints(time_t date)
}
/*
- * Store all of the items to be displayed for the selected day.
- * Items are of four types: recursive events, normal events,
+ * Store all of the items to be displayed for the selected day and the following
+ * (n - 1) days. Items are of four types: recursive events, normal events,
* recursive appointments and normal appointments.
- * The items are stored in the linked list pointed by day_items
- * and the length of the new pad to write is returned.
- * The number of events and appointments in the current day are also updated.
+ * The items are stored in the day_items vector; the number of events and
+ * appointments in the vector is stored in day_items_nb,
*/
void
-day_store_items(time_t date, int include_captions)
+day_store_items(time_t date, int include_captions, int n)
{
unsigned apts, events;
- union aptev_ptr p = { NULL };
+ union aptev_ptr p = { NULL }, d;
+ int i;
day_free_vector();
day_init_vector();
- if (include_captions)
- day_add_item(DAY_HEADING, 0, p);
+ for (i = 0; i < n; i++, date = NEXTDAY(date)) {
+ if (include_captions)
+ day_add_item(DAY_HEADING, 0, date, p);
+
+ events = day_store_recur_events(date);
+ events += day_store_events(date);
+ apts = day_store_recur_apoints(date);
+ apts += day_store_apoints(date);
- events = day_store_recur_events(date);
- events += day_store_events(date);
- apts = day_store_recur_apoints(date);
- apts += day_store_apoints(date);
+ if (include_captions && events > 0 && apts > 0)
+ day_add_item(EVNT_SEPARATOR, 0, date, p);
- if (include_captions && events > 0 && apts > 0)
- day_add_item(DAY_SEPARATOR, 0, p);
+ day_items_nb += events + apts;
+
+ if (events == 0 && apts == 0) {
+ /* Insert dummy event. */
+ d.ev = &dummy;
+ dummy.mesg = _("(none)");
+ day_add_item(EVNT, DUMMY, date, d);
+ day_items_nb++;
+ }
+
+ if (include_captions && i < n - 1)
+ day_add_item(DAY_SEPARATOR, 0, ENDOFDAY(date), p);
+ }
VECTOR_SORT(&day_items, day_cmp);
- day_items_nb = events + apts;
}
/*
@@ -473,8 +510,7 @@ void day_popup_item(struct day_item *day)
struct apoint apt_tmp;
apt_tmp.start = day->start;
apt_tmp.dur = day_item_get_duration(day);
- apoint_sec2str(&apt_tmp, get_slctd_day(), a_st, a_end);
-
+ apoint_sec2str(&apt_tmp, ui_day_selday(), a_st, a_end);
item_in_popup(a_st, a_end, day_item_get_mesg(day),
_("Appointment:"));
} else {
diff --git a/src/event.c b/src/event.c
index ed170fd..c16ca4e 100644
--- a/src/event.c
+++ b/src/event.c
@@ -43,6 +43,8 @@
#include "sha1.h"
llist_t eventlist;
+/* Dummy event for the APP panel for an otherwise empty day. */
+struct event dummy = { DUMMY, 0, "", NULL };
void event_free(struct event *ev)
{
@@ -223,3 +225,9 @@ void event_paste_item(struct event *ev, time_t date)
ev->day = date;
LLIST_ADD_SORTED(&eventlist, ev, event_cmp);
}
+
+/* Return true if the day_item is the dummy event. */
+int event_dummy(struct day_item *item)
+{
+ return item->item.ev == &dummy;
+}
diff --git a/src/ui-day.c b/src/ui-day.c
index bb5063f..32e12bf 100644
--- a/src/ui-day.c
+++ b/src/ui-day.c
@@ -36,8 +36,11 @@
#include "calcurse.h"
-struct day_item day_cut[38] = { {0, 0, {NULL}} };
+struct day_item day_cut[38] = { {0, 0, 0, {NULL}} };
+/*
+ * Return the selected item in the APP panel.
+ */
struct day_item *ui_day_selitem(void)
{
if (day_item_count(0) <= 0)
@@ -46,6 +49,14 @@ struct day_item *ui_day_selitem(void)
return day_get_item(listbox_get_sel(&lb_apt));
}
+/*
+ * Return the day (midnight) of the selected item in the APP panel.
+ */
+time_t ui_day_selday(void)
+{
+ return update_time_in_date(ui_day_selitem()->order, 0, 0);
+}
+
void ui_day_set_selitem_by_aptev_ptr(union aptev_ptr p)
{
int n = day_get_position_by_aptev_ptr(p);
@@ -579,7 +590,7 @@ void ui_day_item_add(void)
const char *enter_str = _("Press [Enter] to continue");
char item_time[LTIME] = "";
char item_mesg[BUFSIZ] = "";
- time_t start = date2sec(*ui_calendar_get_slctd_day(), 0, 0), end, saved = start;
+ time_t start = ui_day_selday(), end, saved = start;
unsigned dur;
int is_appointment = 1;
union aptev_ptr item;
@@ -672,7 +683,7 @@ void ui_day_item_add(void)
item.ev = event_new(item_mesg, 0L, start, 1);
}
io_set_modified();
- day_store_items(get_slctd_day(), 1);
+ day_store_items(get_slctd_day(), 1, day_get_nb());
ui_day_load_items();
ui_day_set_selitem_by_aptev_ptr(item);
}
@@ -963,7 +974,7 @@ void ui_day_item_paste(unsigned reg)
return;
day_item_fork(&day_cut[reg], &day);
- day_paste_item(&day, get_slctd_day());
+ day_paste_item(&day, ui_day_selday());
io_set_modified();
ui_calendar_monthly_view_cache_set_invalid();
@@ -998,9 +1009,9 @@ static char *fmt_day_heading(time_t date)
/* Display appointments in the corresponding panel. */
void ui_day_draw(int n, WINDOW *win, int y, int hilt, void *cb_data)
{
- struct date slctd_date = *ui_calendar_get_slctd_day();
- time_t date = date2sec(slctd_date, 0, 0);
struct day_item *item = day_get_item(n);
+ /* The item order always indicates the date. */
+ time_t date = update_time_in_date(item->order, 0, 0);
int width = lb_apt.sw.w - 2;
hilt = hilt && (wins_slctd() == APP);
@@ -1019,7 +1030,7 @@ void ui_day_draw(int n, WINDOW *win, int y, int hilt, void *cb_data)
(width - utf8_strwidth(buf)) / 2, "%s", buf);
custom_remove_attr(win, ATTR_HIGHEST);
mem_free(buf);
- } else if (item->type == DAY_SEPARATOR) {
+ } else if (item->type == EVNT_SEPARATOR) {
wmove(win, y, 0);
whline(win, 0, width);
}
@@ -1029,7 +1040,9 @@ enum listbox_row_type ui_day_row_type(int n, void *cb_data)
{
struct day_item *item = day_get_item(n);
- if (item->type == DAY_HEADING || item->type == DAY_SEPARATOR)
+ if (item->type == DAY_HEADING ||
+ item->type == EVNT_SEPARATOR ||
+ item->type == DAY_SEPARATOR)
return LISTBOX_ROW_CAPTION;
else
return LISTBOX_ROW_TEXT;
@@ -1041,6 +1054,8 @@ int ui_day_height(int n, void *cb_data)
if (item->type == APPT || item->type == RECUR_APPT)
return 3;
+ else if (item->type == DAY_SEPARATOR)
+ return 2;
else
return 1;
}