Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_gui_dictionary.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
27#define WIDTH 800
28#define HEIGHT 800
29
30#define ALLEGRO_UNSTABLE 1
31
32#include "nilorea/n_common.h"
33#include "nilorea/n_list.h"
34#include "nilorea/n_hash.h"
35#include "nilorea/n_pcre.h"
36#include "nilorea/n_str.h"
37#include "nilorea/n_gui.h"
38#include <allegro5/allegro_ttf.h>
39
40/* dictionnaries are from https://www.bragitoff.com/2016/03/english-dictionary-in-csv-format/ */
41
42ALLEGRO_DISPLAY* display = NULL;
43
44int DONE = 0, /* Flag to check if we are always running */
45 getoptret = 0, /* launch parameter check */
46 log_level = LOG_INFO; /* default LOG_LEVEL */
47
48ALLEGRO_TIMER* fps_timer = NULL;
49ALLEGRO_TIMER* logic_timer = NULL;
50
58
66
67/* application state */
68static HASH_TABLE* dictionary = NULL;
69static LIST* completion = NULL;
70static size_t max_results = 100;
71static char last_search[4096] = "";
72
73/* GUI widget ids */
74static int search_textarea_id = -1;
75static int completion_listbox_id = -1;
76static int definition_label_id = -1;
77static int definition_win_id = -1;
78static int status_win_id = -1;
79static int status_label_id = -1;
80
81/* forward declarations */
82static void update_completion(N_GUI_CTX* gui, const char* text);
83static void update_definitions(N_GUI_CTX* gui, const char* word);
84
85void free_entry_def(void* entry_def) {
87 FreeNoLog(def->type);
89 FreeNoLog(def);
90}
91void free_entry(void* entry_ptr) {
92 DICTIONARY_ENTRY* entry = (DICTIONARY_ENTRY*)entry_ptr;
94 FreeNoLog(entry->key);
95 FreeNoLog(entry);
96}
97
98/* callback: search text changed */
99void on_search_change(int widget_id, const char* text, void* user_data) {
100 (void)widget_id;
101 N_GUI_CTX* gui = (N_GUI_CTX*)user_data;
102 update_completion(gui, text);
103 update_definitions(gui, text);
104}
105
106/* callback: completion list selection */
107void on_completion_select(int widget_id, int index, int selected, void* user_data) {
108 (void)widget_id;
109 if (!selected) return;
110 N_GUI_CTX* gui = (N_GUI_CTX*)user_data;
111
112 const char* item_text = n_gui_listbox_get_item_text(gui, completion_listbox_id, index);
113 if (item_text) {
115 update_completion(gui, item_text);
116 update_definitions(gui, item_text);
117 }
118}
119
120/* callback: clear button */
121void on_clear_click(int widget_id, void* user_data) {
122 (void)widget_id;
123 N_GUI_CTX* gui = (N_GUI_CTX*)user_data;
127}
128
129static void update_completion(N_GUI_CTX* gui, const char* text) {
130 if (strcmp(text, last_search) == 0) return;
131 strncpy(last_search, text, sizeof(last_search) - 1);
132 last_search[sizeof(last_search) - 1] = '\0';
133
134 if (completion) {
136 }
138
140 if (completion) {
141 list_foreach(node, completion) {
142 char* entry_key = (char*)node->ptr;
144 }
145 }
146}
147
148static void update_definitions(N_GUI_CTX* gui, const char* word) {
149 DICTIONARY_ENTRY* entry = NULL;
150 if (ht_get_ptr(dictionary, word, (void**)&entry) == TRUE) {
151 char buf[16384] = "";
152 size_t offset = 0;
153 list_foreach(node, entry->definitions) {
155 int written = snprintf(buf + offset, sizeof(buf) - offset, "%s : %s\n", def->type, def->definition);
156 if (written > 0) offset += (size_t)written;
157 if (offset >= sizeof(buf) - 1) break;
158 }
160 } else {
162 }
163}
164
165int main(int argc, char* argv[]) {
167
168 n_log(LOG_NOTICE, "%s is starting ...", argv[0]);
169
170 /* allegro 5 + addons loading */
171 if (!al_init()) {
172 n_abort("Could not init Allegro.\n");
173 }
174 if (!al_init_image_addon()) {
175 n_abort("Unable to initialize image addon\n");
176 }
177 if (!al_init_primitives_addon()) {
178 n_abort("Unable to initialize primitives addon\n");
179 }
180 if (!al_init_font_addon()) {
181 n_abort("Unable to initialize font addon\n");
182 }
183 if (!al_init_ttf_addon()) {
184 n_abort("Unable to initialize ttf_font addon\n");
185 }
186 if (!al_install_keyboard()) {
187 n_abort("Unable to initialize keyboard handler\n");
188 }
189 if (!al_install_mouse()) {
190 n_abort("Unable to initialize mouse handler\n");
191 }
192 ALLEGRO_EVENT_QUEUE* event_queue = NULL;
193
194 event_queue = al_create_event_queue();
195 if (!event_queue) {
196 fprintf(stderr, "failed to create event_queue!\n");
197 al_destroy_display(display);
198 return -1;
199 }
200
201 char ver_str[128] = "";
202 while ((getoptret = getopt(argc, argv, "hvV:L:")) != EOF) {
203 switch (getoptret) {
204 case 'h':
205 n_log(LOG_NOTICE, "\n %s -h help -v version -V LOG_LEVEL (NOLOG,INFO,NOTICE,ERR,DEBUG) -L OPT_LOG_FILE\n", argv[0]);
206 exit(TRUE);
207 case 'v':
208 sprintf(ver_str, "%s %s", __DATE__, __TIME__);
209 exit(TRUE);
210 break;
211 case 'V':
212 if (!strncmp("INFO", optarg, 6)) {
214 } else {
215 if (!strncmp("NOTICE", optarg, 7)) {
217 } else {
218 if (!strncmp("ERR", optarg, 5)) {
220 } else {
221 if (!strncmp("DEBUG", optarg, 5)) {
223 } else {
224 n_log(LOG_ERR, "%s is not a valid log level\n", optarg);
225 exit(FALSE);
226 }
227 }
228 }
229 }
230 n_log(LOG_NOTICE, "LOG LEVEL UP TO: %d\n", log_level);
232 break;
233 case 'L':
234 n_log(LOG_NOTICE, "LOG FILE: %s\n", optarg);
235 set_log_file(optarg);
236 break;
237 case '?': {
238 switch (optopt) {
239 case 'V':
240 n_log(LOG_ERR, "\nPlease specify a log level after -V. \nAvailable values: NOLOG,VERBOSE,NOTICE,ERROR,DEBUG\n\n");
241 break;
242 case 'L':
243 n_log(LOG_ERR, "\nPlease specify a log file after -L\n");
244 default:
245 break;
246 }
247 }
248 __attribute__((fallthrough));
249 default:
250 n_log(LOG_ERR, "\n %s -h help -v version -V DEBUGLEVEL (NOLOG,VERBOSE,NOTICE,ERROR,DEBUG) -L logfile\n", argv[0]);
251 exit(FALSE);
252 }
253 }
254
255 double fps = 60.0;
256 double fps_delta_time = 1.0 / fps;
257
258 fps_timer = al_create_timer(fps_delta_time);
259
260 al_set_new_display_flags(ALLEGRO_OPENGL | ALLEGRO_WINDOWED);
261 display = al_create_display(WIDTH, HEIGHT);
262 if (!display) {
263 n_abort("Unable to create display\n");
264 }
265 al_set_window_title(display, argv[0]);
266
267 al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP);
268
269 DONE = 0;
270
271 enum APP_KEYS {
272 KEY_ESC
273 };
274 int key[1] = {false};
275
276 al_register_event_source(event_queue, al_get_display_event_source(display));
277 al_start_timer(fps_timer);
278 al_register_event_source(event_queue, al_get_timer_event_source(fps_timer));
279
280 al_register_event_source(event_queue, al_get_keyboard_event_source());
281 al_register_event_source(event_queue, al_get_mouse_event_source());
282
283 ALLEGRO_FONT* font = NULL;
284 font = al_load_font("DATAS/2Dumb.ttf", 18, 0);
285 if (!font) {
286 n_log(LOG_ERR, "Unable to load DATAS/2Dumb.ttf, using builtin font");
287 font = al_create_builtin_font();
288 }
289
290 int do_draw = 0;
291
292 /* Load dictionaries */
293 dictionary = new_ht_trie(256, 32);
294 FILE* dict_file = fopen("DATAS/dictionary.csv", "r");
295 if (!dict_file) {
296 n_abort("Unable to open DATAS/dictionary.csv\n");
297 }
298 char read_buf[16384] = "";
299 char* entry_key = NULL;
300 char* type = NULL;
301 char* definition = NULL;
302 N_PCRE* dico_regexp = npcre_new("\"(.*)\",\"(.*)\",\"(.*)\"", 0);
303
304 while (fgets(read_buf, 16384, dict_file)) {
305 if (npcre_match_capture(read_buf, dico_regexp)) {
306 entry_key = strdup(_str((char*)dico_regexp->match_list[1]));
307 type = strdup(_str((char*)dico_regexp->match_list[2]));
308 definition = strdup(_str((char*)dico_regexp->match_list[3]));
309
310 // n_log(LOG_DEBUG, "matched %s , %s , %s", entry_key, type, definition);
311
312 DICTIONARY_ENTRY* entry = NULL;
313 DICTIONARY_DEFINITION* entry_def = NULL;
314
315 if (ht_get_ptr(dictionary, entry_key, (void**)&entry) == TRUE) {
316 Malloc(entry_def, DICTIONARY_DEFINITION, 1);
317 entry_def->type = strdup(type);
318 entry_def->definition = strdup(definition);
319 list_push(entry->definitions, entry_def, &free_entry_def);
320 } else {
321 Malloc(entry, DICTIONARY_ENTRY, 1);
322
324 entry->key = strdup(entry_key);
325
326 Malloc(entry_def, DICTIONARY_DEFINITION, 1);
327
328 entry_def->type = strdup(type);
329 entry_def->definition = strdup(definition);
330
331 list_push(entry->definitions, entry_def, &free_entry_def);
332
333 ht_put_ptr(dictionary, entry_key, entry, &free_entry, NULL);
334 }
335 FreeNoLog(entry_key);
336 FreeNoLog(type);
337 FreeNoLog(definition);
338
339 npcre_clean_match(dico_regexp);
340 }
341 }
342 fclose(dict_file);
343 npcre_delete(&dico_regexp);
344
345 n_log(LOG_NOTICE, "Dictionary loaded, starting GUI");
346
347 /* ---- Create GUI ---- */
348 N_GUI_CTX* gui = n_gui_new_ctx(font);
349
350 /* set display size for global scrollbars (shown when GUI exceeds display) */
351 n_gui_set_display_size(gui, (float)WIDTH, (float)HEIGHT);
352 /* enable virtual canvas for resolution-independent scaling */
353 n_gui_set_virtual_size(gui, (float)WIDTH, (float)HEIGHT);
354
355 /* --- Search Window --- */
356 int search_win = n_gui_add_window(gui, "Search", 20, 20, 400, 85);
357 n_gui_add_label(gui, search_win, "Type to search:", 10, 5, 280, 20, N_GUI_ALIGN_LEFT);
358 search_textarea_id = n_gui_add_textarea(gui, search_win, 10, 28, 300, 24, 0, 256, on_search_change, gui);
359 n_gui_add_button(gui, search_win, "Clear", 320, 28, 70, 24, N_GUI_SHAPE_ROUNDED, on_clear_click, gui);
361
362 /* --- Completion Window --- */
363 int completion_win = n_gui_add_window(gui, "Completions", 440, 20, 350, 690);
366
367 /* --- Definition Window --- */
368 definition_win_id = n_gui_add_window(gui, "Definition", 20, 120, 400, 590);
371
372 /* --- Status Bar Window --- */
373 status_win_id = n_gui_add_window(gui, "Help", 20, 720, 770, 60);
374 status_label_id = n_gui_add_label(gui, status_win_id, "Type in Search box | Click completions to select | ESC to quit", 5, 2, 760, 20, N_GUI_ALIGN_LEFT);
376
377 /* initial completion */
378 strncpy(last_search, "a", sizeof(last_search) - 1);
380
381 al_clear_keyboard_state(NULL);
382 al_flush_event_queue(event_queue);
383
384 do {
385 ALLEGRO_EVENT ev;
386 al_wait_for_event(event_queue, &ev);
387
388 /* pass events to the GUI.
389 * Use gui_handled to prevent mouse events from reaching game logic
390 * when the cursor is over a GUI window or widget. */
391 int gui_handled = n_gui_process_event(gui, ev);
392 (void)gui_handled;
393
394 if (ev.type == ALLEGRO_EVENT_KEY_DOWN) {
395 switch (ev.keyboard.keycode) {
396 case ALLEGRO_KEY_ESCAPE:
397 key[KEY_ESC] = 1;
398 break;
399 default:
400 break;
401 }
402 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
403 DONE = 1;
404 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE) {
405 al_acknowledge_resize(display);
406 n_gui_set_display_size(gui, (float)al_get_display_width(display),
407 (float)al_get_display_height(display));
408 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN || ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT) {
409 al_clear_keyboard_state(display);
410 al_flush_event_queue(event_queue);
411 }
412
413 if (ev.type == ALLEGRO_EVENT_TIMER) {
414 do_draw = 1;
415 }
416
417 if (do_draw && al_is_event_queue_empty(event_queue)) {
418 al_set_target_bitmap(al_get_backbuffer(display));
419 al_clear_to_color(al_map_rgba(20, 20, 25, 255));
420
421 /* draw all GUI windows and widgets */
423
424 al_flip_display();
425 do_draw = 0;
426 }
427
428 } while (!key[KEY_ESC] && !DONE);
429
431 if (completion)
433
435 al_destroy_event_queue(event_queue);
436 al_destroy_font(font);
437 al_destroy_timer(fps_timer);
438 al_uninstall_system();
439
440 return 0;
441}
int main(void)
ALLEGRO_TIMER * fps_timer
Definition ex_fluid.c:66
int getoptret
Definition ex_fluid.c:60
int DONE
Definition ex_fluid.c:59
int log_level
Definition ex_fluid.c:61
ALLEGRO_TIMER * logic_timer
Definition ex_fluid.c:67
ALLEGRO_DISPLAY * display
Definition ex_fluid.c:53
#define WIDTH
Definition ex_gui.c:36
#define HEIGHT
Definition ex_gui.c:37
char * definition
content of definition
static int search_textarea_id
void free_entry(void *entry_ptr)
static int status_win_id
LIST * definitions
list of DICTIONARY_DEFINITION for that entry
void on_search_change(int widget_id, const char *text, void *user_data)
static int definition_win_id
void on_clear_click(int widget_id, void *user_data)
void on_completion_select(int widget_id, int index, int selected, void *user_data)
static int status_label_id
static int definition_label_id
char * key
key of the entry
static LIST * completion
char * type
type of definition (verb, noun,...)
static void update_completion(N_GUI_CTX *gui, const char *text)
static int completion_listbox_id
void free_entry_def(void *entry_def)
static size_t max_results
static void update_definitions(N_GUI_CTX *gui, const char *word)
static HASH_TABLE * dictionary
static char last_search[4096]
dictionary definition for DICTIONARY_ENTRY
dictionary entry
static N_GUI_CTX * gui
char * key
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:271
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:203
#define _str(__PTR)
define true
Definition n_common.h:192
void n_abort(char const *format,...)
abort program with a text
Definition n_common.c:52
void n_gui_set_display_size(N_GUI_CTX *ctx, float w, float h)
Set the display (viewport) size for global scrollbar computation.
Definition n_gui.c:5176
#define N_GUI_ALIGN_LEFT
left aligned text
Definition n_gui.h:156
void n_gui_textarea_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set the text content of a textarea widget
Definition n_gui.c:1752
void n_gui_label_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set the text of a label widget
Definition n_gui.c:2178
int n_gui_add_listbox(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int selection_mode, void(*on_select)(int, int, int, void *), void *user_data)
Add a listbox widget.
Definition n_gui.c:1336
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
Definition n_gui.c:5767
int n_gui_add_label(N_GUI_CTX *ctx, int window_id, const char *text, float x, float y, float w, float h, int align)
Add a static text label.
Definition n_gui.c:1497
#define N_GUI_WIN_FIXED_POSITION
disable window dragging (default:enable)
Definition n_gui.h:196
int n_gui_listbox_add_item(N_GUI_CTX *ctx, int widget_id, const char *text)
add an item to a listbox widget
Definition n_gui.c:1860
void n_gui_set_virtual_size(N_GUI_CTX *ctx, float w, float h)
Set the virtual canvas size for resolution-independent scaling.
Definition n_gui.c:4974
#define N_GUI_SELECT_SINGLE
single item selection
Definition n_gui.h:142
void n_gui_draw(N_GUI_CTX *ctx)
Draw all visible windows and their widgets.
Definition n_gui.c:4800
int n_gui_add_window(N_GUI_CTX *ctx, const char *title, float x, float y, float w, float h)
Add a new pseudo-window to the context.
Definition n_gui.c:581
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
Definition n_gui.c:466
const char * n_gui_listbox_get_item_text(N_GUI_CTX *ctx, int widget_id, int index)
get the text of a listbox item
Definition n_gui.c:1937
void n_gui_listbox_clear(N_GUI_CTX *ctx, int widget_id)
remove all items from a listbox widget
Definition n_gui.c:1907
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
Definition n_gui.c:523
#define N_GUI_SHAPE_ROUNDED
rounded rectangle shape
Definition n_gui.h:116
void n_gui_window_set_flags(N_GUI_CTX *ctx, int window_id, int flags)
Set feature flags on a window.
Definition n_gui.c:843
#define N_GUI_ALIGN_JUSTIFIED
justified text (spread words to fill width)
Definition n_gui.h:162
int n_gui_add_textarea(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int multiline, size_t char_limit, void(*on_change)(int, const char *, void *), void *user_data)
Add a text area widget.
Definition n_gui.c:1214
int n_gui_add_button(N_GUI_CTX *ctx, int window_id, const char *label, float x, float y, float w, float h, int shape, void(*on_click)(int, void *), void *user_data)
Add a button widget to a window.
Definition n_gui.c:1001
The top-level GUI context that holds all windows.
Definition n_gui.h:882
int ht_get_ptr(HASH_TABLE *table, const char *key, void **val)
get pointer at 'key' from 'table'
Definition n_hash.c:2100
int destroy_ht(HASH_TABLE **table)
empty a table and destroy it
Definition n_hash.c:2234
LIST * ht_get_completion_list(HASH_TABLE *table, const char *keybud, size_t max_results)
get next matching keys in table tree
Definition n_hash.c:2391
int ht_put_ptr(HASH_TABLE *table, const char *key, void *ptr, void(*destructor)(void *ptr), void *(*duplicator)(void *ptr))
put an arbitrary pointer value with given key in the targeted hash table
Definition n_hash.c:2154
HASH_TABLE * new_ht_trie(size_t alphabet_length, size_t alphabet_offset)
create a TRIE hash table with the alphabet_size, each key value beeing decreased by alphabet_offset
Definition n_hash.c:1955
structure of a hash table
Definition n_hash.h:137
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
Definition n_list.c:227
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper, safe for node removal during iteration.
Definition n_list.h:88
int list_destroy(LIST **list)
Empty and Free a list container.
Definition n_list.c:547
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
Definition n_list.c:36
#define MAX_LIST_ITEMS
flag to pass to new_generic_list for the maximum possible number of item in a list
Definition n_list.h:74
Structure of a generic LIST container.
Definition n_list.h:58
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:88
#define LOG_DEBUG
debug-level messages
Definition n_log.h:83
#define LOG_ERR
error conditions
Definition n_log.h:75
int set_log_file(char *file)
Set the logging to a file instead of stderr.
Definition n_log.c:167
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition n_log.c:120
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:79
#define LOG_INFO
informational
Definition n_log.h:81
PCRE2_UCHAR8 ** match_list
populated match list (NULL-terminated) after npcre_match_capture() Allocated by PCRE2 via pcre2_subst...
Definition n_pcre.h:104
N_PCRE * npcre_new(char *str, int flags)
From pcre doc, the flag bits are: PCRE_ANCHORED Force pattern anchoring PCRE_AUTO_CALLOUT Compile aut...
Definition n_pcre.c:80
int npcre_match_capture(char *str, N_PCRE *pcre)
Return TRUE if str matches regexp, and make captures.
Definition n_pcre.c:261
int npcre_clean_match(N_PCRE *pcre)
clean the match list of the last capture, if any
Definition n_pcre.c:164
int npcre_delete(N_PCRE **pcre)
Free a N_PCRE pointer.
Definition n_pcre.c:134
N_PCRE structure.
Definition n_pcre.h:93
Common headers and low-level functions & define.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.
Hash functions and table.
List structures and definitions.
__attribute__((unused))
Definition n_network.c:1233
PCRE helpers for regex matching.
N_STR and string function declaration.