1
0

Implemented action chaining

commit d4e71cdd21347d1d64ef59de4c8bc0afaf775523
Author: shermp <14854761+shermp@users.noreply.github.com>
Date:   Sat Apr 25 19:52:26 2020 +1200

    Log when action is run

    Co-authored-by: Patrick Gaskin <patrick@pgaskin.net>

commit 6533e8cc6a74a0b7246334b4712a96d678aefcd0
Author: shermp <14854761+shermp@users.noreply.github.com>
Date:   Sat Apr 25 19:32:28 2020 +1200

    Fixed field numbers in 'chain' error messages

    Fixes the consequences of copy & paste

commit 9c4311ce5b1eb01b81a18cc19b39025514ba6be4
Author: shermp <14854761+shermp@users.noreply.github.com>
Date:   Sat Apr 25 19:27:30 2020 +1200

    Simplified nm_config_push_action()

    The previous implementation was a bit... convoluted

    Co-authored-by: Patrick Gaskin <patrick@pgaskin.net>

commit e17509a85f1f125e3be4386966d37452c970f8a5
Author: shermp <14854761+shermp@users.noreply.github.com>
Date:   Sat Apr 25 16:29:46 2020 +1200

    Config parser tweaks based on review

    Co-authored-by: Patrick Gaskin <patrick@pgaskin.net>

commit fbea6709ffb3cc755d01bdb7ba3ed06fc901aca5
Author: shermp <14854761+shermp@users.noreply.github.com>
Date:   Sat Apr 25 15:23:22 2020 +1200

    Small documentation tweak

    Co-authored-by: Patrick Gaskin <patrick@pgaskin.net>

commit adbfc4b8edd84ca8ce381b0d4a306678f7039be1
Author: shermp <14854761+shermp@users.noreply.github.com>
Date:   Sat Apr 25 15:12:49 2020 +1200

    Fix config error segfault

    Allocating some memory helps...

commit a0eb89598a74df1e7937c8615c4a4d7d1d17101b
Merge: 17d6e90 7491a97
Author: shermp <14854761+shermp@users.noreply.github.com>
Date:   Sat Apr 25 14:48:06 2020 +1200

    Merge branch 'master' into pre-hook-exp2

commit 17d6e90d62df4b754967d2f2ea94f0d29aa8786d
Author: shermp <14854761+shermp@users.noreply.github.com>
Date:   Sat Apr 25 11:52:41 2020 +1200

    Change loop as suggested

commit fa16f123b6c08fce88227f74b75aa2b1983bef65
Author: shermp <14854761+shermp@users.noreply.github.com>
Date:   Sat Apr 25 11:50:54 2020 +1200

    Initial config parsing implementation for chain

commit d4648c424cd4c79aece73d10549c57a91a49366a
Author: shermp <14854761+shermp@users.noreply.github.com>
Date:   Sat Apr 25 11:09:58 2020 +1200

    Add initial chain documentation

commit fc85239500cb892ee9ecd20c370e664007446d8f
Author: shermp <14854761+shermp@users.noreply.github.com>
Date:   Sat Apr 25 11:02:31 2020 +1200

    Fix broken loop

commit ce71679a42f1fe7f82f9df68e862e6af2f57c18e
Author: shermp <14854761+shermp@users.noreply.github.com>
Date:   Sat Apr 25 10:14:59 2020 +1200

    (WIP) Added nm_menu_action_t for action chaining.

    Note: this commit will NOT compile
This commit is contained in:
shermp
2020-04-25 21:09:27 -04:00
parent 7aabc1d087
commit ff6c38b2da
5 changed files with 95 additions and 40 deletions

View File

@@ -13,6 +13,11 @@
# #
# menu_item:<location>:<label>:<action>:<arg> # menu_item:<location>:<label>:<action>:<arg>
# Adds a menu item (spaces around fields are ignored). # Adds a menu item (spaces around fields are ignored).
# chain:<action>:<arg>
# Adds an action to the chain that began with the preceeding menu_item.
# Actions are performed in the order they are written in the config order.
# chain lines MUST follow the menu_item they are attached to. A new menu_item
# marks the start of the next chain.
# #
# <location> the menu to add the item to, one of: # <location> the menu to add the item to, one of:
# main - the menu in the top-left corner of the home screen # main - the menu in the top-left corner of the home screen

View File

@@ -54,6 +54,12 @@ static void nm_config_push_menu_item(nm_config_t **cfg, nm_menu_item_t *it) {
*cfg = tmp; *cfg = tmp;
} }
static void nm_config_push_action(nm_menu_action_t **cur, nm_menu_action_t *act) {
if (*cur)
(*cur)->next = act;
*cur = act;
}
nm_config_t *nm_config_parse(char **err_out) { nm_config_t *nm_config_parse(char **err_out) {
#define NM_ERR_RET NULL #define NM_ERR_RET NULL
NM_LOG("config: reading config dir %s", NM_CONFIG_DIR); NM_LOG("config: reading config dir %s", NM_CONFIG_DIR);
@@ -100,6 +106,9 @@ nm_config_t *nm_config_parse(char **err_out) {
int line_n = 0; int line_n = 0;
ssize_t line_sz; ssize_t line_sz;
size_t line_bufsz = 0; size_t line_bufsz = 0;
nm_menu_item_t *it = NULL;
nm_menu_action_t *cur_act = NULL;
while ((line_sz = getline(&line, &line_bufsz, cfgfile)) != -1) { while ((line_sz = getline(&line, &line_bufsz, cfgfile)) != -1) {
line_n++; line_n++;
@@ -111,9 +120,10 @@ nm_config_t *nm_config_parse(char **err_out) {
// field 1: type // field 1: type
char *c_typ = strtrim(strsep(&cur, ":")); char *c_typ = strtrim(strsep(&cur, ":"));
if (!strcmp(c_typ, "menu_item")) { if (!strcmp(c_typ, "menu_item")) {
if (it) nm_config_push_menu_item(&cfg, it);
// type: menu_item // type: menu_item
nm_menu_item_t *it = calloc(1, sizeof(nm_menu_item_t)); it = calloc(1, sizeof(nm_menu_item_t));
cur_act = NULL;
// type: menu_item - field 2: location // type: menu_item - field 2: location
char *c_loc = strtrim(strsep(&cur, ":")); char *c_loc = strtrim(strsep(&cur, ":"));
if (!c_loc) RETERR("file %s: line %d: field 2: expected location, got end of line", fn, line_n); if (!c_loc) RETERR("file %s: line %d: field 2: expected location, got end of line", fn, line_n);
@@ -126,10 +136,11 @@ nm_config_t *nm_config_parse(char **err_out) {
if (!c_lbl) RETERR("file %s: line %d: field 3: expected label, got end of line", fn, line_n); if (!c_lbl) RETERR("file %s: line %d: field 3: expected label, got end of line", fn, line_n);
else it->lbl = strdup(c_lbl); else it->lbl = strdup(c_lbl);
nm_menu_action_t *action = calloc(1, sizeof(nm_menu_action_t));
// type: menu_item - field 4: action // type: menu_item - field 4: action
char *c_act = strtrim(strsep(&cur, ":")); char *c_act = strtrim(strsep(&cur, ":"));
if (!c_act) RETERR("file %s: line %d: field 4: expected action, got end of line", fn, line_n); if (!c_act) RETERR("file %s: line %d: field 4: expected action, got end of line", fn, line_n);
#define X(name) else if (!strcmp(c_act, #name)) it->act = NM_ACTION(name); #define X(name) else if (!strcmp(c_act, #name)) action->act = NM_ACTION(name);
NM_ACTIONS NM_ACTIONS
#undef X #undef X
else RETERR("file %s: line %d: field 4: unknown action '%s'", fn, line_n, c_act); else RETERR("file %s: line %d: field 4: unknown action '%s'", fn, line_n, c_act);
@@ -137,12 +148,32 @@ nm_config_t *nm_config_parse(char **err_out) {
// type: menu_item - field 5: argument // type: menu_item - field 5: argument
char *c_arg = strtrim(cur); char *c_arg = strtrim(cur);
if (!c_arg) RETERR("file %s: line %d: field 5: expected argument, got end of line\n", fn, line_n); if (!c_arg) RETERR("file %s: line %d: field 5: expected argument, got end of line\n", fn, line_n);
else it->arg = strdup(c_arg); else action->arg = strdup(c_arg);
nm_config_push_action(&cur_act, action);
it->action = cur_act;
} else if (!strcmp(c_typ, "chain")) {
if (!it) RETERR("file %s: line %d: unexpected chain, no menu_item to link to", fn, line_n);
nm_menu_action_t *action = calloc(1, sizeof(nm_menu_action_t));
nm_config_push_menu_item(&cfg, it); // type: chain - field 2: action
char *c_act = strtrim(strsep(&cur, ":"));
if (!c_act) RETERR("file %s: line %d: field 2: expected action, got end of line", fn, line_n);
#define X(name) else if (!strcmp(c_act, #name)) action->act = NM_ACTION(name);
NM_ACTIONS
#undef X
else RETERR("file %s: line %d: field 2: unknown action '%s'", fn, line_n, c_act);
// type: chain - field 3: argument
char *c_arg = strtrim(cur);
if (!c_arg) RETERR("file %s: line %d: field 3: expected argument, got end of line\n", fn, line_n);
else action->arg = strdup(c_arg);
nm_config_push_action(&cur_act, action);
} else RETERR("file %s: line %d: field 1: unknown type '%s'", fn, line_n, c_typ); } else RETERR("file %s: line %d: field 1: unknown type '%s'", fn, line_n, c_typ);
} }
// Push the last menu item onto the config
if (it) nm_config_push_menu_item(&cfg, it);
it = NULL;
cur_act = NULL;
#undef RETERR #undef RETERR
fclose(cfgfile); fclose(cfgfile);
@@ -156,18 +187,20 @@ nm_config_t *nm_config_parse(char **err_out) {
// add a default entry if none were found // add a default entry if none were found
if (!cfg) { if (!cfg) {
nm_menu_item_t *it = calloc(1, sizeof(nm_menu_item_t)); nm_menu_item_t *it = calloc(1, sizeof(nm_menu_item_t));
nm_menu_action_t *action = calloc(1, sizeof(nm_menu_action_t));
it->loc = NM_MENU_LOCATION_MAIN_MENU; it->loc = NM_MENU_LOCATION_MAIN_MENU;
it->lbl = strdup("NickelMenu"); it->lbl = strdup("NickelMenu");
it->arg = strdup("See KOBOeReader/.add/nm/doc for instructions on how to customize this menu."); action->arg = strdup("See KOBOeReader/.add/nm/doc for instructions on how to customize this menu.");
it->act = NM_ACTION(dbg_toast); action->act = NM_ACTION(dbg_toast);
nm_config_push_menu_item(&cfg, it); nm_config_push_menu_item(&cfg, it);
} }
size_t mm = 0, rm = 0; size_t mm = 0, rm = 0;
for (nm_config_t *cur = cfg; cur; cur = cur->next) { for (nm_config_t *cur = cfg; cur; cur = cur->next) {
if (cur->type == NM_CONFIG_TYPE_MENU_ITEM) { if (cur->type == NM_CONFIG_TYPE_MENU_ITEM) {
NM_LOG("cfg(NM_CONFIG_TYPE_MENU_ITEM) : %d:%s:%p:%s", cur->value.menu_item->loc, cur->value.menu_item->lbl, cur->value.menu_item->act, cur->value.menu_item->arg); for (nm_menu_action_t *cur_act = cur->value.menu_item->action; cur_act; cur_act = cur_act->next)
switch (cur->value.menu_item->loc){ NM_LOG("cfg(NM_CONFIG_TYPE_MENU_ITEM) : %d:%s:%p:%s", cur->value.menu_item->loc, cur->value.menu_item->lbl, cur_act->act, cur_act->arg);
switch (cur->value.menu_item->loc) {
case NM_MENU_LOCATION_MAIN_MENU: mm++; break; case NM_MENU_LOCATION_MAIN_MENU: mm++; break;
case NM_MENU_LOCATION_READER_MENU: rm++; break; case NM_MENU_LOCATION_READER_MENU: rm++; break;
} }
@@ -205,7 +238,16 @@ void nm_config_free(nm_config_t *cfg) {
if (cfg->type == NM_CONFIG_TYPE_MENU_ITEM) { if (cfg->type == NM_CONFIG_TYPE_MENU_ITEM) {
free(cfg->value.menu_item->lbl); free(cfg->value.menu_item->lbl);
free(cfg->value.menu_item->arg); if (cfg->value.menu_item->action) {
nm_menu_action_t *cur = cfg->value.menu_item->action;
nm_menu_action_t *tmp;
while (cur) {
tmp = cur;
cur = cur->next;
free(tmp->arg);
free(tmp);
}
}
free(cfg->value.menu_item); free(cfg->value.menu_item);
} }
free(cfg); free(cfg);

View File

@@ -47,11 +47,11 @@ __attribute__((constructor)) void nm_init() {
items_n = 1; items_n = 1;
items = calloc(items_n, sizeof(nm_menu_item_t*)); items = calloc(items_n, sizeof(nm_menu_item_t*));
items[0] = calloc(1, sizeof(nm_menu_item_t)); items[0] = calloc(1, sizeof(nm_menu_item_t));
items[0]->loc = NM_MENU_LOCATION_MAIN_MENU; items[0]->loc = NM_MENU_LOCATION_MAIN_MENU;
items[0]->lbl = strdup("Config Error"); items[0]->lbl = strdup("Config Error");
items[0]->arg = strdup(err); items[0]->action = calloc(1, sizeof(nm_menu_action_t));
items[0]->act = NM_ACTION(dbg_msg); items[0]->action->arg = strdup(err);
items[0]->action->act = NM_ACTION(dbg_msg);
free(err); free(err);
} else if (!(items = nm_config_get_menu(cfg, &items_n))) { } else if (!(items = nm_config_get_menu(cfg, &items_n))) {

View File

@@ -102,33 +102,36 @@ extern "C" MenuTextItem* _nm_menu_hook(void* _this, QMenu* menu, QString const&
QObject::connect(action, &QAction::triggered, std::function<void(bool)>([it](bool){ QObject::connect(action, &QAction::triggered, std::function<void(bool)>([it](bool){
NM_LOG("Item '%s' pressed...", it->lbl); NM_LOG("Item '%s' pressed...", it->lbl);
char *err; char *err;
nm_action_result_t *res = it->act(it->arg, &err); for (nm_menu_action_t *cur = it->action; cur; cur = cur->next) {
if (err) { NM_LOG("running action %p with argument %s : ", cur->act, cur->arg);
NM_LOG("Got error: '%s', displaying...", err); nm_action_result_t *res = cur->act(cur->arg, &err);
ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QString::fromUtf8(err)); if (err) {
free(err); NM_LOG("Got error: '%s', displaying...", err);
return; ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QString::fromUtf8(err));
} else if (res) { free(err);
NM_LOG("Got result: type=%d msg='%s', handling...", res->type, res->msg); return;
MainWindowController *mwc; } else if (res) {
switch (res->type) { NM_LOG("Got result: type=%d msg='%s', handling...", res->type, res->msg);
case NM_ACTION_RESULT_TYPE_SILENT: MainWindowController *mwc;
break; switch (res->type) {
case NM_ACTION_RESULT_TYPE_MSG: case NM_ACTION_RESULT_TYPE_SILENT:
ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QLatin1String(res->msg)); break;
break; case NM_ACTION_RESULT_TYPE_MSG:
case NM_ACTION_RESULT_TYPE_TOAST: ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QLatin1String(res->msg));
mwc = MainWindowController_sharedInstance(); break;
if (!mwc) { case NM_ACTION_RESULT_TYPE_TOAST:
NM_LOG("toast: could not get shared main window controller pointer"); mwc = MainWindowController_sharedInstance();
if (!mwc) {
NM_LOG("toast: could not get shared main window controller pointer");
break;
}
MainWindowController_toast(mwc, QLatin1String(res->msg), QStringLiteral(""), 1500);
break; break;
} }
MainWindowController_toast(mwc, QLatin1String(res->msg), QStringLiteral(""), 1500); nm_action_result_free(res);
break; } else {
NM_LOG("warning: you should have returned a result with type silent, not null, upon success");
} }
nm_action_result_free(res);
} else {
NM_LOG("warning: you should have returned a result with type silent, not null, upon success");
} }
NM_LOG("Success!"); NM_LOG("Success!");
})); }));

View File

@@ -13,11 +13,16 @@ typedef enum {
NM_MENU_LOCATION_READER_MENU = 2, NM_MENU_LOCATION_READER_MENU = 2,
} nm_menu_location_t; } nm_menu_location_t;
typedef struct nm_menu_action_t {
char *arg;
nm_action_fn_t act; // can block, must return 0 on success, nonzero with out_err set to the malloc'd error message on error
struct nm_menu_action_t *next;
} nm_menu_action_t;
typedef struct { typedef struct {
nm_menu_location_t loc; nm_menu_location_t loc;
char *lbl; char *lbl;
char *arg; nm_menu_action_t *action;
nm_action_fn_t act; // can block, must return 0 on success, nonzero with out_err set to the malloc'd error message on error
} nm_menu_item_t; } nm_menu_item_t;
// nm_menu_hook hooks a dlopen'd libnickel handle to add the specified menus, // nm_menu_hook hooks a dlopen'd libnickel handle to add the specified menus,