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 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
27
28#define WIDTH 800
29#define HEIGHT 800
30
31#define ALLEGRO_UNSTABLE 1
32
33#include "nilorea/n_common.h"
34#include "nilorea/n_list.h"
35#include "nilorea/n_hash.h"
36#include "nilorea/n_pcre.h"
37#include "nilorea/n_str.h"
38#include "nilorea/n_gui.h"
39#include <allegro5/allegro_ttf.h>
40
41/* dictionnaries are from https://www.bragitoff.com/2016/03/english-dictionary-in-csv-format/ */
42
43ALLEGRO_DISPLAY* display = NULL;
44
45int DONE = 0, /* Flag to check if we are always running */
46 getoptret = 0, /* launch parameter check */
47 log_level = LOG_INFO; /* default LOG_LEVEL */
48
49ALLEGRO_TIMER* fps_timer = NULL;
50ALLEGRO_TIMER* logic_timer = NULL;
51
59
67
68/* application state */
69static HASH_TABLE* dictionary = NULL;
70static LIST* completion = NULL;
71static size_t max_results = 100;
72static char last_search[4096] = "";
73
74/* GUI widget ids */
75static int search_textarea_id = -1;
76static int completion_listbox_id = -1;
77static int definition_label_id = -1;
78static int definition_win_id = -1;
79static int status_win_id = -1;
80static int status_label_id = -1;
81
82/* forward declarations */
83static void update_completion(N_GUI_CTX* gui, const char* text);
84static void update_definitions(N_GUI_CTX* gui, const char* word);
85
86void free_entry_def(void* entry_def) {
88 FreeNoLog(def->type);
90 FreeNoLog(def);
91}
92void free_entry(void* entry_ptr) {
93 DICTIONARY_ENTRY* entry = (DICTIONARY_ENTRY*)entry_ptr;
95 FreeNoLog(entry->key);
96 FreeNoLog(entry);
97}
98
99/* callback: search text changed */
100void on_search_change(int widget_id, const char* text, void* user_data) {
101 (void)widget_id;
102 N_GUI_CTX* gui = (N_GUI_CTX*)user_data;
103 update_completion(gui, text);
104 update_definitions(gui, text);
105}
106
107/* callback: completion list selection */
108void on_completion_select(int widget_id, int index, int selected, void* user_data) {
109 (void)widget_id;
110 if (!selected) return;
111 N_GUI_CTX* gui = (N_GUI_CTX*)user_data;
112
113 const char* item_text = n_gui_listbox_get_item_text(gui, completion_listbox_id, index);
114 if (item_text) {
116 update_completion(gui, item_text);
117 update_definitions(gui, item_text);
118 }
119}
120
121/* callback: clear button */
122void on_clear_click(int widget_id, void* user_data) {
123 (void)widget_id;
124 N_GUI_CTX* gui = (N_GUI_CTX*)user_data;
128}
129
130static void update_completion(N_GUI_CTX* gui, const char* text) {
131 if (strcmp(text, last_search) == 0) return;
132 strncpy(last_search, text, sizeof(last_search) - 1);
133 last_search[sizeof(last_search) - 1] = '\0';
134
135 if (completion) {
137 }
139
141 if (completion) {
142 list_foreach(node, completion) {
143 char* entry_key = (char*)node->ptr;
145 }
146 }
147}
148
149static void update_definitions(N_GUI_CTX* gui, const char* word) {
150 DICTIONARY_ENTRY* entry = NULL;
151 if (ht_get_ptr(dictionary, word, (void**)&entry) == TRUE) {
152 char buf[16384] = "";
153 size_t offset = 0;
154 list_foreach(node, entry->definitions) {
156 int written = snprintf(buf + offset, sizeof(buf) - offset, "%s : %s\n", def->type, def->definition);
157 if (written > 0) offset += (size_t)written;
158 if (offset >= sizeof(buf) - 1) break;
159 }
161 } else {
163 }
164}
165
166int main(int argc, char* argv[]) {
168
169 n_log(LOG_NOTICE, "%s is starting ...", argv[0]);
170
171 /* allegro 5 + addons loading */
172 if (!al_init()) {
173 n_abort("Could not init Allegro.\n");
174 }
175 /* The data is in DATAS/ beside the executable: found from wherever the
176 * example is started, not only from examples/. Started elsewhere it used
177 * to fail on its data with a window already open, and exiting that way
178 * crashed on some OpenGL drivers. */
179 {
180 ALLEGRO_PATH* here = al_get_standard_path(ALLEGRO_RESOURCES_PATH);
181
182 if (here) {
183 al_change_directory(al_path_cstr(here, ALLEGRO_NATIVE_PATH_SEP));
184 al_destroy_path(here);
185 }
186 }
187 if (!al_init_image_addon()) {
188 n_abort("Unable to initialize image addon\n");
189 }
190 if (!al_init_primitives_addon()) {
191 n_abort("Unable to initialize primitives addon\n");
192 }
193 if (!al_init_font_addon()) {
194 n_abort("Unable to initialize font addon\n");
195 }
196 if (!al_init_ttf_addon()) {
197 n_abort("Unable to initialize ttf_font addon\n");
198 }
199 if (!al_install_keyboard()) {
200 n_abort("Unable to initialize keyboard handler\n");
201 }
202 if (!al_install_mouse()) {
203 n_abort("Unable to initialize mouse handler\n");
204 }
205 ALLEGRO_EVENT_QUEUE* event_queue = NULL;
206
207 event_queue = al_create_event_queue();
208 if (!event_queue) {
209 fprintf(stderr, "failed to create event_queue!\n");
210 al_destroy_display(display);
211 return -1;
212 }
213
214 char ver_str[128] = "";
215 while ((getoptret = getopt(argc, argv, "hvV:L:")) != EOF) {
216 switch (getoptret) {
217 case 'h':
218 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]);
219 exit(TRUE);
220 case 'v':
221 sprintf(ver_str, "%s %s", __DATE__, __TIME__);
222 exit(TRUE);
223 break;
224 case 'V':
225 if (!strncmp("INFO", optarg, 6)) {
227 } else {
228 if (!strncmp("NOTICE", optarg, 7)) {
230 } else {
231 if (!strncmp("ERR", optarg, 5)) {
233 } else {
234 if (!strncmp("DEBUG", optarg, 5)) {
236 } else {
237 n_log(LOG_ERR, "%s is not a valid log level\n", optarg);
238 exit(FALSE);
239 }
240 }
241 }
242 }
243 n_log(LOG_NOTICE, "LOG LEVEL UP TO: %d\n", log_level);
245 break;
246 case 'L':
247 n_log(LOG_NOTICE, "LOG FILE: %s\n", optarg);
248 set_log_file(optarg);
249 break;
250 case '?': {
251 switch (optopt) {
252 case 'V':
253 n_log(LOG_ERR, "\nPlease specify a log level after -V. \nAvailable values: NOLOG,VERBOSE,NOTICE,ERROR,DEBUG\n\n");
254 break;
255 case 'L':
256 n_log(LOG_ERR, "\nPlease specify a log file after -L\n");
257 default:
258 break;
259 }
260 }
261 __attribute__((fallthrough));
262 default:
263 n_log(LOG_ERR, "\n %s -h help -v version -V DEBUGLEVEL (NOLOG,VERBOSE,NOTICE,ERROR,DEBUG) -L logfile\n", argv[0]);
264 exit(FALSE);
265 }
266 }
267
268 double fps = 60.0;
269 double fps_delta_time = 1.0 / fps;
270
271 fps_timer = al_create_timer(fps_delta_time);
272
273 /* Before any window exists: nothing to release if it is missing. */
274 FILE* dict_file = fopen("DATAS/dictionary.csv", "r");
275 if (!dict_file) {
276 n_log(LOG_ERR, "Unable to open DATAS/dictionary.csv beside the executable");
277 al_destroy_timer(fps_timer);
278 al_destroy_event_queue(event_queue);
279 return 1;
280 }
281
282 al_set_new_display_flags(ALLEGRO_OPENGL | ALLEGRO_WINDOWED);
283 display = al_create_display(WIDTH, HEIGHT);
284 if (!display) {
285 n_abort("Unable to create display\n");
286 }
287 al_set_window_title(display, argv[0]);
288
289 al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP);
290
291 DONE = 0;
292
293 enum APP_KEYS {
294 KEY_ESC
295 };
296 int key[1] = {false};
297
298 al_register_event_source(event_queue, al_get_display_event_source(display));
299 al_start_timer(fps_timer);
300 al_register_event_source(event_queue, al_get_timer_event_source(fps_timer));
301
302 al_register_event_source(event_queue, al_get_keyboard_event_source());
303 al_register_event_source(event_queue, al_get_mouse_event_source());
304
305 ALLEGRO_FONT* font = NULL;
306 font = al_load_font("DATAS/2Dumb.ttf", 18, 0);
307 if (!font) {
308 n_log(LOG_ERR, "Unable to load DATAS/2Dumb.ttf, using builtin font");
309 font = al_create_builtin_font();
310 }
311
312 int do_draw = 0;
313
314 /* Load dictionaries */
315 dictionary = new_ht_trie(256, 32);
316 char read_buf[16384] = "";
317 char* entry_key = NULL;
318 char* type = NULL;
319 char* definition = NULL;
320 N_PCRE* dico_regexp = npcre_new("\"(.*)\",\"(.*)\",\"(.*)\"", 0);
321
322 while (fgets(read_buf, 16384, dict_file)) {
323 if (npcre_match_capture(read_buf, dico_regexp)) {
324 entry_key = strdup(_str((char*)dico_regexp->match_list[1]));
325 type = strdup(_str((char*)dico_regexp->match_list[2]));
326 definition = strdup(_str((char*)dico_regexp->match_list[3]));
327
328 // n_log(LOG_DEBUG, "matched %s , %s , %s", entry_key, type, definition);
329
330 DICTIONARY_ENTRY* entry = NULL;
331 DICTIONARY_DEFINITION* entry_def = NULL;
332
333 if (ht_get_ptr(dictionary, entry_key, (void**)&entry) == TRUE) {
334 Malloc(entry_def, DICTIONARY_DEFINITION, 1);
335 entry_def->type = strdup(type);
336 entry_def->definition = strdup(definition);
337 list_push(entry->definitions, entry_def, &free_entry_def);
338 } else {
339 Malloc(entry, DICTIONARY_ENTRY, 1);
340
342 entry->key = strdup(entry_key);
343
344 Malloc(entry_def, DICTIONARY_DEFINITION, 1);
345
346 entry_def->type = strdup(type);
347 entry_def->definition = strdup(definition);
348
349 list_push(entry->definitions, entry_def, &free_entry_def);
350
351 ht_put_ptr(dictionary, entry_key, entry, &free_entry, NULL);
352 }
353 FreeNoLog(entry_key);
355 FreeNoLog(definition);
356
357 npcre_clean_match(dico_regexp);
358 }
359 }
360 fclose(dict_file);
361 npcre_delete(&dico_regexp);
362
363 n_log(LOG_NOTICE, "Dictionary loaded, starting GUI");
364
365 /* Create GUI */
367
368 /* set display size for global scrollbars (shown when GUI exceeds display) */
369 n_gui_set_display_size(gui, (float)WIDTH, (float)HEIGHT);
370 /* enable virtual canvas for resolution-independent scaling */
371 n_gui_set_virtual_size(gui, (float)WIDTH, (float)HEIGHT);
372
373 /* Search Window */
374 int search_win = n_gui_add_window(gui, "Search", 20, 20, 400, 85);
375 n_gui_add_label(gui, search_win, "Type to search:", 10, 5, 280, 20, N_GUI_ALIGN_LEFT);
376 search_textarea_id = n_gui_add_textarea(gui, search_win, 10, 28, 300, 24, 0, 256, on_search_change, gui);
377 n_gui_add_button(gui, search_win, "Clear", 320, 28, 70, 24, N_GUI_SHAPE_ROUNDED, on_clear_click, gui);
379
380 /* Completion Window */
381 int completion_win = n_gui_add_window(gui, "Completions", 440, 20, 350, 690);
384
385 /* Definition Window */
386 definition_win_id = n_gui_add_window(gui, "Definition", 20, 120, 400, 590);
389
390 /* Status Bar Window */
391 status_win_id = n_gui_add_window(gui, "Help", 20, 720, 770, 60);
392 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);
394
395 /* initial completion */
396 strncpy(last_search, "a", sizeof(last_search) - 1);
398
399 al_clear_keyboard_state(NULL);
400 al_flush_event_queue(event_queue);
401
402 do {
403 ALLEGRO_EVENT ev;
404 al_wait_for_event(event_queue, &ev);
405
406 /* pass events to the GUI.
407 * Use gui_handled to prevent mouse events from reaching game logic
408 * when the cursor is over a GUI window or widget. */
409 int gui_handled = n_gui_process_event(gui, ev);
410 (void)gui_handled;
411
412 if (ev.type == ALLEGRO_EVENT_KEY_DOWN) {
413 switch (ev.keyboard.keycode) {
414 case ALLEGRO_KEY_ESCAPE:
415 key[KEY_ESC] = 1;
416 break;
417 default:
418 break;
419 }
420 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
421 DONE = 1;
422 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE) {
423 al_acknowledge_resize(display);
424 n_gui_set_display_size(gui, (float)al_get_display_width(display),
425 (float)al_get_display_height(display));
426 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN || ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT) {
427 al_clear_keyboard_state(display);
428 al_flush_event_queue(event_queue);
429 }
430
431 if (ev.type == ALLEGRO_EVENT_TIMER) {
432 do_draw = 1;
433 }
434
435 if (do_draw && al_is_event_queue_empty(event_queue)) {
436 al_set_target_bitmap(al_get_backbuffer(display));
437 al_clear_to_color(al_map_rgba(20, 20, 25, 255));
438
439 /* draw all GUI windows and widgets */
441
442 al_flip_display();
443 do_draw = 0;
444 }
445
446 } while (!key[KEY_ESC] && !DONE);
447
449 if (completion)
451
453 al_destroy_event_queue(event_queue);
454 al_destroy_font(font);
455 al_destroy_timer(fps_timer);
456 al_uninstall_system();
457
458 return 0;
459}
int main(void)
Definition ex_atlas.c:117
static int log_level
ALLEGRO_TIMER * fps_timer
Definition ex_fluid.c:65
int getoptret
Definition ex_fluid.c:59
int DONE
Definition ex_fluid.c:58
ALLEGRO_TIMER * logic_timer
Definition ex_fluid.c:66
ALLEGRO_DISPLAY * display
Definition ex_fluid.c:54
#define WIDTH
Definition ex_gui.c:42
#define HEIGHT
Definition ex_gui.c:43
static N_GUI_CTX * gui
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 void type(N_GUI_CTX *gui, const char *s)
Type a whole string into whatever has focus.
volatile char key[227]
TRUE while the key is held.
FONT * font
the default 8x8 font, valid after allegro_init()
#define KEY_ESC
key code
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:272
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
#define _str(__PTR)
define true
Definition n_common.h:193
void n_abort(char const *format,...)
abort program with a text
Definition n_common.c:57
void n_gui_set_display_size(N_GUI_CTX *ctx, float w, float h)
set the display (viewport) size so global scrollbars can be computed.
Definition n_gui.c:12348
#define N_GUI_ALIGN_LEFT
left aligned text
Definition n_gui.h:212
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:4210
void n_gui_label_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set label text content
Definition n_gui.c:6184
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:3285
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
Definition n_gui.c:13611
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:3639
#define N_GUI_WIN_FIXED_POSITION
disable window dragging (default:enable)
Definition n_gui.h:274
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:4359
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:12027
#define N_GUI_SELECT_SINGLE
single item selection
Definition n_gui.h:198
void n_gui_draw(N_GUI_CTX *ctx)
draw all visible GUI windows and widgets
Definition n_gui.c:11735
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:1277
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
Definition n_gui.c:1103
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:4438
void n_gui_listbox_clear(N_GUI_CTX *ctx, int widget_id)
remove all items from a listbox widget
Definition n_gui.c:4406
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
Definition n_gui.c:1209
#define N_GUI_SHAPE_ROUNDED
rounded rectangle shape
Definition n_gui.h:172
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:2515
#define N_GUI_ALIGN_JUSTIFIED
justified text (spread words to fill width)
Definition n_gui.h:218
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:3162
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:2720
The top-level GUI context that holds all windows.
Definition n_gui.h:1669
int ht_get_ptr(HASH_TABLE *table, const char *key, void **val)
get pointer at 'key' from 'table'
Definition n_hash.c:2147
int destroy_ht(HASH_TABLE **table)
empty a table and destroy it
Definition n_hash.c:2281
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:2438
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:2201
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:2002
structure of a hash table
Definition n_hash.h:138
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
Definition n_list.c:228
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper, safe for node removal during iteration.
Definition n_list.h:89
int list_destroy(LIST **list)
Empty and Free a list container.
Definition n_list.c:548
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
Definition n_list.c:37
#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:75
Structure of a generic LIST container.
Definition n_list.h:59
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_DEBUG
debug-level messages
Definition n_log.h:108
#define LOG_ERR
error conditions
Definition n_log.h:100
int set_log_file(char *file)
Set the logging to a file instead of stderr.
Definition n_log.c:170
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
Definition n_log.c:123
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:104
#define LOG_INFO
informational
Definition n_log.h:106
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:82
int npcre_match_capture(char *str, N_PCRE *pcre)
Return TRUE if str matches regexp, and make captures.
Definition n_pcre.c:323
int npcre_clean_match(N_PCRE *pcre)
clean the match list of the last capture, if any
Definition n_pcre.c:166
int npcre_delete(N_PCRE **pcre)
Free a N_PCRE pointer.
Definition n_pcre.c:136
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:1288
PCRE helpers for regex matching.
N_STR and string function declaration.