Add macro support.
Replace WGETCH() with input_char(), which works the same except it supports an input buffer. Calling input_macro(s) sets the input buffer to 's'. The input buffer is returned by input_char() as if it had been typed by the user. If an error occurs (cmderr()), the input buffer is cleared.
This commit is contained in:
parent
44b2e4371e
commit
3440a87ea3
9 changed files with 159 additions and 49 deletions
|
|
@ -24,6 +24,7 @@ typedef struct binding {
|
|||
INT bi_code;
|
||||
tkey_t *bi_key;
|
||||
function_t *bi_func;
|
||||
WCHAR *bi_macro;
|
||||
|
||||
TTS_TAILQ_ENTRY(binding) bi_entries;
|
||||
} binding_t;
|
||||
|
|
@ -32,6 +33,6 @@ typedef TTS_TAILQ_HEAD(bindlist, binding) binding_list_t;
|
|||
extern binding_list_t bindings;
|
||||
|
||||
tkey_t *find_key(const WCHAR *name);
|
||||
void bind_key(const WCHAR *key, const WCHAR *func);
|
||||
void bind_key(const WCHAR *key, const WCHAR *func, int is_macro);
|
||||
|
||||
#endif /* !TTS_BINDINGS_H */
|
||||
|
|
|
|||
16
commands.c
16
commands.c
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
static command_t commands[] = {
|
||||
{ WIDE("bind"), c_bind },
|
||||
{ WIDE("macro"), c_macro },
|
||||
{ WIDE("style"), c_style },
|
||||
{ WIDE("set"), c_set },
|
||||
{ }
|
||||
|
|
@ -80,7 +81,20 @@ c_bind(argc, argv)
|
|||
return;
|
||||
}
|
||||
|
||||
bind_key(argv[1], argv[2]);
|
||||
bind_key(argv[1], argv[2], 0);
|
||||
}
|
||||
|
||||
void
|
||||
c_macro(argc, argv)
|
||||
size_t argc;
|
||||
WCHAR **argv;
|
||||
{
|
||||
if (argc != 3) {
|
||||
cmderr(WIDE("Usage: macro <key> <def>"));
|
||||
return;
|
||||
}
|
||||
|
||||
bind_key(argv[1], argv[2], 1);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ command_t *find_command(const WCHAR *);
|
|||
void c_bind (size_t, WCHAR **);
|
||||
void c_style (size_t, WCHAR **);
|
||||
void c_set (size_t, WCHAR **);
|
||||
void c_macro (size_t, WCHAR **);
|
||||
|
||||
void cmderr (const WCHAR *, ...);
|
||||
void vcmderr (const WCHAR *, va_list);
|
||||
|
|
|
|||
18
functions.c
18
functions.c
|
|
@ -592,14 +592,20 @@ binding_t *bi;
|
|||
|
||||
i = 0;
|
||||
TTS_TAILQ_FOREACH(bi, &bindings, bi_entries) {
|
||||
WCHAR s[128];
|
||||
WCHAR s[128], t[16];
|
||||
|
||||
if (bi->bi_key)
|
||||
SNPRINTF(s, WSIZEOF(s), WIDE("%-10"FMT_L"s %"FMT_L"s (%"FMT_L"s)"),
|
||||
bi->bi_key->ky_name, bi->bi_func->fn_desc,
|
||||
bi->bi_func->fn_name);
|
||||
SNPRINTF(t, WSIZEOF(t), WIDE("%"FMT_L"s"), bi->bi_key->ky_name);
|
||||
else
|
||||
SNPRINTF(s, WSIZEOF(s), WIDE("%"FMT_L"c %"FMT_L"s (%"FMT_L"s)"),
|
||||
bi->bi_code, bi->bi_func->fn_desc, bi->bi_func->fn_name);
|
||||
SNPRINTF(t, WSIZEOF(t), WIDE("%"FMT_L"c"), bi->bi_code);
|
||||
|
||||
if (bi->bi_macro)
|
||||
SNPRINTF(s, WSIZEOF(s), WIDE("%-10"FMT_L"s execute macro: %"FMT_L"s"),
|
||||
t, bi->bi_macro);
|
||||
else
|
||||
SNPRINTF(s, WSIZEOF(s), WIDE("%-10"FMT_L"s %"FMT_L"s (%"FMT_L"s)"),
|
||||
t, bi->bi_func->fn_desc, bi->bi_func->fn_name);
|
||||
|
||||
help[i] = STRDUP(s);
|
||||
i++;
|
||||
}
|
||||
|
|
|
|||
32
str.c
32
str.c
|
|
@ -17,6 +17,7 @@ tokenise(str, res)
|
|||
{
|
||||
int ntoks = 0;
|
||||
const WCHAR *p, *q;
|
||||
WCHAR *r;
|
||||
|
||||
*res = NULL;
|
||||
p = str;
|
||||
|
|
@ -24,6 +25,7 @@ const WCHAR *p, *q;
|
|||
for (;;) {
|
||||
ptrdiff_t sz;
|
||||
int qskip = 0;
|
||||
int isbsl = 0;
|
||||
|
||||
/* Skip leading whitespace */
|
||||
while (ISSPACE(*p))
|
||||
|
|
@ -37,14 +39,13 @@ const WCHAR *p, *q;
|
|||
|
||||
if (*q == '"') {
|
||||
/* Quoted string - scan for end of string */
|
||||
int isbsl = 0;
|
||||
p++;
|
||||
|
||||
while (*++q) {
|
||||
/* Handle escaping with backslash; currently works but the \ isn't
|
||||
* removed from the string.
|
||||
*/
|
||||
if (*q == '\\') {
|
||||
if (!isbsl && (*q == '\\')) {
|
||||
isbsl = 1;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -72,6 +73,33 @@ const WCHAR *p, *q;
|
|||
*res = realloc(*res, sizeof(WCHAR *) * (ntoks + 1));
|
||||
(*res)[ntoks] = malloc(sizeof(WCHAR) * (sz + 1));
|
||||
MEMCPY((*res)[ntoks], p, sz);
|
||||
|
||||
/* Handle \ escapes */
|
||||
for (r = (*res)[ntoks]; r < ((*res)[ntoks] + sz);) {
|
||||
if (!isbsl) {
|
||||
if (*r == '\\') {
|
||||
MEMMOVE(r, r + 1, sz - (r - (*res)[ntoks]));
|
||||
sz--;
|
||||
isbsl = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
r++;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (*r) {
|
||||
case 't': *r = '\t'; break;
|
||||
case 'n': *r = '\n'; break;
|
||||
case 'r': *r = '\r'; break;
|
||||
case 'v': *r = '\v'; break;
|
||||
case '\\': *r = '\\'; break;
|
||||
}
|
||||
|
||||
isbsl = 0;
|
||||
r++;
|
||||
}
|
||||
|
||||
(*res)[ntoks][sz] = 0;
|
||||
ntoks++;
|
||||
|
||||
|
|
|
|||
123
tts.c
123
tts.c
|
|
@ -150,6 +150,8 @@ entry_t *curent;
|
|||
|
||||
int showinv = 0;
|
||||
|
||||
static WCHAR *macro_text, *macro_pos;
|
||||
|
||||
static attrname_t attrnames[] = {
|
||||
{ WIDE("normal"), WA_NORMAL },
|
||||
{ WIDE("bold"), WA_BOLD },
|
||||
|
|
@ -401,32 +403,32 @@ struct kevent evs[2], rev;
|
|||
|
||||
curs_set(0);
|
||||
|
||||
bind_key(WIDE("?"), WIDE("help"));
|
||||
bind_key(WIDE("a"), WIDE("add"));
|
||||
bind_key(WIDE("A"), WIDE("add-old"));
|
||||
bind_key(WIDE("d"), WIDE("delete"));
|
||||
bind_key(WIDE("u"), WIDE("undelete"));
|
||||
bind_key(WIDE("q"), WIDE("quit"));
|
||||
bind_key(WIDE("<CTRL-C>"), WIDE("quit"));
|
||||
bind_key(WIDE("i"), WIDE("invoice"));
|
||||
bind_key(WIDE("b"), WIDE("billable"));
|
||||
bind_key(WIDE("m"), WIDE("mark"));
|
||||
bind_key(WIDE("U"), WIDE("unmarkall"));
|
||||
bind_key(WIDE("<SPACE>"), WIDE("startstop"));
|
||||
bind_key(WIDE("e"), WIDE("edit-desc"));
|
||||
bind_key(WIDE("\\"), WIDE("edit-time"));
|
||||
bind_key(WIDE("<TAB>"), WIDE("showhide-inv"));
|
||||
bind_key(WIDE("c"), WIDE("copy"));
|
||||
bind_key(WIDE("+"), WIDE("add-time"));
|
||||
bind_key(WIDE("-"), WIDE("sub-time"));
|
||||
bind_key(WIDE("/"), WIDE("search"));
|
||||
bind_key(WIDE("$"), WIDE("sync"));
|
||||
bind_key(WIDE("<UP>"), WIDE("prev"));
|
||||
bind_key(WIDE("<DOWN>"), WIDE("next"));
|
||||
bind_key(WIDE(":"), WIDE("execute"));
|
||||
bind_key(WIDE("M"), WIDE("merge"));
|
||||
bind_key(WIDE("r"), WIDE("interrupt"));
|
||||
bind_key(WIDE("R"), WIDE("interrupt"));
|
||||
bind_key(WIDE("?"), WIDE("help"), 0);
|
||||
bind_key(WIDE("a"), WIDE("add"), 0);
|
||||
bind_key(WIDE("A"), WIDE("add-old"), 0);
|
||||
bind_key(WIDE("d"), WIDE("delete"), 0);
|
||||
bind_key(WIDE("u"), WIDE("undelete"), 0);
|
||||
bind_key(WIDE("q"), WIDE("quit"), 0);
|
||||
bind_key(WIDE("<CTRL-C>"), WIDE("quit"), 0);
|
||||
bind_key(WIDE("i"), WIDE("invoice"), 0);
|
||||
bind_key(WIDE("b"), WIDE("billable"), 0);
|
||||
bind_key(WIDE("m"), WIDE("mark"), 0);
|
||||
bind_key(WIDE("U"), WIDE("unmarkall"), 0);
|
||||
bind_key(WIDE("<SPACE>"), WIDE("startstop"), 0);
|
||||
bind_key(WIDE("e"), WIDE("edit-desc"), 0);
|
||||
bind_key(WIDE("\\"), WIDE("edit-time"), 0);
|
||||
bind_key(WIDE("<TAB>"), WIDE("showhide-inv"), 0);
|
||||
bind_key(WIDE("c"), WIDE("copy"), 0);
|
||||
bind_key(WIDE("+"), WIDE("add-time"), 0);
|
||||
bind_key(WIDE("-"), WIDE("sub-time"), 0);
|
||||
bind_key(WIDE("/"), WIDE("search"), 0);
|
||||
bind_key(WIDE("$"), WIDE("sync"), 0);
|
||||
bind_key(WIDE("<UP>"), WIDE("prev"), 0);
|
||||
bind_key(WIDE("<DOWN>"), WIDE("next"), 0);
|
||||
bind_key(WIDE(":"), WIDE("execute"), 0);
|
||||
bind_key(WIDE("M"), WIDE("merge"), 0);
|
||||
bind_key(WIDE("r"), WIDE("interrupt"), 0);
|
||||
bind_key(WIDE("R"), WIDE("interrupt"), 0);
|
||||
|
||||
/*
|
||||
* Make sure we can save (even if it's an empty file or nothing has
|
||||
|
|
@ -496,7 +498,7 @@ struct kevent evs[2], rev;
|
|||
}
|
||||
#endif
|
||||
|
||||
while (GETCH(&c) != ERR) {
|
||||
while (input_char(&c) != ERR) {
|
||||
#ifdef KEY_RESIZE
|
||||
if (c == KEY_RESIZE)
|
||||
continue;
|
||||
|
|
@ -507,8 +509,13 @@ struct kevent evs[2], rev;
|
|||
TTS_TAILQ_FOREACH(bi, &bindings, bi_entries) {
|
||||
if (bi->bi_code != c)
|
||||
continue;
|
||||
bi->bi_func->fn_hdl();
|
||||
goto next;;
|
||||
|
||||
if (!macro_text && bi->bi_macro)
|
||||
input_macro(bi->bi_macro);
|
||||
else if (bi->bi_func)
|
||||
bi->bi_func->fn_hdl();
|
||||
|
||||
goto next;
|
||||
}
|
||||
|
||||
drawstatus(WIDE("Unknown command."));
|
||||
|
|
@ -1022,7 +1029,7 @@ function_t *f;
|
|||
* directly, rather than being looked up in the key table.
|
||||
*/
|
||||
void
|
||||
bind_key(keyname, funcname)
|
||||
bind_key(keyname, funcname, is_macro)
|
||||
const WCHAR *keyname, *funcname;
|
||||
{
|
||||
tkey_t *key = NULL;
|
||||
|
|
@ -1040,15 +1047,23 @@ INT code;
|
|||
} else
|
||||
code = *keyname;
|
||||
|
||||
if ((func = find_func(funcname)) == NULL) {
|
||||
errbox(WIDE("Unknown function \"%"FMT_L"s\""), funcname);
|
||||
return;
|
||||
if (!is_macro) {
|
||||
if ((func = find_func(funcname)) == NULL) {
|
||||
errbox(WIDE("Unknown function \"%"FMT_L"s\""), funcname);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do we already have a binding for this key? */
|
||||
TTS_TAILQ_FOREACH(binding, &bindings, bi_entries) {
|
||||
if (binding->bi_code == code) {
|
||||
binding->bi_func = func;
|
||||
if (is_macro) {
|
||||
binding->bi_func = NULL;
|
||||
binding->bi_macro = STRDUP(funcname);
|
||||
} else {
|
||||
free(binding->bi_macro);
|
||||
binding->bi_func = func;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1058,8 +1073,13 @@ INT code;
|
|||
return;
|
||||
|
||||
binding->bi_key = key;
|
||||
binding->bi_func = func;
|
||||
binding->bi_code = code;
|
||||
|
||||
if (is_macro)
|
||||
binding->bi_macro = STRDUP(funcname);
|
||||
else
|
||||
binding->bi_func = func;
|
||||
|
||||
TTS_TAILQ_INSERT_TAIL(&bindings, binding, bi_entries);
|
||||
}
|
||||
|
||||
|
|
@ -1191,8 +1211,10 @@ vcmderr(msg, ap)
|
|||
|
||||
fprintf(stderr, "\"%s\", line %d: %s\n",
|
||||
curfile, lineno, t);
|
||||
} else
|
||||
} else {
|
||||
input_macro(NULL);
|
||||
vdrawstatus(msg, ap);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1349,3 +1371,32 @@ int h, m, s = 0;
|
|||
sleeptime = 0;
|
||||
}
|
||||
#endif /* USE_DARWIN_POWER */
|
||||
|
||||
void
|
||||
input_macro(s)
|
||||
WCHAR *s;
|
||||
{
|
||||
free(macro_text);
|
||||
macro_text = macro_pos = NULL;
|
||||
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
macro_text = macro_pos = STRDUP(s);
|
||||
}
|
||||
|
||||
int
|
||||
input_char(c)
|
||||
WCHAR *c;
|
||||
{
|
||||
if (macro_pos) {
|
||||
if (*macro_pos) {
|
||||
*c = *macro_pos++;
|
||||
return 0;
|
||||
}
|
||||
free(macro_text);
|
||||
macro_text = macro_pos = NULL;
|
||||
}
|
||||
|
||||
return GETCH(c);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#set bill_advance 0
|
||||
|
||||
#### Bindings
|
||||
#### Bindings and macros
|
||||
#
|
||||
# Use the 'bind' command to (re)define keybindings. Type '?' while TTS is
|
||||
# running for a full list of key bindings.
|
||||
|
|
@ -45,6 +45,12 @@
|
|||
bind j next
|
||||
bind k prev
|
||||
|
||||
# Macros work in a similar way to bindings, except the second argument is a
|
||||
# string which will be executed as if it was typed. For example, the
|
||||
# following macro would add a new entry called "test", and set its timer to
|
||||
# 30 minutes.
|
||||
#macro t "atest\n+30:00\n"
|
||||
|
||||
#### Styling
|
||||
#
|
||||
# You can style UI elements with the 'style' command. Its syntax is:
|
||||
|
|
|
|||
4
ui.c
4
ui.c
|
|
@ -136,7 +136,7 @@ INT c;
|
|||
WADDSTR(pwin, WIDE(" [y/N]? "));
|
||||
wattroff(pwin, A_BOLD);
|
||||
|
||||
while (WGETCH(pwin, &c) == ERR
|
||||
while (input_char(&c) == ERR
|
||||
#ifdef KEY_RESIZE
|
||||
|| (c == KEY_RESIZE)
|
||||
#endif
|
||||
|
|
@ -190,7 +190,7 @@ histent_t *histpos = NULL;
|
|||
wmove(pwin, 0, STRLEN(msg) + 1 + pos);
|
||||
wrefresh(pwin);
|
||||
|
||||
if (WGETCH(pwin, &c) == ERR)
|
||||
if (input_char(&c) == ERR)
|
||||
continue;
|
||||
|
||||
switch (c) {
|
||||
|
|
|
|||
3
wide.h
3
wide.h
|
|
@ -78,4 +78,7 @@ extern int tts_wgetch(WINDOW *, int *);
|
|||
|
||||
#define WSIZEOF(s) (sizeof(s) / sizeof(WCHAR))
|
||||
|
||||
int input_char(WCHAR *);
|
||||
void input_macro(WCHAR *);
|
||||
|
||||
#endif /* !TTS_WIDE_H */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue