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 /) # 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:<action>:<arg>
# chain_failure:<action>:<arg>
# chain_always:<action>:<arg>
# Adds an action to the chain that began with the preceding menu_item. # Adds an action to the chain that began with the preceding menu_item.
# Actions are performed in the order they are written. # Actions are performed in the order they are written.
# Each chain entry MUST follow the menu_item it is attached to. Another # Each chain entry MUST follow the menu_item it is attached to. Another
# menu_item marks the start of the next chain. # 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: # 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 // 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
if (it) nm_config_push_menu_item(&cfg, it);
it = calloc(1, sizeof(nm_menu_item_t)); it = calloc(1, sizeof(nm_menu_item_t));
cur_act = NULL; cur_act = NULL;
// type: menu_item - field 2: location // 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); 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
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, ":")); 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)) action->act = NM_ACTION(name); #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); else action->arg = strdup(c_arg);
nm_config_push_action(&cur_act, action); nm_config_push_action(&cur_act, action);
it->action = cur_act; 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); 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_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 // type: chain - field 2: action
char *c_act = strtrim(strsep(&cur, ":")); 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); 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; 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", 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) 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) { 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;

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 // 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){ 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 = NULL;
bool success = true;
for (nm_menu_action_t *cur = it->action; cur; cur = cur->next) { 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); nm_action_result_t *res = cur->act(cur->arg, &err);
if (err) { if (!(success = err == NULL)) {
NM_LOG("Got error: '%s', displaying...", err); NM_LOG("...error: '%s'", err);
ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QString::fromUtf8(err)); continue;
free(err); } else if (!res) {
return; NM_LOG("...warning: you should have returned a result with type silent, not null, upon success");
} else if (res) { continue;
NM_LOG("Got result: type=%d msg='%s', handling...", res->type, res->msg); }
MainWindowController *mwc; NM_LOG("...result: type=%d msg='%s', handling...", res->type, res->msg);
switch (res->type) { MainWindowController *mwc;
case NM_ACTION_RESULT_TYPE_SILENT: switch (res->type) {
break; case NM_ACTION_RESULT_TYPE_SILENT:
case NM_ACTION_RESULT_TYPE_MSG: break;
ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QLatin1String(res->msg)); case NM_ACTION_RESULT_TYPE_MSG:
break; ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QLatin1String(res->msg));
case NM_ACTION_RESULT_TYPE_TOAST: break;
mwc = MainWindowController_sharedInstance(); case NM_ACTION_RESULT_TYPE_TOAST:
if (!mwc) { mwc = MainWindowController_sharedInstance();
NM_LOG("toast: could not get shared main window controller pointer"); if (!mwc) {
break; NM_LOG("toast: could not get shared main window controller pointer");
}
MainWindowController_toast(mwc, QLatin1String(res->msg), QStringLiteral(""), 1500);
break; break;
} }
nm_action_result_free(res); MainWindowController_toast(mwc, QLatin1String(res->msg), QStringLiteral(""), 1500);
} else { break;
NM_LOG("warning: you should have returned a result with type silent, not null, upon success");
} }
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" { extern "C" {
#endif #endif
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include "action.h" #include "action.h"
@@ -15,6 +16,8 @@ typedef enum {
typedef struct nm_menu_action_t { typedef struct nm_menu_action_t {
char *arg; 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 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; struct nm_menu_action_t *next;
} nm_menu_action_t; } nm_menu_action_t;