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; INT bi_code;
tkey_t *bi_key; tkey_t *bi_key;
function_t *bi_func; function_t *bi_func;
WCHAR *bi_macro;
TTS_TAILQ_ENTRY(binding) bi_entries; TTS_TAILQ_ENTRY(binding) bi_entries;
} binding_t; } binding_t;
@ -32,6 +33,6 @@ typedef TTS_TAILQ_HEAD(bindlist, binding) binding_list_t;
extern binding_list_t bindings; extern binding_list_t bindings;
tkey_t *find_key(const WCHAR *name); 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 */ #endif /* !TTS_BINDINGS_H */

View file

@ -15,6 +15,7 @@
static command_t commands[] = { static command_t commands[] = {
{ WIDE("bind"), c_bind }, { WIDE("bind"), c_bind },
{ WIDE("macro"), c_macro },
{ WIDE("style"), c_style }, { WIDE("style"), c_style },
{ WIDE("set"), c_set }, { WIDE("set"), c_set },
{ } { }
@ -80,7 +81,20 @@ c_bind(argc, argv)
return; 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 void

View file

@ -25,6 +25,7 @@ command_t *find_command(const WCHAR *);
void c_bind (size_t, WCHAR **); void c_bind (size_t, WCHAR **);
void c_style (size_t, WCHAR **); void c_style (size_t, WCHAR **);
void c_set (size_t, WCHAR **); void c_set (size_t, WCHAR **);
void c_macro (size_t, WCHAR **);
void cmderr (const WCHAR *, ...); void cmderr (const WCHAR *, ...);
void vcmderr (const WCHAR *, va_list); void vcmderr (const WCHAR *, va_list);

View file

@ -592,14 +592,20 @@ binding_t *bi;
i = 0; i = 0;
TTS_TAILQ_FOREACH(bi, &bindings, bi_entries) { TTS_TAILQ_FOREACH(bi, &bindings, bi_entries) {
WCHAR s[128]; WCHAR s[128], t[16];
if (bi->bi_key) if (bi->bi_key)
SNPRINTF(s, WSIZEOF(s), WIDE("%-10"FMT_L"s %"FMT_L"s (%"FMT_L"s)"), SNPRINTF(t, WSIZEOF(t), WIDE("%"FMT_L"s"), bi->bi_key->ky_name);
bi->bi_key->ky_name, bi->bi_func->fn_desc,
bi->bi_func->fn_name);
else else
SNPRINTF(s, WSIZEOF(s), WIDE("%"FMT_L"c %"FMT_L"s (%"FMT_L"s)"), SNPRINTF(t, WSIZEOF(t), WIDE("%"FMT_L"c"), bi->bi_code);
bi->bi_code, bi->bi_func->fn_desc, bi->bi_func->fn_name);
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); help[i] = STRDUP(s);
i++; i++;
} }

32
str.c
View file

@ -17,6 +17,7 @@ tokenise(str, res)
{ {
int ntoks = 0; int ntoks = 0;
const WCHAR *p, *q; const WCHAR *p, *q;
WCHAR *r;
*res = NULL; *res = NULL;
p = str; p = str;
@ -24,6 +25,7 @@ const WCHAR *p, *q;
for (;;) { for (;;) {
ptrdiff_t sz; ptrdiff_t sz;
int qskip = 0; int qskip = 0;
int isbsl = 0;
/* Skip leading whitespace */ /* Skip leading whitespace */
while (ISSPACE(*p)) while (ISSPACE(*p))
@ -37,14 +39,13 @@ const WCHAR *p, *q;
if (*q == '"') { if (*q == '"') {
/* Quoted string - scan for end of string */ /* Quoted string - scan for end of string */
int isbsl = 0;
p++; p++;
while (*++q) { while (*++q) {
/* Handle escaping with backslash; currently works but the \ isn't /* Handle escaping with backslash; currently works but the \ isn't
* removed from the string. * removed from the string.
*/ */
if (*q == '\\') { if (!isbsl && (*q == '\\')) {
isbsl = 1; isbsl = 1;
continue; continue;
} }
@ -72,6 +73,33 @@ const WCHAR *p, *q;
*res = realloc(*res, sizeof(WCHAR *) * (ntoks + 1)); *res = realloc(*res, sizeof(WCHAR *) * (ntoks + 1));
(*res)[ntoks] = malloc(sizeof(WCHAR) * (sz + 1)); (*res)[ntoks] = malloc(sizeof(WCHAR) * (sz + 1));
MEMCPY((*res)[ntoks], p, sz); 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; (*res)[ntoks][sz] = 0;
ntoks++; ntoks++;

123
tts.c
View file

@ -150,6 +150,8 @@ entry_t *curent;
int showinv = 0; int showinv = 0;
static WCHAR *macro_text, *macro_pos;
static attrname_t attrnames[] = { static attrname_t attrnames[] = {
{ WIDE("normal"), WA_NORMAL }, { WIDE("normal"), WA_NORMAL },
{ WIDE("bold"), WA_BOLD }, { WIDE("bold"), WA_BOLD },
@ -401,32 +403,32 @@ struct kevent evs[2], rev;
curs_set(0); curs_set(0);
bind_key(WIDE("?"), WIDE("help")); bind_key(WIDE("?"), WIDE("help"), 0);
bind_key(WIDE("a"), WIDE("add")); bind_key(WIDE("a"), WIDE("add"), 0);
bind_key(WIDE("A"), WIDE("add-old")); bind_key(WIDE("A"), WIDE("add-old"), 0);
bind_key(WIDE("d"), WIDE("delete")); bind_key(WIDE("d"), WIDE("delete"), 0);
bind_key(WIDE("u"), WIDE("undelete")); bind_key(WIDE("u"), WIDE("undelete"), 0);
bind_key(WIDE("q"), WIDE("quit")); bind_key(WIDE("q"), WIDE("quit"), 0);
bind_key(WIDE("<CTRL-C>"), WIDE("quit")); bind_key(WIDE("<CTRL-C>"), WIDE("quit"), 0);
bind_key(WIDE("i"), WIDE("invoice")); bind_key(WIDE("i"), WIDE("invoice"), 0);
bind_key(WIDE("b"), WIDE("billable")); bind_key(WIDE("b"), WIDE("billable"), 0);
bind_key(WIDE("m"), WIDE("mark")); bind_key(WIDE("m"), WIDE("mark"), 0);
bind_key(WIDE("U"), WIDE("unmarkall")); bind_key(WIDE("U"), WIDE("unmarkall"), 0);
bind_key(WIDE("<SPACE>"), WIDE("startstop")); bind_key(WIDE("<SPACE>"), WIDE("startstop"), 0);
bind_key(WIDE("e"), WIDE("edit-desc")); bind_key(WIDE("e"), WIDE("edit-desc"), 0);
bind_key(WIDE("\\"), WIDE("edit-time")); bind_key(WIDE("\\"), WIDE("edit-time"), 0);
bind_key(WIDE("<TAB>"), WIDE("showhide-inv")); bind_key(WIDE("<TAB>"), WIDE("showhide-inv"), 0);
bind_key(WIDE("c"), WIDE("copy")); bind_key(WIDE("c"), WIDE("copy"), 0);
bind_key(WIDE("+"), WIDE("add-time")); bind_key(WIDE("+"), WIDE("add-time"), 0);
bind_key(WIDE("-"), WIDE("sub-time")); bind_key(WIDE("-"), WIDE("sub-time"), 0);
bind_key(WIDE("/"), WIDE("search")); bind_key(WIDE("/"), WIDE("search"), 0);
bind_key(WIDE("$"), WIDE("sync")); bind_key(WIDE("$"), WIDE("sync"), 0);
bind_key(WIDE("<UP>"), WIDE("prev")); bind_key(WIDE("<UP>"), WIDE("prev"), 0);
bind_key(WIDE("<DOWN>"), WIDE("next")); bind_key(WIDE("<DOWN>"), WIDE("next"), 0);
bind_key(WIDE(":"), WIDE("execute")); bind_key(WIDE(":"), WIDE("execute"), 0);
bind_key(WIDE("M"), WIDE("merge")); bind_key(WIDE("M"), WIDE("merge"), 0);
bind_key(WIDE("r"), WIDE("interrupt")); bind_key(WIDE("r"), WIDE("interrupt"), 0);
bind_key(WIDE("R"), WIDE("interrupt")); bind_key(WIDE("R"), WIDE("interrupt"), 0);
/* /*
* Make sure we can save (even if it's an empty file or nothing has * 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 #endif
while (GETCH(&c) != ERR) { while (input_char(&c) != ERR) {
#ifdef KEY_RESIZE #ifdef KEY_RESIZE
if (c == KEY_RESIZE) if (c == KEY_RESIZE)
continue; continue;
@ -507,8 +509,13 @@ struct kevent evs[2], rev;
TTS_TAILQ_FOREACH(bi, &bindings, bi_entries) { TTS_TAILQ_FOREACH(bi, &bindings, bi_entries) {
if (bi->bi_code != c) if (bi->bi_code != c)
continue; 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.")); drawstatus(WIDE("Unknown command."));
@ -1022,7 +1029,7 @@ function_t *f;
* directly, rather than being looked up in the key table. * directly, rather than being looked up in the key table.
*/ */
void void
bind_key(keyname, funcname) bind_key(keyname, funcname, is_macro)
const WCHAR *keyname, *funcname; const WCHAR *keyname, *funcname;
{ {
tkey_t *key = NULL; tkey_t *key = NULL;
@ -1040,15 +1047,23 @@ INT code;
} else } else
code = *keyname; code = *keyname;
if ((func = find_func(funcname)) == NULL) { if (!is_macro) {
errbox(WIDE("Unknown function \"%"FMT_L"s\""), funcname); if ((func = find_func(funcname)) == NULL) {
return; errbox(WIDE("Unknown function \"%"FMT_L"s\""), funcname);
return;
}
} }
/* Do we already have a binding for this key? */ /* Do we already have a binding for this key? */
TTS_TAILQ_FOREACH(binding, &bindings, bi_entries) { TTS_TAILQ_FOREACH(binding, &bindings, bi_entries) {
if (binding->bi_code == code) { 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; return;
} }
} }
@ -1058,8 +1073,13 @@ INT code;
return; return;
binding->bi_key = key; binding->bi_key = key;
binding->bi_func = func;
binding->bi_code = code; 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); TTS_TAILQ_INSERT_TAIL(&bindings, binding, bi_entries);
} }
@ -1191,8 +1211,10 @@ vcmderr(msg, ap)
fprintf(stderr, "\"%s\", line %d: %s\n", fprintf(stderr, "\"%s\", line %d: %s\n",
curfile, lineno, t); curfile, lineno, t);
} else } else {
input_macro(NULL);
vdrawstatus(msg, ap); vdrawstatus(msg, ap);
}
} }
/* /*
@ -1349,3 +1371,32 @@ int h, m, s = 0;
sleeptime = 0; sleeptime = 0;
} }
#endif /* USE_DARWIN_POWER */ #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 #set bill_advance 0
#### Bindings #### Bindings and macros
# #
# Use the 'bind' command to (re)define keybindings. Type '?' while TTS is # Use the 'bind' command to (re)define keybindings. Type '?' while TTS is
# running for a full list of key bindings. # running for a full list of key bindings.
@ -45,6 +45,12 @@
bind j next bind j next
bind k prev 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 #### Styling
# #
# You can style UI elements with the 'style' command. Its syntax is: # 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]? ")); WADDSTR(pwin, WIDE(" [y/N]? "));
wattroff(pwin, A_BOLD); wattroff(pwin, A_BOLD);
while (WGETCH(pwin, &c) == ERR while (input_char(&c) == ERR
#ifdef KEY_RESIZE #ifdef KEY_RESIZE
|| (c == KEY_RESIZE) || (c == KEY_RESIZE)
#endif #endif
@ -190,7 +190,7 @@ histent_t *histpos = NULL;
wmove(pwin, 0, STRLEN(msg) + 1 + pos); wmove(pwin, 0, STRLEN(msg) + 1 + pos);
wrefresh(pwin); wrefresh(pwin);
if (WGETCH(pwin, &c) == ERR) if (input_char(&c) == ERR)
continue; continue;
switch (c) { switch (c) {

3
wide.h
View file

@ -78,4 +78,7 @@ extern int tts_wgetch(WINDOW *, int *);
#define WSIZEOF(s) (sizeof(s) / sizeof(WCHAR)) #define WSIZEOF(s) (sizeof(s) / sizeof(WCHAR))
int input_char(WCHAR *);
void input_macro(WCHAR *);
#endif /* !TTS_WIDE_H */ #endif /* !TTS_WIDE_H */