From e3fc73e0c76addc0893ad3ceeba7ef8442133a51 Mon Sep 17 00:00:00 2001
From: Morgan Seltzer <MorganSeltzer000@gmail.com>
Date: Mon, 7 Jun 2021 17:13:23 -0400
Subject: Backend changes for first day of week

Previously only Sunday and Monday were allowed for the first day of the
week, and was internally treated as a binary variable.

This patch changes the backend so all days are accepted, a future patch
will allow users to actually select other days.

Addresses GitHub feature request #321.

Signed-off-by: Morgan Seltzer <MorganSeltzer000@gmail.com>
Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
---
 src/calcurse.h    |   6 ++-
 src/config.c      |   7 ++--
 src/custom.c      |   2 +-
 src/pcal.c        |   4 +-
 src/ui-calendar.c | 108 ++++++++++++++++++------------------------------------
 src/utils.c       |  36 ++++++++++++++++++
 6 files changed, 82 insertions(+), 81 deletions(-)

(limited to 'src')

diff --git a/src/calcurse.h b/src/calcurse.h
index 8d9e3d8..db8dd51 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -152,7 +152,7 @@
  * The argument (d) is the "Sunday"-numbering of member tm_wday in struct tm.
  */
 #define WDAY(d) \
-    (ui_calendar_week_begins_on_monday() ? ((d ? d : WEEKINDAYS) - 1) : d)
+	(modify_wday(d, -ui_calendar_get_wday_start()))
 
 /* Key definitions. */
 #define CTRLVAL         0x1F
@@ -811,7 +811,7 @@ void ui_calendar_set_current_date(void);
 struct date *ui_calendar_get_today(void);
 void ui_calendar_set_first_day_of_week(enum wday);
 void ui_calendar_change_first_day_of_week(void);
-unsigned ui_calendar_week_begins_on_monday(void);
+int ui_calendar_get_wday_start(void);
 void ui_calendar_store_current_date(struct date *);
 void ui_calendar_init_slctd_day(void);
 struct date *ui_calendar_get_slctd_day(void);
@@ -1234,6 +1234,8 @@ time_t date_sec_change(time_t, int, int);
 time_t update_time_in_date(time_t, unsigned, unsigned);
 time_t get_sec_date(struct date);
 long min2sec(unsigned);
+int modify_wday(int,int);
+char *get_wday_default_string(int);
 void draw_scrollbar(struct scrollwin *, int);
 void item_in_popup(const char *, const char *, const char *, const char *);
 time_t get_today(void);
diff --git a/src/config.c b/src/config.c
index b309c28..2df2559 100644
--- a/src/config.c
+++ b/src/config.c
@@ -468,10 +468,9 @@ static int config_serialize_default_panel(char **buf, void *dummy)
 
 static int config_serialize_first_day_of_week(char **buf, void *dummy)
 {
-	if (ui_calendar_week_begins_on_monday())
-		*buf = mem_strdup("monday");
-	else
-		*buf = mem_strdup("sunday");
+	*buf = mem_strdup(get_wday_default_string(ui_calendar_get_wday_start()));
+	/* now stores string with uppercase first letter, changing to lower */
+	**buf = tolower(**buf);
 
 	return 1;
 }
diff --git a/src/custom.c b/src/custom.c
index 889772e..5f66194 100644
--- a/src/custom.c
+++ b/src/custom.c
@@ -702,7 +702,7 @@ static void print_general_option(int i, WINDOW *win, int y, int hilt, void *cb_d
 	case FIRST_DAY_OF_WEEK:
 		custom_apply_attr(win, ATTR_HIGHEST);
 		mvwaddstr(win, y, XPOS + strlen(opt[FIRST_DAY_OF_WEEK]),
-			  ui_calendar_week_begins_on_monday()? _("Monday") :
+			  ui_calendar_get_wday_start()? _("Monday") :
 			  _("Sunday"));
 		custom_remove_attr(win, ATTR_HIGHEST);
 		mvwaddstr(win, y + 1, XPOS,
diff --git a/src/pcal.c b/src/pcal.c
index 78da0bb..16c3b9f 100644
--- a/src/pcal.c
+++ b/src/pcal.c
@@ -102,8 +102,8 @@ static void pcal_export_header(FILE * stream)
 {
 	fputs("# calcurse pcal export\n", stream);
 	fputs("\n# =======\n# options\n# =======\n", stream);
-	fprintf(stream, "opt -A -K -l -m -F %s\n",
-		ui_calendar_week_begins_on_monday()? "Monday" : "Sunday");
+	fprintf(stream, "opt -A -K -l -m -F %s\n", get_wday_default_string(
+		ui_calendar_get_wday_start()));
 	fputs("# Display week number (i.e. 1-52) on every Monday\n",
 	      stream);
 	fprintf(stream, "all monday in all week %%w\n");
diff --git a/src/ui-calendar.c b/src/ui-calendar.c
index 4a8ef44..e4d5289 100644
--- a/src/ui-calendar.c
+++ b/src/ui-calendar.c
@@ -45,14 +45,14 @@
 #include "calcurse.h"
 
 static struct date today, slctd_day;
-static unsigned ui_calendar_view, week_begins_on_monday;
+static unsigned ui_calendar_view;
+static int wday_start; /* this is used in signed arithmetic */
 static pthread_mutex_t date_thread_mutex = PTHREAD_MUTEX_INITIALIZER;
 
-static void draw_monthly_view(struct scrollwin *, struct date *, unsigned);
-static void draw_weekly_view(struct scrollwin *, struct date *, unsigned);
-static void (*draw_calendar[CAL_VIEWS]) (struct scrollwin *, struct date *,
-					 unsigned) = {
-draw_monthly_view, draw_weekly_view};
+static void draw_monthly_view(struct scrollwin *, struct date *);
+static void draw_weekly_view(struct scrollwin *, struct date *);
+static void (*draw_calendar[CAL_VIEWS]) (struct scrollwin *,
+		struct date *) = {draw_monthly_view, draw_weekly_view};
 
 /* Six weeks cover a month. */
 static int monthly_view_cache[WEEKINDAYS * 6];
@@ -148,30 +148,24 @@ struct date *ui_calendar_get_today(void)
 /* Needed to display sunday or monday as the first day of week in calendar. */
 void ui_calendar_set_first_day_of_week(enum wday first_day)
 {
-	switch (first_day) {
-	case SUNDAY:
-		week_begins_on_monday = 0;
-		break;
-	case MONDAY:
-		week_begins_on_monday = 1;
-		break;
-	default:
+	if (first_day >= 0 && first_day <= 6)
+		wday_start = first_day;
+	else {
 		ERROR_MSG(_("ERROR setting first day of week"));
-		week_begins_on_monday = 0;
-		/* NOTREACHED */
+		wday_start = 0;
 	}
 }
 
 /* Swap first day of week in calendar. */
 void ui_calendar_change_first_day_of_week(void)
 {
-	week_begins_on_monday = !week_begins_on_monday;
+	wday_start = !wday_start;
 }
 
 /* Return 1 if week begins on monday, 0 otherwise. */
-unsigned ui_calendar_week_begins_on_monday(void)
+int ui_calendar_get_wday_start(void)
 {
-	return week_begins_on_monday;
+	return wday_start;
 }
 
 /* Fill in the given variable with the current date. */
@@ -219,18 +213,14 @@ void ui_calendar_monthly_view_cache_set_invalid(void)
 	monthly_view_cache_valid = 0;
 }
 
-static int weeknum(const struct tm *t, int firstweekday)
+static int weeknum(const struct tm *t, int wday_start)
 {
 	int wday, wnum;
 
 	wday = t->tm_wday;
-	if (firstweekday == MONDAY) {
-		if (wday == SUNDAY)
-			wday = 6;
-		else
-			wday--;
-	}
-	wnum = ((t->tm_yday + WEEKINDAYS - wday) / WEEKINDAYS);
+	wnum = ((t->tm_yday + WEEKINDAYS + -modify_wday(wday, -wday_start))
+			/ WEEKINDAYS);
+
 	if (wnum < 0)
 		wnum = 0;
 
@@ -296,7 +286,7 @@ static int ISO8601weeknum(const struct tm *t)
  * Return the tm structure for the first day of the first week
  * (containing a day) of the selected month.
  */
-static struct tm get_first_day(unsigned sunday_first)
+static struct tm get_first_day(int wday_start)
 {
 	struct tm t;
 	struct date d;
@@ -308,26 +298,20 @@ static struct tm get_first_day(unsigned sunday_first)
 	t = date2tm(d, 0, 0);
 	mktime(&t);
 	/* get the first day of the week */
-	date_change(&t, 0,
-		    -(sunday_first ?
-		      t.tm_wday :
-		      (t.tm_wday + WEEKINDAYS - 1) % WEEKINDAYS));
+	date_change(&t, 0, -modify_wday(t.tm_wday, -wday_start));
+
 	return t;
 }
 
-static struct tm get_first_weekday(unsigned sunday_first)
+static struct tm get_first_weekday(int wday_start)
 {
-	int c_wday, days_to_remove;
+	int c_wday;
 	struct tm t;
 
 	c_wday = ui_calendar_get_wday(&slctd_day);
-	if (sunday_first)
-		days_to_remove = c_wday;
-	else
-		days_to_remove = c_wday == 0 ? WEEKINDAYS - 1 : c_wday - 1;
-
 	t = date2tm(slctd_day, 0, 0);
-	date_change(&t, 0, -days_to_remove);
+
+	date_change(&t, 0, -modify_wday(c_wday, -wday_start));
 
 	return t;
 }
@@ -346,8 +330,7 @@ static void draw_week_number(struct scrollwin *sw, struct tm t)
 
 /* Draw the monthly view inside calendar panel. */
 static void
-draw_monthly_view(struct scrollwin *sw, struct date *current_day,
-		  unsigned sunday_first)
+draw_monthly_view(struct scrollwin *sw, struct date *current_day)
 {
 	struct date c_day;
 	int slctd, w_day, numdays, j, week = 0;
@@ -373,7 +356,7 @@ draw_monthly_view(struct scrollwin *sw, struct date *current_day,
 	 * Step forward by week until past the last day of the month.
 	 * The first day of the first week may belong to the previous month.
 	 */
-	t = t_first = get_first_day(sunday_first);
+	t = t_first = get_first_day(wday_start);
 	t.tm_mday += WEEKINDAYS;
 	mktime(&t);
 	last_day += WEEKINDAYS;
@@ -423,7 +406,7 @@ draw_monthly_view(struct scrollwin *sw, struct date *current_day,
 	custom_apply_attr(sw->inner, ATTR_HIGHEST);
 	for (j = 0; j < WEEKINDAYS; j++) {
 		mvwaddstr(sw->inner, ofs_y, ofs_x + weekw + 4 * j,
-			nl_langinfo(ABDAY_1 + (1 + j - sunday_first) % WEEKINDAYS));
+			nl_langinfo(ABDAY_1 + modify_wday(j, wday_start)));
 	}
 	custom_remove_attr(sw->inner, ATTR_HIGHEST);
 	WINS_CALENDAR_UNLOCK;
@@ -449,11 +432,9 @@ draw_monthly_view(struct scrollwin *sw, struct date *current_day,
 			if (j == first_day ||
 			    (mo == 1 && j == WEEKINDAYS) ||
 			    (mo == 12 && j >= 4 * WEEKINDAYS)) {
-				if (sunday_first)
-					date_change(&t, 0, 1);
+				date_change(&t, 0, WDAY(MONDAY));
 				week = ISO8601weeknum(&t);
-				if (sunday_first)
-					date_change(&t, 0, -1);
+				date_change(&t, 0, -WDAY(MONDAY));
 			} else
 				week++;
 		}
@@ -506,8 +487,7 @@ draw_monthly_view(struct scrollwin *sw, struct date *current_day,
 
 /* Draw the weekly view inside calendar panel. */
 static void
-draw_weekly_view(struct scrollwin *sw, struct date *current_day,
-		 unsigned sunday_first)
+draw_weekly_view(struct scrollwin *sw, struct date *current_day)
 {
 #define DAYSLICESNO  6
 	const int WCALWIDTH = 28;
@@ -520,14 +500,14 @@ draw_weekly_view(struct scrollwin *sw, struct date *current_day,
 	OFFX = (wins_sbar_width() - 2 - WCALWIDTH) / 2;
 
 	/* Print the week number, calculated from monday. */
-	t = get_first_weekday(0);
+	t = get_first_weekday(MONDAY);
 	draw_week_number(sw, t);
 
 	/* Now draw calendar view. */
 	for (j = 0; j < WEEKINDAYS; j++) {
 		/* get next day */
 		if (j == 0)
-			t = get_first_weekday(sunday_first);
+			t = get_first_weekday(wday_start);
 		else
 			date_change(&t, 0, 1);
 
@@ -538,7 +518,7 @@ draw_weekly_view(struct scrollwin *sw, struct date *current_day,
 		/* print the day names, with regards to the first day of the week */
 		custom_apply_attr(sw->inner, ATTR_HIGHEST);
 		mvwaddstr(sw->inner, OFFY, OFFX + 4 * j,
-			  nl_langinfo(ABDAY_1 + (1 + j - sunday_first) % WEEKINDAYS));
+			nl_langinfo(ABDAY_1 + modify_wday(j, wday_start)));
 		custom_remove_attr(sw->inner, ATTR_HIGHEST);
 
 		/* Check if the day to be printed has an item or not. */
@@ -624,11 +604,9 @@ draw_weekly_view(struct scrollwin *sw, struct date *current_day,
 void ui_calendar_update_panel(void)
 {
 	struct date current_day;
-	unsigned sunday_first;
 
 	ui_calendar_store_current_date(&current_day);
-	sunday_first = !ui_calendar_week_begins_on_monday();
-	draw_calendar[ui_calendar_view] (&sw_cal, &current_day, sunday_first);
+	draw_calendar[ui_calendar_view] (&sw_cal, &current_day);
 	wins_scrollwin_display(&sw_cal, NOHILT);
 }
 
@@ -728,28 +706,14 @@ void ui_calendar_move(enum move move, int count)
 		ret = date_change(&t, count * YEARINMONTHS, 0);
 		break;
 	case WEEK_START:
-		/* Normalize struct tm to get week day number. */
 		mktime(&t);
-		if (ui_calendar_week_begins_on_monday())
-			days_to_remove =
-			    ((t.tm_wday ==
-			      0) ? WEEKINDAYS - 1 : t.tm_wday - 1);
-		else
-			days_to_remove =
-			    ((t.tm_wday == 0) ? 0 : t.tm_wday);
+		days_to_remove = WDAY(t.tm_wday);
 		days_to_remove += (count - 1) * WEEKINDAYS;
 		ret = date_change(&t, 0, -days_to_remove);
 		break;
 	case WEEK_END:
 		mktime(&t);
-		if (ui_calendar_week_begins_on_monday())
-			days_to_add =
-			    ((t.tm_wday ==
-			      0) ? 0 : WEEKINDAYS - t.tm_wday);
-		else
-			days_to_add = ((t.tm_wday == 0) ?
-				       WEEKINDAYS - 1 : WEEKINDAYS - 1 -
-				       t.tm_wday);
+		days_to_add = modify_wday(-t.tm_wday, wday_start - 1);
 		days_to_add += (count - 1) * WEEKINDAYS;
 		ret = date_change(&t, 0, days_to_add);
 		break;
diff --git a/src/utils.c b/src/utils.c
index 1a480df..f631c8c 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -626,6 +626,42 @@ long min2sec(unsigned minutes)
 	return minutes * MININSEC;
 }
 
+int modify_wday(int wday, int shift)
+{
+	return (WEEKINDAYS + wday + shift) % WEEKINDAYS;
+}
+
+/* returns char* representing a wday, used for internal functions */
+char *get_wday_default_string(int wday)
+{
+	switch(wday) {
+		case MONDAY:
+			return "Monday";
+			break;
+		case TUESDAY:
+			return "Tuesday";
+			break;
+		case WEDNESDAY:
+			return "Wednesday";
+			break;
+		case THURSDAY:
+			return "Thursday";
+			break;
+		case FRIDAY:
+			return "Friday";
+			break;
+		case SATURDAY:
+			return "Saturday";
+			break;
+		case SUNDAY:
+			return "Sunday";
+			break;
+		default:
+			return "Sunday";
+			break;
+	}
+}
+
 /*
  * Display a scroll bar when there are so many items that they
  * can not be displayed inside the corresponding panel.
-- 
cgit v1.2.3-70-g09d2