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:
Felicity Tarnell 2014-03-07 23:34:06 +00:00
parent 44b2e4371e
commit 3440a87ea3
9 changed files with 159 additions and 49 deletions

View file

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

View file

@ -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

View file

@ -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);

View file

@ -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
View file

@ -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++;

113
tts.c
View file

@ -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;
if (!macro_text && bi->bi_macro)
input_macro(bi->bi_macro);
else if (bi->bi_func)
bi->bi_func->fn_hdl();
goto next;;
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 (!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) {
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,9 +1211,11 @@ vcmderr(msg, ap)
fprintf(stderr, "\"%s\", line %d: %s\n",
curfile, lineno, t);
} else
} else {
input_macro(NULL);
vdrawstatus(msg, ap);
}
}
/*
* Load configuration commands from the file .name. Expects to be called
@ -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);
}

View file

@ -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
View file

@ -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
View file

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