stamen

Static Menu Generator
git clone git://git.dimitrijedobrota.com/stamen.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |

commit075d692948c0933690afdb61764047bf3e00466f
parentca36d15bfaaa0846e70bb0ad32da1fc11c1d8fef
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateWed, 6 Dec 2023 20:08:29 +0000

Error fixing and new C++ namespace bindings * C++ stamen namespace for improved consistency * Ability to start dynamic menu from custom panel * Fix memory leak in Menu::Enries * Removed redundancy * Update README

Diffstat:
MCMakeLists.txt|+-
MREADME.md|++++++++++++++++++++++-
Mdemo/dynamic.cpp|+++++++++++++--------
Mdemo/main.cpp|+-
Minclude/menu.h|+++++++++++++----------
Minclude/stamen.h|+++++++++++++++-
Msrc/generate.cpp|++++++
Msrc/menu.cpp|+++++++++++----
Msrc/stamen.cpp|+++-

9 files changed, 85 insertions(+), 27 deletions(-)


diff --git a/CMakeLists.txt b/CMakeLists.txt

@@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

project(
Stamen
VERSION 0.0.18
VERSION 0.0.19
DESCRIPTION "Static menu generator"
LANGUAGES C CXX
)

diff --git a/README.md b/README.md

@@ -75,6 +75,8 @@ Please reference demo folder for relevant usage example.

There are a few things needed before you begin.
* All types and functions with prefix stamen_ are also available in namespace
stamen:: in C++ for easier use.
* Panel and item codes must be one word. In addition they must be valid C/C++
function names if static menu is to be build correctly.
* Each free function must have `int name(int)` signature as prescribed by

@@ -125,7 +127,7 @@ terminates, but you can use in any way you see fit.

#### Dynamic menu
In dynamic mode, configuration file is read every time the program is run. In
order to invoke the menu you need to add the following code to your C/C++
order to invoke the menu you need to add the following snippet to your C
program:
```

@@ -144,6 +146,25 @@ stamen_insert("free function code", some_free_function);

stamen_dynamic("panel code");
```
For C++ there is a namespaced version of the functions:
```
#include <stamen.h>
const stamen::display_f stamen_display = stamen::builtin_display;
// read the configuration
stamen::read("path to config");
// register free functions
stamen::insert("free function code", some_free_function);
...
// start the menu on specific panel
stamen::dynamic("panel code");
```
`
## Version History
* 1.0

diff --git a/demo/dynamic.cpp b/demo/dynamic.cpp

@@ -1,7 +1,7 @@

#include <stamen.h>
#include <iostream>
#include <stamen.h>
const stamen_display_f stamen_display = stamen_builtin_display;
const stamen::display_f stamen_display = stamen::builtin_display;
int finish(int) { exit(1); }

@@ -21,11 +21,16 @@ int operation3(int) {

}
int main() {
stamen_read("./bin/demo_menu.conf");
stamen_insert("finish", finish);
stamen_insert("operation1", operation1);
stamen_insert("operation2", operation2);
stamen_insert("operation3", operation3);
stamen_dynamic();
// read the configuration
stamen::read("./bin/demo_menu.conf");
// register free functions
stamen::insert("finish", finish);
stamen::insert("operation1", operation1);
stamen::insert("operation2", operation2);
stamen::insert("operation3", operation3);
// start the menu on specific panel
stamen::dynamic("menu_main");
return 0;
}

diff --git a/demo/main.cpp b/demo/main.cpp

@@ -5,7 +5,7 @@

// need to link against stamen library
// in order to use stamen_builtin_display
const stamen_display_f stamen_display = stamen_builtin_display;
const stamen::display_f stamen_display = stamen::builtin_display;
int operation1(int) {
std::cout << "operation 1" << std::endl;

diff --git a/include/menu.h b/include/menu.h

@@ -9,14 +9,14 @@

#include <unordered_map>
#include <vector>
namespace stamen {
class Menu {
friend class Generator;
using callback_f = stamen_callback_f;
using item_t = stamen_item_t;
static std::unordered_map<std::string, Menu> menu_lookup;
static std::unordered_map<std::string, callback_f> free_lookup;
static std::string display_stub_default;
struct private_ctor_t {};

@@ -28,7 +28,10 @@ public:

Menu(const Menu &) = delete;
Menu &operator=(const Menu &) = delete;
static int dynamic() { return display_stub(-1); };
static int dynamic(const std::string &code) {
display_stub_default = code;
return display_stub(-1);
};
static void read(const std::string &s);
static void print(const std::string &entry) { print(entry, 1); }
static void insert(const std::string &s, callback_f callback) {

@@ -60,18 +63,16 @@ private:

static void print(const std::string &entry, const int depth);
static int display_stub(int idx);
static const Menu *getMenu(const std::string &code) {
const auto it = menu_lookup.find(code);
if (it == menu_lookup.end()) return nullptr;
return &it->second;
}
struct Entries {
struct code_t {
const std::string code;
const std::string prompt;
};
~Entries() {
for (const auto [_, prompt] : items) delete[] prompt;
}
std::vector<code_t> codes;
std::vector<item_t> items;

@@ -89,4 +90,6 @@ private:

Entries entries;
};
} // namespace stamen
#endif

diff --git a/include/stamen.h b/include/stamen.h

@@ -18,11 +18,25 @@ struct stamen_item_t {

typedef int (*stamen_display_f)(const char *, const stamen_item_t[], int);
extern const stamen_display_f stamen_display;
EXTERNC int stamen_dynamic(void);
EXTERNC int stamen_dynamic(const char *code);
EXTERNC void stamen_read(const char *filename);
EXTERNC void stamen_insert(const char *code, stamen_callback_f callback);
EXTERNC int stamen_builtin_display(const char *title,
const stamen_item_t itemv[], int size);
#ifdef __cplusplus
namespace stamen {
using callback_f = stamen_callback_f;
using display_f = stamen_display_f;
using item_t = stamen_item_t;
const auto dynamic = stamen_dynamic;
const auto read = stamen_read;
const auto insert = stamen_insert;
const auto builtin_display = stamen_builtin_display;
} // namespace stamen
#endif
#endif

diff --git a/src/generate.cpp b/src/generate.cpp

@@ -6,6 +6,8 @@

#include <iostream>
#include <string>
namespace stamen {
class Generator {
public:
static void generateInclude(std::ostream &os) {

@@ -39,6 +41,10 @@ public:

}
};
} // namespace stamen
using namespace stamen;
int main(const int argc, const char *argv[]) {
const auto args = std::span(argv, size_t(argc));

diff --git a/src/menu.cpp b/src/menu.cpp

@@ -9,8 +9,11 @@

#include <tuple>
#include <utility>
namespace stamen {
std::unordered_map<std::string, Menu> Menu::menu_lookup;
std::unordered_map<std::string, Menu::callback_f> Menu::free_lookup;
std::unordered_map<std::string, callback_f> Menu::free_lookup;
std::string Menu::display_stub_default;
void Menu::read(const std::string &s) {
std::string line, delim, code, prompt;

@@ -35,8 +38,9 @@ void Menu::read(const std::string &s) {

}
void Menu::print(const std::string &code, const int depth) {
const Menu *menu = getMenu(code);
if (!menu) return;
const auto it = menu_lookup.find(code);
if (it == menu_lookup.end()) return;
const Menu *menu = &it->second;
if (depth == 1) std::cout << std::format("{}({})\n", menu->title, code);

@@ -50,7 +54,8 @@ void Menu::print(const std::string &code, const int depth) {

int Menu::display_stub(int idx) {
static std::deque<const Menu *> st;
const std::string &code = st.size() ? st.back()->getCode(idx) : "menu_main";
const std::string &code =
st.size() ? st.back()->getCode(idx) : display_stub_default;
const auto ml_it = menu_lookup.find(code);
if (ml_it != menu_lookup.end()) {

@@ -68,3 +73,5 @@ int Menu::display_stub(int idx) {

std::cout << "Stamen: nothing to do..." << std::endl;
return 1;
}
} // namespace stamen

diff --git a/src/stamen.cpp b/src/stamen.cpp

@@ -7,7 +7,9 @@

#include <ostream>
#include <variant>
int stamen_dynamic(void) { return Menu::dynamic(); }
using namespace stamen;
int stamen_dynamic(const char *code) { return Menu::dynamic(code); }
void stamen_read(const char *filename) { Menu::read(filename); }
void stamen_insert(const char *code, stamen_callback_f callback) {
Menu::insert(code, callback);