Add status indicators
parent
c58baa84ee
commit
ccb54d36d9
234
ui.c
234
ui.c
|
@ -37,7 +37,7 @@
|
||||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
#define MAX(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) {
|
static void colorInit(void) {
|
||||||
start_color();
|
start_color();
|
||||||
|
@ -106,8 +106,6 @@ static void viewRemove(struct View *view) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static const int LOG_LINES = 256;
|
static const int LOG_LINES = 256;
|
||||||
static const int TOPIC_COLS = 512;
|
|
||||||
static const int INPUT_COLS = 512;
|
|
||||||
|
|
||||||
static int logHeight(const struct View *view) {
|
static int logHeight(const struct View *view) {
|
||||||
return LINES - (view->topic ? 2 : 0) - 2;
|
return LINES - (view->topic ? 2 : 0) - 2;
|
||||||
|
@ -141,6 +139,13 @@ static struct View *viewTag(struct Tag tag) {
|
||||||
return view;
|
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) {
|
static void viewClose(struct View *view) {
|
||||||
viewRemove(view);
|
viewRemove(view);
|
||||||
if (view->topic) delwin(view->topic);
|
if (view->topic) delwin(view->topic);
|
||||||
|
@ -157,61 +162,29 @@ static void viewUnmark(struct View *view) {
|
||||||
view->mark = false;
|
view->mark = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const int COLS_MAX = 512;
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
bool hide;
|
bool hide;
|
||||||
struct View *view;
|
struct View *view;
|
||||||
|
WINDOW *status;
|
||||||
WINDOW *input;
|
WINDOW *input;
|
||||||
} ui;
|
} ui;
|
||||||
|
|
||||||
|
static void uiShow(void) {
|
||||||
|
ui.hide = false;
|
||||||
|
termMode(TERM_FOCUS, true);
|
||||||
|
}
|
||||||
|
|
||||||
void uiHide(void) {
|
void uiHide(void) {
|
||||||
ui.hide = true;
|
ui.hide = true;
|
||||||
termMode(TERM_FOCUS, false);
|
termMode(TERM_FOCUS, false);
|
||||||
endwin();
|
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 uiDraw(void) {
|
void uiDraw(void) {
|
||||||
if (ui.hide) return;
|
if (ui.hide) return;
|
||||||
|
|
||||||
if (ui.view->topic) {
|
if (ui.view->topic) {
|
||||||
pnoutrefresh(
|
pnoutrefresh(
|
||||||
ui.view->topic,
|
ui.view->topic,
|
||||||
|
@ -220,20 +193,31 @@ void uiDraw(void) {
|
||||||
1, lastCol()
|
1, lastCol()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pnoutrefresh(
|
pnoutrefresh(
|
||||||
ui.view->log,
|
ui.view->log,
|
||||||
ui.view->scroll - logHeight(ui.view), 0,
|
ui.view->scroll - logHeight(ui.view), 0,
|
||||||
(ui.view->topic ? 2 : 0), 0,
|
(ui.view->topic ? 2 : 0), 0,
|
||||||
lastLine() - 2, lastCol()
|
lastLine() - 2, lastCol()
|
||||||
);
|
);
|
||||||
|
|
||||||
int _, x;
|
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);
|
getyx(ui.input, _, x);
|
||||||
pnoutrefresh(
|
pnoutrefresh(
|
||||||
ui.input,
|
ui.input,
|
||||||
0, MAX(0, x - lastCol() + 3),
|
0, MAX(0, x - lastCol() + 3),
|
||||||
lastLine() - 1, 0,
|
lastLine(), 0,
|
||||||
lastLine(), lastCol()
|
lastLine(), lastCol()
|
||||||
);
|
);
|
||||||
|
|
||||||
doupdate();
|
doupdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,52 +225,6 @@ static void uiRedraw(void) {
|
||||||
clearok(curscr, true);
|
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[] = {
|
static const short IRC_COLORS[] = {
|
||||||
[IRC_WHITE] = 8 + COLOR_WHITE,
|
[IRC_WHITE] = 8 + COLOR_WHITE,
|
||||||
[IRC_BLACK] = 0 + COLOR_BLACK,
|
[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) {
|
void uiTopic(struct Tag tag, const char *topic) {
|
||||||
struct View *view = viewTag(tag);
|
struct View *view = viewTag(tag);
|
||||||
if (!view->topic) {
|
if (!view->topic) {
|
||||||
view->topic = newpad(2, TOPIC_COLS);
|
view->topic = newpad(2, COLS_MAX);
|
||||||
mvwhline(view->topic, 1, 0, ACS_HLINE, TOPIC_COLS);
|
mvwhline(view->topic, 1, 0, ACS_HLINE, COLS_MAX);
|
||||||
}
|
}
|
||||||
wchar_t *wcs = ambstowcs(topic);
|
wchar_t *wcs = ambstowcs(topic);
|
||||||
if (!wcs) err(EX_DATAERR, "ambstowcs");
|
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;
|
view->hot = true;
|
||||||
beep(); // TODO: Notification.
|
beep(); // TODO: Notification.
|
||||||
}
|
}
|
||||||
|
uiStatus();
|
||||||
}
|
}
|
||||||
addIRC(view->log, line);
|
addIRC(view->log, line);
|
||||||
}
|
}
|
||||||
|
@ -419,6 +435,34 @@ void uiFmt(struct Tag tag, enum UIHeat heat, const wchar_t *format, ...) {
|
||||||
free(wcs);
|
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) {
|
static void logScrollUp(int lines) {
|
||||||
int height = logHeight(ui.view);
|
int height = logHeight(ui.view);
|
||||||
if (ui.view->scroll == height) return;
|
if (ui.view->scroll == height) return;
|
||||||
|
@ -505,7 +549,7 @@ static bool keyChar(wchar_t ch) {
|
||||||
|
|
||||||
static bool keyCode(wchar_t ch) {
|
static bool keyCode(wchar_t ch) {
|
||||||
switch (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_SLEFT: logScrollUp(1); return false;
|
||||||
break; case KEY_SRIGHT: logScrollDown(1); return false;
|
break; case KEY_SRIGHT: logScrollDown(1); return false;
|
||||||
break; case KEY_PPAGE: logPageUp(); return false;
|
break; case KEY_PPAGE: logPageUp(); return false;
|
||||||
|
@ -537,7 +581,7 @@ void uiRead(void) {
|
||||||
if (!update) return;
|
if (!update) return;
|
||||||
|
|
||||||
int y, x;
|
int y, x;
|
||||||
wmove(ui.input, 1, 0);
|
wmove(ui.input, 0, 0);
|
||||||
addIRC(ui.input, editHead());
|
addIRC(ui.input, editHead());
|
||||||
getyx(ui.input, y, x);
|
getyx(ui.input, y, x);
|
||||||
addIRC(ui.input, editTail());
|
addIRC(ui.input, editTail());
|
||||||
|
|
Loading…
Reference in New Issue