Skip to content

Commit

Permalink
Implemented conditional actions (closes #11) (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
pgaskin committed May 13, 2020
1 parent fa1f08a commit bf03f9e
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 32 deletions.
8 changes: 8 additions & 0 deletions res/doc
Original file line number Diff line number Diff line change
Expand Up @@ -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:
#
Expand Down
23 changes: 19 additions & 4 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
66 changes: 38 additions & 28 deletions src/menu.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
if (err) {
NM_LOG("last action returned error %s", err);
ConfirmationDialogFactory_showOKDialog(QString::fromUtf8(it->lbl), QString::fromUtf8(err));
free(err);
}
NM_LOG("Success!");
NM_LOG("done");
}));
}

Expand Down
3 changes: 3 additions & 0 deletions src/menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
extern "C" {
#endif

#include <stdbool.h>
#include <stddef.h>

#include "action.h"
Expand All @@ -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;
Expand Down

0 comments on commit bf03f9e

Please sign in to comment.