summaryrefslogtreecommitdiff
path: root/apps/plugins/keyremap.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/keyremap.c')
-rw-r--r--apps/plugins/keyremap.c522
1 files changed, 507 insertions, 15 deletions
diff --git a/apps/plugins/keyremap.c b/apps/plugins/keyremap.c
index 0a6b705e9d..a4ce1c48e6 100644
--- a/apps/plugins/keyremap.c
+++ b/apps/plugins/keyremap.c
@@ -21,12 +21,12 @@
21 21
22#include "plugin.h" 22#include "plugin.h"
23#include "lang_enum.h" 23#include "lang_enum.h"
24#include "../open_plugin.h"
25 24
26#include "lib/action_helper.h" 25#include "lib/action_helper.h"
27#include "lib/button_helper.h" 26#include "lib/button_helper.h"
28#include "lib/pluginlib_actions.h" 27#include "lib/pluginlib_actions.h"
29#include "lib/printcell_helper.h" 28#include "lib/printcell_helper.h"
29#include "lib/kbd_helper.h"
30 30
31#ifdef ROCKBOX_HAS_LOGF 31#ifdef ROCKBOX_HAS_LOGF
32#define logf rb->logf 32#define logf rb->logf
@@ -102,6 +102,8 @@ enum {
102 M_SETKEYS, 102 M_SETKEYS,
103 M_TESTKEYS, 103 M_TESTKEYS,
104 M_RESETKEYS, 104 M_RESETKEYS,
105 M_EXPORTKEYS,
106 M_IMPORTKEYS,
105 M_SAVEKEYS, 107 M_SAVEKEYS,
106 M_LOADKEYS, 108 M_LOADKEYS,
107 M_DELKEYS, 109 M_DELKEYS,
@@ -125,8 +127,10 @@ MENU_ITEM(M_ROOT, "Key Remap Plugin", M_LAST_MAINITEM - 1),
125MENU_ITEM(M_SETKEYS, "Edit Keymap", 1), 127MENU_ITEM(M_SETKEYS, "Edit Keymap", 1),
126MENU_ITEM(M_TESTKEYS, "Test Keymap", 4), 128MENU_ITEM(M_TESTKEYS, "Test Keymap", 4),
127MENU_ITEM(M_RESETKEYS, "Reset Keymap", 1), 129MENU_ITEM(M_RESETKEYS, "Reset Keymap", 1),
128MENU_ITEM(M_SAVEKEYS, "Save Keymap", 1), 130MENU_ITEM(M_EXPORTKEYS, "Export Text Keymap", 1),
129MENU_ITEM(M_LOADKEYS, "Load Keymaps", 1), 131MENU_ITEM(M_IMPORTKEYS, "Import Text Keymap", 1),
132MENU_ITEM(M_SAVEKEYS, "Save Native Keymap", 1),
133MENU_ITEM(M_LOADKEYS, "Load Native Keymaps", 1),
130MENU_ITEM(M_DELKEYS, "Delete Keymaps", 1), 134MENU_ITEM(M_DELKEYS, "Delete Keymaps", 1),
131MENU_ITEM(M_TMPCORE, "Temp Core Remap", 1), 135MENU_ITEM(M_TMPCORE, "Temp Core Remap", 1),
132MENU_ITEM(M_SETCORE, "Set Core Remap", 1), 136MENU_ITEM(M_SETCORE, "Set Core Remap", 1),
@@ -172,8 +176,24 @@ static struct mainmenu *mainitem(int selected_item)
172 else 176 else
173 return ∅ 177 return ∅
174} 178}
175 179/* Forward Declarations */
180static const char *edit_keymap_name_cb(int selected_item, void* data,char* buf, size_t buf_len);
181static int keyremap_import_file(char *filenamebuf, size_t bufsz);
176static void synclist_set(int id, int selected_item, int items, int sel_size); 182static void synclist_set(int id, int selected_item, int items, int sel_size);
183
184static int prompt_filename(char *buf, size_t bufsz)
185{
186#define KBD_LAYOUT "abcdefghijklmnop\nqrstuvwxyz |()[]\n1234567890 /._-+\n\n" \
187 "\nABCDEFGHIJKLMNOP\nQRSTUVWXYZ |()[]\n1234567890 /._-+"
188 unsigned short kbd[sizeof(KBD_LAYOUT) + 10];
189 unsigned short *kbd_p = kbd;
190 if (!kbd_create_layout(KBD_LAYOUT, kbd, sizeof(kbd)))
191 kbd_p = NULL;
192
193#undef KBD_LAYOUT
194 return rb->kbd_input(buf, bufsz, kbd_p) + 1;
195}
196
177static void synclist_set_update(int id, int selected_item, int items, int sel_size) 197static void synclist_set_update(int id, int selected_item, int items, int sel_size)
178{ 198{
179 SET_MENU_ITEM(lists.selected_item + 1); /* update selected for previous menu*/ 199 SET_MENU_ITEM(lists.selected_item + 1); /* update selected for previous menu*/
@@ -471,13 +491,18 @@ static int keyremap_save_current(const char *filename)
471static void keyremap_save_user_keys(bool notify) 491static void keyremap_save_user_keys(bool notify)
472{ 492{
473 char buf[MAX_PATH]; 493 char buf[MAX_PATH];
474 int i = 1; 494 int i = 0;
475 do 495 do
476 { 496 {
477 rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", KMFDIR, KMFUSER, i, KMFEXT1);
478 i++; 497 i++;
498 rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", KMFDIR, KMFUSER, i, KMFEXT1);
479 } while (i < 100 && rb->file_exists(buf)); 499 } while (i < 100 && rb->file_exists(buf));
480 500
501 if (notify && prompt_filename(buf, sizeof(buf)) <= 0)
502 {
503 return;
504 }
505
481 if (keyremap_save_current(buf) == 0) 506 if (keyremap_save_current(buf) == 0)
482 { 507 {
483 if(notify) 508 if(notify)
@@ -487,6 +512,128 @@ static void keyremap_save_user_keys(bool notify)
487 rb->splashf(HZ *2, "Saved %s", buf); 512 rb->splashf(HZ *2, "Saved %s", buf);
488} 513}
489 514
515static int keyremap_export_current(char *filenamebuf, size_t bufsz)
516{
517 filenamebuf[bufsz - 1] = '\0';
518 int i, j;
519 int ctx_count = 0;
520 size_t entrylen;
521
522 int entry_count = ctx_data.ctx_count + ctx_data.act_count + 1;;/* (ctx_count + ctx_count + act_count + 1) */
523
524 if (entry_count <= 3)
525 return 0;
526
527 int fd = rb->open(filenamebuf, O_WRONLY | O_CREAT | O_TRUNC, 0666);
528
529 if (fd < 0)
530 return -1;
531 rb->fdprintf(fd, "# Key Remap\n# Device: %s\n" \
532 "# Entries: %d\n\n", MODEL_NAME, entry_count - 1);
533 rb->fdprintf(fd, "# Each entry should be PROPER_CASE and on its own line\n" \
534 "# Comments run to end of line \n");
535
536 for (i = 0; i <= entry_count; i++)
537 {
538 entrylen = 0;
539 rb->memset(filenamebuf, 0, bufsz);
540 edit_keymap_name_cb(i, MENU_ID(M_EXPORTKEYS), filenamebuf, bufsz);
541 if (i == 0)
542 {
543 ctx_menu_data.act_fmt = " {%s%s, %s, %s},\n\n";
544 continue;
545 }
546 else if (i == entry_count)
547 {
548 rb->strlcpy(filenamebuf, "}\n\n", bufsz);
549 }
550 char last = '\0';
551 for (j = 0; j < (int)bufsz ;j++)
552 {
553 char ch = filenamebuf[j];
554 if (ch == '$' && last == '\0') /*CONTEXT*/
555 {
556 if (ctx_count > 0)
557 rb->fdprintf(fd, "}\n");
558 ctx_count++;
559 filenamebuf[j] = '\n';
560 }
561 if (ch == '\n' && last == '\n')
562 {
563 entrylen = j;
564 break;
565 }
566 else if (ch == '\0')
567 filenamebuf[j] = ',';
568 last = ch;
569 }
570
571 size_t bytes = rb->write(fd, filenamebuf, entrylen);
572 if (bytes != entrylen)
573 {
574 entry_count = -2;
575 goto fail;
576 }
577 }
578
579fail:
580 rb->close(fd);
581
582 return entry_count;
583}
584
585static void keyremap_export_user_keys(void)
586{
587 const bool notify = true;
588 char buf[MAX_PATH];
589 int i = 0;
590 do
591 {
592 i++;
593 rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", "", KMFUSER, i, ".txt");
594 } while (i < 100 && rb->file_exists(buf));
595
596 if (notify && prompt_filename(buf, sizeof(buf)) <= 0)
597 {
598 return;
599 }
600
601 if (keyremap_export_current(buf, sizeof(buf)) <= 0)
602 {
603 if(notify)
604 rb->splash(HZ *2, "Error Saving");
605 }
606 else if (notify)
607 {
608 rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", "", KMFUSER, i, ".txt");
609 rb->splashf(HZ *2, "Saved %s", buf);
610 }
611}
612
613static void keyremap_import_user_keys(void)
614{
615 char buf[MAX_PATH];
616 struct browse_context browse;
617
618 rb->browse_context_init(&browse, SHOW_ALL, BROWSE_SELECTONLY, "Select Keymap",
619 Icon_Plugin, "/", NULL);
620
621 browse.buf = buf;
622 browse.bufsize = sizeof(buf);
623
624 if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
625 {
626 int ret = keyremap_import_file(buf, sizeof(buf));
627 if (ret <= 0)
628 {
629 keyremap_reset_buffer();
630 rb->splash(HZ *2, "Error Opening");
631 }
632 else
633 rb->splashf(HZ * 2, "Loaded Text Keymap ");
634 }
635}
636
490static int keymap_add_context_entry(int context) 637static int keymap_add_context_entry(int context)
491{ 638{
492 int remap_context = CORE_CONTEXT_REMAP(context); 639 int remap_context = CORE_CONTEXT_REMAP(context);
@@ -538,6 +685,328 @@ fail:
538 return 0; 685 return 0;
539} 686}
540 687
688static int get_action_from_str(char *pfield, size_t bufsz)
689{
690 int act = -1;
691 for (int i=0;i < LAST_ACTION_PLACEHOLDER; i++)
692 {
693 if (rb->strncasecmp(pfield, action_name(i), bufsz) == 0)
694 {
695 logf("Action Found: %s (%d)", pfield, i);
696 act = i;
697 break;
698 }
699 }
700 return act;
701}
702
703static int get_button_from_str(char *pfield, size_t bufsz)
704{
705 int btn = -1;
706 for (int i=0;i < available_button_count; i++)
707 {
708 const struct available_button* abtn = &available_buttons[i];
709 if (rb->strncasecmp(pfield, abtn->name, bufsz) == 0)
710 {
711 logf("Button Found: %s (%lx)", abtn->name, abtn->value);
712 btn = abtn->value;
713 break;
714 }
715 }
716 if (btn < 0) /* Not Fatal */
717 {
718 logf("Invalid Button %s", pfield);
719 rb->splashf(HZ, "Invalid Button %s", pfield);
720 }
721 return btn;
722}
723
724static int parse_action_import_entry(int context, char * pbuf, size_t bufsz)
725{
726 size_t bufleft;
727 int count = 0;
728 char ch;
729 char *pfirst = NULL;
730 char *pfield;
731 int field = -1;
732 int act = -1;
733 int button = BUTTON_NONE;
734 int prebtn = BUTTON_NONE;
735 while ((ch = *(pbuf)) != '\0')
736 {
737 pfield = NULL; /* Valid names */
738 if ((ch >= 'A' && ch <= 'Z') || ch == '_')
739 {
740 if (pfirst == NULL)
741 pfirst = pbuf;
742 }
743 else if (ch == ',')
744 {
745 if (pfirst != NULL)
746 {
747 field++;
748 pfield = pfirst;
749 pfirst = NULL;
750 *pbuf = '\0';
751 }
752 }
753 else if (ch == ' ' || ch == '|'){;}
754 else
755 pfirst = NULL;
756
757 if (field == 1 && pfirst != NULL && pbuf[1] == '\0')
758 {
759 field++;
760 pfield = pfirst;
761 pfirst = NULL;
762 }
763
764 if (pfield != NULL)
765 {
766 char *pf;
767
768 if (field == 0) /* action */
769 {
770 char *pact = pfield;
771 pf = pfield;
772 while ((ch = *(pf)) != '\0')
773 {
774 if(ch == ' ')
775 *pf = '\0';
776 else
777 pf++;
778 }
779 bufleft = bufsz - (pact - pbuf);
780 act = get_action_from_str(pact, bufleft);
781
782 if (act < 0)
783 {
784 logf("Error Action Expected: %s", pact);
785 return -1;
786 }
787 }
788 else if (field == 1 || field == 2) /* button / pre_btn */
789 {
790 char *pbtn = pfield;
791 pf = pfield;
792 while ((ch = *(pf)) != '\0')
793 {
794 if (pf[1] == '\0') /* last item? */
795 {
796 pf++;
797 ch = '|';
798 pfield = NULL;
799 }
800 else if (ch == ' ' || ch == '|')
801 {
802 *pf = '\0';
803 }
804
805 if(ch == '|')
806 {
807 bufleft = bufsz - (pbtn - pbuf);
808 int btn = get_button_from_str(pbtn, bufleft);
809
810 if (btn > BUTTON_NONE)
811 {
812 if (field == 1)
813 button |= btn;
814 else if (field == 2)
815 prebtn |= btn;
816 }
817
818 if (pfield != NULL)
819 {
820 pf++;
821 while ((ch = *(pf)) != '\0')
822 {
823 if (*pf == ' ')
824 pf++;
825 else
826 break;
827 }
828 pbtn = pf;
829 }
830 }
831 else
832 pf++;
833 }
834
835 if (act < 0)
836 {
837 logf("Error Action Expected: %s", pfield);
838 return -1;
839 }
840 }
841
842 pfield = NULL;
843 }
844
845 pbuf++;
846 }
847 if (field == 2)
848 {
849 count = keymap_add_button_entry(context, act, button, prebtn);
850 if (count > 0)
851 {
852 logf("Added: Ctx: %d, Act: %d, Btn: %d PBtn: %d",
853 context, act, button, prebtn);
854 }
855 }
856 return count;
857}
858
859static int keyremap_import_file(char *filenamebuf, size_t bufsz)
860{
861 size_t bufleft;
862 int count = 0;
863 int line = 0;
864 filenamebuf[bufsz - 1] = '\0';
865 logf("keyremap: import %s", filenamebuf);
866 int fd = rb->open(filenamebuf, O_RDONLY);
867 if (fd < 0)
868 return -1;
869
870 char ch;
871 char *pbuf;
872 char *pfirst;
873
874 char *pctx = NULL;
875 char *pact;
876 int ctx = -1;
877
878 keyremap_reset_buffer();
879next_line:
880 while (rb->read_line(fd, filenamebuf, (int) bufsz) > 0)
881 {
882 line++;
883
884
885 pbuf = filenamebuf;
886 pfirst = NULL;
887 pact = NULL;
888 char *pcomment = rb->strchr(pbuf, '#');
889 if (pcomment != NULL)
890 {
891 logf("ln: %d: Skipped Comment: %s", line, pcomment);
892 *pcomment = '\0';
893 }
894
895 while ((ch = *(pbuf)) != '\0')
896 {
897 /* PARSE CONTEXT = { */
898 if ((ch >= 'A' && ch <= 'Z') || ch == '_')
899 {
900 if (pfirst == NULL)
901 pfirst = pbuf;
902 }
903 else if (ch == ' ')
904 {
905 if (ctx < 0 && pfirst != NULL)
906 {
907 *pbuf = '\0';
908 pctx = pfirst;
909 pfirst = NULL;
910 }
911 }
912 else if (ch == '=')
913 {
914 if (ctx < 0 && pfirst != NULL)
915 {
916 *pbuf = '\0';
917 pbuf++;
918 pctx = pfirst;
919 pfirst = NULL;
920 }
921 while ((ch = *(pbuf)) != '\0')
922 {
923 if (ch == '{')
924 break;
925 pbuf++;
926 }
927 if (ch == '{' && pctx != NULL)
928 {
929 bufleft = bufsz - (pctx - filenamebuf);
930 ctx = -1;
931 for (int i=0;i < LAST_CONTEXT_PLACEHOLDER;i++)
932 {
933 if (rb->strncasecmp(pctx, context_name(i), bufleft) == 0)
934 {
935 logf("ln: %d: Context Found: %s (%d)", line, pctx, i);
936 if (keymap_add_context_entry(i) <= 0)
937 logf("ln: %d: Context Exists: %s (%d)", line, pctx, i);
938 ctx = i;
939 goto next_line;
940
941 }
942 }
943 logf("ln: %d: ERROR { Context Expected got: %s", line, pctx);
944 goto fail;
945 }
946 }
947 else if (ch == '}')
948 {
949 if (ctx >= 0)
950 ctx = -1;
951 else
952 {
953 logf("ln: %d: ERROR no context, unexpected close {", line);
954 goto fail;
955 }
956 }
957 else if (ch == '{') /* PARSE FIELDS { ACTION, BUTTON, PREBTN } */
958 {
959 int res = 0;
960 if (ctx >= 0)
961 {
962 pfirst = pbuf;
963
964 while ((ch = *(pbuf)) != '\0')
965 {
966 if (ch == '}')
967 {
968 pact = pfirst + 1;
969 pfirst = NULL;
970 *pbuf = '\0';
971 pbuf = "";
972 continue;
973 }
974 pbuf++;
975 }
976 if (pact != NULL)
977 {
978 bufleft = bufsz - (pact - filenamebuf);
979 logf("ln: %d: Entry Found: {%s} (%d)", line, pact, 0);
980 res = parse_action_import_entry(ctx, pact, bufleft);
981 }
982 }
983 if (res <= 0)
984 {
985 logf("ln: %d: ERROR action entry expected", line);
986 goto fail;
987 }
988 else
989 {
990 pbuf = "";
991 continue;
992 }
993 }
994 else
995 pfirst = NULL;
996 pbuf++;
997 }
998
999 }
1000 rb->close(fd);
1001 count = ctx_data.ctx_count + ctx_data.act_count;
1002 return count;
1003
1004fail:
1005 rb->close(fd);
1006 rb->splashf(HZ * 2, "Error @ line %d", line);
1007 return 0;
1008}
1009
541static int keyremap_load_file(const char *filename) 1010static int keyremap_load_file(const char *filename)
542{ 1011{
543 logf("keyremap: load %s", filename); 1012 logf("keyremap: load %s", filename);
@@ -709,7 +1178,18 @@ static const char *menu_useract_items_cb(int selected_item, void* data,
709 static char buf_button[MAX_BUTTON_NAME * MAX_BUTTON_COMBO]; 1178 static char buf_button[MAX_BUTTON_NAME * MAX_BUTTON_COMBO];
710 static char buf_prebtn[MAX_BUTTON_NAME * MAX_BUTTON_COMBO]; 1179 static char buf_prebtn[MAX_BUTTON_NAME * MAX_BUTTON_COMBO];
711 int i; 1180 int i;
712 (void)data; 1181 int szbtn = sizeof("BUTTON");
1182 int szctx = sizeof("CONTEXT");
1183 int szact = sizeof("ACTION");
1184 const char* ctxfmt = "%s";
1185
1186 if (data == MENU_ID(M_EXPORTKEYS))
1187 {
1188 szbtn = 0;
1189 szctx = 0;
1190 szact = 0;
1191 ctxfmt = "$%s = {\n\n";
1192 }
713 buf[0] = '\0'; 1193 buf[0] = '\0';
714 for(i = 0; i < ctx_data.ctx_count; i ++) 1194 for(i = 0; i < ctx_data.ctx_count; i ++)
715 { 1195 {
@@ -720,7 +1200,7 @@ static const char *menu_useract_items_cb(int selected_item, void* data,
720 context_name(ctx_data.ctx_map[i].context), 1200 context_name(ctx_data.ctx_map[i].context),
721 "Select$to add$actions"); 1201 "Select$to add$actions");
722 else 1202 else
723 rb->snprintf(buf, buf_len, "%s", context_name(ctx_data.ctx_map[i].context)); 1203 rb->snprintf(buf, buf_len, ctxfmt, context_name(ctx_data.ctx_map[i].context));
724 return buf; 1204 return buf;
725 } 1205 }
726 } 1206 }
@@ -730,19 +1210,23 @@ static const char *menu_useract_items_cb(int selected_item, void* data,
730 { 1210 {
731 int context = ctx_data.act_map[i].context; 1211 int context = ctx_data.act_map[i].context;
732 char ctxbuf[action_helper_maxbuffer]; 1212 char ctxbuf[action_helper_maxbuffer];
733 char *pctxbuf = ctxbuf; 1213 char *pctxbuf = "\0";
734 char *pactname; 1214 char *pactname;
735 rb->snprintf(ctxbuf, sizeof(ctxbuf), "%s", context_name(context)); 1215 if (data != MENU_ID(M_EXPORTKEYS))
736 pctxbuf += sizeof("CONTEXT"); 1216 {
1217 pctxbuf = ctxbuf;
1218 rb->snprintf(ctxbuf, sizeof(ctxbuf), ctxfmt, context_name(context));
1219 pctxbuf += szctx;//sizeof("CONTEXT")
1220 }
737 struct button_mapping * bm = &ctx_data.act_map[i].map; 1221 struct button_mapping * bm = &ctx_data.act_map[i].map;
738 pactname = action_name(bm->action_code); 1222 pactname = action_name(bm->action_code);
739 pactname += sizeof("ACTION"); 1223 pactname += szact;//sizeof("ACTION")
740 /* BUTTON & PRE_BUTTON */ 1224 /* BUTTON & PRE_BUTTON */
741 btnval_to_name(buf_button, sizeof(buf_button), bm->button_code); 1225 btnval_to_name(buf_button, sizeof(buf_button), bm->button_code);
742 btnval_to_name(buf_prebtn, sizeof(buf_prebtn), bm->pre_button_code); 1226 btnval_to_name(buf_prebtn, sizeof(buf_prebtn), bm->pre_button_code);
743 1227
744 rb->snprintf(buf, buf_len, ctx_menu_data.act_fmt, pctxbuf, 1228 rb->snprintf(buf, buf_len, ctx_menu_data.act_fmt, pctxbuf,
745 pactname, buf_button + sizeof("BUTTON"), buf_prebtn + sizeof("BUTTON")); 1229 pactname, buf_button + szbtn, buf_prebtn + szbtn);
746 return buf; 1230 return buf;
747 } 1231 }
748 } 1232 }
@@ -752,7 +1236,6 @@ static const char *menu_useract_items_cb(int selected_item, void* data,
752static const char *edit_keymap_name_cb(int selected_item, void* data, 1236static const char *edit_keymap_name_cb(int selected_item, void* data,
753 char* buf, size_t buf_len) 1237 char* buf, size_t buf_len)
754{ 1238{
755 (void)data;
756 buf[0] = '\0'; 1239 buf[0] = '\0';
757 if (selected_item == 0) 1240 if (selected_item == 0)
758 { 1241 {
@@ -930,6 +1413,16 @@ int menu_action_root(int *action, int selected_item, bool* exit, struct gui_sync
930 keyremap_save_user_keys(true); 1413 keyremap_save_user_keys(true);
931 goto default_handler; 1414 goto default_handler;
932 } 1415 }
1416 else if (cur->menuid == MENU_ID(M_EXPORTKEYS))
1417 {
1418 keyremap_export_user_keys();
1419 goto default_handler;
1420 }
1421 else if (cur->menuid == MENU_ID(M_IMPORTKEYS))
1422 {
1423 keyremap_import_user_keys();
1424 goto default_handler;
1425 }
933 else if (cur->menuid == MENU_ID(M_DELKEYS) || 1426 else if (cur->menuid == MENU_ID(M_DELKEYS) ||
934 cur->menuid == MENU_ID(M_LOADKEYS)) 1427 cur->menuid == MENU_ID(M_LOADKEYS))
935 { 1428 {
@@ -1118,7 +1611,6 @@ int menu_action_testkeys(int *action, int selected_item, bool* exit, struct gui_
1118 goto default_handler; 1611 goto default_handler;
1119 } 1612 }
1120 1613
1121
1122 if (*action == ACTION_STD_CANCEL && selected_item == 0 && keytest.keymap != NULL) 1614 if (*action == ACTION_STD_CANCEL && selected_item == 0 && keytest.keymap != NULL)
1123 { 1615 {
1124 keytest.index -= 2; 1616 keytest.index -= 2;