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... */