Mercurial > ~mikael > mcabber > hg
diff mcabber/mcabber/screen.c @ 2304:fa8365fb6ac2
[PATCH 1/3] New option: vi_mode
If the new vi_mode option is set to 1, let MCabber's non-chat mode
accept a few commands loosely based on those available in vi(1)'s normal
mode, e.g.:
A Call "/roster unread_first".
a Call "/roster unread_next".
F Call "/roster group_prev".
f Call "/roster group_next".
G Call "/roster bottom".
gg Call "/roster top".
i Enter chat mode.
[<n>]j Call "/roster down [<n>]".
[<n>]k Call "/roster up [<n>]".
n Repeat the previous search (if any).
O Call "/roster unread_first" and open chat window.
o Call "/roster unread_next" and open chat window.
ZZ Call "/quit".
zM Call "/group fold" for all groups.
zR Call "/group unfold" for all groups.
<Space> Call "/group toggle" for the current group.
'' Call "/roster alternate".
! Toggle attention flag for current buddy.
# Toggle unread messages flag for current buddy.
/<str> Call "/roster search <str>".
:q Call "/quit".
:wq Call "/quit".
:x Call "/quit".
:<n> Jump to line <n> in the roster.
:<cmd> Call "/<cmd>" (unless <cmd> matches one of the above commands).
author | Holger Weiß <holger@zedat.fu-berlin.de> |
---|---|
date | Wed, 22 Jul 2015 19:25:22 +0200 |
parents | f181418db215 |
children | 5b1a63dc2b1a |
line wrap: on
line diff
--- a/mcabber/mcabber/screen.c Mon Jan 30 18:46:15 2017 +0100 +++ b/mcabber/mcabber/screen.c Wed Jul 22 19:25:22 2015 +0200 @@ -95,6 +95,9 @@ static void spellcheck(char *, char *); #endif +static void open_chat_window(void); +static void clear_inputline(void); + static GHashTable *winbufhash; typedef struct { @@ -4175,7 +4178,8 @@ static inline void refresh_inputline(void) { #if defined(WITH_ENCHANT) || defined(WITH_ASPELL) - if (settings_opt_get_int("spell_enable")) { + if (settings_opt_get_int("spell_enable") && + (chatmode || !settings_opt_get_int("vi_mode"))) { memset(maskLine, 0, INPUTLINE_LENGTH+1); spellcheck(inputLine, maskLine); } @@ -4426,12 +4430,53 @@ #endif } +static void scr_process_vi_arrow_key(int key) +{ + const char *l; + char mask[INPUTLINE_LENGTH+1] = "/roster search "; + size_t cmd_len = strlen(mask); + size_t str_len = strlen(inputLine) - 1; + + switch (inputLine[0]) { + case ':': + inputLine[0] = '/'; + if (key == KEY_UP) + l = scr_cmdhisto_prev(inputLine, ptr_inputline - inputLine); + else + l = scr_cmdhisto_next(inputLine, ptr_inputline - inputLine); + if (l) + strcpy(inputLine, l); + inputLine[0] = ':'; + break; + case '/': + if (cmd_len + str_len > INPUTLINE_LENGTH) + return; + + memcpy(mask + cmd_len, inputLine + 1, str_len + 1); + if (key == KEY_UP) + l = scr_cmdhisto_prev(mask, ptr_inputline - inputLine + cmd_len - 1); + else + l = scr_cmdhisto_next(mask, ptr_inputline - inputLine + cmd_len - 1); + if (l) + strcpy(inputLine + 1, l + cmd_len); + break; + default: + if (key == KEY_UP) + process_command(mkcmdstr("roster up"), TRUE); + else + process_command(mkcmdstr("roster down"), TRUE); + break; + } +} + // scr_process_key(key) // Handle the pressed key, in the command line (bottom). void scr_process_key(keycode kcode) { int key = kcode.value; int display_char = FALSE; + int vi_completion = FALSE; + static int ex_or_search_mode = FALSE; lock_chatstate = FALSE; @@ -4448,6 +4493,253 @@ key = ERR; // Do not process any further } + if (settings_opt_get_int("vi_mode") && !chatmode) { + int got_cmd_prefix = FALSE; + int unrecognized = FALSE; + static char search_cmd[INPUTLINE_LENGTH+1] = "/roster search "; + + if (key == KEY_UP || key == KEY_DOWN) { + scr_process_vi_arrow_key(key); + key = ERR; // Do not process any further + } else if (ex_or_search_mode) { + switch (key) { + case 27: // Escape + clear_inputline(); + ex_or_search_mode = FALSE; + break; + case 9: // Tab + case 353: // Shift-Tab + if (inputLine[0] == ':') { + inputLine[0] = '/'; + vi_completion = TRUE; + } + break; + case 13: // Enter + case 343: // Enter on Maemo + switch (inputLine[0]) { + case ':': + { + char *p = strchr(inputLine, 0); + + while (*--p == ' ' && p > inputLine) + *p = 0; + } + if (!strcmp(inputLine, ":x") || + !strcmp(inputLine, ":q") || + !strcmp(inputLine, ":wq")) + strcpy(inputLine, ":quit"); + if (isdigit((int)(unsigned char)inputLine[1]) && + strlen(inputLine) <= 9) { + process_command(mkcmdstr("roster top"), TRUE); + memcpy(inputLine + 13, inputLine + 1, 10); + memcpy(inputLine + 1, "roster down ", 12); + } + inputLine[0] = '/'; + process_command(inputLine, TRUE); + scr_cmdhisto_addline(inputLine); + break; + case '/': + { + size_t cmd_len = sizeof("/roster search ") - 1; + size_t str_len = strlen(inputLine) - 1; + + if (cmd_len + str_len > INPUTLINE_LENGTH) + return; + + memcpy(search_cmd + cmd_len, inputLine + 1, + str_len + 1); + } + process_command(search_cmd, TRUE); + scr_cmdhisto_addline(search_cmd); + break; + } + ex_or_search_mode = FALSE; + break; + } + } else if (key >= '0' && key <= '9') { + got_cmd_prefix = TRUE; + } else { + switch (key) { + case '/': + case ':': + ex_or_search_mode = TRUE; + break; + case ' ': + process_command(mkcmdstr("group toggle"), TRUE); + break; + case '!': + { + const char *bjid = buddy_getjid(BUDDATA(current_buddy)); + + if (bjid) { + guint type = buddy_gettype(BUDDATA(current_buddy)); + guint prio = buddy_getuiprio(BUDDATA(current_buddy)); + + if (type & ROSTER_TYPE_ROOM && + prio < ROSTER_UI_PRIO_MUC_HL_MESSAGE) { + roster_setuiprio(bjid, FALSE, + ROSTER_UI_PRIO_MUC_HL_MESSAGE, prio_set); + roster_msg_setflag(bjid, FALSE, TRUE); + } else if (type & ROSTER_TYPE_USER && + prio < ROSTER_UI_PRIO_ATTENTION_MESSAGE) { + roster_setuiprio(bjid, FALSE, + ROSTER_UI_PRIO_ATTENTION_MESSAGE, prio_set); + roster_msg_setflag(bjid, FALSE, TRUE); + } else { + roster_msg_setflag(bjid, FALSE, FALSE); + } + scr_update_roster(); + } + } + break; + case '#': + { + const char *bjid = buddy_getjid(BUDDATA(current_buddy)); + + if (bjid) { + unsigned short bflags = buddy_getflags(BUDDATA(current_buddy)); + + if (bflags & ROSTER_FLAG_MSG) + roster_msg_setflag(bjid, FALSE, FALSE); + else + roster_msg_setflag(bjid, FALSE, TRUE); + + scr_update_roster(); + } + } + break; + case '\'': + if (inputLine[0] == '\'') + process_command(mkcmdstr("roster alternate"), TRUE); + else + got_cmd_prefix = TRUE; + break; + case 'A': + process_command(mkcmdstr("roster unread_first"), TRUE); + break; + case 'a': + process_command(mkcmdstr("roster unread_next"), TRUE); + break; + case 'F': + process_command(mkcmdstr("roster group_prev"), TRUE); + break; + case 'f': + process_command(mkcmdstr("roster group_next"), TRUE); + break; + case 'G': + process_command(mkcmdstr("roster bottom"), TRUE); + break; + case 'g': + if (inputLine[0] == 'g') + process_command(mkcmdstr("roster top"), TRUE); + else { + clear_inputline(); + got_cmd_prefix = TRUE; + } + break; + case 'i': + open_chat_window(); + break; + case 'j': + if (isdigit((int)(unsigned char)inputLine[0]) && + strlen(inputLine) <= 9) { + char down_cmd[32] = "/roster down "; + + strcat(down_cmd, inputLine); + process_command(down_cmd, TRUE); + } else + process_command(mkcmdstr("roster down"), TRUE); + break; + case 'k': + if (isdigit((int)(unsigned char)inputLine[0]) && + strlen(inputLine) <= 9) { + char up_cmd[32] = "/roster up "; + + strcat(up_cmd, inputLine); + process_command(up_cmd, TRUE); + } else + process_command(mkcmdstr("roster up "), TRUE); + break; + case 'M': + if (inputLine[0] == 'z') { + GSList *groups = compl_list(ROSTER_TYPE_GROUP); + GSList *g; + + for (g = groups; g; g = g_slist_next(g)) { + char fold_cmd[128] = "/group fold "; + size_t cmd_len = strlen(fold_cmd); + size_t grp_len = strlen(g->data); + + if (cmd_len + grp_len + 1 > sizeof(fold_cmd)) + continue; + memcpy(fold_cmd + cmd_len, g->data, grp_len + 1); + process_command(fold_cmd, TRUE); + g_free(g->data); + } + g_slist_free(groups); + } else + unrecognized = TRUE; + break; + case 'n': + process_command(search_cmd, TRUE); + break; + case 'O': + process_command(mkcmdstr("roster unread_first"), TRUE); + open_chat_window(); + break; + case 'o': + process_command(mkcmdstr("roster unread_next"), TRUE); + open_chat_window(); + break; + case 'R': + if (inputLine[0] == 'z') { + GSList *groups = compl_list(ROSTER_TYPE_GROUP); + GSList *g; + + for (g = groups; g; g = g_slist_next(g)) { + char fold_cmd[128] = "/group unfold "; + size_t cmd_len = strlen(fold_cmd); + size_t grp_len = strlen(g->data); + + if (cmd_len + grp_len + 1 > sizeof(fold_cmd)) + continue; + memcpy(fold_cmd + cmd_len, g->data, grp_len + 1); + process_command(fold_cmd, TRUE); + g_free(g->data); + } + g_slist_free(groups); + } else + unrecognized = TRUE; + break; + case 'Z': + if (inputLine[0] == 'Z') + process_command(mkcmdstr("quit"), TRUE); + else { + clear_inputline(); + got_cmd_prefix = TRUE; + } + break; + case 'z': + clear_inputline(); + got_cmd_prefix = TRUE; + break; + case 13: // Enter + case 343: // Enter on Maemo + break; + default: + unrecognized = TRUE; + break; + } + cmdhisto_cur = NULL; + } + if (!ex_or_search_mode && !got_cmd_prefix) { + clear_inputline(); + if (!unrecognized) + key = ERR; // Do not process any further + } + lock_chatstate = TRUE; + } + if (kcode.utf8) { if (key != ERR && !kcode.mcode) display_char = TRUE; @@ -4513,8 +4805,13 @@ if (completion_started && key != 9 && key != 353 && key != KEY_RESIZE) scr_end_current_completion(); + else if (vi_completion) + inputLine[0] = ':'; refresh_inputline(); + if (ex_or_search_mode && inputLine[0] != ':' && inputLine[0] != '/') + ex_or_search_mode = FALSE; + if (!lock_chatstate) { // Set chat state to composing (1) if the user is currently composing, // i.e. not an empty line and not a command line. @@ -4693,4 +4990,19 @@ } #endif +static void open_chat_window(void) +{ + last_activity_buddy = current_buddy; + scr_check_auto_away(TRUE); + scr_set_chatmode(TRUE); + scr_show_buddy_window(); +} + +static void clear_inputline(void) +{ + ptr_inputline = inputLine; + *ptr_inputline = 0; + inputline_offset = 0; +} + /* vim: set et cindent cinoptions=>2\:2(0 ts=2 sw=2: For Vim users... */