stamen

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

commit 075d692948c0933690afdb61764047bf3e00466f
parent ca36d15bfaaa0846e70bb0ad32da1fc11c1d8fef
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Wed, 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:
M CMakeLists.txt | + -
M README.md | ++++++++++++++++++++++ -
M demo/dynamic.cpp | +++++++++++++ --------
M demo/main.cpp | + -
M include/menu.h | +++++++++++++ ----------
M include/stamen.h | +++++++++++++++ -
M src/generate.cpp | ++++++
M src/menu.cpp | +++++++++++ ----
M src/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);