* New selection and selection_search menu locations. * Argument transformation for selection menu items.
This commit is contained in:
44
res/doc
44
res/doc
@@ -16,11 +16,39 @@
|
|||||||
# Adds a menu item.
|
# Adds a menu item.
|
||||||
#
|
#
|
||||||
# <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
|
||||||
# (in firmware 4.23.15505+, this menu was removed, so a new item will be added to the tabs on the bottom-right)
|
# (in firmware 4.23.15505+, this menu was removed, so a new item will be added to the tabs on the bottom-right)
|
||||||
# reader - the overflow menu in the reader
|
# reader - the overflow menu in the reader
|
||||||
# browser - the menu in the bottom-right of the web browser
|
# browser - the menu in the bottom-right of the web browser
|
||||||
# library - the menu in the filter bar for the "My Books" and "My Articles" library views
|
# library - the menu in the filter bar for the "My Books" and "My Articles" library views
|
||||||
|
# selection - the selection menu (4.20.14622+)
|
||||||
|
# all arguments will support substitutions in the form {A|B|C} as follows (see the end of this file for examples):
|
||||||
|
# A is the string to substitute
|
||||||
|
# 1 - current selection
|
||||||
|
# B is zero or more transformations applied from left to right:
|
||||||
|
# a - all to lowercase
|
||||||
|
# A - all to uppercase
|
||||||
|
# f - keep everything up to the first whitespace character
|
||||||
|
# n - remove non-alphanumeric
|
||||||
|
# s - trim whitespace
|
||||||
|
# S - normalize whitespace (trim, then collapse multiple whitespace chars)
|
||||||
|
# u - replace whitespace with underscores
|
||||||
|
# w - remove all whitespace
|
||||||
|
# x - special: return an error if the string is empty at this point
|
||||||
|
# C is zero or more escape methods applied from left to right:
|
||||||
|
# " - escape for double-quoted strings (JSON-compatible, C-compatible):
|
||||||
|
# " => \"
|
||||||
|
# newline => \n
|
||||||
|
# backspace => \b
|
||||||
|
# tab => \t
|
||||||
|
# form-feed => \f
|
||||||
|
# carriage-return => \r
|
||||||
|
# \ => \\
|
||||||
|
# $ - escape for single-quoted strings (bash-compatible):
|
||||||
|
# ' => '"'"'
|
||||||
|
# % - url encode all non-alphanumeric chars
|
||||||
|
# selection_search - the search sub-menu of the selection menu (4.20.14622+)
|
||||||
|
# see above
|
||||||
# <label> the label to show on the menu item (must not contain :)
|
# <label> the label to show on the menu item (must not contain :)
|
||||||
# <action> the type of action to run, one of:
|
# <action> the type of action to run, one of:
|
||||||
# cmd_spawn - starts a command in the background
|
# cmd_spawn - starts a command in the background
|
||||||
@@ -188,6 +216,12 @@
|
|||||||
# menu_item :browser :Invert Screen :nickel_setting :toggle :invert
|
# menu_item :browser :Invert Screen :nickel_setting :toggle :invert
|
||||||
# menu_item :browser :Open Pop-Up :nickel_browser :modal
|
# menu_item :browser :Open Pop-Up :nickel_browser :modal
|
||||||
#
|
#
|
||||||
|
# And here are some examples of selection menu substitutions:
|
||||||
|
#
|
||||||
|
# menu_item :selection :Run command :cmd_output :500:echo '{1||$}'
|
||||||
|
# menu_item :selection :Complex example :cmd_output :500:echo '{1|aS|"$}'
|
||||||
|
# menu_item :selection_search :Search DuckDuckGo :nickel_browser :modal:https://duckduckgo.com/?q={1|S|%}
|
||||||
|
#
|
||||||
# If there is an error in the configuration, an item which displays it will be
|
# If there is an error in the configuration, an item which displays it will be
|
||||||
# added to the main menu. If an internal error occurs, it is written to syslog,
|
# added to the main menu. If an internal error occurs, it is written to syslog,
|
||||||
# which can be viewed over telnet or SSH (the username is root) with the command
|
# which can be viewed over telnet or SSH (the username is root) with the command
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
#include <QLayout>
|
#include <QLayout>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QUrl>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QWidgetAction>
|
#include <QWidgetAction>
|
||||||
|
|
||||||
@@ -78,6 +80,16 @@ void (*MenuTextItem_MenuTextItem)(MenuTextItem*, QWidget* parent, bool checkable
|
|||||||
void (*MenuTextItem_setText)(MenuTextItem*, QString const& text);
|
void (*MenuTextItem_setText)(MenuTextItem*, QString const& text);
|
||||||
void (*MenuTextItem_registerForTapGestures)(MenuTextItem*);
|
void (*MenuTextItem_registerForTapGestures)(MenuTextItem*);
|
||||||
|
|
||||||
|
// Selection menu stuff (14622+).
|
||||||
|
typedef void SelectionMenuController; // note: items are re-initialized every time the menu is opened
|
||||||
|
typedef QWidget SelectionMenuView;
|
||||||
|
typedef void WebSearchMixinBase;
|
||||||
|
void (*SelectionMenuController_lookupWikipedia)(SelectionMenuController*);
|
||||||
|
void (*SelectionMenuController_lookupWeb)(SelectionMenuController*);
|
||||||
|
void (*SelectionMenuController_addMenuItem)(SelectionMenuController*, SelectionMenuView* smv, MenuTextItem* mti, const char *slot); // note: the MenuTextItem is created by SelectionMenuController_createMenuTextItem, with smv as the parent (the first QWidget* argument)
|
||||||
|
void (*SelectionMenuView_addMenuItem)(SelectionMenuView*, MenuTextItem *mti); // note: this adds the separator and the item (it doesn't connect signals or things like that)
|
||||||
|
void (*WebSearchMixinBase_doWikipediaSearch)(WebSearchMixinBase *, QString const& selection, QString const& locale);
|
||||||
|
|
||||||
static struct nh_info NickelMenu = (struct nh_info){
|
static struct nh_info NickelMenu = (struct nh_info){
|
||||||
.name = "NickelMenu",
|
.name = "NickelMenu",
|
||||||
.desc = "Integrated launcher for Nickel.",
|
.desc = "Integrated launcher for Nickel.",
|
||||||
@@ -97,6 +109,10 @@ static struct nh_hook NickelMenuHook[] = {
|
|||||||
// bottom nav main menu button injection (15505+)
|
// bottom nav main menu button injection (15505+)
|
||||||
{.sym = "_ZN11MainNavViewC1EP7QWidget", .sym_new = "_nm_menu_hook2", .lib = "libnickel.so.1.0.0", .out = nh_symoutptr(MainNavView_MainNavView), .desc = "bottom nav main menu button injection (15505+)", .optional = true}, //libnickel 4.23.15505 * _ZN11MainNavViewC1EP7QWidget
|
{.sym = "_ZN11MainNavViewC1EP7QWidget", .sym_new = "_nm_menu_hook2", .lib = "libnickel.so.1.0.0", .out = nh_symoutptr(MainNavView_MainNavView), .desc = "bottom nav main menu button injection (15505+)", .optional = true}, //libnickel 4.23.15505 * _ZN11MainNavViewC1EP7QWidget
|
||||||
|
|
||||||
|
// selection menu injection
|
||||||
|
{.sym = "_ZN23SelectionMenuController11addMenuItemEP17SelectionMenuViewP12MenuTextItemPKc", .sym_new = "_nm_menu_hook3", .lib = "libnickel.so.1.0.0", .out = nh_symoutptr(SelectionMenuController_addMenuItem), .desc = "selection menu injection", .optional = true}, //libnickel 4.20.14622 * _ZN23SelectionMenuController11addMenuItemEP17SelectionMenuViewP12MenuTextItemPKc
|
||||||
|
{.sym = "_ZN18WebSearchMixinBase17doWikipediaSearchERK7QStringS2_", .sym_new = "_nm_menu_hook4", .lib = "libnickel.so.1.0.0", .out = nh_symoutptr(WebSearchMixinBase_doWikipediaSearch), .desc = "selection menu injection (wikipedia handler)", .optional = true}, //libnickel 4.20.14622 * _ZN18WebSearchMixinBase17doWikipediaSearchERK7QStringS2_
|
||||||
|
|
||||||
// null
|
// null
|
||||||
{0},
|
{0},
|
||||||
};
|
};
|
||||||
@@ -122,6 +138,11 @@ static struct nh_dlsym NickelMenuDlsym[] = {
|
|||||||
{.name = "_ZN12MenuTextItem7setTextERK7QString", .out = nh_symoutptr(MenuTextItem_setText), .desc = "bottom nav main menu button injection (15505+)", .optional = true}, //libnickel 4.23.15505 * _ZN12MenuTextItem7setTextERK7QString
|
{.name = "_ZN12MenuTextItem7setTextERK7QString", .out = nh_symoutptr(MenuTextItem_setText), .desc = "bottom nav main menu button injection (15505+)", .optional = true}, //libnickel 4.23.15505 * _ZN12MenuTextItem7setTextERK7QString
|
||||||
{.name = "_ZN12MenuTextItem22registerForTapGesturesEv", .out = nh_symoutptr(MenuTextItem_registerForTapGestures), .desc = "bottom nav main menu button injection (15505+)", .optional = true}, //libnickel 4.23.15505 * _ZN12MenuTextItem22registerForTapGesturesEv
|
{.name = "_ZN12MenuTextItem22registerForTapGesturesEv", .out = nh_symoutptr(MenuTextItem_registerForTapGestures), .desc = "bottom nav main menu button injection (15505+)", .optional = true}, //libnickel 4.23.15505 * _ZN12MenuTextItem22registerForTapGesturesEv
|
||||||
|
|
||||||
|
// selection menu injection (14622+)
|
||||||
|
{.name = "_ZN17SelectionMenuView11addMenuItemEP12MenuTextItem", .out = nh_symoutptr(SelectionMenuView_addMenuItem), .desc = "selection menu injection (14622+)", .optional = true}, //libnickel 4.20.14622 * _ZN17SelectionMenuView11addMenuItemEP12MenuTextItem
|
||||||
|
{.name = "_ZN23SelectionMenuController15lookupWikipediaEv", .out = nh_symoutptr(SelectionMenuController_lookupWikipedia), .desc = "selection menu injection (14622+)", .optional = true}, //libnickel 4.20.14622 * _ZN23SelectionMenuController15lookupWikipediaEv
|
||||||
|
{.name = "_ZN23SelectionMenuController9lookupWebEv", .out = nh_symoutptr(SelectionMenuController_lookupWeb), .desc = "selection menu injection (14622+)", .optional = true}, //libnickel 4.20.14622 * _ZN23SelectionMenuController9lookupWebEv
|
||||||
|
|
||||||
// null
|
// null
|
||||||
{0},
|
{0},
|
||||||
};
|
};
|
||||||
@@ -142,9 +163,13 @@ NickelHook(
|
|||||||
// and separator.
|
// and separator.
|
||||||
QAction *AbstractNickelMenuController_createAction_before(QAction *before, nm_menu_location_t loc, bool last_in_group, void *_this, QMenu *menu, QWidget *widget, bool close, bool enabled, bool separator);
|
QAction *AbstractNickelMenuController_createAction_before(QAction *before, nm_menu_location_t loc, bool last_in_group, void *_this, QMenu *menu, QWidget *widget, bool close, bool enabled, bool separator);
|
||||||
|
|
||||||
|
// nm_argtranform_t transforms an action's argument and returns a new malloc'd
|
||||||
|
// string. On error, it should return NULL and set nm_err.
|
||||||
|
typedef char *(*nm_argtransform_t)(void *data, const char *arg);
|
||||||
|
|
||||||
// nm_menu_item_do runs a nm_menu_item_t and must be called from the thread of a
|
// nm_menu_item_do runs a nm_menu_item_t and must be called from the thread of a
|
||||||
// signal handler.
|
// signal handler. argtransform and argtransform_data are optional.
|
||||||
static void nm_menu_item_do(nm_menu_item_t *it);
|
static void nm_menu_item_do(nm_menu_item_t *it, nm_argtransform_t argtransform, void *argtransform_data);
|
||||||
|
|
||||||
// _nm_menu_inject handles the QMenu::aboutToShow signal and injects menu items.
|
// _nm_menu_inject handles the QMenu::aboutToShow signal and injects menu items.
|
||||||
static void _nm_menu_inject(void *nmc, QMenu *menu, nm_menu_location_t loc, int at);
|
static void _nm_menu_inject(void *nmc, QMenu *menu, nm_menu_location_t loc, int at);
|
||||||
@@ -322,7 +347,7 @@ extern "C" __attribute__((visibility("default"))) void _nm_menu_hook2(MainNavVie
|
|||||||
|
|
||||||
QObject::connect(ac, &QAction::triggered, [it](bool) {
|
QObject::connect(ac, &QAction::triggered, [it](bool) {
|
||||||
NM_LOG("item '%s' pressed...", it->lbl);
|
NM_LOG("item '%s' pressed...", it->lbl);
|
||||||
nm_menu_item_do(it);
|
nm_menu_item_do(it, NULL, NULL);
|
||||||
NM_LOG("done");
|
NM_LOG("done");
|
||||||
}); // 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
|
||||||
}
|
}
|
||||||
@@ -341,6 +366,185 @@ extern "C" __attribute__((visibility("default"))) void _nm_menu_hook2(MainNavVie
|
|||||||
NM_LOG("Added button.");
|
NM_LOG("Added button.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// _nm_menu_hook4_item gets/sets the current menu item. It must only be called
|
||||||
|
// by one thread at a time. The item will be cleared after it has been used.
|
||||||
|
nm_menu_item_t *_nm_menu_hook4_item(nm_menu_item_t *it) {
|
||||||
|
static nm_menu_item_t *its = NULL;
|
||||||
|
if (it)
|
||||||
|
return (its = it);
|
||||||
|
if (!its)
|
||||||
|
return NULL;
|
||||||
|
nm_menu_item_t *tmp = its;
|
||||||
|
its = NULL;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" __attribute__((visibility("default"))) void _nm_menu_hook3(SelectionMenuController *_this, SelectionMenuView* smv, MenuTextItem* mti, const char *slot) {
|
||||||
|
NM_LOG("hook3: %p %p %p %s", _this, smv, mti, slot);
|
||||||
|
SelectionMenuController_addMenuItem(_this, smv, mti, slot);
|
||||||
|
|
||||||
|
if (!SelectionMenuView_addMenuItem || !SelectionMenuController_lookupWikipedia || !SelectionMenuController_lookupWeb) {
|
||||||
|
NM_LOG("could not find required SelectionMenuView and SelectionMenuController symbols for adding selection menu items");
|
||||||
|
ConfirmationDialogFactory_showOKDialog(QLatin1String("NickelMenu"), QLatin1String("Could not find required SelectionMenuView and SelectionMenuController symbols for adding selection menu items (this is a bug)."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is important for another reason other than positioning: it only displays if Volume::canSearch()
|
||||||
|
nm_menu_location_t loc;
|
||||||
|
if (!strcmp(slot, "1showSearchOptions()")) //libnickel 4.20.14622 * _ZN23SelectionMenuController17showSearchOptionsEv
|
||||||
|
loc = NM_MENU_LOCATION(selection);
|
||||||
|
else if (!strcmp(slot, "2lookupWeb()")) //libnickel 4.20.14622 * _ZN23SelectionMenuController9lookupWebEv
|
||||||
|
loc = NM_MENU_LOCATION(selection_search);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
NM_LOG("Found search item, injecting menu items after it.");
|
||||||
|
|
||||||
|
NM_LOG("checking for config updates");
|
||||||
|
int rev = nm_global_config_update();
|
||||||
|
NM_LOG("revision = %d", rev);
|
||||||
|
|
||||||
|
NM_LOG("adding items");
|
||||||
|
|
||||||
|
size_t items_n;
|
||||||
|
nm_menu_item_t **items = nm_global_config_items(&items_n);
|
||||||
|
|
||||||
|
if (!items) {
|
||||||
|
NM_LOG("failed to get menu items");
|
||||||
|
ConfirmationDialogFactory_showOKDialog(QLatin1String("NickelMenu"), QLatin1String("Failed to get menu items (this might be a bug)."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < items_n; i++) {
|
||||||
|
nm_menu_item_t *it = items[i];
|
||||||
|
if (it->loc != loc)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
NM_LOG("adding item '%s'...", it->lbl);
|
||||||
|
|
||||||
|
// based on _ZN23SelectionMenuController18createMenuTextItemEP7QWidgetRK7QString
|
||||||
|
|
||||||
|
MenuTextItem *mti = reinterpret_cast<MenuTextItem*>(calloc(1, 256)); // about 3x larger than the 15505 size (92)
|
||||||
|
if (!it) {
|
||||||
|
NM_LOG("failed to allocate memory for config item");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuTextItem_MenuTextItem(mti, smv, false, true);
|
||||||
|
MenuTextItem_setText(mti, QString::fromUtf8(it->lbl));
|
||||||
|
MenuTextItem_registerForTapGestures(mti);
|
||||||
|
|
||||||
|
// based on _ZN23SelectionMenuController18setupSearchOptionsEb and _ZN23SelectionMenuController11addMenuItemEP17SelectionMenuViewP12MenuTextItemPKc, plus some custom stuff for better compatibility
|
||||||
|
|
||||||
|
QPushButton *sh = new QPushButton(smv); // HACK: we use a QPushButton as an adaptor so we can connect an old-style signal with the new-style connect without needing a custom QObject
|
||||||
|
if (!QWidget::connect(mti, SIGNAL(tapped(bool)), sh, SIGNAL(pressed()))) {
|
||||||
|
NM_LOG("Failed to connect SIGNAL(tapped(bool)) on MenuTextItem to SIGNAL(pressed()) on the QPushButton shim, cannot add custom selection menu item.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sh->setVisible(false);
|
||||||
|
|
||||||
|
QObject::connect(sh, &QPushButton::pressed, [_this, it]() {
|
||||||
|
NM_LOG("item '%s' pressed...", it->lbl);
|
||||||
|
_nm_menu_hook4_item(it); // this is safe since it is a pointer captured by value
|
||||||
|
NM_LOG("triggering lookupWikipedia() slot");
|
||||||
|
SelectionMenuController_lookupWikipedia(_this);
|
||||||
|
});
|
||||||
|
|
||||||
|
SelectionMenuView_addMenuItem(smv, mti);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
QString const& selection;
|
||||||
|
} nm_selmenu_argtransform_data_t;
|
||||||
|
|
||||||
|
char *_nm_selmenu_argtransform(void *data, const char *arg) {
|
||||||
|
nm_selmenu_argtransform_data_t *d = (nm_selmenu_argtransform_data_t*)(data);
|
||||||
|
|
||||||
|
QString src = QString::fromUtf8(arg), res;
|
||||||
|
QRegularExpression re = QRegularExpression("\\{([1])\\|([aAfnsSuwx]*)\\|([\"$%]*)\\}");
|
||||||
|
|
||||||
|
for (QStringRef x = src.midRef(0); x.length() > 0;) {
|
||||||
|
QRegularExpressionMatch m = re.match(x.toString());
|
||||||
|
|
||||||
|
if (!m.hasMatch()) {
|
||||||
|
res += x;
|
||||||
|
x = x.mid(x.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString tmp;
|
||||||
|
|
||||||
|
for (int k = 0; k < m.capturedLength(1); k++) {
|
||||||
|
switch (m.capturedRef(1).at(k).toLatin1()) {
|
||||||
|
case '1': tmp = d->selection; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int k = 0; k < m.capturedLength(2); k++) {
|
||||||
|
switch (m.capturedRef(2).at(k).toLatin1()) {
|
||||||
|
case 'a': tmp = tmp.toLower(); break;
|
||||||
|
case 'A': tmp = tmp.toUpper(); break;
|
||||||
|
case 'f': tmp = tmp.split(QRegularExpression("\\s")).first(); break;
|
||||||
|
case 'n': tmp = tmp.remove(QRegularExpression("[^0-9a-zA-Z]")); break;
|
||||||
|
case 's': tmp = tmp.trimmed(); break;
|
||||||
|
case 'S': tmp = tmp.simplified(); break;
|
||||||
|
case 'u': if (tmp.length() == 0) { nm_err_set("argtransform: empty substitution result for %s", qPrintable(m.capturedRef(0).toString())); return NULL; }; break;
|
||||||
|
case 'w': tmp = tmp.remove(QRegularExpression("\\s")); break;
|
||||||
|
case 'x': tmp = tmp.replace(QRegularExpression("\\s"), "_"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int k = 0; k < m.capturedLength(3); k++) {
|
||||||
|
switch (m.capturedRef(3).at(k).toLatin1()) {
|
||||||
|
case '"':
|
||||||
|
tmp = tmp
|
||||||
|
.replace("\"", "\\\"")
|
||||||
|
.replace("\n", "\\n")
|
||||||
|
.replace("\b", "\\b")
|
||||||
|
.replace("\t", "\\t")
|
||||||
|
.replace("\f", "\\f")
|
||||||
|
.replace("\r", "\\r")
|
||||||
|
.replace("\\", "\\\\");
|
||||||
|
break;
|
||||||
|
case '$':
|
||||||
|
tmp = tmp.replace("'", "'\"'\"'");
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
tmp = QUrl::toPercentEncoding(tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res += x.left(m.capturedStart());
|
||||||
|
res += tmp;
|
||||||
|
x = x.mid(m.capturedEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
char *x = strdup(res.toUtf8().data());
|
||||||
|
if (!x)
|
||||||
|
nm_err_set("argtransform: could not allocate memory: %m");
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" __attribute__((visibility("default"))) void _nm_menu_hook4(WebSearchMixinBase *_this, QString const& selection, QString const& locale) {
|
||||||
|
NM_LOG("hook4: %p %s %s", _this, qPrintable(selection), qPrintable(locale));
|
||||||
|
|
||||||
|
nm_menu_item_t *it = _nm_menu_hook4_item(NULL);
|
||||||
|
if (!it) {
|
||||||
|
NM_LOG("No current menu item, continuing with default wikipedia search.");
|
||||||
|
WebSearchMixinBase_doWikipediaSearch(_this, locale, selection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NM_LOG("continuing execution of item %p (%s)", it, it->lbl);
|
||||||
|
nm_selmenu_argtransform_data_t data = (nm_selmenu_argtransform_data_t){
|
||||||
|
.selection = selection,
|
||||||
|
};
|
||||||
|
nm_menu_item_do(it, _nm_selmenu_argtransform, (void*)(&data)); // this is safe since data will not be used after this returns
|
||||||
|
NM_LOG("done");
|
||||||
|
}
|
||||||
|
|
||||||
void _nm_menu_inject(void *nmc, QMenu *menu, nm_menu_location_t loc, int at) {
|
void _nm_menu_inject(void *nmc, QMenu *menu, nm_menu_location_t loc, int at) {
|
||||||
NM_LOG("inject %d @ %d", loc, at);
|
NM_LOG("inject %d @ %d", loc, at);
|
||||||
|
|
||||||
@@ -398,7 +602,7 @@ void _nm_menu_inject(void *nmc, QMenu *menu, nm_menu_location_t loc, int at) {
|
|||||||
|
|
||||||
QObject::connect(action, &QAction::triggered, [it](bool){
|
QObject::connect(action, &QAction::triggered, [it](bool){
|
||||||
NM_LOG("item '%s' pressed...", it->lbl);
|
NM_LOG("item '%s' pressed...", it->lbl);
|
||||||
nm_menu_item_do(it);
|
nm_menu_item_do(it, NULL, NULL);
|
||||||
NM_LOG("done");
|
NM_LOG("done");
|
||||||
}); // 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
|
||||||
}
|
}
|
||||||
@@ -407,7 +611,7 @@ void _nm_menu_inject(void *nmc, QMenu *menu, nm_menu_location_t loc, int at) {
|
|||||||
menu->setProperty("nm_config_rev", rev_n);
|
menu->setProperty("nm_config_rev", rev_n);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nm_menu_item_do(nm_menu_item_t *it) {
|
void nm_menu_item_do(nm_menu_item_t *it, nm_argtransform_t argtransform, void *argtransform_data) {
|
||||||
const char *err = NULL;
|
const char *err = NULL;
|
||||||
bool success = true;
|
bool success = true;
|
||||||
int skip = 0;
|
int skip = 0;
|
||||||
@@ -426,7 +630,18 @@ void nm_menu_item_do(nm_menu_item_t *it) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
nm_action_result_t *res = cur->act(cur->arg);
|
nm_action_result_t *res = NULL;
|
||||||
|
if (!argtransform) {
|
||||||
|
res = cur->act(cur->arg);
|
||||||
|
} else {
|
||||||
|
NM_LOG("...applying argtransform");
|
||||||
|
char *arg = (*argtransform)(argtransform_data, cur->arg);
|
||||||
|
if (arg) {
|
||||||
|
NM_LOG("...applied argtransform: %s", arg);
|
||||||
|
res = cur->act(arg);
|
||||||
|
free(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
err = nm_err();
|
err = nm_err();
|
||||||
|
|
||||||
if (err == NULL && res && res->type == NM_ACTION_RESULT_TYPE_SKIP) {
|
if (err == NULL && res && res->type == NM_ACTION_RESULT_TYPE_SKIP) {
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ extern "C" {
|
|||||||
X(main) \
|
X(main) \
|
||||||
X(reader) \
|
X(reader) \
|
||||||
X(browser) \
|
X(browser) \
|
||||||
X(library)
|
X(library) \
|
||||||
|
X(selection) \
|
||||||
|
X(selection_search)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NM_MENU_LOCATION_NONE = 0, // to allow it to be checked with if
|
NM_MENU_LOCATION_NONE = 0, // to allow it to be checked with if
|
||||||
|
|||||||
Reference in New Issue
Block a user