94 lines
3.1 KiB
C++
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
|