summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--catgirl.14
-rw-r--r--chat.h1
-rw-r--r--command.c12
-rw-r--r--ui.c213
4 files changed, 129 insertions, 101 deletions
diff --git a/catgirl.1 b/catgirl.1
index e9a35d8..ca3976b 100644
--- a/catgirl.1
+++ b/catgirl.1
@@ -1,4 +1,4 @@
-.Dd February 12, 2020
+.Dd February 13, 2020
 .Dt CATGIRL 1
 .Os
 .
@@ -286,6 +286,8 @@ Type
 .Ic q
 to return to
 .Nm .
+.It Ic /move Oo Ar name Oc Ar num
+Move named window to number.
 .It Ic /open Op Ar count
 Open each of
 .Ar count
diff --git a/chat.h b/chat.h
index f0dc6cb..0ef0952 100644
--- a/chat.h
+++ b/chat.h
@@ -169,6 +169,7 @@ void uiHide(void);
 void uiDraw(void);
 void uiShowID(size_t id);
 void uiShowNum(size_t num);
+void uiMoveID(size_t id, size_t num);
 void uiCloseID(size_t id);
 void uiCloseNum(size_t id);
 void uiRead(void);
diff --git a/command.c b/command.c
index 8166e2b..f801666 100644
--- a/command.c
+++ b/command.c
@@ -159,6 +159,17 @@ static void commandWindow(size_t id, char *params) {
 	}
 }
 
+static void commandMove(size_t id, char *params) {
+	if (!params) return;
+	char *name = strsep(&params, " ");
+	if (params) {
+		id = idFind(name);
+		if (id) uiMoveID(id, strtoul(params, NULL, 10));
+	} else {
+		uiMoveID(id, strtoul(name, NULL, 10));
+	}
+}
+
 static void commandClose(size_t id, char *params) {
 	if (!params) {
 		uiCloseID(id);
@@ -213,6 +224,7 @@ static const struct Handler {
 	{ "/join", .fn = commandJoin, .restricted = true },
 	{ "/list", .fn = commandList },
 	{ "/me", .fn = commandMe },
+	{ "/move", .fn = commandMove },
 	{ "/msg", .fn = commandMsg, .restricted = true },
 	{ "/names", .fn = commandNames },
 	{ "/nick", .fn = commandNick },
diff --git a/ui.c b/ui.c
index 0bf619d..b1eedaf 100644
--- a/ui.c
+++ b/ui.c
@@ -86,38 +86,52 @@ struct Window {
 	enum Heat heat;
 	int unreadCount;
 	int unreadLines;
-	struct Window *prev;
-	struct Window *next;
 };
 
 static struct {
-	struct Window *active;
-	struct Window *other;
-	struct Window *head;
-	struct Window *tail;
+	size_t show;
+	size_t swap;
+	struct Window *ptrs[IDCap];
+	size_t len;
 } windows;
 
-static void windowAdd(struct Window *window) {
-	if (windows.tail) windows.tail->next = window;
-	window->prev = windows.tail;
-	window->next = NULL;
-	windows.tail = window;
-	if (!windows.head) windows.head = window;
+static size_t windowPush(struct Window *window) {
+	assert(windows.len < IDCap);
+	windows.ptrs[windows.len] = window;
+	return windows.len++;
+}
+
+static size_t windowInsert(size_t num, struct Window *window) {
+	assert(windows.len < IDCap);
+	assert(num <= windows.len);
+	memmove(
+		&windows.ptrs[num + 1],
+		&windows.ptrs[num],
+		sizeof(*windows.ptrs) * (windows.len - num)
+	);
+	windows.ptrs[num] = window;
+	windows.len++;
+	return num;
 }
 
-static void windowRemove(struct Window *window) {
-	if (window->prev) window->prev->next = window->next;
-	if (window->next) window->next->prev = window->prev;
-	if (windows.head == window) windows.head = window->next;
-	if (windows.tail == window) windows.tail = window->prev;
+static struct Window *windowRemove(size_t num) {
+	assert(num < windows.len);
+	struct Window *window = windows.ptrs[num];
+	windows.len--;
+	memmove(
+		&windows.ptrs[num],
+		&windows.ptrs[num + 1],
+		sizeof(*windows.ptrs) * (windows.len - num)
+	);
+	return window;
 }
 
-static struct Window *windowFor(size_t id) {
-	struct Window *window;
-	for (window = windows.head; window; window = window->next) {
-		if (window->id == id) return window;
+static size_t windowFor(size_t id) {
+	for (size_t num = 0; num < windows.len; ++num) {
+		if (windows.ptrs[num]->id == id) return num;
 	}
-	window = calloc(1, sizeof(*window));
+
+	struct Window *window = calloc(1, sizeof(*window));
 	if (!window) err(EX_OSERR, "malloc");
 
 	window->id = id;
@@ -127,8 +141,15 @@ static struct Window *windowFor(size_t id) {
 	wmove(window->pad, WindowLines - 1, 0);
 	window->mark = true;
 
-	windowAdd(window);
-	return window;
+	return windowPush(window);
+}
+
+static void windowFree(struct Window *window) {
+	for (size_t i = 0; i < BufferCap; ++i) {
+		free(window->buffer.lines[i]);
+	}
+	delwin(window->pad);
+	free(window);
 }
 
 static short colorPairs;
@@ -256,7 +277,7 @@ void uiInit(void) {
 	keypad(input, true);
 	nodelay(input, true);
 
-	windows.active = windowFor(Network);
+	windowFor(Network);
 	uiShow();
 }
 
@@ -269,7 +290,7 @@ static char prevTitle[sizeof(title)];
 void uiDraw(void) {
 	if (hidden) return;
 	wnoutrefresh(status);
-	struct Window *window = windows.active;
+	const struct Window *window = windows.ptrs[windows.show];
 	pnoutrefresh(
 		window->pad,
 		WindowLines - window->scroll - PAGE_LINES + !!window->scroll, 0,
@@ -381,19 +402,18 @@ static void statusUpdate(void) {
 	enum Heat otherHeat = Cold;
 	wmove(status, 0, 0);
 
-	int num;
-	const struct Window *window;
-	for (num = 0, window = windows.head; window; ++num, window = window->next) {
-		if (!window->heat && window != windows.active) continue;
-		if (window != windows.active) {
+	for (size_t num = 0; num < windows.len; ++num) {
+		const struct Window *window = windows.ptrs[num];
+		if (!window->heat && num != windows.show) continue;
+		if (num != windows.show) {
 			otherUnread += window->unreadCount;
 			if (window->heat > otherHeat) otherHeat = window->heat;
 		}
 		int trunc;
 		char buf[256];
 		snprintf(
-			buf, sizeof(buf), "\3%d%s %d %s %n(\3%02d%d\3%d) ",
-			idColors[window->id], (window == windows.active ? "\26" : ""),
+			buf, sizeof(buf), "\3%d%s %zu %s %n(\3%02d%d\3%d) ",
+			idColors[window->id], (num == windows.show ? "\26" : ""),
 			num, idNames[window->id],
 			&trunc, (window->heat > Warm ? White : idColors[window->id]),
 			window->unreadCount,
@@ -404,7 +424,7 @@ static void statusUpdate(void) {
 	}
 	wclrtoeol(status);
 
-	window = windows.active;
+	const struct Window *window = windows.ptrs[windows.show];
 	snprintf(title, sizeof(title), "%s %s", self.network, idNames[window->id]);
 	if (window->mark && window->unreadCount) {
 		snprintf(
@@ -441,11 +461,11 @@ void uiShow(void) {
 	putp(EnterPasteMode);
 	fflush(stdout);
 	hidden = false;
-	unmark(windows.active);
+	unmark(windows.ptrs[windows.show]);
 }
 
 void uiHide(void) {
-	mark(windows.active);
+	mark(windows.ptrs[windows.show]);
 	hidden = true;
 	putp(ExitFocusMode);
 	putp(ExitPasteMode);
@@ -560,7 +580,7 @@ static void notify(size_t id, const char *str) {
 }
 
 void uiWrite(size_t id, enum Heat heat, const time_t *src, const char *str) {
-	struct Window *window = windowFor(id);
+	struct Window *window = windows.ptrs[windowFor(id)];
 	time_t clock = (src ? *src : time(NULL));
 	bufferPush(&window->buffer, clock, str);
 
@@ -614,11 +634,11 @@ static void reflow(struct Window *window) {
 static void resize(void) {
 	mvwin(marker, LINES - 2, 0);
 	int height, width;
-	getmaxyx(windows.active->pad, height, width);
+	getmaxyx(windows.ptrs[0]->pad, height, width);
 	if (width == COLS) return;
-	for (struct Window *window = windows.head; window; window = window->next) {
-		wresize(window->pad, BufferCap, COLS);
-		reflow(window);
+	for (size_t num = 0; num < windows.len; ++num) {
+		wresize(windows.ptrs[num]->pad, BufferCap, COLS);
+		reflow(windows.ptrs[num]);
 	}
 	(void)height;
 	statusUpdate();
@@ -690,7 +710,7 @@ static void inputAdd(struct Style *style, const char *str) {
 }
 
 static void inputUpdate(void) {
-	size_t id = windows.active->id;
+	size_t id = windows.ptrs[windows.show]->id;
 	size_t pos;
 	char *buf = editBuffer(&pos);
 
@@ -743,13 +763,12 @@ static void inputUpdate(void) {
 	wmove(input, y, x);
 }
 
-static void windowShow(struct Window *window) {
-	if (!window) return;
-	touchwin(window->pad);
-	windows.other = windows.active;
-	windows.active = window;
-	mark(windows.other);
-	unmark(windows.active);
+static void windowShow(size_t num) {
+	touchwin(windows.ptrs[num]->pad);
+	windows.swap = windows.show;
+	windows.show = num;
+	mark(windows.ptrs[windows.swap]);
+	unmark(windows.ptrs[windows.show]);
 	inputUpdate();
 }
 
@@ -758,31 +777,29 @@ void uiShowID(size_t id) {
 }
 
 void uiShowNum(size_t num) {
-	struct Window *window = windows.head;
-	for (size_t i = 0; i < num; ++i) {
-		window = window->next;
-		if (!window) return;
+	if (num < windows.len) windowShow(num);
+}
+
+void uiMoveID(size_t id, size_t num) {
+	struct Window *window = windowRemove(windowFor(id));
+	if (num < windows.len) {
+		windowShow(windowInsert(num, window));
+	} else {
+		windowShow(windowPush(window));
 	}
-	windowShow(window);
 }
 
-static void windowClose(struct Window *window) {
-	if (window->id == Network) return;
+static void windowClose(size_t num) {
+	if (windows.ptrs[num]->id == Network) return;
+	struct Window *window = windowRemove(num);
 	completeClear(window->id);
-	if (windows.active == window) {
-		if (windows.other && windows.other != window) {
-			windowShow(windows.other);
-		} else {
-			windowShow(window->prev ? window->prev : window->next);
-		}
-	}
-	if (windows.other == window) windows.other = NULL;
-	windowRemove(window);
-	for (size_t i = 0; i < BufferCap; ++i) {
-		free(window->buffer.lines[i]);
+	windowFree(window);
+	if (windows.swap >= num) windows.swap--;
+	if (windows.show == num) {
+		windowShow(windows.swap);
+	} else if (windows.show > num) {
+		windows.show--;
 	}
-	delwin(window->pad);
-	free(window);
 	statusUpdate();
 }
 
@@ -791,36 +808,31 @@ void uiCloseID(size_t id) {
 }
 
 void uiCloseNum(size_t num) {
-	struct Window *window = windows.head;
-	for (size_t i = 0; i < num; ++i) {
-		window = window->next;
-		if (!window) return;
-	}
-	windowClose(window);
+	if (num < windows.len) windowClose(num);
 }
 
 static void showAuto(void) {
-	static struct Window *other;
-	if (windows.other != other) {
-		other = windows.active;
-	}
-	for (struct Window *window = windows.head; window; window = window->next) {
-		if (window->heat < Hot) continue;
-		windowShow(window);
-		windows.other = other;
+	static size_t swap;
+	if (windows.swap != swap) {
+		swap = windows.show;
+	}
+	for (size_t num = 0; num < windows.len; ++num) {
+		if (windows.ptrs[num]->heat < Hot) continue;
+		windowShow(num);
+		windows.swap = swap;
 		return;
 	}
-	for (struct Window *window = windows.head; window; window = window->next) {
-		if (window->heat < Warm) continue;
-		windowShow(window);
-		windows.other = other;
+	for (size_t num = 0; num < windows.len; ++num) {
+		if (windows.ptrs[num]->heat < Warm) continue;
+		windowShow(num);
+		windows.swap = swap;
 		return;
 	}
-	windowShow(windows.other);
+	windowShow(windows.swap);
 }
 
 static void keyCode(int code) {
-	struct Window *window = windows.active;
+	struct Window *window = windows.ptrs[windows.show];
 	size_t id = window->id;
 	switch (code) {
 		break; case KEY_RESIZE:  resize();
@@ -829,7 +841,7 @@ static void keyCode(int code) {
 		break; case KeyPasteOn:; // TODO
 		break; case KeyPasteOff:; // TODO
 
-		break; case KeyMetaSlash: windowShow(windows.other);
+		break; case KeyMetaSlash: windowShow(windows.swap);
 
 		break; case KeyMetaA: showAuto();
 		break; case KeyMetaB: edit(id, EditPrevWord, 0);
@@ -861,7 +873,8 @@ static void keyCode(int code) {
 }
 
 static void keyCtrl(wchar_t ch) {
-	size_t id = windows.active->id;
+	struct Window *window = windows.ptrs[windows.show];
+	size_t id = window->id;
 	switch (ch ^ L'@') {
 		break; case L'?': edit(id, EditDeletePrev, 0);
 		break; case L'A': edit(id, EditHead, 0);
@@ -875,19 +888,19 @@ static void keyCtrl(wchar_t ch) {
 		break; case L'J': edit(id, EditEnter, 0);
 		break; case L'K': edit(id, EditDeleteTail, 0);
 		break; case L'L': clearok(curscr, true);
-		break; case L'N': windowShow(windows.active->next);
-		break; case L'O': windowShow(windows.other);
-		break; case L'P': windowShow(windows.active->prev);
+		break; case L'N': uiShowNum(windows.show + 1);
+		break; case L'O': windowShow(windows.swap);
+		break; case L'P': uiShowNum(windows.show - 1);
 		break; case L'T': edit(id, EditTranspose, 0);
 		break; case L'U': edit(id, EditDeleteHead, 0);
-		break; case L'V': windowScroll(windows.active, -(PAGE_LINES - 2));
+		break; case L'V': windowScroll(window, -(PAGE_LINES - 2));
 		break; case L'W': edit(id, EditDeletePrevWord, 0);
 		break; case L'Y': edit(id, EditPaste, 0);
 	}
 }
 
 static void keyStyle(wchar_t ch) {
-	size_t id = windows.active->id;
+	size_t id = windows.ptrs[windows.show]->id;
 	switch (iswcntrl(ch) ? ch ^ L'@' : (wchar_t)towupper(ch)) {
 		break; case L'B': edit(id, EditInsert, B);
 		break; case L'C': edit(id, EditInsert, C);
@@ -923,7 +936,7 @@ void uiRead(void) {
 		} else if (iswcntrl(ch)) {
 			keyCtrl(ch);
 		} else {
-			edit(windows.active->id, EditInsert, ch);
+			edit(windows.ptrs[windows.show]->id, EditInsert, ch);
 		}
 		style = false;
 	}
@@ -953,8 +966,8 @@ int uiSave(const char *name) {
 	if (!file) return -1;
 
 	if (writeTime(file, Signatures[0])) return -1;
-	const struct Window *window;
-	for (window = windows.head; window; window = window->next) {
+	for (size_t num = 0; num < windows.len; ++num) {
+		const struct Window *window = windows.ptrs[num];
 		if (writeString(file, idNames[window->id])) return -1;
 		for (size_t i = 0; i < BufferCap; ++i) {
 			time_t time = bufferTime(&window->buffer, i);
@@ -1003,7 +1016,7 @@ void uiLoad(const char *name) {
 	char *buf = NULL;
 	size_t cap = 0;
 	while (0 < readString(file, &buf, &cap)) {
-		struct Window *window = windowFor(idFor(buf));
+		struct Window *window = windows.ptrs[windowFor(idFor(buf))];
 		for (;;) {
 			time_t time = readTime(file);
 			if (!time) break;