summary refs log tree commit diff
path: root/ui.c
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2018-09-02 14:04:05 -0400
committerJune McEnroe <june@causal.agency>2018-09-02 14:04:05 -0400
commitccb54d36d93d22b00858a167beb6e0f728986b7a (patch)
treece4ebaf6882daed4179489bd7f8fee759efb03a5 /ui.c
parentWrite terminating null in allocating wcs/mbs conversions (diff)
downloadcatgirl-ccb54d36d93d22b00858a167beb6e0f728986b7a.tar.gz
catgirl-ccb54d36d93d22b00858a167beb6e0f728986b7a.zip
Add status indicators
Diffstat (limited to '')
-rw-r--r--ui.c234
1 files changed, 139 insertions, 95 deletions
diff --git a/ui.c b/ui.c
index 7cfb417..8b3c1eb 100644
--- a/ui.c
+++ b/ui.c
@@ -37,7 +37,7 @@
 #define MIN(a, b) ((a) < (b) ? (a) : (b))
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
 
-#define CTRL(c)   ((c) & 037)
+#define CTRL(c)   ((c) ^ 0100)
 
 static void colorInit(void) {
 	start_color();
@@ -105,9 +105,7 @@ static void viewRemove(struct View *view) {
 	views.tags[view->tag.id] = NULL;
 }
 
-static const int LOG_LINES   = 256;
-static const int TOPIC_COLS  = 512;
-static const int INPUT_COLS  = 512;
+static const int LOG_LINES = 256;
 
 static int logHeight(const struct View *view) {
 	return LINES - (view->topic ? 2 : 0) - 2;
@@ -141,6 +139,13 @@ static struct View *viewTag(struct Tag tag) {
 	return view;
 }
 
+static void viewResize(void) {
+	for (struct View *view = views.head; view; view = view->next) {
+		wresize(view->log, LOG_LINES, COLS);
+		wmove(view->log, lastLogLine(), lastCol());
+	}
+}
+
 static void viewClose(struct View *view) {
 	viewRemove(view);
 	if (view->topic) delwin(view->topic);
@@ -157,61 +162,29 @@ static void viewUnmark(struct View *view) {
 	view->mark = false;
 }
 
+static const int COLS_MAX = 512;
+
 static struct {
 	bool hide;
 	struct View *view;
+	WINDOW *status;
 	WINDOW *input;
 } ui;
 
-void uiHide(void) {
-	ui.hide = true;
-	termMode(TERM_FOCUS, false);
-	endwin();
-}
-
 static void uiShow(void) {
 	ui.hide = false;
 	termMode(TERM_FOCUS, true);
 }
 
-void uiInit(void) {
-	setlocale(LC_CTYPE, "");
-	initscr();
-	cbreak();
-	noecho();
-
-	colorInit();
-	termInit();
-
-	ui.input = newpad(2, INPUT_COLS);
-	mvwhline(ui.input, 0, 0, ACS_HLINE, INPUT_COLS);
-	wmove(ui.input, 1, 0);
-	keypad(ui.input, true);
-	nodelay(ui.input, true);
-
-	ui.view = viewTag(TAG_STATUS);
-	termTitle(TAG_STATUS.name);
-
-	uiShow();
-}
-
-void uiExit(void) {
-	uiHide();
-	printf(
-		"This program is AGPLv3 free software!\n"
-		"The source is available at <" SOURCE_URL ">.\n"
-	);
-}
-
-static void uiResize(void) {
-	for (struct View *view = views.head; view; view = view->next) {
-		wresize(view->log, LOG_LINES, COLS);
-		wmove(view->log, lastLogLine(), lastCol());
-	}
+void uiHide(void) {
+	ui.hide = true;
+	termMode(TERM_FOCUS, false);
+	endwin();
 }
 
 void uiDraw(void) {
 	if (ui.hide) return;
+
 	if (ui.view->topic) {
 		pnoutrefresh(
 			ui.view->topic,
@@ -220,20 +193,31 @@ void uiDraw(void) {
 			1, lastCol()
 		);
 	}
+
 	pnoutrefresh(
 		ui.view->log,
 		ui.view->scroll - logHeight(ui.view), 0,
 		(ui.view->topic ? 2 : 0), 0,
 		lastLine() - 2, lastCol()
 	);
+
 	int _, x;
+	getyx(ui.status, _, x);
+	pnoutrefresh(
+		ui.status,
+		0, MAX(0, x - lastCol() - 1),
+		lastLine() - 1, 0,
+		lastLine() - 1, lastCol()
+	);
+
 	getyx(ui.input, _, x);
 	pnoutrefresh(
 		ui.input,
 		0, MAX(0, x - lastCol() + 3),
-		lastLine() - 1, 0,
+		lastLine(), 0,
 		lastLine(), lastCol()
 	);
+
 	doupdate();
 }
 
@@ -241,52 +225,6 @@ static void uiRedraw(void) {
 	clearok(curscr, true);
 }
 
-static void uiView(struct View *view) {
-	termTitle(view->tag.name);
-	if (view->topic) touchwin(view->topic);
-	touchwin(view->log);
-	viewMark(ui.view);
-	viewUnmark(view);
-	ui.view = view;
-}
-
-static void uiClose(struct View *view) {
-	if (ui.view == view) {
-		if (view->next) {
-			uiView(view->next);
-		} else if (view->prev) {
-			uiView(view->prev);
-		} else {
-			return;
-		}
-	}
-	viewClose(view);
-}
-
-void uiViewTag(struct Tag tag) {
-	uiView(viewTag(tag));
-}
-
-void uiCloseTag(struct Tag tag) {
-	uiClose(viewTag(tag));
-}
-
-void uiViewNum(int num) {
-	if (num < 0) {
-		for (struct View *view = views.tail; view; view = view->prev) {
-			if (++num) continue;
-			uiView(view);
-			break;
-		}
-	} else {
-		for (struct View *view = views.head; view; view = view->next) {
-			if (num--) continue;
-			uiView(view);
-			break;
-		}
-	}
-}
-
 static const short IRC_COLORS[] = {
 	[IRC_WHITE]       = 8 + COLOR_WHITE,
 	[IRC_BLACK]       = 0 + COLOR_BLACK,
@@ -381,11 +319,88 @@ static void addIRC(WINDOW *win, const wchar_t *str) {
 	}
 }
 
+static void uiStatus(void) {
+	mvwhline(ui.status, 0, 0, ACS_HLINE, COLS);
+	mvwaddch(ui.status, 0, COLS, ACS_RTEE);
+
+	int num = 0;
+	int count = 0;
+	for (const struct View *view = views.head; view; view = view->next, ++num) {
+		if (!view->unread) continue;
+		bool status = (view->tag.id == TAG_STATUS.id);
+
+		int unread;
+		wchar_t *str;
+		int len = aswprintf(
+			&str, L",\3%02d%d\3%s%s%n(%d)",
+			(view->hot ? IRC_YELLOW : IRC_WHITE), num,
+			&status[":"], (status ? "" : view->tag.name),
+			&unread, view->unread
+		);
+		if (len < 0) err(EX_OSERR, "aswprintf");
+		if (view->unread == 1) str[unread] = L'\0';
+
+		addIRC(ui.status, count ? str : &str[1]);
+		free(str);
+		count++;
+	}
+
+	waddch(ui.status, count ? ACS_LTEE : '\b');
+	waddch(ui.status, ACS_HLINE);
+}
+
+static void uiView(struct View *view) {
+	termTitle(view->tag.name);
+	if (view->topic) touchwin(view->topic);
+	touchwin(view->log);
+	viewMark(ui.view);
+	viewUnmark(view);
+	ui.view = view;
+	uiStatus();
+}
+
+static void uiClose(struct View *view) {
+	if (ui.view == view) {
+		if (view->next) {
+			uiView(view->next);
+		} else if (view->prev) {
+			uiView(view->prev);
+		} else {
+			return;
+		}
+	}
+	viewClose(view);
+}
+
+void uiViewTag(struct Tag tag) {
+	uiView(viewTag(tag));
+}
+
+void uiCloseTag(struct Tag tag) {
+	uiClose(viewTag(tag));
+}
+
+void uiViewNum(int num) {
+	if (num < 0) {
+		for (struct View *view = views.tail; view; view = view->prev) {
+			if (++num) continue;
+			uiView(view);
+			break;
+		}
+	} else {
+		for (struct View *view = views.head; view; view = view->next) {
+			if (num--) continue;
+			uiView(view);
+			break;
+		}
+	}
+}
+
 void uiTopic(struct Tag tag, const char *topic) {
 	struct View *view = viewTag(tag);
 	if (!view->topic) {
-		view->topic = newpad(2, TOPIC_COLS);
-		mvwhline(view->topic, 1, 0, ACS_HLINE, TOPIC_COLS);
+		view->topic = newpad(2, COLS_MAX);
+		mvwhline(view->topic, 1, 0, ACS_HLINE, COLS_MAX);
 	}
 	wchar_t *wcs = ambstowcs(topic);
 	if (!wcs) err(EX_DATAERR, "ambstowcs");
@@ -404,6 +419,7 @@ void uiLog(struct Tag tag, enum UIHeat heat, const wchar_t *line) {
 			view->hot = true;
 			beep(); // TODO: Notification.
 		}
+		uiStatus();
 	}
 	addIRC(view->log, line);
 }
@@ -419,6 +435,34 @@ void uiFmt(struct Tag tag, enum UIHeat heat, const wchar_t *format, ...) {
 	free(wcs);
 }
 
+void uiInit(void) {
+	setlocale(LC_CTYPE, "");
+	initscr();
+	cbreak();
+	noecho();
+
+	colorInit();
+	termInit();
+
+	ui.status = newpad(1, COLS_MAX);
+	ui.input = newpad(1, COLS_MAX);
+	keypad(ui.input, true);
+	nodelay(ui.input, true);
+
+	ui.view = viewTag(TAG_STATUS);
+	uiViewTag(TAG_STATUS);
+	uiStatus();
+	uiShow();
+}
+
+void uiExit(void) {
+	uiHide();
+	printf(
+		"This program is AGPLv3 free software!\n"
+		"The source is available at <" SOURCE_URL ">.\n"
+	);
+}
+
 static void logScrollUp(int lines) {
 	int height = logHeight(ui.view);
 	if (ui.view->scroll == height) return;
@@ -505,7 +549,7 @@ static bool keyChar(wchar_t ch) {
 
 static bool keyCode(wchar_t ch) {
 	switch (ch) {
-		break; case KEY_RESIZE:    uiResize(); return false;
+		break; case KEY_RESIZE:    viewResize(); return false;
 		break; case KEY_SLEFT:     logScrollUp(1); return false;
 		break; case KEY_SRIGHT:    logScrollDown(1); return false;
 		break; case KEY_PPAGE:     logPageUp(); return false;
@@ -537,7 +581,7 @@ void uiRead(void) {
 	if (!update) return;
 
 	int y, x;
-	wmove(ui.input, 1, 0);
+	wmove(ui.input, 0, 0);
 	addIRC(ui.input, editHead());
 	getyx(ui.input, y, x);
 	addIRC(ui.input, editTail());