+/// __ _____ _ __ _ _
+/// \ \/ / _ \ '_ \| | | |
+/// > < __/ | | | |_| |
+/// /_/\_\___|_| |_|\__,_|
+///
+/// Copyright (c) 1997 - Ognjen 'xolatile' Milan Robovic
+///
+/// xolatile@chud.cyou - xenu - Tiny menu unity header, depending on Xross and XUI headers, hard to use but efficient.
+///
+/// This program is free software, free as in freedom and as in free beer, you can redistribute it and/or modify it under the terms of the GNU
+/// General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version if you wish...
+///
+/// This program is distributed in the hope that it will be useful, but it is probably not, and without any warranty, without even the implied
+/// warranty of merchantability or fitness for a particular purpose, because it is pointless. Please see the GNU (Geenoo) General Public License
+/// for more details, if you dare, it is a lot of text that nobody wants to read...
+
+typedef enum {
+ menu_layout_vertical, menu_layout_horizontal,
+ menu_layout_count
+} menu_layout_enumeration;
+
+typedef enum {
+ menu_type_empty,
+ menu_type_window, menu_type_menu, menu_type_status, menu_type_frame,
+ menu_type_button, menu_type_separator, menu_type_string, menu_type_sprite,
+ menu_type_check_box, menu_type_icon, menu_type_overicon, menu_type_ubericon,
+ menu_type_text_field, menu_type_signal_field, menu_type_cursor_field, menu_type_slider_field,
+ menu_type_text_box, menu_type_code_box, menu_type_help_box, menu_type_data_box,
+ menu_type_count
+} menu_type_enumeration;
+
+typedef struct {
+ character * data;
+ natural font;
+ real scale;
+ natural colour;
+} menu_string;
+
+typedef struct {
+ natural data;
+ real scale;
+ natural colour;
+} menu_sprite;
+
+typedef struct {
+ menu_layout_enumeration layout;
+ menu_type_enumeration type;
+ menu_string * string;
+ menu_string * number;
+ menu_sprite * sprite;
+ integer x;
+ integer y;
+ natural width;
+ natural height;
+ natural offset;
+ natural count;
+ generic * * array;
+ generic * root;
+} menu_structure;
+
+static procedure menu_append (menu_structure * root, menu_structure * menu) {
+ ++root->count;
+
+ root->array = reallocate (root->array, root->count * sizeof (* root->array));
+
+ menu->root = root;
+
+ root->array [root->count - 1] = menu;
+}
+
+static procedure menu_remove (menu_structure * menu) {
+ if (menu->string != null) { menu->string = deallocate (menu->string); }
+ if (menu->number != null) { menu->number = deallocate (menu->number); }
+ if (menu->sprite != null) { menu->sprite = deallocate (menu->sprite); }
+
+ for (natural index = 0; index < menu->count; ++index) {
+ menu_remove (menu->array [index]);
+ }
+
+ if (menu->array != null) {
+ menu->array = deallocate (menu->array);
+ }
+
+ menu = deallocate (menu);
+}
+
+static menu_string * menu_create_string (character * data, natural font, real scale, natural colour) {
+ menu_string * string = allocate (sizeof (* string));
+
+ string->data = data;
+ string->font = font;
+ string->scale = scale;
+ string->colour = colour;
+
+ return (string);
+}
+
+static menu_sprite * menu_create_sprite (natural data, real scale, natural colour) {
+ menu_sprite * sprite = allocate (sizeof (* sprite));
+
+ sprite->data = data;
+ sprite->scale = scale;
+ sprite->colour = colour;
+
+ return (sprite);
+}
+
+static menu_structure * menu_create_base (menu_layout_enumeration layout, menu_type_enumeration type, menu_string * string,
+ menu_string * number, menu_sprite * sprite, integer x, integer y, natural offset) {
+ menu_structure * menu = allocate (sizeof (* menu));
+
+ menu->layout = layout;
+ menu->type = type;
+ menu->string = string;
+ menu->number = number;
+ menu->sprite = sprite;
+ menu->x = x;
+ menu->y = y;
+ menu->offset = offset;
+
+ return (menu);
+}
+
+static menu_structure * menu_create_root (menu_layout_enumeration layout, menu_type_enumeration type, natural offset) {
+ return (menu_create_base (layout, type, null, null, null, 0, 0, offset));
+}
+
+static menu_structure * menu_create_button (menu_layout_enumeration layout, menu_string * text, menu_sprite * icon, natural offset) {
+ return (menu_create_base (layout, menu_type_button, text, null, icon, 0, 0, offset));
+}
+
+static natural menu_string_width (cross_structure * cross, menu_string * string) {
+ return ((string == null) ? 0 : cross_string_width (cross, string->data, string->font, string->scale));
+}
+
+static natural menu_string_height (cross_structure * cross, menu_string * string) {
+ return ((string == null) ? 0 : cross_string_height (cross, string->data, string->font, string->scale));
+}
+
+static natural menu_sprite_width (cross_structure * cross, menu_sprite * sprite) {
+ return ((sprite == null) ? 0 : cross_sprite_width (cross, sprite->data));
+}
+
+static natural menu_sprite_height (cross_structure * cross, menu_sprite * sprite) {
+ return ((sprite == null) ? 0 : cross_sprite_height (cross, sprite->data));
+}
+
+static procedure menu_configure (cross_structure * cross, menu_structure * menu) {
+ for (natural index = 0; index < menu->count; ++index) {
+ menu_structure * submenu = menu->array [index];
+
+ menu_configure (cross, submenu);
+ }
+
+ for (natural index = 0; index < menu->count; ++index) {
+ menu_structure * submenu = menu->array [index];
+
+ if (menu->layout == menu_layout_vertical) {
+ menu->width = maximum_macro (menu->width, submenu->width + 2 * menu->offset);
+ menu->height += submenu->height;
+ }
+
+ if (menu->layout == menu_layout_horizontal) {
+ menu->width += submenu->width;
+ menu->height = maximum_macro (menu->height, submenu->height + 2 * menu->offset);
+ }
+ }
+
+ if (menu->layout == menu_layout_vertical) {
+ menu->width += maximum_natural (3, menu_sprite_width (cross, menu->sprite),
+ menu_string_width (cross, menu->string),
+ menu_string_width (cross, menu->number));
+
+ menu->height += menu_sprite_height (cross, menu->sprite)
+ + menu_string_height (cross, menu->string)
+ + menu_string_height (cross, menu->number);
+ }
+
+ if (menu->layout == menu_layout_horizontal) {
+ menu->width += menu_sprite_width (cross, menu->sprite)
+ + menu_string_width (cross, menu->string)
+ + menu_string_width (cross, menu->number);
+
+ menu->height += maximum_natural (3, menu_sprite_height (cross, menu->sprite),
+ menu_string_height (cross, menu->string),
+ menu_string_height (cross, menu->number));
+ }
+
+ menu->width += 2 * menu->offset;
+ menu->height += 2 * menu->offset;
+
+ if ((menu->count != 0) && (menu->root != null)) {
+ menu->width -= 2 * menu->offset * (menu->layout == menu_layout_vertical);
+ menu->height -= 2 * menu->offset * (menu->layout == menu_layout_horizontal);
+ }
+}
+
+static procedure menu_center (cross_structure * cross, menu_structure * menu) {
+ menu->x = cross_center_x (cross, menu->width);
+ menu->y = cross_center_y (cross, menu->height);
+}
+
+static procedure menu_subrender (cross_structure * cross, ui_structure * ui, menu_structure * menu) {
+ switch (menu->type) {
+ case (menu_type_empty): break;
+
+ case (menu_type_window): ui_render_window (cross, ui, menu->x, menu->y, menu->width, menu->height); break;
+ case (menu_type_menu): ui_render_menu (cross, ui, menu->x, menu->y, menu->width, menu->height); break;
+ case (menu_type_status): ui_render_status (cross, ui, menu->x, menu->y, menu->width, menu->height); break;
+ case (menu_type_frame): ui_render_frame (cross, ui, menu->x, menu->y, menu->width, menu->height); break;
+
+ case (menu_type_button): {
+ boolean sprite_is_used = (menu->sprite != null) && (menu->sprite->data != ~ 0u);
+ ui_render_button (cross, ui, menu->x, menu->y, menu->width, menu->height);
+ if (sprite_is_used == true) {
+ cross_render_sprite_colour (cross, menu->sprite->data, menu->x + menu->offset, menu->y + menu->offset, menu->sprite->colour);
+ }
+ if ((menu->string != null) && (menu->string->data != null)) {
+ integer x = menu->x + menu->offset + (sprite_is_used == true) * menu_sprite_width (cross, menu->sprite) * (menu->layout == menu_layout_horizontal);
+ integer y = menu->y + menu->offset + (sprite_is_used == true) * menu_sprite_height (cross, menu->sprite) * (menu->layout == menu_layout_vertical);
+ cross_render_string (cross, menu->string->data, menu->string->font, x, y, menu->string->scale, menu->string->colour);
+ }
+ } break;
+
+ default: break;
+ }
+}
+
+static procedure menu_render (cross_structure * cross, ui_structure * ui, menu_structure * menu) {
+ menu_subrender (cross, ui, menu);
+
+ for (natural index = 0; index < menu->count; ++index) {
+ menu_structure * submenu = menu->array [index];
+
+ menu_structure * root = submenu->root;
+
+ submenu->x = root->x + root->offset;
+ submenu->y = root->y + root->offset;
+
+ for (natural subindex = 0; subindex < index; ++subindex) {
+ menu_structure * base = root->array [subindex];
+
+ submenu->x += (root->layout == menu_layout_horizontal) * base->width;
+ submenu->y += (root->layout == menu_layout_vertical) * base->height;
+ }
+
+ menu_render (cross, ui, submenu);
+ }
+}