Mercurial > hg
comparison 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 |
comparison
equal
deleted
inserted
replaced
2303:4f3821bda633 | 2304:fa8365fb6ac2 |
---|---|
92 #endif | 92 #endif |
93 | 93 |
94 #if defined(WITH_ENCHANT) || defined(WITH_ASPELL) | 94 #if defined(WITH_ENCHANT) || defined(WITH_ASPELL) |
95 static void spellcheck(char *, char *); | 95 static void spellcheck(char *, char *); |
96 #endif | 96 #endif |
97 | |
98 static void open_chat_window(void); | |
99 static void clear_inputline(void); | |
97 | 100 |
98 static GHashTable *winbufhash; | 101 static GHashTable *winbufhash; |
99 | 102 |
100 typedef struct { | 103 typedef struct { |
101 GList *hbuf; | 104 GList *hbuf; |
4173 #endif | 4176 #endif |
4174 | 4177 |
4175 static inline void refresh_inputline(void) | 4178 static inline void refresh_inputline(void) |
4176 { | 4179 { |
4177 #if defined(WITH_ENCHANT) || defined(WITH_ASPELL) | 4180 #if defined(WITH_ENCHANT) || defined(WITH_ASPELL) |
4178 if (settings_opt_get_int("spell_enable")) { | 4181 if (settings_opt_get_int("spell_enable") && |
4182 (chatmode || !settings_opt_get_int("vi_mode"))) { | |
4179 memset(maskLine, 0, INPUTLINE_LENGTH+1); | 4183 memset(maskLine, 0, INPUTLINE_LENGTH+1); |
4180 spellcheck(inputLine, maskLine); | 4184 spellcheck(inputLine, maskLine); |
4181 } | 4185 } |
4182 print_checked_line(); | 4186 print_checked_line(); |
4183 wclrtoeol(inputWnd); | 4187 wclrtoeol(inputWnd); |
4424 scr_LogPrint(LPRINT_NORMAL, | 4428 scr_LogPrint(LPRINT_NORMAL, |
4425 "WARNING: Compiled without full UTF-8 support!"); | 4429 "WARNING: Compiled without full UTF-8 support!"); |
4426 #endif | 4430 #endif |
4427 } | 4431 } |
4428 | 4432 |
4433 static void scr_process_vi_arrow_key(int key) | |
4434 { | |
4435 const char *l; | |
4436 char mask[INPUTLINE_LENGTH+1] = "/roster search "; | |
4437 size_t cmd_len = strlen(mask); | |
4438 size_t str_len = strlen(inputLine) - 1; | |
4439 | |
4440 switch (inputLine[0]) { | |
4441 case ':': | |
4442 inputLine[0] = '/'; | |
4443 if (key == KEY_UP) | |
4444 l = scr_cmdhisto_prev(inputLine, ptr_inputline - inputLine); | |
4445 else | |
4446 l = scr_cmdhisto_next(inputLine, ptr_inputline - inputLine); | |
4447 if (l) | |
4448 strcpy(inputLine, l); | |
4449 inputLine[0] = ':'; | |
4450 break; | |
4451 case '/': | |
4452 if (cmd_len + str_len > INPUTLINE_LENGTH) | |
4453 return; | |
4454 | |
4455 memcpy(mask + cmd_len, inputLine + 1, str_len + 1); | |
4456 if (key == KEY_UP) | |
4457 l = scr_cmdhisto_prev(mask, ptr_inputline - inputLine + cmd_len - 1); | |
4458 else | |
4459 l = scr_cmdhisto_next(mask, ptr_inputline - inputLine + cmd_len - 1); | |
4460 if (l) | |
4461 strcpy(inputLine + 1, l + cmd_len); | |
4462 break; | |
4463 default: | |
4464 if (key == KEY_UP) | |
4465 process_command(mkcmdstr("roster up"), TRUE); | |
4466 else | |
4467 process_command(mkcmdstr("roster down"), TRUE); | |
4468 break; | |
4469 } | |
4470 } | |
4471 | |
4429 // scr_process_key(key) | 4472 // scr_process_key(key) |
4430 // Handle the pressed key, in the command line (bottom). | 4473 // Handle the pressed key, in the command line (bottom). |
4431 void scr_process_key(keycode kcode) | 4474 void scr_process_key(keycode kcode) |
4432 { | 4475 { |
4433 int key = kcode.value; | 4476 int key = kcode.value; |
4434 int display_char = FALSE; | 4477 int display_char = FALSE; |
4478 int vi_completion = FALSE; | |
4479 static int ex_or_search_mode = FALSE; | |
4435 | 4480 |
4436 lock_chatstate = FALSE; | 4481 lock_chatstate = FALSE; |
4437 | 4482 |
4438 switch (kcode.mcode) { | 4483 switch (kcode.mcode) { |
4439 case 0: | 4484 case 0: |
4444 break; | 4489 break; |
4445 case MKEY_META: | 4490 case MKEY_META: |
4446 default: | 4491 default: |
4447 bindcommand(kcode); | 4492 bindcommand(kcode); |
4448 key = ERR; // Do not process any further | 4493 key = ERR; // Do not process any further |
4494 } | |
4495 | |
4496 if (settings_opt_get_int("vi_mode") && !chatmode) { | |
4497 int got_cmd_prefix = FALSE; | |
4498 int unrecognized = FALSE; | |
4499 static char search_cmd[INPUTLINE_LENGTH+1] = "/roster search "; | |
4500 | |
4501 if (key == KEY_UP || key == KEY_DOWN) { | |
4502 scr_process_vi_arrow_key(key); | |
4503 key = ERR; // Do not process any further | |
4504 } else if (ex_or_search_mode) { | |
4505 switch (key) { | |
4506 case 27: // Escape | |
4507 clear_inputline(); | |
4508 ex_or_search_mode = FALSE; | |
4509 break; | |
4510 case 9: // Tab | |
4511 case 353: // Shift-Tab | |
4512 if (inputLine[0] == ':') { | |
4513 inputLine[0] = '/'; | |
4514 vi_completion = TRUE; | |
4515 } | |
4516 break; | |
4517 case 13: // Enter | |
4518 case 343: // Enter on Maemo | |
4519 switch (inputLine[0]) { | |
4520 case ':': | |
4521 { | |
4522 char *p = strchr(inputLine, 0); | |
4523 | |
4524 while (*--p == ' ' && p > inputLine) | |
4525 *p = 0; | |
4526 } | |
4527 if (!strcmp(inputLine, ":x") || | |
4528 !strcmp(inputLine, ":q") || | |
4529 !strcmp(inputLine, ":wq")) | |
4530 strcpy(inputLine, ":quit"); | |
4531 if (isdigit((int)(unsigned char)inputLine[1]) && | |
4532 strlen(inputLine) <= 9) { | |
4533 process_command(mkcmdstr("roster top"), TRUE); | |
4534 memcpy(inputLine + 13, inputLine + 1, 10); | |
4535 memcpy(inputLine + 1, "roster down ", 12); | |
4536 } | |
4537 inputLine[0] = '/'; | |
4538 process_command(inputLine, TRUE); | |
4539 scr_cmdhisto_addline(inputLine); | |
4540 break; | |
4541 case '/': | |
4542 { | |
4543 size_t cmd_len = sizeof("/roster search ") - 1; | |
4544 size_t str_len = strlen(inputLine) - 1; | |
4545 | |
4546 if (cmd_len + str_len > INPUTLINE_LENGTH) | |
4547 return; | |
4548 | |
4549 memcpy(search_cmd + cmd_len, inputLine + 1, | |
4550 str_len + 1); | |
4551 } | |
4552 process_command(search_cmd, TRUE); | |
4553 scr_cmdhisto_addline(search_cmd); | |
4554 break; | |
4555 } | |
4556 ex_or_search_mode = FALSE; | |
4557 break; | |
4558 } | |
4559 } else if (key >= '0' && key <= '9') { | |
4560 got_cmd_prefix = TRUE; | |
4561 } else { | |
4562 switch (key) { | |
4563 case '/': | |
4564 case ':': | |
4565 ex_or_search_mode = TRUE; | |
4566 break; | |
4567 case ' ': | |
4568 process_command(mkcmdstr("group toggle"), TRUE); | |
4569 break; | |
4570 case '!': | |
4571 { | |
4572 const char *bjid = buddy_getjid(BUDDATA(current_buddy)); | |
4573 | |
4574 if (bjid) { | |
4575 guint type = buddy_gettype(BUDDATA(current_buddy)); | |
4576 guint prio = buddy_getuiprio(BUDDATA(current_buddy)); | |
4577 | |
4578 if (type & ROSTER_TYPE_ROOM && | |
4579 prio < ROSTER_UI_PRIO_MUC_HL_MESSAGE) { | |
4580 roster_setuiprio(bjid, FALSE, | |
4581 ROSTER_UI_PRIO_MUC_HL_MESSAGE, prio_set); | |
4582 roster_msg_setflag(bjid, FALSE, TRUE); | |
4583 } else if (type & ROSTER_TYPE_USER && | |
4584 prio < ROSTER_UI_PRIO_ATTENTION_MESSAGE) { | |
4585 roster_setuiprio(bjid, FALSE, | |
4586 ROSTER_UI_PRIO_ATTENTION_MESSAGE, prio_set); | |
4587 roster_msg_setflag(bjid, FALSE, TRUE); | |
4588 } else { | |
4589 roster_msg_setflag(bjid, FALSE, FALSE); | |
4590 } | |
4591 scr_update_roster(); | |
4592 } | |
4593 } | |
4594 break; | |
4595 case '#': | |
4596 { | |
4597 const char *bjid = buddy_getjid(BUDDATA(current_buddy)); | |
4598 | |
4599 if (bjid) { | |
4600 unsigned short bflags = buddy_getflags(BUDDATA(current_buddy)); | |
4601 | |
4602 if (bflags & ROSTER_FLAG_MSG) | |
4603 roster_msg_setflag(bjid, FALSE, FALSE); | |
4604 else | |
4605 roster_msg_setflag(bjid, FALSE, TRUE); | |
4606 | |
4607 scr_update_roster(); | |
4608 } | |
4609 } | |
4610 break; | |
4611 case '\'': | |
4612 if (inputLine[0] == '\'') | |
4613 process_command(mkcmdstr("roster alternate"), TRUE); | |
4614 else | |
4615 got_cmd_prefix = TRUE; | |
4616 break; | |
4617 case 'A': | |
4618 process_command(mkcmdstr("roster unread_first"), TRUE); | |
4619 break; | |
4620 case 'a': | |
4621 process_command(mkcmdstr("roster unread_next"), TRUE); | |
4622 break; | |
4623 case 'F': | |
4624 process_command(mkcmdstr("roster group_prev"), TRUE); | |
4625 break; | |
4626 case 'f': | |
4627 process_command(mkcmdstr("roster group_next"), TRUE); | |
4628 break; | |
4629 case 'G': | |
4630 process_command(mkcmdstr("roster bottom"), TRUE); | |
4631 break; | |
4632 case 'g': | |
4633 if (inputLine[0] == 'g') | |
4634 process_command(mkcmdstr("roster top"), TRUE); | |
4635 else { | |
4636 clear_inputline(); | |
4637 got_cmd_prefix = TRUE; | |
4638 } | |
4639 break; | |
4640 case 'i': | |
4641 open_chat_window(); | |
4642 break; | |
4643 case 'j': | |
4644 if (isdigit((int)(unsigned char)inputLine[0]) && | |
4645 strlen(inputLine) <= 9) { | |
4646 char down_cmd[32] = "/roster down "; | |
4647 | |
4648 strcat(down_cmd, inputLine); | |
4649 process_command(down_cmd, TRUE); | |
4650 } else | |
4651 process_command(mkcmdstr("roster down"), TRUE); | |
4652 break; | |
4653 case 'k': | |
4654 if (isdigit((int)(unsigned char)inputLine[0]) && | |
4655 strlen(inputLine) <= 9) { | |
4656 char up_cmd[32] = "/roster up "; | |
4657 | |
4658 strcat(up_cmd, inputLine); | |
4659 process_command(up_cmd, TRUE); | |
4660 } else | |
4661 process_command(mkcmdstr("roster up "), TRUE); | |
4662 break; | |
4663 case 'M': | |
4664 if (inputLine[0] == 'z') { | |
4665 GSList *groups = compl_list(ROSTER_TYPE_GROUP); | |
4666 GSList *g; | |
4667 | |
4668 for (g = groups; g; g = g_slist_next(g)) { | |
4669 char fold_cmd[128] = "/group fold "; | |
4670 size_t cmd_len = strlen(fold_cmd); | |
4671 size_t grp_len = strlen(g->data); | |
4672 | |
4673 if (cmd_len + grp_len + 1 > sizeof(fold_cmd)) | |
4674 continue; | |
4675 memcpy(fold_cmd + cmd_len, g->data, grp_len + 1); | |
4676 process_command(fold_cmd, TRUE); | |
4677 g_free(g->data); | |
4678 } | |
4679 g_slist_free(groups); | |
4680 } else | |
4681 unrecognized = TRUE; | |
4682 break; | |
4683 case 'n': | |
4684 process_command(search_cmd, TRUE); | |
4685 break; | |
4686 case 'O': | |
4687 process_command(mkcmdstr("roster unread_first"), TRUE); | |
4688 open_chat_window(); | |
4689 break; | |
4690 case 'o': | |
4691 process_command(mkcmdstr("roster unread_next"), TRUE); | |
4692 open_chat_window(); | |
4693 break; | |
4694 case 'R': | |
4695 if (inputLine[0] == 'z') { | |
4696 GSList *groups = compl_list(ROSTER_TYPE_GROUP); | |
4697 GSList *g; | |
4698 | |
4699 for (g = groups; g; g = g_slist_next(g)) { | |
4700 char fold_cmd[128] = "/group unfold "; | |
4701 size_t cmd_len = strlen(fold_cmd); | |
4702 size_t grp_len = strlen(g->data); | |
4703 | |
4704 if (cmd_len + grp_len + 1 > sizeof(fold_cmd)) | |
4705 continue; | |
4706 memcpy(fold_cmd + cmd_len, g->data, grp_len + 1); | |
4707 process_command(fold_cmd, TRUE); | |
4708 g_free(g->data); | |
4709 } | |
4710 g_slist_free(groups); | |
4711 } else | |
4712 unrecognized = TRUE; | |
4713 break; | |
4714 case 'Z': | |
4715 if (inputLine[0] == 'Z') | |
4716 process_command(mkcmdstr("quit"), TRUE); | |
4717 else { | |
4718 clear_inputline(); | |
4719 got_cmd_prefix = TRUE; | |
4720 } | |
4721 break; | |
4722 case 'z': | |
4723 clear_inputline(); | |
4724 got_cmd_prefix = TRUE; | |
4725 break; | |
4726 case 13: // Enter | |
4727 case 343: // Enter on Maemo | |
4728 break; | |
4729 default: | |
4730 unrecognized = TRUE; | |
4731 break; | |
4732 } | |
4733 cmdhisto_cur = NULL; | |
4734 } | |
4735 if (!ex_or_search_mode && !got_cmd_prefix) { | |
4736 clear_inputline(); | |
4737 if (!unrecognized) | |
4738 key = ERR; // Do not process any further | |
4739 } | |
4740 lock_chatstate = TRUE; | |
4449 } | 4741 } |
4450 | 4742 |
4451 if (kcode.utf8) { | 4743 if (kcode.utf8) { |
4452 if (key != ERR && !kcode.mcode) | 4744 if (key != ERR && !kcode.mcode) |
4453 display_char = TRUE; | 4745 display_char = TRUE; |
4511 } | 4803 } |
4512 } | 4804 } |
4513 | 4805 |
4514 if (completion_started && key != 9 && key != 353 && key != KEY_RESIZE) | 4806 if (completion_started && key != 9 && key != 353 && key != KEY_RESIZE) |
4515 scr_end_current_completion(); | 4807 scr_end_current_completion(); |
4808 else if (vi_completion) | |
4809 inputLine[0] = ':'; | |
4516 refresh_inputline(); | 4810 refresh_inputline(); |
4811 | |
4812 if (ex_or_search_mode && inputLine[0] != ':' && inputLine[0] != '/') | |
4813 ex_or_search_mode = FALSE; | |
4517 | 4814 |
4518 if (!lock_chatstate) { | 4815 if (!lock_chatstate) { |
4519 // Set chat state to composing (1) if the user is currently composing, | 4816 // Set chat state to composing (1) if the user is currently composing, |
4520 // i.e. not an empty line and not a command line. | 4817 // i.e. not an empty line and not a command line. |
4521 if (inputLine[0] == 0 || inputLine[0] == COMMAND_CHAR) | 4818 if (inputLine[0] == 0 || inputLine[0] == COMMAND_CHAR) |
4691 memset(&checked[start - line_start], SPELLBADCHAR, line - start); | 4988 memset(&checked[start - line_start], SPELLBADCHAR, line - start); |
4692 } | 4989 } |
4693 } | 4990 } |
4694 #endif | 4991 #endif |
4695 | 4992 |
4993 static void open_chat_window(void) | |
4994 { | |
4995 last_activity_buddy = current_buddy; | |
4996 scr_check_auto_away(TRUE); | |
4997 scr_set_chatmode(TRUE); | |
4998 scr_show_buddy_window(); | |
4999 } | |
5000 | |
5001 static void clear_inputline(void) | |
5002 { | |
5003 ptr_inputline = inputLine; | |
5004 *ptr_inputline = 0; | |
5005 inputline_offset = 0; | |
5006 } | |
5007 | |
4696 /* vim: set et cindent cinoptions=>2\:2(0 ts=2 sw=2: For Vim users... */ | 5008 /* vim: set et cindent cinoptions=>2\:2(0 ts=2 sw=2: For Vim users... */ |