diff --git a/src/chat.c b/src/chat.c index 39c806d..8353ff9 100644 --- a/src/chat.c +++ b/src/chat.c @@ -342,9 +342,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) else if (key == KEY_END) { /* END key: move cursor to end of line */ if (ctx->pos != ctx->len) { ctx->pos = ctx->len; - int end_y = (ctx->len / x2) + (y2 - CURS_Y_OFFSET); - int end_x = ctx->len % x2; - wmove(self->window, end_y, end_x); + mv_curs_end(self->window, ctx->len, y2, x2); } } @@ -370,6 +368,22 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) } } + else if (key == KEY_UP) { /* fetches previous item in history */ + if (ctx->hst_pos >= 0) { + fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, &ctx->hst_tot, + &ctx->hst_pos, LN_HIST_MV_UP); + mv_curs_end(self->window, ctx->len, y2, x2); + } + } + + else if (key == KEY_DOWN) { /* fetches next item in history */ + if (ctx->hst_pos < ctx->hst_tot) { + fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, &ctx->hst_tot, + &ctx->hst_pos, LN_HIST_MV_DWN); + mv_curs_end(self->window, ctx->len, y2, x2); + } + } + else if (key == '\t') { /* TAB key: command */ if (ctx->len > 1 && ctx->line[0] == '/') { int diff = complete_line(ctx->line, &ctx->pos, &ctx->len, chat_cmd_list, AC_NUM_CHAT_COMMANDS, @@ -414,6 +428,9 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key) wclrtobot(self->window); bool close_win = false; + if (!string_is_empty(line)) + add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos); + if (line[0] == '/') { if (close_win = !strcmp(line, "/close")) { int f_num = self->num; diff --git a/src/groupchat.c b/src/groupchat.c index 4371d00..f3e1d2b 100644 --- a/src/groupchat.c +++ b/src/groupchat.c @@ -15,6 +15,7 @@ #include "misc_tools.h" #include "groupchat.h" #include "prompt.h" +#include "toxic_strings.h" extern char *DATA_FILE; extern int store_data(Tox *m, char *path); @@ -308,6 +309,22 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key) } } + else if (key == KEY_UP) { /* fetches previous item in history */ + if (ctx->hst_pos >= 0) { + fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, &ctx->hst_tot, + &ctx->hst_pos, LN_HIST_MV_UP); + mv_curs_end(self->window, ctx->len, y2, x2); + } + } + + else if (key == KEY_DOWN) { /* fetches next item in history */ + if (ctx->hst_pos < ctx->hst_tot) { + fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, &ctx->hst_tot, + &ctx->hst_pos, LN_HIST_MV_DWN); + mv_curs_end(self->window, ctx->len, y2, x2); + } + } + else if (key == '\t') { /* TAB key: completes peer name */ if (ctx->len > 0) { int diff; @@ -371,6 +388,9 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key) wclrtobot(self->window); bool close_win = false; + if (!string_is_empty(line)) + add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos); + if (line[0] == '/') { if (close_win = strcmp(line, "/close") == 0) { set_active_window(0); diff --git a/src/misc_tools.c b/src/misc_tools.c index cfa98fb..5cbc764 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -185,3 +185,11 @@ bool valid_nick(uint8_t *nick) return true; } + +/* Moves cursor to the end of the line in given window */ +void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x) +{ + int end_y = (len / max_x) + (max_y - CURS_Y_OFFSET); + int end_x = len % max_x; + wmove(w, end_y, end_x); +} \ No newline at end of file diff --git a/src/misc_tools.h b/src/misc_tools.h index 796a332..74e8c7c 100644 --- a/src/misc_tools.h +++ b/src/misc_tools.h @@ -43,3 +43,6 @@ int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2); - cannot start with a space - must not contain contiguous spaces */ bool valid_nick(uint8_t *nick); + +/* Moves the cursor to the end of the line in given window */ +void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x); diff --git a/src/prompt.c b/src/prompt.c index 6a0f6c9..af00edc 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -163,8 +163,25 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key) else if (key == KEY_RIGHT) { if (prt->pos < prt->len) ++prt->pos; + } - } else if (key == '\t') { /* TAB key: completes command */ + else if (key == KEY_UP) { /* fetches previous item in history */ + if (prt->hst_pos >= 0) { + wmove(self->window, prt->orig_y, X_OFST); + fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, &prt->hst_tot, + &prt->hst_pos, LN_HIST_MV_UP); + } + } + + else if (key == KEY_DOWN) { /* fetches next item in history */ + if (prt->hst_pos < prt->hst_tot) { + wmove(self->window, prt->orig_y, X_OFST); + fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, &prt->hst_tot, + &prt->hst_pos, LN_HIST_MV_DWN); + } + } + + else if (key == '\t') { /* TAB key: completes command */ if (prt->len > 1 && prt->line[0] == '/') complete_line(prt->line, &prt->pos, &prt->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE); @@ -190,6 +207,9 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key) if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1) memset(&line, 0, sizeof(line)); + if (!string_is_empty(line)) + add_line_to_hist(prt->line, prt->len, prt->ln_history, &prt->hst_tot, &prt->hst_pos); + execute(self->window, self, m, line, GLOBAL_COMMAND_MODE); reset_buf(prt->line, &prt->pos, &prt->len); } diff --git a/src/toxic_strings.c b/src/toxic_strings.c index c9e323a..83b7178 100644 --- a/src/toxic_strings.c +++ b/src/toxic_strings.c @@ -7,6 +7,7 @@ #include "toxic_windows.h" #include "misc_tools.h" +#include "toxic_strings.h" /* Adds char to buffer at pos */ void add_char_to_buf(wchar_t *buf, size_t *pos, size_t *len, wint_t ch) @@ -88,6 +89,53 @@ void reset_buf(wchar_t *buf, size_t *pos, size_t *len) *len = 0; } +/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to end of history. + Assumes entries are of size MAX_STR_SIZE */ +void add_line_to_hist(const wchar_t *buf, size_t len, void *ln_history, int *hst_tot, int *hst_pos) +{ + if (len > MAX_STR_SIZE) + return; + + wchar_t *hst = (wchar_t *) ln_history; + + /* If history is full make room for newest entry and don't increment hst_tot */ + if (*hst_tot == MAX_LINE_HIST) { + int i; + + for (i = 0; i < MAX_LINE_HIST - 1; ++i) + wmemcpy(&hst[MAX_STR_SIZE * i], &hst[MAX_STR_SIZE * (i + 1)], MAX_STR_SIZE); + } else { + ++(*hst_tot); + } + + *hst_pos = *hst_tot; + wmemcpy(&hst[(*hst_tot - 1) * MAX_STR_SIZE], buf, len + 1); +} + +/* copies history item at hst_pos to buf. Sets pos and len to the len of the history item. + hst_pos is decremented or incremented depending on key_dir. + Assumes history entries are of size MAX_STR_SIZE */ +void fetch_hist_item(wchar_t *buf, size_t *pos, size_t *len, const void *ln_history, int *hst_tot, + int *hst_pos, int key_dir) +{ + if (key_dir == LN_HIST_MV_UP) { + if (--(*hst_pos) < 0) + ++(*hst_pos); + } else { + if (++(*hst_pos) == *hst_tot) + --(*hst_pos); + } + + wchar_t *hst = (wchar_t *) ln_history; + const wchar_t *hst_line = &hst[*hst_pos * MAX_STR_SIZE]; + size_t h_len = wcslen(hst_line); + + wmemcpy(buf, hst_line, h_len + 1); + + *pos = h_len; + *len = h_len; +} + /* looks for the first instance in list that begins with the last entered word in buf according to pos, then fills buf with the complete word. e.g. "Hello jo" would complete the buffer with "Hello john". @@ -160,7 +208,7 @@ int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int if (char_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1) return -1; - wmemcpy(buf, newbuf, MAX_STR_SIZE); + wcscpy(buf, newbuf); *len += (size_t) diff; *pos += (size_t) diff; diff --git a/src/toxic_strings.h b/src/toxic_strings.h index 7d4874a..15d95a8 100644 --- a/src/toxic_strings.h +++ b/src/toxic_strings.h @@ -29,3 +29,18 @@ void reset_buf(wchar_t *buf, size_t *pos, size_t *len); Returns the difference between the old len and new len of buf on success, -1 if error */ int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int n_items, int size); + +enum { + LN_HIST_MV_UP, + LN_HIST_MV_DWN, +}; + +/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to last history item. + Assumes entries are of size MAX_STR_SIZE */ +void add_line_to_hist(const wchar_t *buf, size_t len, void *hst, int *hst_tot, int *hst_pos); + +/* copies history item at hst_pos to buf. Sets pos and len to the len of the history item. + hst_pos is decremented or incremented depending on key_dir. + Assumes history entries are of size MAX_STR_SIZE */ +void fetch_hist_item(wchar_t *buf, size_t *pos, size_t *len, const void *ln_history, int *hst_tot, + int *hst_pos, int key_dir); \ No newline at end of file diff --git a/src/toxic_windows.h b/src/toxic_windows.h index 9c5ef17..b345867 100644 --- a/src/toxic_windows.h +++ b/src/toxic_windows.h @@ -110,11 +110,18 @@ struct StatusBar { bool is_online; }; +#define MAX_LINE_HIST 64 + /* chat and groupchat window/buffer holder */ struct ChatContext { wchar_t line[MAX_STR_SIZE]; size_t pos; size_t len; + + wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE]; + int hst_pos; + int hst_tot; + WINDOW *history; WINDOW *linewin; WINDOW *sidebar; @@ -128,6 +135,11 @@ struct PromptBuf { bool at_bottom; /* true if line end is at bottom of window */ int orig_y; /* y axis point of line origin */ bool scroll; /* used for prompt window hack to determine when to scroll down */ + + wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE]; + int hst_pos; + int hst_tot; + WINDOW *linewin; };