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
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 if (!al_init_image_addon()) {
176 n_abort("Unable to initialize image addon\n");
177 }
178 if (!al_init_primitives_addon()) {
179 n_abort("Unable to initialize primitives addon\n");
180 }
181 if (!al_init_font_addon()) {
182 n_abort("Unable to initialize font addon\n");
183 }
184 if (!al_init_ttf_addon()) {
185 n_abort("Unable to initialize ttf_font addon\n");
186 }
187 if (!al_install_keyboard()) {
188 n_abort("Unable to initialize keyboard handler\n");
189 }
190 if (!al_install_mouse()) {
191 n_abort("Unable to initialize mouse handler\n");
192 }
193 ALLEGRO_EVENT_QUEUE* event_queue = NULL;
194
195 event_queue = al_create_event_queue();
196 if (!event_queue) {
197 fprintf(stderr, "failed to create event_queue!\n");
198 al_destroy_display(display);
199 return -1;
200 }
201
202 char ver_str[128] = "";
203 while ((getoptret = getopt(argc, argv, "hvV:L:")) != EOF) {
204 switch (getoptret) {
205 case 'h':
206 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]);
207 exit(TRUE);
208 case 'v':
209 sprintf(ver_str, "%s %s", __DATE__, __TIME__);
210 exit(TRUE);
211 break;
212 case 'V':
213 if (!strncmp("INFO", optarg, 6)) {
215 } else {
216 if (!strncmp("NOTICE", optarg, 7)) {
218 } else {
219 if (!strncmp("ERR", optarg, 5)) {
221 } else {
222 if (!strncmp("DEBUG", optarg, 5)) {
224 } else {
225 n_log(LOG_ERR, "%s is not a valid log level\n", optarg);
226 exit(FALSE);
227 }
228 }
229 }
230 }
231 n_log(LOG_NOTICE, "LOG LEVEL UP TO: %d\n", log_level);
233 break;
234 case 'L':
235 n_log(LOG_NOTICE, "LOG FILE: %s\n", optarg);
236 set_log_file(optarg);
237 break;
238 case '?': {
239 switch (optopt) {
240 case 'V':
241 n_log(LOG_ERR, "\nPlease specify a log level after -V. \nAvailable values: NOLOG,VERBOSE,NOTICE,ERROR,DEBUG\n\n");
242 break;
243 case 'L':
244 n_log(LOG_ERR, "\nPlease specify a log file after -L\n");
245 default:
246 break;
247 }
248 }
249 __attribute__((fallthrough));
250 default:
251 n_log(LOG_ERR, "\n %s -h help -v version -V DEBUGLEVEL (NOLOG,VERBOSE,NOTICE,ERROR,DEBUG) -L logfile\n", argv[0]);
252 exit(FALSE);
253 }
254 }
255
256 double fps = 60.0;
257 double fps_delta_time = 1.0 / fps;
258
259 fps_timer = al_create_timer(fps_delta_time);
260
261 al_set_new_display_flags(ALLEGRO_OPENGL | ALLEGRO_WINDOWED);
262 display = al_create_display(WIDTH, HEIGHT);
263 if (!display) {
264 n_abort("Unable to create display\n");
265 }
266 al_set_window_title(display, argv[0]);
267
268 al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP);
269
270 DONE = 0;
271
272 enum APP_KEYS {
273 KEY_ESC
274 };
275 int key[1] = {false};
276
277 al_register_event_source(event_queue, al_get_display_event_source(display));
278 al_start_timer(fps_timer);
279 al_register_event_source(event_queue, al_get_timer_event_source(fps_timer));
280
281 al_register_event_source(event_queue, al_get_keyboard_event_source());
282 al_register_event_source(event_queue, al_get_mouse_event_source());
283
284 ALLEGRO_FONT* font = NULL;
285 font = al_load_font("DATAS/2Dumb.ttf", 18, 0);
286 if (!font) {
287 n_log(LOG_ERR, "Unable to load DATAS/2Dumb.ttf, using builtin font");
288 font = al_create_builtin_font();
289 }
290
291 int do_draw = 0;
292
293 /* Load dictionaries */
294 dictionary = new_ht_trie(256, 32);
295 FILE* dict_file = fopen("DATAS/dictionary.csv", "r");
296 if (!dict_file) {
297 n_abort("Unable to open DATAS/dictionary.csv\n");
298 }
299 char read_buf[16384] = "";
300 char* entry_key = NULL;
301 char* type = NULL;
302 char* definition = NULL;
303 N_PCRE* dico_regexp = npcre_new("\"(.*)\",\"(.*)\",\"(.*)\"", 0);
304
305 while (fgets(read_buf, 16384, dict_file)) {
306 if (npcre_match_capture(read_buf, dico_regexp)) {
307 entry_key = strdup(_str((char*)dico_regexp->match_list[1]));
308 type = strdup(_str((char*)dico_regexp->match_list[2]));
309 definition = strdup(_str((char*)dico_regexp->match_list[3]));
310
311 // n_log(LOG_DEBUG, "matched %s , %s , %s", entry_key, type, definition);
312
313 DICTIONARY_ENTRY* entry = NULL;
314 DICTIONARY_DEFINITION* entry_def = NULL;
315
316 if (ht_get_ptr(dictionary, entry_key, (void**)&entry) == TRUE) {
317 Malloc(entry_def, DICTIONARY_DEFINITION, 1);
318 entry_def->type = strdup(type);
319 entry_def->definition = strdup(definition);
320 list_push(entry->definitions, entry_def, &free_entry_def);
321 } else {
322 Malloc(entry, DICTIONARY_ENTRY, 1);
323
325 entry->key = strdup(entry_key);
326
327 Malloc(entry_def, DICTIONARY_DEFINITION, 1);
328
329 entry_def->type = strdup(type);
330 entry_def->definition = strdup(definition);
331
332 list_push(entry->definitions, entry_def, &free_entry_def);
333
334 ht_put_ptr(dictionary, entry_key, entry, &free_entry, NULL);
335 }
336 FreeNoLog(entry_key);
337 FreeNoLog(type);
338 FreeNoLog(definition);
339
340 npcre_clean_match(dico_regexp);
341 }
342 }
343 fclose(dict_file);
344 npcre_delete(&dico_regexp);
345
346 n_log(LOG_NOTICE, "Dictionary loaded, starting GUI");
347
348 /* Create GUI */
349 N_GUI_CTX* gui = n_gui_new_ctx(font);
350
351 /* set display size for global scrollbars (shown when GUI exceeds display) */
352 n_gui_set_display_size(gui, (float)WIDTH, (float)HEIGHT);
353 /* enable virtual canvas for resolution-independent scaling */
354 n_gui_set_virtual_size(gui, (float)WIDTH, (float)HEIGHT);
355
356 /* Search Window */
357 int search_win = n_gui_add_window(gui, "Search", 20, 20, 400, 85);
358 n_gui_add_label(gui, search_win, "Type to search:", 10, 5, 280, 20, N_GUI_ALIGN_LEFT);
359 search_textarea_id = n_gui_add_textarea(gui, search_win, 10, 28, 300, 24, 0, 256, on_search_change, gui);
360 n_gui_add_button(gui, search_win, "Clear", 320, 28, 70, 24, N_GUI_SHAPE_ROUNDED, on_clear_click, gui);
362
363 /* Completion Window */
364 int completion_win = n_gui_add_window(gui, "Completions", 440, 20, 350, 690);
367
368 /* Definition Window */
369 definition_win_id = n_gui_add_window(gui, "Definition", 20, 120, 400, 590);
372
373 /* Status Bar Window */
374 status_win_id = n_gui_add_window(gui, "Help", 20, 720, 770, 60);
375 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);
377
378 /* initial completion */
379 strncpy(last_search, "a", sizeof(last_search) - 1);
381
382 al_clear_keyboard_state(NULL);
383 al_flush_event_queue(event_queue);
384
385 do {
386 ALLEGRO_EVENT ev;
387 al_wait_for_event(event_queue, &ev);
388
389 /* pass events to the GUI.
390 * Use gui_handled to prevent mouse events from reaching game logic
391 * when the cursor is over a GUI window or widget. */
392 int gui_handled = n_gui_process_event(gui, ev);
393 (void)gui_handled;
394
395 if (ev.type == ALLEGRO_EVENT_KEY_DOWN) {
396 switch (ev.keyboard.keycode) {
397 case ALLEGRO_KEY_ESCAPE:
398 key[KEY_ESC] = 1;
399 break;
400 default:
401 break;
402 }
403 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
404 DONE = 1;
405 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE) {
406 al_acknowledge_resize(display);
407 n_gui_set_display_size(gui, (float)al_get_display_width(display),
408 (float)al_get_display_height(display));
409 } else if (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN || ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT) {
410 al_clear_keyboard_state(display);
411 al_flush_event_queue(event_queue);
412 }
413
414 if (ev.type == ALLEGRO_EVENT_TIMER) {
415 do_draw = 1;
416 }
417
418 if (do_draw && al_is_event_queue_empty(event_queue)) {
419 al_set_target_bitmap(al_get_backbuffer(display));
420 al_clear_to_color(al_map_rgba(20, 20, 25, 255));
421
422 /* draw all GUI windows and widgets */
424
425 al_flip_display();
426 do_draw = 0;
427 }
428
429 } while (!key[KEY_ESC] && !DONE);
430
432 if (completion)
434
436 al_destroy_event_queue(event_queue);
437 al_destroy_font(font);
438 al_destroy_timer(fps_timer);
439 al_uninstall_system();
440
441 return 0;
442}
int main(void)
ALLEGRO_TIMER * fps_timer
Definition ex_fluid.c:65
int getoptret
Definition ex_fluid.c:59
int DONE
Definition ex_fluid.c:58
int log_level
Definition ex_fluid.c:60
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
char * key
#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:53
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:9034
#define N_GUI_ALIGN_LEFT
left aligned text
Definition n_gui.h:189
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:3263
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:4511
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:2620
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
Definition n_gui.c:9836
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:2966
#define N_GUI_WIN_FIXED_POSITION
disable window dragging (default:enable)
Definition n_gui.h:231
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:3371
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:8831
#define N_GUI_SELECT_SINGLE
single item selection
Definition n_gui.h:175
void n_gui_draw(N_GUI_CTX *ctx)
Draw all visible windows and their widgets.
Definition n_gui.c:8542
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:1063
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
Definition n_gui.c:903
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:3448
void n_gui_listbox_clear(N_GUI_CTX *ctx, int widget_id)
remove all items from a listbox widget
Definition n_gui.c:3418
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
Definition n_gui.c:995
#define N_GUI_SHAPE_ROUNDED
rounded rectangle shape
Definition n_gui.h:149
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:1968
#define N_GUI_ALIGN_JUSTIFIED
justified text (spread words to fill width)
Definition n_gui.h:195
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:2498
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:2124
The top-level GUI context that holds all windows.
Definition n_gui.h:1354
int ht_get_ptr(HASH_TABLE *table, const char *key, void **val)
get pointer at 'key' from 'table'
Definition n_hash.c:2110
int destroy_ht(HASH_TABLE **table)
empty a table and destroy it
Definition n_hash.c:2244
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:2401
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:2164
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:1965
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:89
#define LOG_DEBUG
debug-level messages
Definition n_log.h:84
#define LOG_ERR
error conditions
Definition n_log.h:76
int set_log_file(char *file)
Set the logging to a file instead of stderr.
Definition n_log.c:168
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition n_log.c:121
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:80
#define LOG_INFO
informational
Definition n_log.h:82
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:1286
PCRE helpers for regex matching.
N_STR and string function declaration.