From 4284ca91bc0fd04851a34c67dae1068f3c1defc9 Mon Sep 17 00:00:00 2001
From: Lars Henriksen <LarsHenriksen@get2net.dk>
Date: Sat, 16 Mar 2019 08:27:45 +0100
Subject: 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>
---
 src/calcurse.c |  16 +++++++--
 src/calcurse.h |   9 ++++--
 src/day.c      |  17 ++++++----
 src/ui-day.c   | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 121 insertions(+), 21 deletions(-)

(limited to 'src')

diff --git a/src/calcurse.c b/src/calcurse.c
index b99f210..6313818 100644
--- a/src/calcurse.c
+++ b/src/calcurse.c
@@ -60,7 +60,7 @@ static void do_storage(int day_changed)
 		ui_day_sel_reset();
 
 	/* The day_items vector. */
-	day_store_items(get_slctd_day(), 1, day_get_nb());
+	day_store_items(get_slctd_day(), 1, day_get_days());
 	/* The APP listbox. */
 	ui_day_load_items();
 
@@ -414,7 +414,12 @@ static inline void key_move_up(void)
 	if (wins_slctd() == CAL) {
 		key_generic_prev_week();
 	} else if (wins_slctd() == APP) {
-		ui_day_sel_move(-1);
+		if (!ui_day_sel_move(-1)) {
+			ui_calendar_move(DAY_PREV, 1);
+			do_storage(1);
+			ui_day_sel_dayend();
+			wins_update(FLAG_CAL);
+		}
 		wins_update(FLAG_APP);
 	} else if (wins_slctd() == TOD) {
 		ui_todo_sel_move(-1);
@@ -434,7 +439,12 @@ static inline void key_move_down(void)
 	if (wins_slctd() == CAL) {
 		key_generic_next_week();
 	} else if (wins_slctd() == APP) {
-		ui_day_sel_move(1);
+		if (!ui_day_sel_move(1)) {
+			ui_calendar_move(DAY_NEXT, 1);
+			do_storage(1);
+			ui_day_sel_daybegin(day_get_days() - 1);
+			wins_update(FLAG_CAL);
+		}
 		wins_update(FLAG_APP);
 	} else if (wins_slctd() == TOD) {
 		ui_todo_sel_move(1);
diff --git a/src/calcurse.h b/src/calcurse.h
index 333e1b9..e4cfe79 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -421,6 +421,7 @@ enum day_item_type {
 	EVNT_SEPARATOR,
 	RECUR_APPT,
 	APPT,
+	EMPTY_SEPARATOR,
 	DAY_SEPARATOR
 };
 
@@ -764,7 +765,7 @@ int parse_args(int, char **);
 /* calendar.c */
 extern struct day_item empty_day;
 
-/* ui_calendar.c */
+/* ui-calendar.c */
 void ui_calendar_view_next(void);
 void ui_calendar_view_prev(void);
 void ui_calendar_set_view(int);
@@ -803,10 +804,10 @@ void custom_keys_config(void);
 void custom_config_main(void);
 
 /* day.c */
-int day_get_nb(void);
 int day_set_sel_data(struct day_item *);
 int day_check_sel_data(void);
 int day_sel_index(void);
+int day_get_days(void);
 void day_free_vector(void);
 char *day_item_get_mesg(struct day_item *);
 char *day_item_get_note(struct day_item *);
@@ -1115,7 +1116,9 @@ struct day_item *ui_day_get_sel(void);
 time_t ui_day_sel_date(void);
 void ui_day_sel_reset(void);
 void ui_day_set_sel(struct day_item *);
-void ui_day_sel_move(int);
+int ui_day_sel_move(int);
+void ui_day_sel_daybegin(int);
+void ui_day_sel_dayend(void);
 void ui_day_draw(int, WINDOW *, int, int, void *);
 enum listbox_row_type ui_day_row_type(int, void *);
 int ui_day_height(int, void *);
diff --git a/src/day.c b/src/day.c
index 4c6e1a1..c340988 100644
--- a/src/day.c
+++ b/src/day.c
@@ -42,7 +42,7 @@
 
 #include "calcurse.h"
 
-static unsigned day_nb = 7;
+static unsigned day_days = 5;
 static vector_t day_items;
 static unsigned day_items_nb = 0;
 
@@ -107,9 +107,9 @@ int day_sel_index(void)
 	return -1;
 }
 
-int day_get_nb(void)
+int day_get_days(void)
 {
-	return day_nb;
+	return day_days;
 }
 
 static void day_free(struct day_item *day)
@@ -397,6 +397,7 @@ static int day_store_recur_apoints(time_t date)
 {
 	llist_item_t *i;
 	union aptev_ptr p;
+	time_t occurrence;
 	int a_nb = 0;
 
 	LLIST_TS_LOCK(&recur_alist_p);
@@ -404,8 +405,6 @@ static int day_store_recur_apoints(time_t date)
 		struct recur_apoint *rapt = LLIST_TS_GET_DATA(i);
 
 		p.rapt = rapt;
-
-		time_t occurrence;
 		/* As for appointments */
 		if (recur_apoint_find_occurrence(rapt, date, &occurrence)) {
 			day_add_item(RECUR_APPT,
@@ -451,7 +450,7 @@ day_store_items(time_t date, int include_captions, int n)
 
 		day_items_nb += events + apts;
 
-		if (events == 0 && apts == 0) {
+		if (include_captions && events == 0 && apts == 0) {
 			/* Insert dummy event. */
 			d.ev = &dummy;
 			dummy.mesg = _("(none)");
@@ -459,8 +458,12 @@ day_store_items(time_t date, int include_captions, int n)
 			day_items_nb++;
 		}
 
-		if (include_captions && i < n - 1)
+		if (include_captions) {
+			/* Two empty lines between days. */
+			if (apts == 0)
+				day_add_item(EMPTY_SEPARATOR, 0, ENDOFDAY(date), p);
 			day_add_item(DAY_SEPARATOR, 0, ENDOFDAY(date), p);
+		}
 	}
 
 	VECTOR_SORT(&day_items, day_cmp);
diff --git a/src/ui-day.c b/src/ui-day.c
index 4b37a90..ab4e7d9 100644
--- a/src/ui-day.c
+++ b/src/ui-day.c
@@ -70,6 +70,45 @@ time_t ui_day_sel_date(void)
 	return update_time_in_date(ui_day_get_sel()->order, 0, 0);
 }
 
+/*
+ * If possible, move the selection to the beginning
+ * of previous, current or next day.
+ */
+static void daybegin(int dir)
+{
+	dir = dir > 0 ? 1 : (dir < 0 ? -1 : 0);
+	int sel = listbox_get_sel(&lb_apt);
+
+	switch (dir) {
+	case -1:
+		while (day_get_item(sel)->type != DAY_HEADING)
+			sel--;
+		if (sel == 0)
+			goto leave;
+		sel--;
+		while (day_get_item(sel)->type != DAY_HEADING)
+			sel--;
+		break;
+	case 0:
+		while (day_get_item(sel)->type != DAY_HEADING)
+			sel--;
+		break;
+	case 1:
+		while (day_get_item(sel)->type != DAY_SEPARATOR)
+			sel++;
+		if (sel == lb_apt.item_count - 1) {
+			while (day_get_item(sel)->type != DAY_HEADING)
+				sel--;
+			goto leave;
+		} else
+			sel++;
+		break;
+	}
+  leave:
+	listbox_set_sel(&lb_apt, sel);
+	listbox_item_in_view(&lb_apt, sel);
+}
+
 /*
  * Request the user to enter a new start time.
  * Input: start time and duration in seconds.
@@ -758,6 +797,10 @@ void ui_day_item_delete(unsigned reg)
 
 			io_set_modified();
 			ui_calendar_monthly_view_cache_set_invalid();
+			/* Keep the selection on the same day. */
+			day_set_sel_data(
+				day_get_item(listbox_get_sel(&lb_apt) - 1)
+			);
 			return;
 		default:
 			return;
@@ -768,7 +811,8 @@ void ui_day_item_delete(unsigned reg)
 	p = day_cut_item(date, listbox_get_sel(&lb_apt));
 	day_cut[reg].type = p->type;
 	day_cut[reg].item = p->item;
-
+	/* Keep the selection on the same day. */
+	day_set_sel_data(day_get_item(listbox_get_sel(&lb_apt) - 1));
 	io_set_modified();
 	ui_calendar_monthly_view_cache_set_invalid();
 }
@@ -993,11 +1037,51 @@ void ui_day_load_items(void)
 void ui_day_sel_reset(void)
 {
 	listbox_set_sel(&lb_apt, 0);
+	/* Make the day visible. */
+	if (lb_apt.item_sel)
+		listbox_item_in_view(&lb_apt, lb_apt.item_sel - 1);
+}
+
+int ui_day_sel_move(int delta)
+{
+	int ret;
+
+	ret = listbox_sel_move(&lb_apt, delta);
+	/* When moving up, make the line above visible. */
+	if (delta < 0 && ret && lb_apt.item_sel)
+		listbox_item_in_view(&lb_apt, lb_apt.item_sel - 1);
+	return ret;
 }
 
-void ui_day_sel_move(int delta)
+/*
+ * Move the selection to the beginning of the current day or
+ * the day n days before or after.
+ */
+void ui_day_sel_daybegin(int n)
+{
+	if (n == 0) {
+		daybegin(0);
+		return;
+	}
+	int dir = n > 0 ? 1 : -1;
+	n = dir * n;
+	for (int i = 0; i < n; i++)
+		daybegin(dir);
+}
+
+/*
+ * Move the selection to the end of the current day.
+ */
+void ui_day_sel_dayend(void)
 {
-	listbox_sel_move(&lb_apt, delta);
+	int sel = listbox_get_sel(&lb_apt);
+
+	while (day_get_item(sel)->type != DAY_SEPARATOR)
+		sel++;
+	while (lb_apt.type[sel] != LISTBOX_ROW_TEXT)
+		sel--;
+	listbox_set_sel(&lb_apt, sel);
+	listbox_item_in_view(&lb_apt, sel);
 }
 
 static char *fmt_day_heading(time_t date)
@@ -1036,8 +1120,8 @@ void ui_day_draw(int n, WINDOW *win, int y, int hilt, void *cb_data)
 		custom_remove_attr(win, ATTR_HIGHEST);
 		mem_free(buf);
 	} else if (item->type == EVNT_SEPARATOR) {
-		wmove(win, y, 0);
-		whline(win, 0, width);
+		wmove(win, y, 1);
+		whline(win, 0, width - 2);
 	}
 }
 
@@ -1047,6 +1131,7 @@ enum listbox_row_type ui_day_row_type(int n, void *cb_data)
 
 	if (item->type == DAY_HEADING ||
 	    item->type == EVNT_SEPARATOR ||
+	    item->type == EMPTY_SEPARATOR ||
 	    item->type == DAY_SEPARATOR)
 		return LISTBOX_ROW_CAPTION;
 	else
@@ -1057,10 +1142,9 @@ int ui_day_height(int n, void *cb_data)
 {
 	struct day_item *item = day_get_item(n);
 
-	if (item->type == APPT || item->type == RECUR_APPT)
+	if (item->type == APPT ||
+	    item->type == RECUR_APPT)
 		return 3;
-	else if (item->type == DAY_SEPARATOR)
-		return 2;
 	else
 		return 1;
 }
-- 
cgit v1.2.3-70-g09d2