Files
reasampler/src/shell/actions/action_registry.cpp
T

94 lines
3.1 KiB
C++

// action_registry.cpp — see action_registry.h. Needs no REAPER API pointers:
// rec->Register is a member call on the dispatch struct REAPER hands the entry point.
#include "shell/actions/action_registry.h"
#include <cstring>
#include <deque>
#include <string>
#include "core/version/app_version.h" // channelCommandId / channelActionName
namespace reasampler {
namespace {
using version::channelActionName;
using version::channelCommandId;
// std::deque never invalidates references on push_back, so a c_str() handed to
// REAPER stays valid until process exit. Memoized by suffix so register and
// mirror-unregister get the SAME id pointer.
std::deque<std::string> g_strStore;
// std::deque so element addresses never move after push_back — REAPER keeps each
// &accel until the mirror-unregister.
struct TableEntry {
ActionTableRow row;
const char* id = nullptr; // interned channel-qualified command id
int cmd = 0; // minted command id (0 = mint failed / inert row)
gaccel_register_t accel{}; // Actions-list entry; address handed to REAPER
};
std::deque<TableEntry> g_table;
} // namespace
const char* channelIdFor(const char* suffix) {
const std::string composed = channelCommandId(suffix);
for (const std::string& s : g_strStore)
if (s == composed) return s.c_str();
g_strStore.push_back(composed);
return g_strStore.back().c_str();
}
int registerAction(reaper_plugin_info_t* rec, const char* suffix,
gaccel_register_t& accel, const char* phrase) {
const char* id = channelIdFor(suffix);
const int cmd = rec->Register("command_id", (void*)id);
if (cmd) {
g_strStore.push_back(channelActionName(phrase));
accel.accel.cmd = cmd;
accel.desc = g_strStore.back().c_str();
rec->Register("gaccel", (void*)&accel);
}
return cmd;
}
void registerActionTable(reaper_plugin_info_t* rec, const ActionTableRow* rows,
std::size_t count) {
g_table.clear(); // idempotent-by-construction: a re-register never doubles-up rows
for (std::size_t i = 0; i < count; ++i) {
g_table.push_back(TableEntry{rows[i]});
TableEntry& e = g_table.back();
e.id = channelIdFor(e.row.suffix);
e.cmd = registerAction(rec, e.row.suffix, e.accel, e.row.phrase);
}
}
bool actionTableHandleCommand(int command) {
if (command == 0) return false;
for (const TableEntry& e : g_table)
if (e.cmd != 0 && command == e.cmd) {
e.row.run(e.row.arg);
return true;
}
return false;
}
int actionTableCommandId(const char* suffix) {
for (const TableEntry& e : g_table)
if (std::strcmp(e.row.suffix, suffix) == 0) return e.cmd;
return 0;
}
void unregisterActionTable(reaper_plugin_info_t* rec) {
// Reverse table order, mirroring the register loop. Each '-command_id'
// re-presents the SAME interned pointer channelIdFor handed out at register.
for (auto it = g_table.rbegin(); it != g_table.rend(); ++it) {
rec->Register("-gaccel", (void*)&it->accel);
rec->Register("-command_id", (void*)it->id);
}
}
} // namespace reasampler