1
0

Implemented conditional actions (closes #11) (#20)

This commit is contained in:
Patrick Gaskin
2020-05-13 11:37:35 -04:00
committed by GitHub
parent fa1f08a369
commit bf03f9e22b
4 changed files with 68 additions and 32 deletions

View File

@@ -62,10 +62,18 @@
# cmd_output - the timeout in milliseconds (0 < t < 10000), a colon, then the command line to pass to /bin/sh -c (started in /)
#
# chain:<action>:<arg>
# chain_failure:<action>:<arg>
# chain_always:<action>:<arg>
# Adds an action to the chain that began with the preceding menu_item.
# Actions are performed in the order they are written.
# Each chain entry MUST follow the menu_item it is attached to. Another
# menu_item marks the start of the next chain.
# By default, each action only executes if the previous one was successful.
# If chain_failure is used, the action is only executed if the last one
# failed. If chain_always is used, the action is executed no matter what.
# Any error message is only displayed if the action is the last one which
# was executed in the chain (not necessarily the last one in the config
# file).
#
# For example, you might have a configuration file in KOBOeReader/.adds/nm/mystuff like:
#

View File

@@ -108,8 +108,8 @@ nm_config_t *nm_config_parse(char **err_out) {
// field 1: type
char *c_typ = strtrim(strsep(&cur, ":"));
if (!strcmp(c_typ, "menu_item")) {
if (it) nm_config_push_menu_item(&cfg, it);
// type: menu_item
if (it) nm_config_push_menu_item(&cfg, it);
it = calloc(1, sizeof(nm_menu_item_t));
cur_act = NULL;
// type: menu_item - field 2: location
@@ -124,8 +124,10 @@ 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);
else it->lbl = strdup(c_lbl);
nm_menu_action_t *action = calloc(1, sizeof(nm_menu_action_t));
// type: menu_item - field 4: action
nm_menu_action_t *action = calloc(1, sizeof(nm_menu_action_t));
action->on_failure = true;
action->on_success = true;
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);
#define X(name) else if (!strcmp(c_act, #name)) action->act = NM_ACTION(name);
@@ -139,10 +141,22 @@ nm_config_t *nm_config_parse(char **err_out) {
else action->arg = strdup(c_arg);
nm_config_push_action(&cur_act, action);
it->action = cur_act;
} else if (!strcmp(c_typ, "chain")) {
} else if (!strncmp(c_typ, "chain", 5)) {
// type: 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));
if (!strcmp(c_typ, "chain")) {
action->on_failure = false;
action->on_success = true;
} else if (!strcmp(c_typ, "chain_always")) {
action->on_failure = true;
action->on_success = true;
} else if (!strcmp(c_typ, "chain_failure")) {
action->on_failure = true;
action->on_success = false;
} else RETERR("file %s: line %d: field 1: unknown type '%s'", fn, line_n, c_typ);
// 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);
@@ -187,8 +201,9 @@ nm_config_t *nm_config_parse(char **err_out) {
size_t mm = 0, rm = 0;
for (nm_config_t *cur = cfg; cur; cur = cur->next) {
if (cur->type == NM_CONFIG_TYPE_MENU_ITEM) {
NM_LOG("cfg(NM_CONFIG_TYPE_MENU_ITEM) : %d:%s", cur->value.menu_item->loc, cur->value.menu_item->lbl);
for (nm_menu_action_t *cur_act = cur->value.menu_item->action; cur_act; cur_act = cur_act->next)
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);
NM_LOG("...cfg(NM_CONFIG_TYPE_MENU_ITEM) (%s%s%s) : %p:%s", cur_act->on_success ? "on_success" : "", (cur_act->on_success && cur_act->on_failure) ? ", " : "", cur_act->on_failure ? "on_failure" : "", cur_act->act, cur_act->arg);
switch (cur->value.menu_item->loc) {
case NM_MENU_LOCATION_MAIN_MENU: mm++; break;
case NM_MENU_LOCATION_READER_MENU: rm++; break;

View File

@@ -143,40 +143,50 @@ extern "C" MenuTextItem* _nm_menu_hook(void* _this, QMenu* menu, QString const&
// note: we're capturing by value, i.e. the pointer to the global variable, rather then the stack variable, so this is safe
QObject::connect(action, &QAction::triggered, std::function<void(bool)>([it](bool){
NM_LOG("Item '%s' pressed...", it->lbl);
char *err;
NM_LOG("item '%s' pressed...", it->lbl);
char *err = NULL;
bool success = true;
for (nm_menu_action_t *cur = it->action; cur; cur = cur->next) {
NM_LOG("running action %p with argument %s : ", cur->act, cur->arg);
NM_LOG("action %p with argument %s : ", cur->act, cur->arg);
NM_LOG("...success=%d ; on_success=%d on_failure=%d", success, cur->on_success, cur->on_failure);
if (!((success && cur->on_success) || (!success && cur->on_failure))) {
NM_LOG("...skipping action due to condition flags");
continue;
}
free(err);
nm_action_result_t *res = cur->act(cur->arg, &err);
if (err) {
NM_LOG("Got error: '%s', displaying...", err);
ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QString::fromUtf8(err));
free(err);
return;
} else if (res) {
NM_LOG("Got result: type=%d msg='%s', handling...", res->type, res->msg);
MainWindowController *mwc;
switch (res->type) {
case NM_ACTION_RESULT_TYPE_SILENT:
break;
case NM_ACTION_RESULT_TYPE_MSG:
ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QLatin1String(res->msg));
break;
case NM_ACTION_RESULT_TYPE_TOAST:
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);
if (!(success = err == NULL)) {
NM_LOG("...error: '%s'", err);
continue;
} else if (!res) {
NM_LOG("...warning: you should have returned a result with type silent, not null, upon success");
continue;
}
NM_LOG("...result: type=%d msg='%s', handling...", res->type, res->msg);
MainWindowController *mwc;
switch (res->type) {
case NM_ACTION_RESULT_TYPE_SILENT:
break;
case NM_ACTION_RESULT_TYPE_MSG:
ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QLatin1String(res->msg));
break;
case NM_ACTION_RESULT_TYPE_TOAST:
mwc = MainWindowController_sharedInstance();
if (!mwc) {
NM_LOG("toast: could not get shared main window controller pointer");
break;
}
nm_action_result_free(res);
} else {
NM_LOG("warning: you should have returned a result with type silent, not null, upon success");
MainWindowController_toast(mwc, QLatin1String(res->msg), QStringLiteral(""), 1500);
break;
}
nm_action_result_free(res);
}
NM_LOG("Success!");
if (err) {
NM_LOG("last action returned error %s", err);
ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QString::fromUtf8(err));
free(err);
}
NM_LOG("done");
}));
}

View File

@@ -4,6 +4,7 @@
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include "action.h"
@@ -15,6 +16,8 @@ typedef enum {
typedef struct nm_menu_action_t {
char *arg;
bool on_success;
bool on_failure;
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;