8
res/doc
8
res/doc
@@ -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:
|
||||
#
|
||||
|
||||
23
src/config.c
23
src/config.c
@@ -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;
|
||||
|
||||
66
src/menu.cc
66
src/menu.cc
@@ -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");
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user