From 2a15531bb9df283060e0eb0e7e93ecf33e494b4d Mon Sep 17 00:00:00 2001
From: Lukas Fleischer <calcurse@cryptocrack.de>
Date: Sun, 18 May 2014 09:47:19 +0200
Subject: Add support for caption rows in list boxes

This adds support for rows that cannot be selected. Such rows can be
used for section headings and the like.

Signed-off-by: Lukas Fleischer <calcurse@cryptocrack.de>
---
 src/calcurse.h | 16 +++++++++++--
 src/custom.c   |  9 ++++++--
 src/listbox.c  | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 src/notify.c   |  9 +++++++-
 src/ui-day.c   |  5 +++++
 src/ui-todo.c  |  5 +++++
 src/wins.c     |  6 +++--
 7 files changed, 105 insertions(+), 16 deletions(-)

(limited to 'src')

diff --git a/src/calcurse.h b/src/calcurse.h
index 07f6b78..8072304 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -511,13 +511,21 @@ struct scrollwin {
 };
 
 /* Generic list box structure. */
+enum listbox_row_type {
+	LISTBOX_ROW_TEXT,
+	LISTBOX_ROW_CAPTION
+};
+
+typedef enum listbox_row_type (*listbox_fn_item_type_t) (int, void *);
 typedef int (*listbox_fn_item_height_t) (int, void *);
 typedef void (*listbox_fn_draw_item_t) (int, WINDOW *, int, int, void *);
 
 struct listbox {
 	struct scrollwin sw;
 	unsigned item_count;
-	unsigned item_sel;
+	int item_sel;
+	listbox_fn_item_type_t fn_type;
+	enum listbox_row_type *type;
 	listbox_fn_item_height_t fn_height;
 	unsigned *ch;
 	listbox_fn_draw_item_t fn_draw;
@@ -793,7 +801,9 @@ int keys_check_missing_bindings(void);
 void keys_fill_missing(void);
 
 /* listbox.c */
-void listbox_init(struct listbox *, int, int, int, int, const char *, listbox_fn_item_height_t, listbox_fn_draw_item_t);
+void listbox_init(struct listbox *, int, int, int, int, const char *,
+		  listbox_fn_item_type_t, listbox_fn_item_height_t,
+		  listbox_fn_draw_item_t);
 void listbox_delete(struct listbox *);
 void listbox_resize(struct listbox *, int, int, int, int);
 void listbox_set_cb_data(struct listbox *, void *);
@@ -951,6 +961,7 @@ void ui_day_load_items(void);
 void ui_day_sel_reset(void);
 void ui_day_sel_move(int);
 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 *);
 void ui_day_update_panel(int);
 void ui_day_popup_item(void);
@@ -964,6 +975,7 @@ void ui_todo_delete(void);
 void ui_todo_edit(void);
 void ui_todo_pipe(void);
 void ui_todo_draw(int, WINDOW *, int, int, void *);
+enum listbox_row_type ui_todo_row_type(int, void *);
 int ui_todo_height(int, void *);
 void ui_todo_load_items(void);
 void ui_todo_sel_move(int);
diff --git a/src/custom.c b/src/custom.c
index bb425a6..688ca21 100644
--- a/src/custom.c
+++ b/src/custom.c
@@ -684,6 +684,11 @@ static void print_general_option(int i, WINDOW *win, int y, int hilt, void *cb_d
 		custom_remove_attr(win, ATTR_HIGHEST);
 }
 
+static enum listbox_row_type general_option_row_type(int i, void *cb_data)
+{
+	return LISTBOX_ROW_TEXT;
+}
+
 static int general_option_height(int i, void *cb_data)
 {
 	if (i == 9)
@@ -771,8 +776,8 @@ void custom_general_config(void)
 
 	clear();
 	listbox_init(&lb, 0, 0, notify_bar() ? row - 3 : row - 2, col,
-		     _("general options"), general_option_height,
-		     print_general_option);
+		     _("general options"), general_option_row_type,
+		     general_option_height, print_general_option);
 	listbox_load_items(&lb, 10);
 	listbox_draw_deco(&lb, 0);
 	status_mesg("", "");
diff --git a/src/listbox.c b/src/listbox.c
index 80b5dae..245eeef 100644
--- a/src/listbox.c
+++ b/src/listbox.c
@@ -37,12 +37,15 @@
 #include "calcurse.h"
 
 void listbox_init(struct listbox *lb, int y, int x, int h, int w,
-		  const char *label, listbox_fn_item_height_t fn_height,
+		  const char *label, listbox_fn_item_type_t fn_type,
+		  listbox_fn_item_height_t fn_height,
 		  listbox_fn_draw_item_t fn_draw)
 {
 	EXIT_IF(lb == NULL, "null pointer");
 	wins_scrollwin_init(&(lb->sw), y, x, h, w, label);
 	lb->item_count = lb->item_sel = 0;
+	lb->fn_type = fn_type;
+	lb->type = NULL;
 	lb->fn_height = fn_height;
 	lb->ch = NULL;
 	lb->fn_draw = fn_draw;
@@ -53,6 +56,7 @@ void listbox_delete(struct listbox *lb)
 {
 	EXIT_IF(lb == NULL, "null pointer");
 	wins_scrollwin_delete(&(lb->sw));
+	free(lb->type);
 	free(lb->ch);
 }
 
@@ -78,9 +82,15 @@ void listbox_load_items(struct listbox *lb, int item_count)
 	if (lb->item_sel >= item_count)
 		lb->item_sel = item_count - 1;
 
+	if (item_count == 0)
+		return;
+
+	free(lb->type);
 	free(lb->ch);
+	lb->type = xmalloc(item_count * sizeof(unsigned));
 	lb->ch = xmalloc((item_count + 1) * sizeof(unsigned));
 	for (i = 0, ch = 0; i < item_count; i++) {
+		lb->type[i] = lb->fn_type(i, lb->cb_data);
 		lb->ch[i] = ch;
 		ch += lb->fn_height(i, lb->cb_data);
 	}
@@ -113,12 +123,53 @@ int listbox_get_sel(struct listbox *lb)
 	return lb->item_sel;
 }
 
-void listbox_set_sel(struct listbox *lb, unsigned pos)
+static void listbox_fix_sel(struct listbox *lb, int direction)
 {
-	lb->item_sel = pos;
+	int did_flip = 0;
+
+	if (lb->item_count == 0 || direction == 0)
+		return;
+
+	direction = direction > 0 ? 1 : -1;
+
+	while (lb->type[lb->item_sel] != LISTBOX_ROW_TEXT) {
+		if ((direction == -1 && lb->item_sel == 0) ||
+		    (direction == 1 && lb->item_sel == lb->item_count - 1)) {
+			if (did_flip) {
+				lb->item_sel = -1;
+				return;
+			}
+			direction = -direction;
+			did_flip = 1;
+		} else {
+			lb->item_sel += direction;
+		}
+	}
+}
+
+static void listbox_fix_visible_region(struct listbox *lb)
+{
+	int i;
 
 	wins_scrollwin_ensure_visible(&(lb->sw), lb->ch[lb->item_sel]);
 	wins_scrollwin_ensure_visible(&(lb->sw), lb->ch[lb->item_sel + 1] - 1);
+
+	i = lb->item_sel - 1;
+	while (i >= 0 && lb->type[i] != LISTBOX_ROW_TEXT) {
+		wins_scrollwin_ensure_visible(&(lb->sw), lb->ch[i]);
+		wins_scrollwin_ensure_visible(&(lb->sw), lb->ch[i + 1] - 1);
+		i++;
+	}
+}
+
+void listbox_set_sel(struct listbox *lb, unsigned pos)
+{
+	lb->item_sel = pos;
+	listbox_fix_sel(lb, 1);
+	if (lb->item_sel < 0)
+		return;
+
+	listbox_fix_visible_region(lb);
 }
 
 void listbox_sel_move(struct listbox *lb, int delta)
@@ -126,13 +177,15 @@ void listbox_sel_move(struct listbox *lb, int delta)
 	if (lb->item_count == 0)
 		return;
 
-	if (delta < 0 && lb->item_sel < -delta)
+	lb->item_sel += delta;
+	if (lb->item_sel < 0)
 		lb->item_sel = 0;
-	else if (delta > 0 && lb->item_sel + delta >= lb->item_count)
+	else if (lb->item_sel >= lb->item_count)
 		lb->item_sel = lb->item_count - 1;
-	else
-		lb->item_sel += delta;
 
-	wins_scrollwin_ensure_visible(&(lb->sw), lb->ch[lb->item_sel]);
-	wins_scrollwin_ensure_visible(&(lb->sw), lb->ch[lb->item_sel + 1] - 1);
+	listbox_fix_sel(lb, delta);
+	if (lb->item_sel < 0)
+		return;
+
+	listbox_fix_visible_region(lb);
 }
diff --git a/src/notify.c b/src/notify.c
index 1adaded..532bd01 100644
--- a/src/notify.c
+++ b/src/notify.c
@@ -658,6 +658,11 @@ static void print_config_option(int i, WINDOW *win, int y, int hilt, void *cb_da
 		custom_remove_attr(win, ATTR_HIGHEST);
 }
 
+static enum listbox_row_type config_option_row_type(int i, void *cb_data)
+{
+	return LISTBOX_ROW_TEXT;
+}
+
 static int config_option_height(int i, void *cb_data)
 {
 	return 3;
@@ -761,7 +766,9 @@ void notify_config_bar(void)
 	int ch;
 
 	clear();
-	listbox_init(&lb, 0, 0, notify_bar() ? row - 3 : row - 2, col, _("notification options"), config_option_height, print_config_option);
+	listbox_init(&lb, 0, 0, notify_bar() ? row - 3 : row - 2, col,
+		     _("notification options"), config_option_row_type,
+		     config_option_height, print_config_option);
 	listbox_load_items(&lb, 8);
 	listbox_draw_deco(&lb, 0);
 	status_mesg("", "");
diff --git a/src/ui-day.c b/src/ui-day.c
index d1e5843..e52b6d2 100644
--- a/src/ui-day.c
+++ b/src/ui-day.c
@@ -843,6 +843,11 @@ void ui_day_draw(int n, WINDOW *win, int y, int hilt, void *cb_data)
 
 }
 
+enum listbox_row_type ui_day_row_type(int i, void *cb_data)
+{
+	return LISTBOX_ROW_TEXT;
+}
+
 int ui_day_height(int n, void *cb_data)
 {
 	struct day_item *item = day_get_item(n);
diff --git a/src/ui-todo.c b/src/ui-todo.c
index c7b29f4..8daf283 100644
--- a/src/ui-todo.c
+++ b/src/ui-todo.c
@@ -177,6 +177,11 @@ void ui_todo_draw(int n, WINDOW *win, int y, int hilt, void *cb_data)
 	*((llist_item_t **)cb_data) = i->next;
 }
 
+enum listbox_row_type ui_todo_row_type(int i, void *cb_data)
+{
+	return LISTBOX_ROW_TEXT;
+}
+
 int ui_todo_height(int n, void *cb_data)
 {
 	return 1;
diff --git a/src/wins.c b/src/wins.c
index 729e014..0c12d8e 100644
--- a/src/wins.c
+++ b/src/wins.c
@@ -250,11 +250,13 @@ static void wins_init_panels(void)
 			    wins_sbar_width(), _("Calendar"));
 
 	listbox_init(&lb_apt, win[APP].y, win[APP].x, win[APP].h, win[APP].w,
-		     _("Appointments"), ui_day_height, ui_day_draw);
+		     _("Appointments"), ui_day_row_type, ui_day_height,
+		     ui_day_draw);
 	ui_day_load_items();
 
 	listbox_init(&lb_todo, win[TOD].y, win[TOD].x, win[TOD].h, win[TOD].w,
-		     _("TODO"), ui_todo_height, ui_todo_draw);
+		     _("TODO"), ui_todo_row_type, ui_todo_height,
+		     ui_todo_draw);
 	ui_todo_load_items();
 }
 
-- 
cgit v1.2.3-70-g09d2